Skip to content

Commit 9e8eb1f

Browse files
[Storage] stg98 Features (Azure#24581)
* [Storage] Swagger updates stg 98 (Azure#24339) * swagger updates stg 98 storage * go mod tidy * [Storage] NFS STG98 New APIs (Azure#24467) * new apis for nfs over rest * recordings * recordings * [Storage][Azblob] Add support for request intent for File->Blob Copy (Azure#24519) * support for intent header * user delegation sas new line
1 parent 4d18ecf commit 9e8eb1f

39 files changed

+853
-226
lines changed

sdk/storage/azblob/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 1.6.2-beta.1 (Unreleased)
44

55
### Features Added
6+
* Add support for x-ms-file-request-intent header for blob copy APIs.
67

78
### Breaking Changes
89

sdk/storage/azblob/appendblob/client_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,66 @@ func (s *AppendBlobUnrecordedTestsSuite) TestAppendBlockFromURL() {
489489
_require.Equal(destBuffer, sourceData)
490490
}
491491

492+
func (s *AppendBlobUnrecordedTestsSuite) TestAppendBlockFromURWithLRequestIntentHeader() {
493+
_require := require.New(s.T())
494+
testName := s.T().Name()
495+
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
496+
_require.NoError(err)
497+
containerName := testcommon.GenerateContainerName(testName)
498+
containerClient := testcommon.CreateNewContainer(context.Background(), _require, containerName, svcClient)
499+
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)
500+
501+
contentSize := 4 * 1024 // 4KB
502+
r, sourceData := testcommon.GetDataAndReader(testName, contentSize)
503+
contentMD5 := md5.Sum(sourceData)
504+
srcBlob := containerClient.NewAppendBlobClient(testcommon.GenerateBlobName("appendsrc"))
505+
destBlob := containerClient.NewAppendBlobClient(testcommon.GenerateBlobName("appenddest"))
506+
507+
// Prepare source abClient for copy.
508+
_, err = srcBlob.Create(context.Background(), nil)
509+
_require.NoError(err)
510+
511+
_, err = srcBlob.AppendBlock(context.Background(), streaming.NopCloser(r), nil)
512+
_require.NoError(err)
513+
514+
// Get source abClient URL with SAS for AppendBlockFromURL.
515+
srcBlobParts, _ := blob.ParseURL(srcBlob.URL())
516+
517+
keyCredential, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault)
518+
_require.NoError(err)
519+
perms := sas.BlobPermissions{Read: true}
520+
521+
srcBlobParts.SAS, err = sas.BlobSignatureValues{
522+
Protocol: sas.ProtocolHTTPS, // Users MUST use HTTPS (not HTTP)
523+
ExpiryTime: time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
524+
ContainerName: srcBlobParts.ContainerName,
525+
BlobName: srcBlobParts.BlobName,
526+
Permissions: perms.String(),
527+
}.SignWithSharedKey(keyCredential)
528+
_require.NoError(err)
529+
530+
srcBlobURLWithSAS := srcBlobParts.String()
531+
532+
// Append block from URL.
533+
_, err = destBlob.Create(context.Background(), nil)
534+
_require.NoError(err)
535+
requestIntent := blob.FileShareTokenIntentBackup
536+
appendFromURLResp, err := destBlob.AppendBlockFromURL(context.Background(), srcBlobURLWithSAS, &appendblob.AppendBlockFromURLOptions{
537+
SourceContentValidation: blob.SourceContentValidationTypeMD5(contentMD5[:]),
538+
FileRequestIntent: &requestIntent,
539+
})
540+
541+
_require.NoError(err)
542+
_require.Equal(*appendFromURLResp.BlobAppendOffset, "0")
543+
544+
// Check data integrity through downloading.
545+
destBuffer := make([]byte, 4*1024)
546+
downloadBufferOptions := blob.DownloadBufferOptions{Range: blob.HTTPRange{Offset: 0, Count: 4096}}
547+
_, err = destBlob.DownloadBuffer(context.Background(), destBuffer, &downloadBufferOptions)
548+
_require.NoError(err)
549+
_require.Equal(destBuffer, sourceData)
550+
}
551+
492552
func (s *AppendBlobUnrecordedTestsSuite) TestBlobEncryptionScopeSAS() {
493553
_require := require.New(s.T())
494554
testName := s.T().Name()

sdk/storage/azblob/appendblob/models.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ type AppendBlockFromURLOptions struct {
112112

113113
CPKScopeInfo *blob.CPKScopeInfo
114114

115+
FileRequestIntent *blob.FileShareTokenIntent
116+
115117
SourceModifiedAccessConditions *blob.SourceModifiedAccessConditions
116118

117119
AccessConditions *blob.AccessConditions
@@ -130,6 +132,7 @@ func (o *AppendBlockFromURLOptions) format() (*generated.AppendBlobClientAppendB
130132
options := &generated.AppendBlobClientAppendBlockFromURLOptions{
131133
SourceRange: exported.FormatHTTPRange(o.Range),
132134
CopySourceAuthorization: o.CopySourceAuthorization,
135+
FileRequestIntent: o.FileRequestIntent,
133136
}
134137

135138
if o.SourceContentValidation != nil {

sdk/storage/azblob/blob/client_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,60 @@ func (s *BlobUnrecordedTestsSuite) TestCopyBlobFromUrlSourceContentMD5() {
267267
_require.Error(err)
268268
}
269269

270+
func (s *BlobUnrecordedTestsSuite) TestCopyBlobFromUrlOptions() {
271+
_require := require.New(s.T())
272+
testName := s.T().Name()
273+
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
274+
if err != nil {
275+
s.Fail("Unable to fetch service client because " + err.Error())
276+
}
277+
278+
containerName := testcommon.GenerateContainerName(testName)
279+
containerClient := testcommon.CreateNewContainer(context.Background(), _require, containerName, svcClient)
280+
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)
281+
282+
const contentSize = 8 * 1024 // 8 KB
283+
content := make([]byte, contentSize)
284+
body := bytes.NewReader(content)
285+
286+
srcBlob := containerClient.NewBlockBlobClient("srcblob")
287+
destBlob := containerClient.NewBlockBlobClient("destblob")
288+
289+
// Prepare source bbClient for copy.
290+
_, err = srcBlob.Upload(context.Background(), streaming.NopCloser(body), nil)
291+
_require.NoError(err)
292+
293+
expiryTime, err := time.Parse(time.UnixDate, "Fri Jun 11 20:00:00 UTC 2049")
294+
_require.NoError(err)
295+
296+
credential, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault)
297+
if err != nil {
298+
s.T().Fatal("Couldn't fetch credential because " + err.Error())
299+
}
300+
301+
// Get source blob url with SAS for StageFromURL.
302+
sasQueryParams, err := sas.AccountSignatureValues{
303+
Protocol: sas.ProtocolHTTPS,
304+
ExpiryTime: expiryTime,
305+
Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(),
306+
ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(),
307+
}.SignWithSharedKey(credential)
308+
_require.NoError(err)
309+
310+
srcBlobParts, _ := blob.ParseURL(srcBlob.URL())
311+
srcBlobParts.SAS = sasQueryParams
312+
srcBlobURLWithSAS := srcBlobParts.String()
313+
314+
requestIntent := blob.FileShareTokenIntentBackup
315+
316+
// Invoke CopyFromURL.
317+
resp, err := destBlob.CopyFromURL(context.Background(), srcBlobURLWithSAS, &blob.CopyFromURLOptions{
318+
FileRequestIntent: &requestIntent,
319+
})
320+
_require.NoError(err)
321+
_require.NotNil(resp)
322+
}
323+
270324
// This test simulates DownloadFile/Buffer methods,
271325
// and verifies length and content of file
272326
func (s *BlobUnrecordedTestsSuite) TestUploadDownloadBlockBlob() {

sdk/storage/azblob/blob/constants.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,17 @@ func (s SourceContentValidationTypeMD5) Apply(src generated.SourceContentSetter)
233233
func (SourceContentValidationTypeMD5) notPubliclyImplementable() {}
234234

235235
var _ SourceContentValidationType = (SourceContentValidationTypeMD5)(nil)
236+
237+
// FileShareTokenIntent is file request intent with valid value as Backup
238+
type FileShareTokenIntent = generated.FileShareTokenIntent
239+
240+
const (
241+
FileShareTokenIntentBackup FileShareTokenIntent = "backup"
242+
)
243+
244+
// PossibleFileShareTokenIntentValues returns the possible values for the FileShareTokenIntent const type.
245+
func PossibleFileShareTokenIntentValues() []FileShareTokenIntent {
246+
return []FileShareTokenIntent{
247+
FileShareTokenIntentBackup,
248+
}
249+
}

sdk/storage/azblob/blob/models.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ type CopyFromURLOptions struct {
523523
BlobTags map[string]string
524524
// Only Bearer type is supported. Credentials should be a valid OAuth access token to copy source.
525525
CopySourceAuthorization *string
526+
// File request Intent. Valid value is backup.
527+
FileRequestIntent *FileShareTokenIntent
526528
// Specifies the date time when the blobs immutability policy is set to expire.
527529
ImmutabilityPolicyExpiry *time.Time
528530
// Specifies the immutability policy mode to set on the blob.
@@ -558,6 +560,7 @@ func (o *CopyFromURLOptions) format() (*generated.BlobClientCopyFromURLOptions,
558560
CopySourceAuthorization: o.CopySourceAuthorization,
559561
ImmutabilityPolicyExpiry: o.ImmutabilityPolicyExpiry,
560562
ImmutabilityPolicyMode: o.ImmutabilityPolicyMode,
563+
FileRequestIntent: o.FileRequestIntent,
561564
LegalHold: o.LegalHold,
562565
Metadata: o.Metadata,
563566
SourceContentMD5: o.SourceContentMD5,

sdk/storage/azblob/blockblob/client_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,62 @@ func (s *BlockBlobUnrecordedTestsSuite) TestStageBlockFromURLWithCRC64() {
565565
testcommon.ValidateBlobErrorCode(_require, err, bloberror.CRC64Mismatch)
566566
}
567567

568+
func (s *BlockBlobUnrecordedTestsSuite) TestStageBlockFromURLWithRequestIntent() {
569+
_require := require.New(s.T())
570+
testName := s.T().Name()
571+
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
572+
_require.NoError(err)
573+
containerName := testcommon.GenerateContainerName(testName)
574+
containerClient := testcommon.CreateNewContainer(context.Background(), _require, containerName, svcClient)
575+
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)
576+
577+
contentSize := 4 * 1024 // 4 KB
578+
r, _ := testcommon.GetDataAndReader(testName, contentSize)
579+
rsc := streaming.NopCloser(r)
580+
581+
srcBlob := containerClient.NewBlockBlobClient("src" + testcommon.GenerateBlobName(testName))
582+
destBlob := containerClient.NewBlockBlobClient("dst" + testcommon.GenerateBlobName(testName))
583+
584+
// Prepare source bbClient for copy.
585+
_, err = srcBlob.Upload(context.Background(), rsc, nil)
586+
_require.NoError(err)
587+
588+
// Get source blob url with SAS for StageFromURL.
589+
srcBlobParts, _ := blob.ParseURL(srcBlob.URL())
590+
sharedKeyCredential, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault)
591+
_require.NoError(err)
592+
perms := sas.BlobPermissions{Read: true}
593+
594+
srcBlobParts.SAS, err = sas.BlobSignatureValues{
595+
Protocol: sas.ProtocolHTTPS, // Users MUST use HTTPS (not HTTP)
596+
ExpiryTime: time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
597+
ContainerName: srcBlobParts.ContainerName,
598+
BlobName: srcBlobParts.BlobName,
599+
Permissions: perms.String(),
600+
}.SignWithSharedKey(sharedKeyCredential)
601+
_require.NoError(err)
602+
603+
srcBlobURLWithSAS := srcBlobParts.String()
604+
605+
// Stage blocks from URL.
606+
blockIDs := testcommon.GenerateBlockIDsList(2)
607+
requestIntent := blob.FileShareTokenIntentBackup
608+
609+
opts := blockblob.StageBlockFromURLOptions{
610+
FileRequestIntent: &requestIntent,
611+
}
612+
613+
stageResponse, err := destBlob.StageBlockFromURL(context.Background(), blockIDs[0], srcBlobURLWithSAS, &opts)
614+
_require.NoError(err)
615+
_require.NotNil(stageResponse)
616+
617+
// Check block list.
618+
blockList, err := destBlob.GetBlockList(context.Background(), blockblob.BlockListTypeAll, nil)
619+
_require.NoError(err)
620+
_require.NotNil(blockList.BlockList)
621+
_require.Nil(blockList.BlockList.CommittedBlocks)
622+
}
623+
568624
//
569625
// func (s *BlockBlobUnrecordedTestsSuite) TestCopyBlockBlobFromURL() {
570626
// _require := require.New(s.T())
@@ -1180,6 +1236,33 @@ func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURLWithHeaders() {
11801236
_require.EqualValues(resp.Metadata, testcommon.BasicMetadata)
11811237
}
11821238

1239+
func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromURLWithIntent() {
1240+
_require := require.New(s.T())
1241+
testName := s.T().Name()
1242+
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
1243+
_require.NoError(err)
1244+
1245+
requestIntent := blob.FileShareTokenIntentBackup
1246+
1247+
containerClient, _, destBlob, srcBlobURLWithSAS, _ := setUpPutBlobFromURLTest(testName, _require, svcClient)
1248+
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)
1249+
1250+
options := blockblob.UploadBlobFromURLOptions{
1251+
Tags: testcommon.BasicBlobTagsMap,
1252+
HTTPHeaders: &testcommon.BasicHeaders,
1253+
Metadata: testcommon.BasicMetadata,
1254+
FileRequestIntent: &requestIntent,
1255+
}
1256+
1257+
pbResp, err := destBlob.UploadBlobFromURL(context.Background(), srcBlobURLWithSAS, &options)
1258+
_require.NotNil(pbResp)
1259+
_require.NoError(err)
1260+
1261+
// Check dest and source properties
1262+
_, err = destBlob.GetProperties(context.Background(), nil)
1263+
_require.NoError(err)
1264+
}
1265+
11831266
func (s *BlockBlobUnrecordedTestsSuite) TestPutBlobFromUrlWithCPK() {
11841267
_require := require.New(s.T())
11851268
testName := s.T().Name()

sdk/storage/azblob/blockblob/models.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ type UploadBlobFromURLOptions struct {
8282
// Only Bearer type is supported. Credentials should be a valid OAuth access token to copy source.
8383
CopySourceAuthorization *string
8484

85+
// Valid value is backup
86+
FileRequestIntent *blob.FileShareTokenIntent
87+
8588
// Optional, default is true. Indicates if properties from the source blob should be copied.
8689
CopySourceBlobProperties *bool
8790

@@ -115,6 +118,7 @@ func (o *UploadBlobFromURLOptions) format() (*generated.BlockBlobClientPutBlobFr
115118
options := generated.BlockBlobClientPutBlobFromURLOptions{
116119
BlobTagsString: shared.SerializeBlobTagsToStrPtr(o.Tags),
117120
CopySourceAuthorization: o.CopySourceAuthorization,
121+
FileRequestIntent: o.FileRequestIntent,
118122
CopySourceBlobProperties: o.CopySourceBlobProperties,
119123
CopySourceTags: o.CopySourceTags,
120124
Metadata: o.Metadata,
@@ -164,6 +168,9 @@ type StageBlockFromURLOptions struct {
164168
// SourceContentValidation contains the validation mechanism used on the range of bytes read from the source.
165169
SourceContentValidation blob.SourceContentValidationType
166170

171+
// File request Intent. Valid value is backup.
172+
FileRequestIntent *blob.FileShareTokenIntent
173+
167174
// Range specifies a range of bytes. The default value is all bytes.
168175
Range blob.HTTPRange
169176

@@ -180,6 +187,7 @@ func (o *StageBlockFromURLOptions) format() (*generated.BlockBlobClientStageBloc
180187
options := &generated.BlockBlobClientStageBlockFromURLOptions{
181188
CopySourceAuthorization: o.CopySourceAuthorization,
182189
SourceRange: exported.FormatHTTPRange(o.Range),
190+
FileRequestIntent: o.FileRequestIntent,
183191
}
184192

185193
if o.SourceContentValidation != nil {

sdk/storage/azblob/internal/generated/autorest.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ go: true
77
clear-output-folder: false
88
version: "^3.0.0"
99
license-header: MICROSOFT_MIT_NO_VERSION
10-
input-file: "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/ae95eb6a4701d844bada7d1c4f5ecf4a7444e5b8/specification/storage/data-plane/Microsoft.BlobStorage/stable/2025-01-05/blob.json"
10+
input-file: "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/30318bc99a721df4ebdfdbf40696739ede75564f/specification/storage/data-plane/Microsoft.BlobStorage/stable/2025-07-05/blob.json"
1111
credential-scope: "https://storage.azure.com/.default"
1212
output-folder: ../generated
1313
file-prefix: "zz_"
@@ -67,7 +67,7 @@ directive:
6767
$.items.enum.push("permissions");
6868
```
6969
70-
### Updating service version to 2025-05-05
70+
### Updating service version to 2025-07-05
7171
```yaml
7272
directive:
7373
- from:
@@ -80,7 +80,7 @@ directive:
8080
where: $
8181
transform: >-
8282
return $.
83-
replaceAll(`[]string{"2025-01-05"}`, `[]string{ServiceVersion}`);
83+
replaceAll(`[]string{"2025-05-05"}`, `[]string{ServiceVersion}`);
8484
```
8585
8686
### Fix CRC Response Header in PutBlob response

sdk/storage/azblob/internal/generated/constants.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66

77
package generated
88

9-
const ServiceVersion = "2025-05-05"
9+
const ServiceVersion = "2025-07-05"

0 commit comments

Comments
 (0)