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