Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy Blob from URL/Put Blob from URL copy source tags #21128

1 change: 1 addition & 0 deletions sdk/storage/azblob/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features Added
* Added support for [Cold tier](https://learn.microsoft.com/azure/storage/blobs/access-tiers-overview?tabs=azure-portal).
* Added CopySourceTag option for UploadBlobFromURLOptions

### Breaking Changes

Expand Down
117 changes: 116 additions & 1 deletion sdk/storage/azblob/blockblob/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ func setUpPutBlobFromURLTest(testName string, _require *require.Assertions, svcC
sasQueryParams, err := sas.AccountSignatureValues{
Protocol: sas.ProtocolHTTPS,
ExpiryTime: expiryTime,
Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(),
Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true, Tag: true}).String(),
ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(),
}.SignWithSharedKey(credential)
_require.Nil(err)
Expand Down Expand Up @@ -884,6 +884,121 @@ func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURL() {
_require.Equal(destBuffer, sourceData)
}

func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURLWithCopySourceTagsDefault() {
_require := require.New(s.T())
testName := s.T().Name()
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

containerClient, srcBlob, destBlob, srcBlobURLWithSAS, _ := setUpPutBlobFromURLTest(testName, _require, svcClient)
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)

// Set tags to source
srcBlobTagsMap := map[string]string{
"source": "tags",
}
_, err = srcBlob.SetTags(context.Background(), srcBlobTagsMap, nil)
_require.NoError(err)

// Dest tags
destBlobTagsMap := map[string]string{
"dest": "tags",
}

// By default, the CopySourceTag header is Replace
options := blockblob.UploadBlobFromURLOptions{
Tags: destBlobTagsMap,
}

// Invoke UploadBlobFromURL
pbResp, err := destBlob.UploadBlobFromURL(context.Background(), srcBlobURLWithSAS, &options)
_require.NotNil(pbResp)
_require.NoError(err)

// Get tags from dest and check if tags got replaced with dest tags
resp, err := destBlob.GetTags(context.Background(), nil)
_require.NoError(err)
_require.Equal(*resp.BlobTagSet[0].Key, "dest")
_require.Equal(*resp.BlobTagSet[0].Value, "tags")
}

func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURLWithCopySourceTagsReplace() {
siminsavani-msft marked this conversation as resolved.
Show resolved Hide resolved
_require := require.New(s.T())
testName := s.T().Name()
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

containerClient, srcBlob, destBlob, srcBlobURLWithSAS, _ := setUpPutBlobFromURLTest(testName, _require, svcClient)
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)

// Set tags to source
srcBlobTagsMap := map[string]string{
"source": "tags",
}
_, err = srcBlob.SetTags(context.Background(), srcBlobTagsMap, nil)
_require.NoError(err)

// Dest tags
destBlobTagsMap := map[string]string{
"dest": "tags",
}

options := blockblob.UploadBlobFromURLOptions{
Tags: destBlobTagsMap,
CopySourceTags: to.Ptr(blockblob.BlobCopySourceTagsReplace),
}

// Invoke UploadBlobFromURL
pbResp, err := destBlob.UploadBlobFromURL(context.Background(), srcBlobURLWithSAS, &options)
_require.NotNil(pbResp)
_require.NoError(err)

// Get tags from dest and check if tags got replaced with dest tags
resp, err := destBlob.GetTags(context.Background(), nil)
_require.NoError(err)
_require.Equal(*resp.BlobTagSet[0].Key, "dest")
_require.Equal(*resp.BlobTagSet[0].Value, "tags")
}

func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURLWithCopySourceTagsCopy() {
_require := require.New(s.T())
testName := s.T().Name()
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

containerClient, srcBlob, destBlob, srcBlobURLWithSAS, _ := setUpPutBlobFromURLTest(testName, _require, svcClient)
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)

// Set tags to source
srcBlobTagsMap := map[string]string{
"source": "tags",
}
_, err = srcBlob.SetTags(context.Background(), srcBlobTagsMap, nil)
_require.NoError(err)

// Set tags to dest to ensure that COPY works
destBlobTagsMap := map[string]string{
"dest": "tags",
}
_, err = destBlob.SetTags(context.Background(), destBlobTagsMap, nil)
_require.NoError(err)

options := blockblob.UploadBlobFromURLOptions{
CopySourceTags: to.Ptr(blockblob.BlobCopySourceTagsCopy),
}

// Invoke UploadBlobFromURL
pbResp, err := destBlob.UploadBlobFromURL(context.Background(), srcBlobURLWithSAS, &options)
_require.NotNil(pbResp)
_require.NoError(err)

// Get tags from dest and check if it matches source tags
resp, err := destBlob.GetTags(context.Background(), nil)
_require.NoError(err)
_require.Equal(*resp.BlobTagSet[0].Key, "source")
_require.Equal(*resp.BlobTagSet[0].Value, "tags")
}

func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURLNegative() {
_require := require.New(s.T())
testName := s.T().Name()
Expand Down
8 changes: 8 additions & 0 deletions sdk/storage/azblob/blockblob/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@ const (
func PossibleBlockListTypeValues() []BlockListType {
return generated.PossibleBlockListTypeValues()
}

// BlobCopySourceTags - can be 'COPY' or 'REPLACE'
type BlobCopySourceTags = generated.BlobCopySourceTags

const (
BlobCopySourceTagsCopy = generated.BlobCopySourceTagsCOPY
BlobCopySourceTagsReplace = generated.BlobCopySourceTagsREPLACE
)
4 changes: 4 additions & 0 deletions sdk/storage/azblob/blockblob/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ type UploadBlobFromURLOptions struct {
// Optional, default is true. Indicates if properties from the source blob should be copied.
CopySourceBlobProperties *bool

// Optional, default 'replace'. Indicates if source tags should be copied or replaced with the tags specified by x-ms-tags.
CopySourceTags *BlobCopySourceTags

// Optional. Specifies a user-defined name-value pair associated with the blob.
Metadata map[string]*string

Expand Down Expand Up @@ -113,6 +116,7 @@ func (o *UploadBlobFromURLOptions) format() (*generated.BlockBlobClientPutBlobFr
BlobTagsString: shared.SerializeBlobTagsToStrPtr(o.Tags),
CopySourceAuthorization: o.CopySourceAuthorization,
CopySourceBlobProperties: o.CopySourceBlobProperties,
CopySourceTags: o.CopySourceTags,
Metadata: o.Metadata,
SourceContentMD5: o.SourceContentMD5,
Tier: o.Tier,
Expand Down