1package azblob
2
3import (
4	"context"
5	"encoding/base64"
6	"encoding/binary"
7	"io/ioutil"
8	"time"
9
10	"crypto/md5"
11
12	"bytes"
13	"strings"
14
15	chk "gopkg.in/check.v1" // go get gopkg.in/check.v1
16)
17
18func (s *aztestsSuite) TestGetBlobPropertiesUsingVID(c *chk.C) {
19	bsu := getBSU()
20	containerURL, _ := createNewContainer(c, bsu)
21	defer deleteContainer(c, containerURL)
22	blobURL, _ := createNewAppendBlob(c, containerURL)
23
24	blobProp, _ := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
25	createResp, err := blobURL.Create(ctx, BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfMatch: blobProp.ETag()}}, nil, ClientProvidedKeyOptions{})
26	c.Assert(err, chk.IsNil)
27	c.Assert(createResp.VersionID(), chk.NotNil)
28	blobProp, _ = blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
29	c.Assert(createResp.VersionID(), chk.Equals, blobProp.VersionID())
30	c.Assert(createResp.LastModified(), chk.DeepEquals, blobProp.LastModified())
31	c.Assert(createResp.ETag(), chk.Equals, blobProp.ETag())
32	c.Assert(blobProp.IsCurrentVersion(), chk.Equals, "true")
33}
34
35func (s *aztestsSuite) TestSetBlobMetadataReturnsVID(c *chk.C) {
36	bsu := getBSU()
37	containerURL, _ := createNewContainer(c, bsu)
38	defer deleteContainer(c, containerURL)
39	blobURL, blobName := createNewBlockBlob(c, containerURL)
40	metadata := Metadata{"test_key_1": "test_value_1", "test_key_2": "2019"}
41	resp, err := blobURL.SetMetadata(ctx, metadata, BlobAccessConditions{}, ClientProvidedKeyOptions{})
42	c.Assert(err, chk.IsNil)
43	c.Assert(resp.VersionID(), chk.NotNil)
44
45	listBlobResp, err := containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{Details: BlobListingDetails{Metadata: true}})
46
47	c.Assert(err, chk.IsNil)
48	c.Assert(listBlobResp.Segment.BlobItems[0].Name, chk.Equals, blobName)
49	c.Assert(listBlobResp.Segment.BlobItems[0].Metadata, chk.HasLen, 2)
50	c.Assert(listBlobResp.Segment.BlobItems[0].Metadata, chk.DeepEquals, metadata)
51}
52
53func (s *aztestsSuite) TestCreateAndDownloadBlobSpecialCharactersWithVID(c *chk.C) {
54	bsu := getBSU()
55	containerURL, _ := createNewContainer(c, bsu)
56	defer deleteContainer(c, containerURL)
57	data := []rune("-._/()$=',~0123456789")
58	for i := 0; i < len(data); i++ {
59		blobName := "abc" + string(data[i])
60		blobURL := containerURL.NewBlockBlobURL(blobName)
61		resp, err := blobURL.Upload(ctx, strings.NewReader(string(data[i])), BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
62		c.Assert(err, chk.IsNil)
63		c.Assert(resp.VersionID(), chk.NotNil)
64
65		dResp, err := blobURL.WithVersionID(resp.VersionID()).Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
66		c.Assert(err, chk.IsNil)
67		d1, err := ioutil.ReadAll(dResp.Body(RetryReaderOptions{}))
68		c.Assert(dResp.Version(), chk.Not(chk.Equals), "")
69		c.Assert(string(d1), chk.DeepEquals, string(data[i]))
70		versionId := dResp.r.rawResponse.Header.Get("x-ms-version-id")
71		c.Assert(versionId, chk.NotNil)
72		c.Assert(versionId, chk.Equals, resp.VersionID())
73	}
74}
75
76func (s *aztestsSuite) TestDeleteSpecificBlobVersion(c *chk.C) {
77	bsu := getBSU()
78	containerURL, _ := createNewContainer(c, bsu)
79	defer deleteContainer(c, containerURL)
80	blobURL, _ := getBlockBlobURL(c, containerURL)
81
82	blockBlobUploadResp, err := blobURL.Upload(ctx, bytes.NewReader([]byte("data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
83	c.Assert(err, chk.IsNil)
84	c.Assert(blockBlobUploadResp.VersionID(), chk.NotNil)
85	versionID1 := blockBlobUploadResp.VersionID()
86
87	blockBlobUploadResp, err = blobURL.Upload(ctx, bytes.NewReader([]byte("updated_data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
88	c.Assert(err, chk.IsNil)
89	c.Assert(blockBlobUploadResp.VersionID(), chk.NotNil)
90
91	listBlobsResp, err := containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{Details: BlobListingDetails{Versions: true}})
92	c.Assert(err, chk.IsNil)
93	c.Assert(listBlobsResp.Segment.BlobItems, chk.HasLen, 2)
94
95	// Deleting previous version snapshot.
96	deleteResp, err := blobURL.WithVersionID(versionID1).Delete(ctx, DeleteSnapshotsOptionNone, BlobAccessConditions{})
97	c.Assert(err, chk.IsNil)
98	c.Assert(deleteResp.StatusCode(), chk.Equals, 202)
99
100	listBlobsResp, err = containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{Details: BlobListingDetails{Versions: true}})
101	c.Assert(err, chk.IsNil)
102	c.Assert(listBlobsResp.Segment.BlobItems, chk.NotNil)
103	if len(listBlobsResp.Segment.BlobItems) != 1 {
104		c.Fail()
105	}
106}
107
108func (s *aztestsSuite) TestDeleteSpecificBlobVersionWithBlobSAS(c *chk.C) {
109	bsu := getBSU()
110	credential, err := getGenericCredential("")
111	if err != nil {
112		c.Fatal(err)
113	}
114	containerURL, containerName := createNewContainer(c, bsu)
115	defer deleteContainer(c, containerURL)
116	blobURL, blobName := getBlockBlobURL(c, containerURL)
117
118	resp, err := blobURL.Upload(ctx, bytes.NewReader([]byte("data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
119	c.Assert(err, chk.IsNil)
120	versionId := resp.VersionID()
121	c.Assert(versionId, chk.NotNil)
122
123	resp, err = blobURL.Upload(ctx, bytes.NewReader([]byte("updated_data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
124	c.Assert(err, chk.IsNil)
125	c.Assert(resp.VersionID(), chk.NotNil)
126
127	blobParts := NewBlobURLParts(blobURL.URL())
128	blobParts.VersionID = versionId
129	blobParts.SAS, err = BlobSASSignatureValues{
130		Protocol:      SASProtocolHTTPS,
131		ExpiryTime:    time.Now().UTC().Add(1 * time.Hour),
132		ContainerName: containerName,
133		BlobName:      blobName,
134		Permissions:   BlobSASPermissions{Delete: true, DeletePreviousVersion: true}.String(),
135	}.NewSASQueryParameters(credential)
136	if err != nil {
137		c.Fatal(err)
138	}
139
140	sbURL := NewBlockBlobURL(blobParts.URL(), containerURL.client.p)
141	deleteResp, err := sbURL.Delete(ctx, DeleteSnapshotsOptionNone, BlobAccessConditions{})
142	c.Assert(deleteResp, chk.IsNil)
143
144	listBlobResp, err := containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{Details: BlobListingDetails{Versions: true}})
145	c.Assert(err, chk.IsNil)
146	for _, blob := range listBlobResp.Segment.BlobItems {
147		c.Assert(blob.VersionID, chk.Not(chk.Equals), versionId)
148	}
149}
150
151func (s *aztestsSuite) TestDownloadSpecificBlobVersion(c *chk.C) {
152	bsu := getBSU()
153	containerURL, _ := createNewContainer(c, bsu)
154	defer deleteContainer(c, containerURL)
155	blobURL, _ := getBlockBlobURL(c, containerURL)
156
157	blockBlobUploadResp, err := blobURL.Upload(ctx, bytes.NewReader([]byte("data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
158	c.Assert(err, chk.IsNil)
159	c.Assert(blockBlobUploadResp, chk.NotNil)
160	versionId1 := blockBlobUploadResp.VersionID()
161
162	blockBlobUploadResp, err = blobURL.Upload(ctx, bytes.NewReader([]byte("updated_data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
163	c.Assert(err, chk.IsNil)
164	c.Assert(blockBlobUploadResp, chk.NotNil)
165	versionId2 := blockBlobUploadResp.VersionID()
166	c.Assert(blockBlobUploadResp.VersionID(), chk.NotNil)
167
168	// Download previous version of snapshot.
169	blobURL = blobURL.WithVersionID(versionId1)
170	blockBlobDeleteResp, err := blobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
171	c.Assert(err, chk.IsNil)
172	data, err := ioutil.ReadAll(blockBlobDeleteResp.Response().Body)
173	c.Assert(string(data), chk.Equals, "data")
174
175	// Download current version of snapshot.
176	blobURL = blobURL.WithVersionID(versionId2)
177	blockBlobDeleteResp, err = blobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
178	c.Assert(err, chk.IsNil)
179	data, err = ioutil.ReadAll(blockBlobDeleteResp.Response().Body)
180	c.Assert(string(data), chk.Equals, "updated_data")
181}
182
183func (s *aztestsSuite) TestCreateBlobSnapshotReturnsVID(c *chk.C) {
184	bsu := getBSU()
185	containerURL, _ := createNewContainer(c, bsu)
186	defer delContainer(c, containerURL)
187	blobURL := containerURL.NewBlockBlobURL(generateBlobName())
188	uploadResp, err := blobURL.Upload(ctx, bytes.NewReader([]byte("updated_data")), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
189	c.Assert(err, chk.IsNil)
190	c.Assert(uploadResp.VersionID(), chk.NotNil)
191
192	csResp, err := blobURL.CreateSnapshot(ctx, Metadata{}, BlobAccessConditions{}, ClientProvidedKeyOptions{})
193	c.Assert(err, chk.IsNil)
194	c.Assert(csResp.VersionID(), chk.NotNil)
195	lbResp, err := containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{
196		Details: BlobListingDetails{Versions: true, Snapshots: true},
197	})
198	c.Assert(lbResp, chk.NotNil)
199	if len(lbResp.Segment.BlobItems) < 2 {
200		c.Fail()
201	}
202
203	_, err = blobURL.Delete(ctx, DeleteSnapshotsOptionInclude, BlobAccessConditions{})
204	lbResp, err = containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{
205		Details: BlobListingDetails{Versions: true, Snapshots: true},
206	})
207	c.Assert(lbResp, chk.NotNil)
208	if len(lbResp.Segment.BlobItems) < 2 {
209		c.Fail()
210	}
211	for _, blob := range lbResp.Segment.BlobItems {
212		c.Assert(blob.Snapshot, chk.Equals, "")
213	}
214}
215
216func (s *aztestsSuite) TestCopyBlobFromURLWithSASReturnsVID(c *chk.C) {
217	bsu := getBSU()
218	credential, err := getGenericCredential("")
219	if err != nil {
220		c.Fatal("Invalid credential")
221	}
222	container, _ := createNewContainer(c, bsu)
223	defer delContainer(c, container)
224
225	testSize := 4 * 1024 * 1024 // 4MB
226	r, sourceData := getRandomDataAndReader(testSize)
227	sourceDataMD5Value := md5.Sum(sourceData)
228	ctx := context.Background()
229	srcBlob := container.NewBlockBlobURL(generateBlobName())
230	destBlob := container.NewBlockBlobURL(generateBlobName())
231
232	uploadSrcResp, err := srcBlob.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
233	c.Assert(err, chk.IsNil)
234	c.Assert(uploadSrcResp.Response().StatusCode, chk.Equals, 201)
235	c.Assert(uploadSrcResp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
236
237	srcBlobParts := NewBlobURLParts(srcBlob.URL())
238
239	srcBlobParts.SAS, err = BlobSASSignatureValues{
240		Protocol:      SASProtocolHTTPS,                     // Users MUST use HTTPS (not HTTP)
241		ExpiryTime:    time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
242		ContainerName: srcBlobParts.ContainerName,
243		BlobName:      srcBlobParts.BlobName,
244		Permissions:   BlobSASPermissions{Read: true}.String(),
245	}.NewSASQueryParameters(credential)
246	if err != nil {
247		c.Fatal(err)
248	}
249
250	srcBlobURLWithSAS := srcBlobParts.URL()
251
252	resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], DefaultAccessTier, nil)
253	c.Assert(err, chk.IsNil)
254	c.Assert(resp.Response().StatusCode, chk.Equals, 202)
255	c.Assert(resp.Version(), chk.Not(chk.Equals), "")
256	c.Assert(resp.CopyID(), chk.Not(chk.Equals), "")
257	c.Assert(string(resp.CopyStatus()), chk.DeepEquals, "success")
258	c.Assert(resp.VersionID(), chk.NotNil)
259
260	downloadResp, err := destBlob.BlobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
261	c.Assert(err, chk.IsNil)
262	destData, err := ioutil.ReadAll(downloadResp.Body(RetryReaderOptions{}))
263	c.Assert(err, chk.IsNil)
264	c.Assert(destData, chk.DeepEquals, sourceData)
265	c.Assert(downloadResp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
266	c.Assert(len(downloadResp.NewMetadata()), chk.Equals, 1)
267	_, badMD5 := getRandomDataAndReader(16)
268	_, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, badMD5, DefaultAccessTier, nil)
269	c.Assert(err, chk.NotNil)
270
271	resp, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, nil, DefaultAccessTier, nil)
272	c.Assert(err, chk.IsNil)
273	c.Assert(resp.Response().StatusCode, chk.Equals, 202)
274	c.Assert(resp.XMsContentCrc64(), chk.Not(chk.Equals), "")
275	c.Assert(resp.Response().Header.Get("x-ms-version"), chk.Equals, ServiceVersion)
276	c.Assert(resp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
277}
278
279func (s *aztestsSuite) TestCreateBlockBlobReturnsVID(c *chk.C) {
280	bsu := getBSU()
281	containerURL, _ := createNewContainer(c, bsu)
282	defer delContainer(c, containerURL)
283
284	testSize := 2 * 1024 * 1024 // 1MB
285	r, _ := getRandomDataAndReader(testSize)
286	ctx := context.Background() // Use default Background context
287	blobURL := containerURL.NewBlockBlobURL(generateBlobName())
288
289	// Prepare source blob for copy.
290	uploadResp, err := blobURL.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
291	c.Assert(err, chk.IsNil)
292	c.Assert(uploadResp.Response().StatusCode, chk.Equals, 201)
293	c.Assert(uploadResp.rawResponse.Header.Get("x-ms-version"), chk.Equals, ServiceVersion)
294	c.Assert(uploadResp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
295
296	csResp, err := blobURL.CreateSnapshot(ctx, Metadata{}, BlobAccessConditions{}, ClientProvidedKeyOptions{})
297	c.Assert(err, chk.IsNil)
298	c.Assert(csResp.Response().StatusCode, chk.Equals, 201)
299	c.Assert(csResp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
300
301	listBlobResp, err := containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{Details: BlobListingDetails{Snapshots: true}})
302	c.Assert(err, chk.IsNil)
303	c.Assert(listBlobResp.rawResponse.Header.Get("x-ms-request-id"), chk.NotNil)
304	if len(listBlobResp.Segment.BlobItems) < 2 {
305		c.Fail()
306	}
307
308	deleteResp, err := blobURL.Delete(ctx, DeleteSnapshotsOptionOnly, BlobAccessConditions{})
309	c.Assert(err, chk.IsNil)
310	c.Assert(deleteResp.Response().StatusCode, chk.Equals, 202)
311	c.Assert(deleteResp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
312
313	listBlobResp, err = containerURL.ListBlobsFlatSegment(ctx, Marker{}, ListBlobsSegmentOptions{Details: BlobListingDetails{Snapshots: true, Versions: true}})
314	c.Assert(err, chk.IsNil)
315	c.Assert(listBlobResp.rawResponse.Header.Get("x-ms-request-id"), chk.NotNil)
316	if len(listBlobResp.Segment.BlobItems) == 0 {
317		c.Fail()
318	}
319	blobs := listBlobResp.Segment.BlobItems
320	c.Assert(blobs[0].Snapshot, chk.Equals, "")
321}
322
323func (s *aztestsSuite) TestPutBlockListReturnsVID(c *chk.C) {
324	blockIDIntToBase64 := func(blockID int) string {
325		binaryBlockID := (&[4]byte{})[:]
326		binary.LittleEndian.PutUint32(binaryBlockID, uint32(blockID))
327		return base64.StdEncoding.EncodeToString(binaryBlockID)
328	}
329	bsu := getBSU()
330	containerURL, _ := createNewContainer(c, bsu)
331	defer delContainer(c, containerURL)
332
333	blobURL := containerURL.NewBlockBlobURL(generateBlobName())
334
335	data := []string{"Azure ", "Storage ", "Block ", "Blob."}
336	base64BlockIDs := make([]string, len(data))
337
338	for index, d := range data {
339		base64BlockIDs[index] = blockIDIntToBase64(index)
340		resp, err := blobURL.StageBlock(ctx, base64BlockIDs[index], strings.NewReader(d), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
341		if err != nil {
342			c.Fail()
343		}
344		c.Assert(resp.Response().StatusCode, chk.Equals, 201)
345		c.Assert(resp.Version(), chk.Not(chk.Equals), "")
346	}
347
348	commitResp, err := blobURL.CommitBlockList(ctx, base64BlockIDs, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
349	c.Assert(err, chk.IsNil)
350	c.Assert(commitResp.VersionID(), chk.NotNil)
351
352	contentResp, err := blobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
353	c.Assert(err, chk.IsNil)
354	contentData, err := ioutil.ReadAll(contentResp.Body(RetryReaderOptions{}))
355	c.Assert(contentData, chk.DeepEquals, []uint8(strings.Join(data, "")))
356}
357
358func (s *aztestsSuite) TestSyncCopyBlobReturnsVID(c *chk.C) {
359
360}
361
362func (s *aztestsSuite) TestCreatePageBlobReturnsVID(c *chk.C) {
363	bsu := getBSU()
364	container, _ := createNewContainer(c, bsu)
365	defer delContainer(c, container)
366
367	blob, _ := createNewPageBlob(c, container)
368	putResp, err := blob.UploadPages(context.Background(), 0, getReaderToRandomBytes(1024), PageBlobAccessConditions{}, nil, ClientProvidedKeyOptions{})
369	c.Assert(err, chk.IsNil)
370	c.Assert(putResp.Response().StatusCode, chk.Equals, 201)
371	c.Assert(putResp.LastModified().IsZero(), chk.Equals, false)
372	c.Assert(putResp.ETag(), chk.Not(chk.Equals), ETagNone)
373	c.Assert(putResp.Version(), chk.Not(chk.Equals), "")
374	c.Assert(putResp.rawResponse.Header.Get("x-ms-version-id"), chk.NotNil)
375
376	gpResp, err := blob.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
377	c.Assert(err, chk.IsNil)
378	c.Assert(gpResp, chk.NotNil)
379}
380