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

Fix container->container behaviour w/ preserve-permissions #2150

Merged
merged 3 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions cmd/copyEnumeratorInit.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,14 @@ func (cca *CookedCopyCmdArgs) initEnumerator(jobPartOrder common.CopyJobPartOrde
}

// When copying a container directly to a container, strip the top directory, unless we're attempting to persist permissions.
if srcLevel == ELocationLevel.Container() && dstLevel == ELocationLevel.Container() && cca.FromTo.From().IsRemote() && cca.FromTo.To().IsRemote() && !cca.preservePermissions.IsTruthy() {
cca.StripTopDir = true
if srcLevel == ELocationLevel.Container() && dstLevel == ELocationLevel.Container() && cca.FromTo.From().IsRemote() && cca.FromTo.To().IsRemote() {
if cca.preservePermissions.IsTruthy() {
// if we're preserving permissions, we need to keep the top directory, but with container->container, we don't need to add the container name to the path.
// asSubdir is a better option than stripTopDir as stripTopDir disincludes the root.
cca.asSubdir = false
} else {
cca.StripTopDir = true
}
}

// Create a Remote resource resolver
Expand Down
40 changes: 39 additions & 1 deletion e2etest/declarativeResourceManagers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package e2etest

import (
"github.com/Azure/azure-storage-azcopy/v10/azbfs"
"net/url"
"os"
"path"
Expand Down Expand Up @@ -235,6 +236,28 @@ func (r *resourceBlobContainer) createFiles(a asserter, s *scenario, isSource bo
options.accessTier = s.p.accessTier
}
scenarioHelper{}.generateBlobsFromList(a, options)

// set root ACL
if r.accountType == EAccountType.HierarchicalNamespaceEnabled() {
containerURLParts := azblob.NewBlobURLParts(r.containerURL.URL())

for _,v := range options.generateFromListOptions.fs {
if v.name == "" {
if v.creationProperties.adlsPermissionsACL == nil {
break
}

rootURL := TestResourceFactory{}.GetDatalakeServiceURL(r.accountType).NewFileSystemURL(containerURLParts.ContainerName).NewDirectoryURL("/")

_, err := rootURL.SetAccessControl(ctx, azbfs.BlobFSAccessControl{
ACL: *v.creationProperties.adlsPermissionsACL,
})
a.AssertNoErr(err)

break
}
}
}
}

func (r *resourceBlobContainer) createFile(a asserter, o *testObject, s *scenario, isSource bool) {
Expand Down Expand Up @@ -294,7 +317,22 @@ func (r *resourceBlobContainer) appendSourcePath(filePath string, useSas bool) {
}

func (r *resourceBlobContainer) getAllProperties(a asserter) map[string]*objectProperties {
return scenarioHelper{}.enumerateContainerBlobProperties(a, *r.containerURL)
objects := scenarioHelper{}.enumerateContainerBlobProperties(a, *r.containerURL)

if r.accountType == EAccountType.HierarchicalNamespaceEnabled() {
urlParts := azblob.NewBlobURLParts(r.containerURL.URL())
fsURL := TestResourceFactory{}.GetDatalakeServiceURL(r.accountType).NewFileSystemURL(urlParts.ContainerName).NewDirectoryURL("/")

ACL, err := fsURL.GetAccessControl(ctx)
a.AssertNoErr(err)

objects[""] = &objectProperties{
entityType: common.EEntityType.Folder(),
adlsPermissionsACL: &ACL.ACL,
}
}

return objects
}

func (r *resourceBlobContainer) downloadContent(a asserter, options downloadContentOptions) []byte {
Expand Down
55 changes: 54 additions & 1 deletion e2etest/declarativeScenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ package e2etest
import (
"crypto/md5"
"fmt"
"io"
"io/fs"
"net/url"
"os"
"path"
Expand Down Expand Up @@ -169,7 +171,55 @@ func (s *scenario) uploadLogs(logDir string) {
if s.state.result == nil || os.Getenv("AZCOPY_E2E_LOG_OUTPUT") == "" {
return // nothing to upload
}
s.a.Assert(os.Rename(logDir, filepath.Join(os.Getenv("AZCOPY_E2E_LOG_OUTPUT"), s.state.result.jobID.String())), equals(), nil)
// sometimes, the log dir cannot be copied because the destination is on another drive. So, we'll copy the files instead by hand.
files, err := os.ReadDir(logDir)
s.a.AssertNoErr(err, "Failed to read log dir")
jobId := ""
for _, file := range files { // first, find the job ID
if strings.HasSuffix(file.Name(), ".log") {
jobId = strings.TrimSuffix(strings.TrimSuffix(file.Name(), "-scanning"), ".log")
break
}
}

// Create the destination log directory
destLogDir := filepath.Join(os.Getenv("AZCOPY_E2E_LOG_OUTPUT"), jobId)
err = os.MkdirAll(destLogDir, os.ModePerm|os.ModeDir)
s.a.AssertNoErr(err, "Failed to create log dir")

// Copy the files by hand
err = filepath.WalkDir(logDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
relPath := strings.TrimPrefix(path, logDir)
if d.IsDir() {
err = os.MkdirAll(filepath.Join(destLogDir, relPath), os.ModePerm|os.ModeDir)
return err
}

// copy the file
srcFile, err := os.Open(path)
if err != nil {
return err
}

destFile, err := os.Create(filepath.Join(destLogDir, relPath))
if err != nil {
return err
}

defer srcFile.Close()
defer destFile.Close()

_, err = io.Copy(destFile, srcFile)
if err != nil {
return err
}

return err
})
s.a.AssertNoErr(err, "Failed to copy log files")
}

func (s *scenario) runHook(h hookFunc) bool {
Expand Down Expand Up @@ -412,6 +462,9 @@ func (s *scenario) getTransferInfo() (srcRoot string, dstRoot string, expectFold
addedDirAtDest = tf.destTarget
}
dstRoot = fmt.Sprintf("%s%c%s", dstRoot, os.PathSeparator, addedDirAtDest)
} else if s.state.source.isContainerLike() && s.state.dest.isContainerLike() && s.p.preserveSMBPermissions {
// Preserving permissions includes the root folder, but for container-container, we don't expect any added folder name.
expectRootFolder = true
} else {
if tf.objectTarget == "" && tf.destTarget == "" {
addedDirAtDest = srcBase
Expand Down
17 changes: 16 additions & 1 deletion e2etest/scenario_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,6 @@ func (s scenarioHelper) enumerateContainerBlobProperties(a asserter, containerUR
result := make(map[string]*objectProperties)

for marker := (azblob.Marker{}); marker.NotDone(); {

listBlob, err := containerURL.ListBlobsFlatSegment(context.TODO(), marker, azblob.ListBlobsSegmentOptions{Details: azblob.BlobListingDetails{Metadata: true, Tags: true}})
a.AssertNoErr(err)

Expand Down Expand Up @@ -895,6 +894,22 @@ func (s scenarioHelper) enumerateShareFileProperties(a asserter, shareURL azfile
result := make(map[string]*objectProperties)

root := shareURL.NewRootDirectoryURL()
rootProps, err := root.GetProperties(ctx)
a.AssertNoErr(err)
rootAttr := uint32(azfile.ParseFileAttributeFlagsString(rootProps.FileAttributes()))
var rootPerm *string
if permKey := rootProps.FilePermissionKey(); permKey != "" {
sharePerm, err := shareURL.GetPermission(ctx, permKey)
a.AssertNoErr(err, "Failed to get permissions from key")

rootPerm = &sharePerm.Permission
}
result[""] = &objectProperties{
entityType: common.EEntityType.Folder(),
smbPermissionsSddl: rootPerm,
smbAttributes: &rootAttr,
}

dirQ = append(dirQ, root)
for i := 0; i < len(dirQ); i++ {
currentDirURL := dirQ[i]
Expand Down
2 changes: 1 addition & 1 deletion e2etest/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (Validator) ValidateCopyTransfersAreScheduled(c asserter, isSrcEncoded bool

if transfer.Dst != os.DevNull { // Don't check if the destination is NUL-- It won't be correct.
// the relative paths should be equal
c.Assert(srcRelativeFilePath, equals(), dstRelativeFilePath)
c.Assert(dstRelativeFilePath, equals(), srcRelativeFilePath)
}

// look up the path from the expected transfers, make sure it exists
Expand Down
31 changes: 31 additions & 0 deletions e2etest/zt_preserve_smb_properties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,34 @@ func TestProperties_SMBTimes(t *testing.T) {
"",
)
}

func TestProperties_EnsureContainerBehavior(t *testing.T) {
RunScenarios(
t,
eOperation.Copy(),
eTestFromTo.Other(common.EFromTo.FileFile()),
eValidate.Auto(),
anonymousAuthOnly,
anonymousAuthOnly,
params{
recursive: true,
preserveSMBInfo: BoolPointer(true),
preserveSMBPermissions: true,
},
nil,
testFiles{
defaultSize: "1K",
shouldTransfer: []interface{}{
folder(""),
f("aeiou.txt"),
folder("a"),
f("a/asdf.txt"),
folder("b"),
f("b/1234.txt"),
},
},
EAccountType.Standard(),
EAccountType.Standard(),
"",
)
}