1package azblob
2
3import (
4	"context"
5	"encoding/base64"
6	"encoding/binary"
7	"fmt"
8	"io/ioutil"
9	"math"
10	"time"
11
12	"crypto/md5"
13
14	"bytes"
15	"strings"
16
17	guuid "github.com/google/uuid"
18	chk "gopkg.in/check.v1" // go get gopkg.in/check.v1
19)
20
21func (s *aztestsSuite) TestStageGetBlocks(c *chk.C) {
22	bsu := getBSU()
23	container, _ := createNewContainer(c, bsu)
24	defer delContainer(c, container)
25
26	blob := container.NewBlockBlobURL(generateBlobName())
27
28	blockID := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0)))
29
30	putResp, err := blob.StageBlock(context.Background(), blockID, getReaderToRandomBytes(1024), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
31	c.Assert(err, chk.IsNil)
32	c.Assert(putResp.Response().StatusCode, chk.Equals, 201)
33	c.Assert(putResp.ContentMD5(), chk.Not(chk.Equals), "")
34	c.Assert(putResp.RequestID(), chk.Not(chk.Equals), "")
35	c.Assert(putResp.Version(), chk.Not(chk.Equals), "")
36	c.Assert(putResp.Date().IsZero(), chk.Equals, false)
37
38	blockList, err := blob.GetBlockList(context.Background(), BlockListAll, LeaseAccessConditions{})
39	c.Assert(err, chk.IsNil)
40	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
41	c.Assert(blockList.LastModified().IsZero(), chk.Equals, true)
42	c.Assert(blockList.ETag(), chk.Equals, ETagNone)
43	c.Assert(blockList.ContentType(), chk.Not(chk.Equals), "")
44	c.Assert(blockList.BlobContentLength(), chk.Equals, int64(-1))
45	c.Assert(blockList.RequestID(), chk.Not(chk.Equals), "")
46	c.Assert(blockList.Version(), chk.Not(chk.Equals), "")
47	c.Assert(blockList.Date().IsZero(), chk.Equals, false)
48	c.Assert(blockList.CommittedBlocks, chk.HasLen, 0)
49	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 1)
50
51	listResp, err := blob.CommitBlockList(context.Background(), []string{blockID}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
52	c.Assert(err, chk.IsNil)
53	c.Assert(listResp.Response().StatusCode, chk.Equals, 201)
54	c.Assert(listResp.LastModified().IsZero(), chk.Equals, false)
55	c.Assert(listResp.ETag(), chk.Not(chk.Equals), ETagNone)
56	c.Assert(listResp.ContentMD5(), chk.Not(chk.Equals), "")
57	c.Assert(listResp.RequestID(), chk.Not(chk.Equals), "")
58	c.Assert(listResp.Version(), chk.Not(chk.Equals), "")
59	c.Assert(listResp.Date().IsZero(), chk.Equals, false)
60
61	blockList, err = blob.GetBlockList(context.Background(), BlockListAll, LeaseAccessConditions{})
62	c.Assert(err, chk.IsNil)
63	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
64	c.Assert(blockList.LastModified().IsZero(), chk.Equals, false)
65	c.Assert(blockList.ETag(), chk.Not(chk.Equals), ETagNone)
66	c.Assert(blockList.ContentType(), chk.Not(chk.Equals), "")
67	c.Assert(blockList.BlobContentLength(), chk.Equals, int64(1024))
68	c.Assert(blockList.RequestID(), chk.Not(chk.Equals), "")
69	c.Assert(blockList.Version(), chk.Not(chk.Equals), "")
70	c.Assert(blockList.Date().IsZero(), chk.Equals, false)
71	c.Assert(blockList.CommittedBlocks, chk.HasLen, 1)
72	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 0)
73}
74
75func (s *aztestsSuite) TestStageBlockFromURL(c *chk.C) {
76	bsu := getBSU()
77	credential, err := getGenericCredential("")
78	if err != nil {
79		c.Fatal("Invalid credential")
80	}
81	container, _ := createNewContainer(c, bsu)
82	defer delContainer(c, container)
83
84	testSize := 8 * 1024 * 1024 // 8MB
85	r, sourceData := getRandomDataAndReader(testSize)
86	ctx := context.Background() // Use default Background context
87	srcBlob := container.NewBlockBlobURL(generateBlobName())
88	destBlob := container.NewBlockBlobURL(generateBlobName())
89
90	// Prepare source blob for copy.
91	uploadSrcResp, err := srcBlob.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
92	c.Assert(err, chk.IsNil)
93	c.Assert(uploadSrcResp.Response().StatusCode, chk.Equals, 201)
94
95	// Get source blob URL with SAS for StageFromURL.
96	srcBlobParts := NewBlobURLParts(srcBlob.URL())
97
98	srcBlobParts.SAS, err = BlobSASSignatureValues{
99		Protocol:      SASProtocolHTTPS,                     // Users MUST use HTTPS (not HTTP)
100		ExpiryTime:    time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
101		ContainerName: srcBlobParts.ContainerName,
102		BlobName:      srcBlobParts.BlobName,
103		Permissions:   BlobSASPermissions{Read: true}.String(),
104	}.NewSASQueryParameters(credential)
105	if err != nil {
106		c.Fatal(err)
107	}
108
109	srcBlobURLWithSAS := srcBlobParts.URL()
110
111	// Stage blocks from URL.
112	blockID1, blockID2 := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0))), base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 1)))
113	stageResp1, err := destBlob.StageBlockFromURL(ctx, blockID1, srcBlobURLWithSAS, 0, 4*1024*1024, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{})
114	c.Assert(err, chk.IsNil)
115	c.Assert(stageResp1.Response().StatusCode, chk.Equals, 201)
116	c.Assert(stageResp1.ContentMD5(), chk.Not(chk.Equals), "")
117	c.Assert(stageResp1.RequestID(), chk.Not(chk.Equals), "")
118	c.Assert(stageResp1.Version(), chk.Not(chk.Equals), "")
119	c.Assert(stageResp1.Date().IsZero(), chk.Equals, false)
120
121	stageResp2, err := destBlob.StageBlockFromURL(ctx, blockID2, srcBlobURLWithSAS, 4*1024*1024, CountToEnd, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{})
122	c.Assert(err, chk.IsNil)
123	c.Assert(stageResp2.Response().StatusCode, chk.Equals, 201)
124	c.Assert(stageResp2.ContentMD5(), chk.Not(chk.Equals), "")
125	c.Assert(stageResp2.RequestID(), chk.Not(chk.Equals), "")
126	c.Assert(stageResp2.Version(), chk.Not(chk.Equals), "")
127	c.Assert(stageResp2.Date().IsZero(), chk.Equals, false)
128
129	// Check block list.
130	blockList, err := destBlob.GetBlockList(context.Background(), BlockListAll, LeaseAccessConditions{})
131	c.Assert(err, chk.IsNil)
132	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
133	c.Assert(blockList.CommittedBlocks, chk.HasLen, 0)
134	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 2)
135
136	// Commit block list.
137	listResp, err := destBlob.CommitBlockList(context.Background(), []string{blockID1, blockID2}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
138	c.Assert(err, chk.IsNil)
139	c.Assert(listResp.Response().StatusCode, chk.Equals, 201)
140
141	// Check data integrity through downloading.
142	downloadResp, err := destBlob.BlobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
143	c.Assert(err, chk.IsNil)
144	destData, err := ioutil.ReadAll(downloadResp.Body(RetryReaderOptions{}))
145	c.Assert(err, chk.IsNil)
146	c.Assert(destData, chk.DeepEquals, sourceData)
147}
148
149func (s *aztestsSuite) TestCopyBlockBlobFromURL(c *chk.C) {
150	bsu := getBSU()
151	credential, err := getGenericCredential("")
152	if err != nil {
153		c.Fatal("Invalid credential")
154	}
155	container, _ := createNewContainer(c, bsu)
156	defer delContainer(c, container)
157
158	testSize := 8 * 1024 * 1024 // 8MB
159	r, sourceData := getRandomDataAndReader(testSize)
160	sourceDataMD5Value := md5.Sum(sourceData)
161	ctx := context.Background() // Use default Background context
162	srcBlob := container.NewBlockBlobURL(generateBlobName())
163	destBlob := container.NewBlockBlobURL(generateBlobName())
164
165	// Prepare source blob for copy.
166	uploadSrcResp, err := srcBlob.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
167	c.Assert(err, chk.IsNil)
168	c.Assert(uploadSrcResp.Response().StatusCode, chk.Equals, 201)
169
170	// Get source blob URL with SAS for StageFromURL.
171	srcBlobParts := NewBlobURLParts(srcBlob.URL())
172
173	srcBlobParts.SAS, err = BlobSASSignatureValues{
174		Protocol:      SASProtocolHTTPS,                     // Users MUST use HTTPS (not HTTP)
175		ExpiryTime:    time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
176		ContainerName: srcBlobParts.ContainerName,
177		BlobName:      srcBlobParts.BlobName,
178		Permissions:   BlobSASPermissions{Read: true}.String(),
179	}.NewSASQueryParameters(credential)
180	if err != nil {
181		c.Fatal(err)
182	}
183
184	srcBlobURLWithSAS := srcBlobParts.URL()
185
186	// Invoke copy blob from URL.
187	resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], DefaultAccessTier, nil)
188	c.Assert(err, chk.IsNil)
189	c.Assert(resp.Response().StatusCode, chk.Equals, 202)
190	c.Assert(resp.ETag(), chk.Not(chk.Equals), "")
191	c.Assert(resp.RequestID(), chk.Not(chk.Equals), "")
192	c.Assert(resp.Version(), chk.Not(chk.Equals), "")
193	c.Assert(resp.Date().IsZero(), chk.Equals, false)
194	c.Assert(resp.CopyID(), chk.Not(chk.Equals), "")
195	c.Assert(resp.ContentMD5(), chk.DeepEquals, sourceDataMD5Value[:])
196	c.Assert(string(resp.CopyStatus()), chk.DeepEquals, "success")
197
198	// Check data integrity through downloading.
199	downloadResp, err := destBlob.BlobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
200	c.Assert(err, chk.IsNil)
201	destData, err := ioutil.ReadAll(downloadResp.Body(RetryReaderOptions{}))
202	c.Assert(err, chk.IsNil)
203	c.Assert(destData, chk.DeepEquals, sourceData)
204
205	// Make sure the metadata got copied over
206	c.Assert(len(downloadResp.NewMetadata()), chk.Equals, 1)
207
208	// Edge case 1: Provide bad MD5 and make sure the copy fails
209	_, badMD5 := getRandomDataAndReader(16)
210	_, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, badMD5, DefaultAccessTier, nil)
211	c.Assert(err, chk.NotNil)
212
213	// Edge case 2: Not providing any source MD5 should see the CRC getting returned instead
214	resp, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, nil, DefaultAccessTier, nil)
215	c.Assert(err, chk.IsNil)
216	c.Assert(resp.Response().StatusCode, chk.Equals, 202)
217	c.Assert(resp.XMsContentCrc64(), chk.Not(chk.Equals), "")
218}
219
220func (s *aztestsSuite) TestBlobSASQueryParamOverrideResponseHeaders(c *chk.C) {
221	bsu := getBSU()
222	credential, err := getGenericCredential("")
223	if err != nil {
224		c.Fatal("Invalid credential")
225	}
226	container, _ := createNewContainer(c, bsu)
227	defer delContainer(c, container)
228
229	testSize := 8 * 1024 * 1024 // 8MB
230	r, _ := getRandomDataAndReader(testSize)
231	ctx := context.Background() // Use default Background context
232	blob := container.NewBlockBlobURL(generateBlobName())
233
234	uploadResp, err := blob.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
235	c.Assert(err, chk.IsNil)
236	c.Assert(uploadResp.Response().StatusCode, chk.Equals, 201)
237
238	// Get blob URL with SAS.
239	blobParts := NewBlobURLParts(blob.URL())
240
241	cacheControlVal := "cache-control-override"
242	contentDispositionVal := "content-disposition-override"
243	contentEncodingVal := "content-encoding-override"
244	contentLanguageVal := "content-language-override"
245	contentTypeVal := "content-type-override"
246
247	blobParts.SAS, err = BlobSASSignatureValues{
248		Protocol:           SASProtocolHTTPS,                     // Users MUST use HTTPS (not HTTP)
249		ExpiryTime:         time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
250		ContainerName:      blobParts.ContainerName,
251		BlobName:           blobParts.BlobName,
252		Permissions:        BlobSASPermissions{Read: true}.String(),
253		CacheControl:       cacheControlVal,
254		ContentDisposition: contentDispositionVal,
255		ContentEncoding:    contentEncodingVal,
256		ContentLanguage:    contentLanguageVal,
257		ContentType:        contentTypeVal,
258	}.NewSASQueryParameters(credential)
259	if err != nil {
260		c.Fatal(err)
261	}
262
263	blobURL := NewBlobURL(blobParts.URL(), NewPipeline(NewAnonymousCredential(), PipelineOptions{}))
264
265	gResp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
266	c.Assert(err, chk.IsNil)
267	c.Assert(gResp.CacheControl(), chk.Equals, cacheControlVal)
268	c.Assert(gResp.ContentDisposition(), chk.Equals, contentDispositionVal)
269	c.Assert(gResp.ContentEncoding(), chk.Equals, contentEncodingVal)
270	c.Assert(gResp.ContentLanguage(), chk.Equals, contentLanguageVal)
271	c.Assert(gResp.ContentType(), chk.Equals, contentTypeVal)
272}
273
274func (s *aztestsSuite) TestStageBlockWithMD5(c *chk.C) {
275	bsu := getBSU()
276	container, _ := createNewContainer(c, bsu)
277	defer delContainer(c, container)
278
279	blob := container.NewBlockBlobURL(generateBlobName())
280	blockID := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0)))
281
282	// test put block with valid MD5 value
283	readerToBody, body := getRandomDataAndReader(1024)
284	md5Value := md5.Sum(body)
285	putResp, err := blob.StageBlock(context.Background(), blockID, readerToBody, LeaseAccessConditions{}, md5Value[:], ClientProvidedKeyOptions{})
286	c.Assert(err, chk.IsNil)
287	c.Assert(putResp.Response().StatusCode, chk.Equals, 201)
288	c.Assert(putResp.ContentMD5(), chk.DeepEquals, md5Value[:])
289	c.Assert(putResp.RequestID(), chk.Not(chk.Equals), "")
290	c.Assert(putResp.Version(), chk.Not(chk.Equals), "")
291	c.Assert(putResp.Date().IsZero(), chk.Equals, false)
292
293	// test put block with bad MD5 value
294	readerToBody, body = getRandomDataAndReader(1024)
295	_, badMD5 := getRandomDataAndReader(16)
296	putResp, err = blob.StageBlock(context.Background(), blockID, readerToBody, LeaseAccessConditions{}, badMD5[:], ClientProvidedKeyOptions{})
297	validateStorageError(c, err, ServiceCodeMd5Mismatch)
298}
299
300func (s *aztestsSuite) TestBlobPutBlobNonEmptyBody(c *chk.C) {
301	bsu := getBSU()
302	containerURL, _ := createNewContainer(c, bsu)
303	defer deleteContainer(c, containerURL)
304	blobURL, _ := getBlockBlobURL(c, containerURL)
305
306	_, err := blobURL.Upload(ctx, strings.NewReader(blockBlobDefaultData), BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
307	c.Assert(err, chk.IsNil)
308
309	resp, err := blobURL.Download(ctx, 0, 0, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
310	c.Assert(err, chk.IsNil)
311	data, err := ioutil.ReadAll(resp.Response().Body)
312	c.Assert(string(data), chk.Equals, blockBlobDefaultData)
313}
314
315func (s *aztestsSuite) TestBlobPutBlobHTTPHeaders(c *chk.C) {
316	bsu := getBSU()
317	containerURL, _ := createNewContainer(c, bsu)
318	defer deleteContainer(c, containerURL)
319	blobURL, _ := getBlockBlobURL(c, containerURL)
320
321	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), basicHeaders, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
322	c.Assert(err, chk.IsNil)
323
324	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
325	c.Assert(err, chk.IsNil)
326	h := resp.NewHTTPHeaders()
327	h.ContentMD5 = nil // the service generates a MD5 value, omit before comparing
328	c.Assert(h, chk.DeepEquals, basicHeaders)
329}
330
331func (s *aztestsSuite) TestBlobPutBlobMetadataNotEmpty(c *chk.C) {
332	bsu := getBSU()
333	containerURL, _ := createNewContainer(c, bsu)
334	defer deleteContainer(c, containerURL)
335	blobURL, _ := getBlockBlobURL(c, containerURL)
336
337	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
338	c.Assert(err, chk.IsNil)
339
340	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
341	c.Assert(err, chk.IsNil)
342	c.Assert(resp.NewMetadata(), chk.DeepEquals, basicMetadata)
343}
344
345func (s *aztestsSuite) TestBlobPutBlobMetadataEmpty(c *chk.C) {
346	bsu := getBSU()
347	containerURL, _ := createNewContainer(c, bsu)
348	defer deleteContainer(c, containerURL)
349	blobURL, _ := getBlockBlobURL(c, containerURL)
350
351	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
352	c.Assert(err, chk.IsNil)
353
354	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
355	c.Assert(err, chk.IsNil)
356	c.Assert(resp.NewMetadata(), chk.HasLen, 0)
357}
358
359func (s *aztestsSuite) TestBlobPutBlobMetadataInvalid(c *chk.C) {
360	bsu := getBSU()
361	containerURL, _ := createNewContainer(c, bsu)
362	defer deleteContainer(c, containerURL)
363	blobURL, _ := getBlockBlobURL(c, containerURL)
364
365	_, err := blobURL.Upload(ctx, nil, BlobHTTPHeaders{}, Metadata{"In valid!": "bar"}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
366	c.Assert(strings.Contains(err.Error(), validationErrorSubstring), chk.Equals, true)
367}
368
369func (s *aztestsSuite) TestBlobPutBlobIfModifiedSinceTrue(c *chk.C) {
370	bsu := getBSU()
371	containerURL, _ := createNewContainer(c, bsu)
372	defer deleteContainer(c, containerURL)
373	blobURL, _ := createNewBlockBlob(c, containerURL)
374
375	currentTime := getRelativeTimeGMT(-10)
376
377	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfModifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
378	c.Assert(err, chk.IsNil)
379
380	validateUpload(c, blobURL)
381}
382
383func (s *aztestsSuite) TestBlobPutBlobIfModifiedSinceFalse(c *chk.C) {
384	bsu := getBSU()
385	containerURL, _ := createNewContainer(c, bsu)
386	defer deleteContainer(c, containerURL)
387	blobURL, _ := createNewBlockBlob(c, containerURL)
388
389	currentTime := getRelativeTimeGMT(10)
390
391	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfModifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
392	validateStorageError(c, err, ServiceCodeConditionNotMet)
393}
394
395func (s *aztestsSuite) TestBlobPutBlobIfUnmodifiedSinceTrue(c *chk.C) {
396	bsu := getBSU()
397	containerURL, _ := createNewContainer(c, bsu)
398	defer deleteContainer(c, containerURL)
399	blobURL, _ := createNewBlockBlob(c, containerURL)
400
401	currentTime := getRelativeTimeGMT(10)
402
403	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfUnmodifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
404	c.Assert(err, chk.IsNil)
405
406	validateUpload(c, blobURL)
407}
408
409func (s *aztestsSuite) TestBlobPutBlobIfUnmodifiedSinceFalse(c *chk.C) {
410	bsu := getBSU()
411	containerURL, _ := createNewContainer(c, bsu)
412	defer deleteContainer(c, containerURL)
413	blobURL, _ := createNewBlockBlob(c, containerURL)
414
415	currentTime := getRelativeTimeGMT(-10)
416
417	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfUnmodifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
418	validateStorageError(c, err, ServiceCodeConditionNotMet)
419}
420
421func (s *aztestsSuite) TestBlobPutBlobIfMatchTrue(c *chk.C) {
422	bsu := getBSU()
423	containerURL, _ := createNewContainer(c, bsu)
424	defer deleteContainer(c, containerURL)
425	blobURL, _ := createNewBlockBlob(c, containerURL)
426
427	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
428	c.Assert(err, chk.IsNil)
429
430	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfMatch: resp.ETag()}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
431	c.Assert(err, chk.IsNil)
432
433	validateUpload(c, blobURL)
434}
435
436func (s *aztestsSuite) TestBlobPutBlobIfMatchFalse(c *chk.C) {
437	bsu := getBSU()
438	containerURL, _ := createNewContainer(c, bsu)
439	defer deleteContainer(c, containerURL)
440	blobURL, _ := createNewBlockBlob(c, containerURL)
441
442	_, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
443	c.Assert(err, chk.IsNil)
444
445	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfMatch: ETag("garbage")}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
446	validateStorageError(c, err, ServiceCodeConditionNotMet)
447}
448
449func (s *aztestsSuite) TestBlobPutBlobIfNoneMatchTrue(c *chk.C) {
450	bsu := getBSU()
451	containerURL, _ := createNewContainer(c, bsu)
452	defer deleteContainer(c, containerURL)
453	blobURL, _ := createNewBlockBlob(c, containerURL)
454
455	_, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
456	c.Assert(err, chk.IsNil)
457
458	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfNoneMatch: ETag("garbage")}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
459	c.Assert(err, chk.IsNil)
460
461	validateUpload(c, blobURL)
462}
463
464func (s *aztestsSuite) TestBlobPutBlobIfNoneMatchFalse(c *chk.C) {
465	bsu := getBSU()
466	containerURL, _ := createNewContainer(c, bsu)
467	defer deleteContainer(c, containerURL)
468	blobURL, _ := createNewBlockBlob(c, containerURL)
469
470	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
471	c.Assert(err, chk.IsNil)
472
473	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfNoneMatch: resp.ETag()}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
474	validateStorageError(c, err, ServiceCodeConditionNotMet)
475}
476
477var blockID string // a single blockID used in tests when only a single ID is needed
478
479func init() {
480	u := [64]byte{}
481	binary.BigEndian.PutUint32(u[len(guuid.UUID{}):], math.MaxUint32)
482	blockID = base64.StdEncoding.EncodeToString(u[:])
483}
484
485func (s *aztestsSuite) TestBlobGetBlockListNone(c *chk.C) {
486	bsu := getBSU()
487	containerURL, _ := createNewContainer(c, bsu)
488	defer deleteContainer(c, containerURL)
489	blobURL, _ := getBlockBlobURL(c, containerURL)
490
491	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
492	c.Assert(err, chk.IsNil)
493
494	resp, err := blobURL.GetBlockList(ctx, BlockListNone, LeaseAccessConditions{})
495	c.Assert(err, chk.IsNil)
496	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
497	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0) // Not specifying a block list type should default to only returning committed blocks
498}
499
500func (s *aztestsSuite) TestBlobGetBlockListUncommitted(c *chk.C) {
501	bsu := getBSU()
502	containerURL, _ := createNewContainer(c, bsu)
503	defer deleteContainer(c, containerURL)
504	blobURL, _ := getBlockBlobURL(c, containerURL)
505
506	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
507	c.Assert(err, chk.IsNil)
508
509	resp, err := blobURL.GetBlockList(ctx, BlockListUncommitted, LeaseAccessConditions{})
510	c.Assert(err, chk.IsNil)
511	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
512	c.Assert(resp.UncommittedBlocks, chk.HasLen, 1)
513}
514
515func (s *aztestsSuite) TestBlobGetBlockListCommitted(c *chk.C) {
516	bsu := getBSU()
517	containerURL, _ := createNewContainer(c, bsu)
518	defer deleteContainer(c, containerURL)
519	blobURL, _ := getBlockBlobURL(c, containerURL)
520
521	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
522	c.Assert(err, chk.IsNil)
523
524	_, err = blobURL.CommitBlockList(ctx, []string{blockID}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
525
526	resp, err := blobURL.GetBlockList(ctx, BlockListCommitted, LeaseAccessConditions{})
527	c.Assert(err, chk.IsNil)
528	c.Assert(resp.CommittedBlocks, chk.HasLen, 1)
529	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
530}
531
532func (s *aztestsSuite) TestBlobGetBlockListCommittedEmpty(c *chk.C) {
533	bsu := getBSU()
534	containerURL, _ := createNewContainer(c, bsu)
535	defer deleteContainer(c, containerURL)
536	blobURL, _ := getBlockBlobURL(c, containerURL)
537
538	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
539	c.Assert(err, chk.IsNil)
540
541	resp, err := blobURL.GetBlockList(ctx, BlockListCommitted, LeaseAccessConditions{})
542	c.Assert(err, chk.IsNil)
543	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
544	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
545}
546
547func (s *aztestsSuite) TestBlobGetBlockListBothEmpty(c *chk.C) {
548	bsu := getBSU()
549	containerURL, _ := createNewContainer(c, bsu)
550	defer deleteContainer(c, containerURL)
551	blobURL, _ := getBlockBlobURL(c, containerURL)
552
553	_, err := blobURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
554	validateStorageError(c, err, ServiceCodeBlobNotFound)
555}
556
557func (s *aztestsSuite) TestBlobGetBlockListBothNotEmpty(c *chk.C) {
558	bsu := getBSU()
559	containerURL, _ := createNewContainer(c, bsu)
560	defer deleteContainer(c, containerURL)
561	blobURL, _ := getBlockBlobURL(c, containerURL)
562
563	id := newID()
564
565	// Put and commit two blocks
566	_, err := blobURL.StageBlock(ctx, id.next(), strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
567	c.Assert(err, chk.IsNil)
568	_, err = blobURL.StageBlock(ctx, id.next(), strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
569	c.Assert(err, chk.IsNil)
570	_, err = blobURL.CommitBlockList(ctx, id.issued(), BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
571	c.Assert(err, chk.IsNil)
572
573	// Put two uncommitted blocks
574	_, err = blobURL.StageBlock(ctx, id.next(), strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
575	c.Assert(err, chk.IsNil)
576	_, err = blobURL.StageBlock(ctx, id.next(), strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
577	c.Assert(err, chk.IsNil)
578
579	resp, err := blobURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
580	c.Assert(err, chk.IsNil)
581	c.Assert(resp.CommittedBlocks[0].Name, chk.Equals, id.issued()[0])
582	c.Assert(resp.CommittedBlocks[1].Name, chk.Equals, id.issued()[1])   // Committed blocks are returned in the order they are committed (in the commit list)
583	c.Assert(resp.UncommittedBlocks[0].Name, chk.Equals, id.issued()[2]) // Uncommitted blocks are returned in alphabetical order
584	c.Assert(resp.UncommittedBlocks[1].Name, chk.Equals, id.issued()[3])
585}
586
587func (s *aztestsSuite) TestBlobGetBlockListInvalidType(c *chk.C) {
588	bsu := getBSU()
589	containerURL, _ := createNewContainer(c, bsu)
590	defer deleteContainer(c, containerURL)
591	blobURL, _ := getBlockBlobURL(c, containerURL)
592
593	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
594	c.Assert(err, chk.IsNil)
595
596	_, err = blobURL.GetBlockList(ctx, BlockListType("garbage"), LeaseAccessConditions{})
597	validateStorageError(c, err, ServiceCodeInvalidQueryParameterValue)
598}
599
600func (s *aztestsSuite) TestBlobGetBlockListSnapshot(c *chk.C) {
601	bsu := getBSU()
602	containerURL, _ := createNewContainer(c, bsu)
603	defer deleteContainer(c, containerURL)
604	blobURL, _ := getBlockBlobURL(c, containerURL)
605
606	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
607	c.Assert(err, chk.IsNil)
608	_, err = blobURL.CommitBlockList(ctx, []string{blockID}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
609	c.Assert(err, chk.IsNil)
610
611	resp, err := blobURL.CreateSnapshot(ctx, nil, BlobAccessConditions{}, ClientProvidedKeyOptions{})
612	c.Assert(err, chk.IsNil)
613	snapshotURL := blobURL.WithSnapshot(resp.Snapshot())
614
615	resp2, err := snapshotURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
616	c.Assert(err, chk.IsNil)
617	c.Assert(resp2.CommittedBlocks, chk.HasLen, 1)
618}
619
620func (s *aztestsSuite) TestBlobPutBlockIDInvalidCharacters(c *chk.C) {
621	bsu := getBSU()
622	containerURL, _ := createNewContainer(c, bsu)
623	defer deleteContainer(c, containerURL)
624	blobURL, _ := getBlockBlobURL(c, containerURL)
625
626	_, err := blobURL.StageBlock(ctx, "!!", strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
627	validateStorageError(c, err, ServiceCodeInvalidQueryParameterValue)
628}
629
630func (s *aztestsSuite) TestBlobPutBlockIDInvalidLength(c *chk.C) {
631	bsu := getBSU()
632	containerURL, _ := createNewContainer(c, bsu)
633	defer deleteContainer(c, containerURL)
634	blobURL, _ := getBlockBlobURL(c, containerURL)
635
636	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
637	c.Assert(err, chk.IsNil)
638	_, err = blobURL.StageBlock(ctx, "00000000", strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
639	validateStorageError(c, err, ServiceCodeInvalidBlobOrBlock)
640}
641
642func (s *aztestsSuite) TestBlobPutBlockEmptyBody(c *chk.C) {
643	bsu := getBSU()
644	containerURL, _ := createNewContainer(c, bsu)
645	defer deleteContainer(c, containerURL)
646	blobURL, _ := getBlockBlobURL(c, containerURL)
647
648	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(""), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
649	validateStorageError(c, err, ServiceCodeInvalidHeaderValue)
650}
651
652func setupPutBlockListTest(c *chk.C) (containerURL ContainerURL, blobURL BlockBlobURL, id string) {
653	bsu := getBSU()
654	containerURL, _ = createNewContainer(c, bsu)
655	blobURL, _ = getBlockBlobURL(c, containerURL)
656
657	_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
658	c.Assert(err, chk.IsNil)
659	return containerURL, blobURL, blockID
660}
661
662func (s *aztestsSuite) TestBlobPutBlockListInvalidID(c *chk.C) {
663	containerURL, blobURL, id := setupPutBlockListTest(c)
664	defer deleteContainer(c, containerURL)
665
666	_, err := blobURL.CommitBlockList(ctx, []string{id[:2]}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
667	validateStorageError(c, err, ServiceCodeInvalidBlockID)
668}
669
670func (s *aztestsSuite) TestBlobPutBlockListDuplicateBlocks(c *chk.C) {
671	containerURL, blobURL, id := setupPutBlockListTest(c)
672	defer deleteContainer(c, containerURL)
673
674	_, err := blobURL.CommitBlockList(ctx, []string{id, id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
675	c.Assert(err, chk.IsNil)
676
677	resp, err := blobURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
678	c.Assert(err, chk.IsNil)
679	c.Assert(resp.CommittedBlocks, chk.HasLen, 2)
680}
681
682func (s *aztestsSuite) TestBlobPutBlockListEmptyList(c *chk.C) {
683	containerURL, blobURL, _ := setupPutBlockListTest(c)
684	defer deleteContainer(c, containerURL)
685
686	_, err := blobURL.CommitBlockList(ctx, []string{}, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
687	c.Assert(err, chk.IsNil)
688
689	resp, err := blobURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
690	c.Assert(err, chk.IsNil)
691	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
692}
693
694func (s *aztestsSuite) TestBlobPutBlockListMetadataEmpty(c *chk.C) {
695	containerURL, blobURL, id := setupPutBlockListTest(c)
696	defer deleteContainer(c, containerURL)
697
698	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
699	c.Assert(err, chk.IsNil)
700
701	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
702	c.Assert(err, chk.IsNil)
703	c.Assert(resp.NewMetadata(), chk.HasLen, 0)
704}
705
706func (s *aztestsSuite) TestBlobPutBlockListMetadataNonEmpty(c *chk.C) {
707	containerURL, blobURL, id := setupPutBlockListTest(c)
708	defer deleteContainer(c, containerURL)
709
710	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, basicMetadata, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
711	c.Assert(err, chk.IsNil)
712
713	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
714	c.Assert(err, chk.IsNil)
715	c.Assert(resp.NewMetadata(), chk.DeepEquals, basicMetadata)
716}
717
718func (s *aztestsSuite) TestBlobPutBlockListHTTPHeaders(c *chk.C) {
719	containerURL, blobURL, id := setupPutBlockListTest(c)
720	defer deleteContainer(c, containerURL)
721
722	_, err := blobURL.CommitBlockList(ctx, []string{id}, basicHeaders, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
723	c.Assert(err, chk.IsNil)
724
725	resp, _ := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
726	h := resp.NewHTTPHeaders()
727	c.Assert(h, chk.DeepEquals, basicHeaders)
728}
729
730func (s *aztestsSuite) TestBlobPutBlockListHTTPHeadersEmpty(c *chk.C) {
731	containerURL, blobURL, id := setupPutBlockListTest(c)
732	defer deleteContainer(c, containerURL)
733
734	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{ContentDisposition: "my_disposition"}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
735	c.Assert(err, chk.IsNil)
736
737	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
738	c.Assert(err, chk.IsNil)
739
740	resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
741	c.Assert(err, chk.IsNil)
742	c.Assert(resp.ContentDisposition(), chk.Equals, "")
743}
744
745func validateBlobCommitted(c *chk.C, blobURL BlockBlobURL) {
746	resp, err := blobURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
747	c.Assert(err, chk.IsNil)
748	c.Assert(resp.CommittedBlocks, chk.HasLen, 1)
749}
750
751func (s *aztestsSuite) TestBlobPutBlockListIfModifiedSinceTrue(c *chk.C) {
752	containerURL, blobURL, id := setupPutBlockListTest(c)
753	defer deleteContainer(c, containerURL)
754	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
755	c.Assert(err, chk.IsNil)
756
757	currentTime := getRelativeTimeGMT(-10)
758
759	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfModifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
760	c.Assert(err, chk.IsNil)
761
762	validateBlobCommitted(c, blobURL)
763}
764
765func (s *aztestsSuite) TestBlobPutBlockListIfModifiedSinceFalse(c *chk.C) {
766	containerURL, blobURL, id := setupPutBlockListTest(c)
767	defer deleteContainer(c, containerURL)
768
769	currentTime := getRelativeTimeGMT(10)
770
771	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfModifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
772	validateStorageError(c, err, ServiceCodeConditionNotMet)
773}
774
775func (s *aztestsSuite) TestBlobPutBlockListIfUnmodifiedSinceTrue(c *chk.C) {
776	containerURL, blobURL, id := setupPutBlockListTest(c)
777	defer deleteContainer(c, containerURL)
778	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
779	c.Assert(err, chk.IsNil)
780
781	currentTime := getRelativeTimeGMT(10)
782
783	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfUnmodifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
784	c.Assert(err, chk.IsNil)
785
786	validateBlobCommitted(c, blobURL)
787}
788
789func (s *aztestsSuite) TestBlobPutBlockListIfUnmodifiedSinceFalse(c *chk.C) {
790	containerURL, blobURL, id := setupPutBlockListTest(c)
791	blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
792	defer deleteContainer(c, containerURL)
793
794	currentTime := getRelativeTimeGMT(-10)
795
796	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfUnmodifiedSince: currentTime}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
797
798	validateStorageError(c, err, ServiceCodeConditionNotMet)
799}
800
801func (s *aztestsSuite) TestBlobPutBlockListIfMatchTrue(c *chk.C) {
802	containerURL, blobURL, id := setupPutBlockListTest(c)
803	defer deleteContainer(c, containerURL)
804	resp, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
805	c.Assert(err, chk.IsNil)
806
807	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfMatch: resp.ETag()}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
808	c.Assert(err, chk.IsNil)
809
810	validateBlobCommitted(c, blobURL)
811}
812
813func (s *aztestsSuite) TestBlobPutBlockListIfMatchFalse(c *chk.C) {
814	containerURL, blobURL, id := setupPutBlockListTest(c)
815	defer deleteContainer(c, containerURL)
816	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
817	c.Assert(err, chk.IsNil)
818
819	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfMatch: ETag("garbage")}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
820
821	validateStorageError(c, err, ServiceCodeConditionNotMet)
822}
823
824func (s *aztestsSuite) TestBlobPutBlockListIfNoneMatchTrue(c *chk.C) {
825	containerURL, blobURL, id := setupPutBlockListTest(c)
826	defer deleteContainer(c, containerURL)
827	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
828	c.Assert(err, chk.IsNil)
829
830	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfNoneMatch: ETag("garbage")}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
831	c.Assert(err, chk.IsNil)
832
833	validateBlobCommitted(c, blobURL)
834}
835
836func (s *aztestsSuite) TestBlobPutBlockListIfNoneMatchFalse(c *chk.C) {
837	containerURL, blobURL, id := setupPutBlockListTest(c)
838	defer deleteContainer(c, containerURL)
839	resp, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{}) // The blob must actually exist to have a modifed time
840	c.Assert(err, chk.IsNil)
841
842	_, err = blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{ModifiedAccessConditions: ModifiedAccessConditions{IfNoneMatch: resp.ETag()}}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
843
844	validateStorageError(c, err, ServiceCodeConditionNotMet)
845}
846
847func (s *aztestsSuite) TestBlobPutBlockListValidateData(c *chk.C) {
848	containerURL, blobURL, id := setupPutBlockListTest(c)
849	defer deleteContainer(c, containerURL)
850
851	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
852
853	resp, err := blobURL.Download(ctx, 0, 0, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
854	c.Assert(err, chk.IsNil)
855	data, _ := ioutil.ReadAll(resp.Response().Body)
856	c.Assert(string(data), chk.Equals, blockBlobDefaultData)
857}
858
859func (s *aztestsSuite) TestBlobPutBlockListModifyBlob(c *chk.C) {
860	containerURL, blobURL, id := setupPutBlockListTest(c)
861	defer deleteContainer(c, containerURL)
862
863	_, err := blobURL.CommitBlockList(ctx, []string{id}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
864	c.Assert(err, chk.IsNil)
865
866	_, err = blobURL.StageBlock(ctx, "0001", bytes.NewReader([]byte("new data")), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
867	c.Assert(err, chk.IsNil)
868	_, err = blobURL.StageBlock(ctx, "0010", bytes.NewReader([]byte("new data")), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
869	c.Assert(err, chk.IsNil)
870	_, err = blobURL.StageBlock(ctx, "0011", bytes.NewReader([]byte("new data")), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
871	c.Assert(err, chk.IsNil)
872	_, err = blobURL.StageBlock(ctx, "0100", bytes.NewReader([]byte("new data")), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
873	c.Assert(err, chk.IsNil)
874
875	_, err = blobURL.CommitBlockList(ctx, []string{"0001", "0011"}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, nil, ClientProvidedKeyOptions{})
876	c.Assert(err, chk.IsNil)
877
878	resp, err := blobURL.GetBlockList(ctx, BlockListAll, LeaseAccessConditions{})
879	c.Assert(err, chk.IsNil)
880	c.Assert(resp.CommittedBlocks, chk.HasLen, 2)
881	c.Assert(resp.CommittedBlocks[0].Name, chk.Equals, "0001")
882	c.Assert(resp.CommittedBlocks[1].Name, chk.Equals, "0011")
883	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
884}
885
886func (s *aztestsSuite) TestSetTierOnBlobUpload(c *chk.C) {
887	bsu := getBSU()
888	containerURL, _ := createNewContainer(c, bsu)
889	defer deleteContainer(c, containerURL)
890	for _, tier := range []AccessTierType{AccessTierArchive, AccessTierCool, AccessTierHot} {
891		blobURL, _ := getBlockBlobURL(c, containerURL)
892
893		_, err := blobURL.Upload(ctx, strings.NewReader("Test Data"), basicHeaders, nil, BlobAccessConditions{}, tier, nil, ClientProvidedKeyOptions{})
894		c.Assert(err, chk.IsNil)
895
896		resp, err := blobURL.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
897		c.Assert(err, chk.IsNil)
898		c.Assert(resp.AccessTier(), chk.Equals, string(tier))
899	}
900}
901
902func (s *aztestsSuite) TestBlobSetTierOnCommit(c *chk.C) {
903	bsu := getBSU()
904	containerURL, _ := createNewContainer(c, bsu)
905	defer deleteContainer(c, containerURL)
906
907	for _, tier := range []AccessTierType{AccessTierCool, AccessTierHot} {
908		blobURL, _ := getBlockBlobURL(c, containerURL)
909
910		_, err := blobURL.StageBlock(ctx, blockID, strings.NewReader(blockBlobDefaultData), LeaseAccessConditions{}, nil, ClientProvidedKeyOptions{})
911		c.Assert(err, chk.IsNil)
912
913		_, err = blobURL.CommitBlockList(ctx, []string{blockID}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, tier, nil, ClientProvidedKeyOptions{})
914
915		resp, err := blobURL.GetBlockList(ctx, BlockListCommitted, LeaseAccessConditions{})
916		c.Assert(err, chk.IsNil)
917		c.Assert(resp.CommittedBlocks, chk.HasLen, 1)
918		c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
919	}
920}
921
922func (s *aztestsSuite) TestSetTierOnCopyBlockBlobFromURL(c *chk.C) {
923	bsu := getBSU()
924
925	container, _ := createNewContainer(c, bsu)
926	defer delContainer(c, container)
927
928	testSize := 1 * 1024 * 1024
929	r, sourceData := getRandomDataAndReader(testSize)
930	sourceDataMD5Value := md5.Sum(sourceData)
931	ctx := context.Background()
932	srcBlob := container.NewBlockBlobURL(generateBlobName())
933
934	// Setting blob tier as "cool"
935	uploadSrcResp, err := srcBlob.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, AccessTierCool, nil, ClientProvidedKeyOptions{})
936	c.Assert(err, chk.IsNil)
937	c.Assert(uploadSrcResp.Response().StatusCode, chk.Equals, 201)
938
939	// Get source blob URL with SAS for StageFromURL.
940	srcBlobParts := NewBlobURLParts(srcBlob.URL())
941
942	credential, err := getGenericCredential("")
943	if err != nil {
944		c.Fatal("Invalid credential")
945	}
946	srcBlobParts.SAS, err = BlobSASSignatureValues{
947		Protocol:      SASProtocolHTTPS,
948		ExpiryTime:    time.Now().UTC().Add(2 * time.Hour),
949		ContainerName: srcBlobParts.ContainerName,
950		BlobName:      srcBlobParts.BlobName,
951		Permissions:   BlobSASPermissions{Read: true}.String(),
952	}.NewSASQueryParameters(credential)
953	if err != nil {
954		c.Fatal(err)
955	}
956
957	srcBlobURLWithSAS := srcBlobParts.URL()
958	for _, tier := range []AccessTierType{AccessTierArchive, AccessTierCool, AccessTierHot} {
959		destBlob := container.NewBlockBlobURL(generateBlobName())
960		resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], tier, nil)
961		c.Assert(err, chk.IsNil)
962		c.Assert(resp.Response().StatusCode, chk.Equals, 202)
963		c.Assert(string(resp.CopyStatus()), chk.DeepEquals, "success")
964
965		destBlobPropResp, err := destBlob.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
966		c.Assert(err, chk.IsNil)
967		c.Assert(destBlobPropResp.AccessTier(), chk.Equals, string(tier))
968
969	}
970}
971
972func (s *aztestsSuite) TestSetTierOnStageBlockFromURL(c *chk.C) {
973	bsu := getBSU()
974	credential, err := getGenericCredential("")
975	if err != nil {
976		c.Fatal("Invalid credential")
977	}
978	container, _ := createNewContainer(c, bsu)
979	defer delContainer(c, container)
980
981	testSize := 8 * 1024 * 1024 // 8MB
982	r, sourceData := getRandomDataAndReader(testSize)
983	ctx := context.Background() // Use default Background context
984	srcBlob := container.NewBlockBlobURL("src" + generateBlobName())
985	destBlob := container.NewBlockBlobURL("dst" + generateBlobName())
986	tier := AccessTierCool
987
988	// Prepare source blob for copy.
989	uploadSrcResp, err := srcBlob.Upload(ctx, r, BlobHTTPHeaders{}, Metadata{}, BlobAccessConditions{}, tier, nil, ClientProvidedKeyOptions{})
990	c.Assert(err, chk.IsNil)
991	c.Assert(uploadSrcResp.Response().StatusCode, chk.Equals, 201)
992
993	// Get source blob URL with SAS for StageFromURL.
994	srcBlobParts := NewBlobURLParts(srcBlob.URL())
995
996	srcBlobParts.SAS, err = BlobSASSignatureValues{
997		Protocol:      SASProtocolHTTPS,                     // Users MUST use HTTPS (not HTTP)
998		ExpiryTime:    time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
999		ContainerName: srcBlobParts.ContainerName,
1000		BlobName:      srcBlobParts.BlobName,
1001		Permissions:   BlobSASPermissions{Read: true}.String(),
1002	}.NewSASQueryParameters(credential)
1003	if err != nil {
1004		c.Fatal(err)
1005	}
1006
1007	srcBlobURLWithSAS := srcBlobParts.URL()
1008
1009	// Stage blocks from URL.
1010	blockID1, blockID2 := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0))), base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 1)))
1011	stageResp1, err := destBlob.StageBlockFromURL(ctx, blockID1, srcBlobURLWithSAS, 0, 4*1024*1024, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{})
1012	c.Assert(err, chk.IsNil)
1013	c.Assert(stageResp1.Response().StatusCode, chk.Equals, 201)
1014	c.Assert(stageResp1.ContentMD5(), chk.Not(chk.Equals), "")
1015	c.Assert(stageResp1.RequestID(), chk.Not(chk.Equals), "")
1016	c.Assert(stageResp1.Version(), chk.Not(chk.Equals), "")
1017	c.Assert(stageResp1.Date().IsZero(), chk.Equals, false)
1018
1019	stageResp2, err := destBlob.StageBlockFromURL(ctx, blockID2, srcBlobURLWithSAS, 4*1024*1024, CountToEnd, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{})
1020	c.Assert(err, chk.IsNil)
1021	c.Assert(stageResp2.Response().StatusCode, chk.Equals, 201)
1022	c.Assert(stageResp2.ContentMD5(), chk.Not(chk.Equals), "")
1023	c.Assert(stageResp2.RequestID(), chk.Not(chk.Equals), "")
1024	c.Assert(stageResp2.Version(), chk.Not(chk.Equals), "")
1025	c.Assert(stageResp2.Date().IsZero(), chk.Equals, false)
1026
1027	// Check block list.
1028	blockList, err := destBlob.GetBlockList(context.Background(), BlockListAll, LeaseAccessConditions{})
1029	c.Assert(err, chk.IsNil)
1030	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
1031	c.Assert(blockList.CommittedBlocks, chk.HasLen, 0)
1032	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 2)
1033
1034	// Commit block list.
1035	listResp, err := destBlob.CommitBlockList(context.Background(), []string{blockID1, blockID2}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, tier, nil, ClientProvidedKeyOptions{})
1036	c.Assert(err, chk.IsNil)
1037	c.Assert(listResp.Response().StatusCode, chk.Equals, 201)
1038
1039	// Check data integrity through downloading.
1040	downloadResp, err := destBlob.BlobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
1041	c.Assert(err, chk.IsNil)
1042	destData, err := ioutil.ReadAll(downloadResp.Body(RetryReaderOptions{}))
1043	c.Assert(err, chk.IsNil)
1044	c.Assert(destData, chk.DeepEquals, sourceData)
1045
1046	// Get properties to validate the tier
1047	destBlobPropResp, err := destBlob.GetProperties(ctx, BlobAccessConditions{}, ClientProvidedKeyOptions{})
1048	c.Assert(err, chk.IsNil)
1049	c.Assert(destBlobPropResp.AccessTier(), chk.Equals, string(tier))
1050}
1051