Skip to content

Commit aedcd0b

Browse files
authored
Merge pull request #1684 from Azure/dev
AzCopy Release 10.14.0
2 parents 475f8e1 + 48531aa commit aedcd0b

File tree

92 files changed

+2047
-640
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+2047
-640
lines changed

ChangeLog.md

+26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11

22
# Change Log
33

4+
## Version 10.14.0
5+
6+
### New features
7+
1. Feature to [permanently delete](https://docs.microsoft.com/en-us/rest/api/storageservices/delete-blob#remarks) soft-deleted
8+
snapshots/versions of the blobs has been added (preview). `--permanent-delete=none/snapshots/version/snapshotsandversions`.
9+
2. Feature to preserve properties and ACLs when copying to Azure file share root directory.
10+
3. Pin all APIs to use the default service version `2020-04-08` and let users decide the service version via
11+
`AZCOPY_DEFAULT_SERVICE_API_VERSION` environment variable. Previously, few APIs were not respecting the `AZCOPY_DEFAULT_SERVICE_API_VERSION` environment variable.
12+
13+
### Bug fixes
14+
1. Fixed issue in which AzCopy failed to copy to classic blob container with `preserve blob access tier`.
15+
2. Fixed [issue 1630](https://github.com/Azure/azure-storage-azcopy/issues/1630) : AzCopy created extra empty
16+
directories at destination while performing S2S transfer from one ADLS Gen2 account to another ADLS Gen2 account.
17+
3. Changed the way AzCopy was using to obtain and set ACLs to ensure accuracy.
18+
4. Clarify error message for `azcopy sync` when source or destination cannot be detected.
19+
5. Report error when client provided key(CPK) encryption is applied to DFS endpoint.
20+
6. Fixed [issue 1596](https://github.com/Azure/azure-storage-azcopy/issues/1596) : AzCopy failed to transfer files
21+
(with '/.' in their path) from AWS S3 to Azure blob storage.
22+
7. Fixed [issue 1474](https://github.com/Azure/azure-storage-azcopy/issues/1474) : AzCopy panicked when trying to re-create an already open plan file.
23+
8. Improved handling of Auth error against single file.
24+
9. Fixed [issue 1640](https://github.com/Azure/azure-storage-azcopy/issues/1640) : Recursive copy from GCS bucket to Azure container failed
25+
with `FileIgnored` error when using `--exclude-path`.
26+
10. Fixed [issue 1655](https://github.com/Azure/azure-storage-azcopy/issues/1655) : AzCopy panicked when using `--include-before` flag.
27+
11. Fixed [issue 1609](https://github.com/Azure/azure-storage-azcopy/issues/1609) : `blockid` converted to lower case in AzCopy logs.
28+
12. Fixed [issue 1643](https://github.com/Azure/azure-storage-azcopy/issues/1643), [issue 1661](https://github.com/Azure/azure-storage-azcopy/issues/1661) : Updated Golang version to `1.16.10` to fix security vulnerabilities in Golang packages.
29+
430
## Version 10.13.0
531

632
### New features

azbfs/parsing_urls.go

+6
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ func NewBfsURLParts(u url.URL) BfsURLParts {
8080
return up
8181
}
8282

83+
func GetSasQueryParams(sas string) SASQueryParameters {
84+
paramsMap, _ := url.ParseQuery(sas)
85+
SAS := newSASQueryParameters(paramsMap, true)
86+
return SAS
87+
}
88+
8389
// URL returns a URL object whose fields are initialized from the BfsURLParts fields.
8490
func (up BfsURLParts) URL() url.URL {
8591
path := ""

azbfs/url_directory.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,17 @@ func (d DirectoryURL) Rename(ctx context.Context, options RenameDirectoryOptions
174174
fileSystemName = &d.filesystem
175175
}
176176

177-
renameSource := "/" + d.filesystem + "/" + d.pathParameter
178-
179177
urlParts := NewBfsURLParts(d.directoryClient.URL())
180178
urlParts.FileSystemName = *fileSystemName
181179
urlParts.DirectoryOrFilePath = options.DestinationPath
180+
// ensure we use our source's SAS token in the x-ms-rename-source header
181+
renameSource := "/" + d.filesystem + "/" + d.pathParameter + "?" + urlParts.SAS.Encode()
182182

183+
// if we're changing our source SAS to a new SAS in the rename
184+
// in the case the user wants to have limited permissions per directory: sas1 for directory1 and sas2 for directory2
185+
if options.DestinationSas != nil && *options.DestinationSas != "" {
186+
urlParts.SAS = GetSasQueryParams(*options.DestinationSas)
187+
}
183188
destinationDirectoryURL := NewDirectoryURL(urlParts.URL(), d.directoryClient.Pipeline())
184189

185190
_, err := destinationDirectoryURL.directoryClient.Create(ctx, *fileSystemName, options.DestinationPath, PathResourceNone, nil, PathRenameModeLegacy,

azbfs/url_file.go

+15-10
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ type BlobFSHTTPHeaders struct {
2929

3030
// BlobFSAccessControl represents the set of custom headers available for defining access conditions for the content.
3131
type BlobFSAccessControl struct {
32-
Owner string
33-
Group string
34-
ACL string // Combining ACL & Permissions = invalid for SetAccessControl.
32+
Owner string
33+
Group string
34+
ACL string // Combining ACL & Permissions = invalid for SetAccessControl.
3535
Permissions string
3636
}
3737

@@ -72,18 +72,18 @@ func (f FileURL) GetParentDir() (DirectoryURL, error) {
7272

7373
// Create creates a new file or replaces a file. Note that this method only initializes the file.
7474
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/create.
75-
func (f FileURL) Create(ctx context.Context, headers BlobFSHTTPHeaders) (*PathCreateResponse, error) {
76-
return f.CreateWithOptions(ctx, CreateFileOptions{Headers: headers})
75+
func (f FileURL) Create(ctx context.Context, headers BlobFSHTTPHeaders, permissions BlobFSAccessControl) (*PathCreateResponse, error) {
76+
return f.CreateWithOptions(ctx, CreateFileOptions{Headers: headers}, permissions)
7777
}
7878

7979
// Create creates a new file or replaces a file. Note that this method only initializes the file.
8080
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/create.
81-
func (f FileURL) CreateWithOptions(ctx context.Context, options CreateFileOptions) (*PathCreateResponse, error) {
81+
func (f FileURL) CreateWithOptions(ctx context.Context, options CreateFileOptions, permissions BlobFSAccessControl) (*PathCreateResponse, error) {
8282
return f.fileClient.Create(ctx, f.fileSystemName, f.path, PathResourceFile,
8383
nil, PathRenameModeNone, nil, nil, nil, nil,
8484
&options.Headers.CacheControl, &options.Headers.ContentType, &options.Headers.ContentEncoding,
8585
&options.Headers.ContentLanguage, &options.Headers.ContentDisposition, nil, nil, nil,
86-
buildMetadataString(options.Metadata), nil, nil,
86+
buildMetadataString(options.Metadata), &permissions.Permissions, nil,
8787
nil, nil, nil, nil, nil,
8888
nil, nil, nil, nil, nil,
8989
nil)
@@ -224,12 +224,17 @@ func (f FileURL) Rename(ctx context.Context, options RenameFileOptions) (FileURL
224224
fileSystemName = &f.fileSystemName
225225
}
226226

227-
renameSource := "/" + f.fileSystemName + "/" + f.path
228-
229227
urlParts := NewBfsURLParts(f.fileClient.URL())
230228
urlParts.FileSystemName = *fileSystemName
231229
urlParts.DirectoryOrFilePath = options.DestinationPath
230+
// ensure we use our source's SAS token in the x-ms-rename-source header
231+
renameSource := "/" + f.fileSystemName + "/" + f.path + "?" + urlParts.SAS.Encode()
232232

233+
// if we're changing our source SAS to a new SAS in the rename
234+
// in the case the user wants to have limited permissions per directory: sas1 for file1 and sas2 for file2
235+
if options.DestinationSas != nil && *options.DestinationSas != "" {
236+
urlParts.SAS = GetSasQueryParams(*options.DestinationSas)
237+
}
233238
destinationFileURL := NewFileURL(urlParts.URL(), f.fileClient.Pipeline())
234239

235240
_, err := destinationFileURL.fileClient.Create(ctx, *fileSystemName, options.DestinationPath, PathResourceNone, nil, PathRenameModeLegacy,
@@ -279,4 +284,4 @@ func (f FileURL) SetAccessControl(ctx context.Context, permissions BlobFSAccessC
279284
nil, nil, &permissions.Owner, &permissions.Group, perms, acl,
280285
nil, nil, nil, nil, &overrideHttpVerb,
281286
nil, nil, nil, nil)
282-
}
287+
}

azbfs/zc_directoryRequestOptions.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ type RenameDirectoryOptions struct {
1717
DestinationFileSystem *string
1818
// The destination path for the directory.
1919
DestinationPath string
20-
}
20+
// The new SAS for a destination Directory
21+
DestinationSas *string
22+
}

azbfs/zc_fileRequestOptions.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ type RenameFileOptions struct {
1717
DestinationFileSystem *string
1818
// The destination path for the file.
1919
DestinationPath string
20-
}
20+
// The new SAS for a destination file
21+
DestinationSas *string
22+
}

azbfs/zt_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func createNewFileFromFileSystem(c *chk.C, fileSystem azbfs.FileSystemURL) (file
141141

142142
file, name = getFileURLFromDirectory(c, dir)
143143

144-
cResp, err := file.Create(ctx, azbfs.BlobFSHTTPHeaders{})
144+
cResp, err := file.Create(ctx, azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
145145
c.Assert(err, chk.IsNil)
146146
c.Assert(cResp.StatusCode(), chk.Equals, 201)
147147

azbfs/zt_url_directory_test.go

+104-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package azbfs_test
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
7+
"net/url"
8+
"strings"
9+
"time"
610

711
"github.com/Azure/azure-storage-azcopy/v10/azbfs"
812
chk "gopkg.in/check.v1"
@@ -127,7 +131,7 @@ func (dus *DirectoryUrlSuite) TestCreateDirectoryAndFiles(c *chk.C) {
127131

128132
// Create fileUrl from directoryUrl and create file inside the directory
129133
fileUrl, _ := getFileURLFromDirectory(c, dirUrl)
130-
fresp, err := fileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{})
134+
fresp, err := fileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
131135
defer delFile(c, fileUrl)
132136

133137
c.Assert(err, chk.IsNil)
@@ -237,7 +241,7 @@ func (dus *DirectoryUrlSuite) TestDirectoryStructure(c *chk.C) {
237241

238242
// Create a file inside directory
239243
fileUrl, _ := getFileURLFromDirectory(c, dirUrl)
240-
fresp, err := fileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{})
244+
fresp, err := fileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
241245
defer delFile(c, fileUrl)
242246

243247
c.Assert(err, chk.IsNil)
@@ -250,7 +254,7 @@ func (dus *DirectoryUrlSuite) TestDirectoryStructure(c *chk.C) {
250254

251255
// create a file inside the sub-dir created above
252256
subDirfileUrl, _ := getFileURLFromDirectory(c, subDirUrl)
253-
fresp, err = subDirfileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{})
257+
fresp, err = subDirfileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
254258
defer delFile(c, subDirfileUrl)
255259

256260
c.Assert(err, chk.IsNil)
@@ -292,7 +296,7 @@ func (dus *DirectoryUrlSuite) TestListDirectoryWithSpaces(c *chk.C) {
292296

293297
// Create a file inside directory
294298
fileUrl, _ := getFileURLFromDirectory(c, dirUrl)
295-
_, err = fileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{})
299+
_, err = fileUrl.Create(context.Background(), azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
296300
defer delFile(c, fileUrl)
297301

298302
// list the directory created above.
@@ -394,7 +398,7 @@ func (dus *DirectoryUrlSuite) TestSetACL(c *chk.C) {
394398

395399
// Create a file
396400
fileUrl := dirURL.NewFileURL("foo.bar")
397-
_, err = fileUrl.Create(ctx, azbfs.BlobFSHTTPHeaders{})
401+
_, err = fileUrl.Create(ctx, azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
398402
c.Assert(err, chk.IsNil)
399403

400404
// Grab it's default ACLs
@@ -416,3 +420,98 @@ func (dus *DirectoryUrlSuite) TestSetACL(c *chk.C) {
416420

417421
// Don't bother testing the root ACLs, since it calls into the directoryclient
418422
}
423+
424+
func (s *FileURLSuite) TestRenameDirectoryWithSas(c *chk.C) {
425+
name, key := getAccountAndKey()
426+
credential := azbfs.NewSharedKeyCredential(name, key)
427+
sasQueryParams, err := azbfs.AccountSASSignatureValues{
428+
Protocol: azbfs.SASProtocolHTTPS,
429+
ExpiryTime: time.Now().Add(48 * time.Hour),
430+
Permissions: azbfs.AccountSASPermissions{Read: true, List: true, Write: true, Delete: true, Add: true, Create: true, Update: true, Process: true}.String(),
431+
Services: azbfs.AccountSASServices{File: true, Blob: true, Queue: true}.String(),
432+
ResourceTypes: azbfs.AccountSASResourceTypes{Service: true, Container: true, Object: true}.String(),
433+
}.NewSASQueryParameters(credential)
434+
c.Assert(err, chk.IsNil)
435+
436+
qp := sasQueryParams.Encode()
437+
rawURL := fmt.Sprintf("https://%s.dfs.core.windows.net/?%s",
438+
credential.AccountName(), qp)
439+
fullURL, err := url.Parse(rawURL)
440+
c.Assert(err, chk.IsNil)
441+
442+
fsu := azbfs.NewServiceURL(*fullURL, azbfs.NewPipeline(azbfs.NewAnonymousCredential(), azbfs.PipelineOptions{}))
443+
444+
fileSystemURL, _ := createNewFileSystem(c, fsu)
445+
defer delFileSystem(c, fileSystemURL)
446+
447+
dirURL, dirName := createNewDirectoryFromFileSystem(c, fileSystemURL)
448+
dirRename := dirName + "rename"
449+
450+
renamedDirURL, err := dirURL.Rename(context.Background(), azbfs.RenameDirectoryOptions{DestinationPath: dirRename})
451+
c.Assert(renamedDirURL, chk.NotNil)
452+
c.Assert(err, chk.IsNil)
453+
454+
// Check that the old directory does not exist
455+
getPropertiesResp, err := dirURL.GetProperties(context.Background())
456+
c.Assert(err, chk.NotNil) // TODO: I want to check the status code is 404 but not sure how since the resp is nil
457+
c.Assert(getPropertiesResp, chk.IsNil)
458+
459+
// Check that the renamed directory does exist
460+
getPropertiesResp, err = renamedDirURL.GetProperties(context.Background())
461+
c.Assert(getPropertiesResp.StatusCode(), chk.Equals, http.StatusOK)
462+
c.Assert(err, chk.IsNil)
463+
}
464+
465+
func (s *FileURLSuite) TestRenameDirectoryWithDestinationSas(c *chk.C) {
466+
name, key := getAccountAndKey()
467+
credential := azbfs.NewSharedKeyCredential(name, key)
468+
sourceSasQueryParams, err := azbfs.AccountSASSignatureValues{
469+
Protocol: azbfs.SASProtocolHTTPS,
470+
ExpiryTime: time.Now().Add(48 * time.Hour),
471+
Permissions: azbfs.AccountSASPermissions{Read: true, List: true, Write: true, Delete: true, Add: true, Create: true, Update: true, Process: true}.String(),
472+
Services: azbfs.AccountSASServices{File: true, Blob: true, Queue: true}.String(),
473+
ResourceTypes: azbfs.AccountSASResourceTypes{Service: true, Container: true, Object: true}.String(),
474+
}.NewSASQueryParameters(credential)
475+
c.Assert(err, chk.IsNil)
476+
destinationSasQueryParams, err := azbfs.AccountSASSignatureValues{
477+
Protocol: azbfs.SASProtocolHTTPS,
478+
ExpiryTime: time.Now().Add(24 * time.Hour),
479+
Permissions: azbfs.AccountSASPermissions{Read: true, Write: true, Delete: true, Add: true, Create: true, Update: true, Process: true}.String(),
480+
Services: azbfs.AccountSASServices{File: true, Blob: true}.String(),
481+
ResourceTypes: azbfs.AccountSASResourceTypes{Service: true, Container: true, Object: true}.String(),
482+
}.NewSASQueryParameters(credential)
483+
c.Assert(err, chk.IsNil)
484+
485+
sourceQp := sourceSasQueryParams.Encode()
486+
destQp := destinationSasQueryParams.Encode()
487+
rawURL := fmt.Sprintf("https://%s.dfs.core.windows.net/?%s",
488+
credential.AccountName(), sourceQp)
489+
fullURL, err := url.Parse(rawURL)
490+
c.Assert(err, chk.IsNil)
491+
492+
fsu := azbfs.NewServiceURL(*fullURL, azbfs.NewPipeline(azbfs.NewAnonymousCredential(), azbfs.PipelineOptions{}))
493+
494+
fileSystemURL, _ := createNewFileSystem(c, fsu)
495+
defer delFileSystem(c, fileSystemURL)
496+
497+
dirURL, dirName := createNewDirectoryFromFileSystem(c, fileSystemURL)
498+
dirRename := dirName + "rename"
499+
500+
renamedDirURL, err := dirURL.Rename(
501+
context.Background(), azbfs.RenameDirectoryOptions{DestinationPath: dirRename, DestinationSas: &destQp})
502+
c.Assert(renamedDirURL, chk.NotNil)
503+
c.Assert(err, chk.IsNil)
504+
found := strings.Contains(renamedDirURL.String(), destQp)
505+
// make sure the correct SAS is used
506+
c.Assert(found, chk.Equals, true)
507+
508+
// Check that the old directory does not exist
509+
getPropertiesResp, err := dirURL.GetProperties(context.Background())
510+
c.Assert(err, chk.NotNil) // TODO: I want to check the status code is 404 but not sure how since the resp is nil
511+
c.Assert(getPropertiesResp, chk.IsNil)
512+
513+
// Check that the renamed directory does exist
514+
getPropertiesResp, err = renamedDirURL.GetProperties(context.Background())
515+
c.Assert(getPropertiesResp.StatusCode(), chk.Equals, http.StatusOK)
516+
c.Assert(err, chk.IsNil)
517+
}

0 commit comments

Comments
 (0)