diff --git a/cmd/copy.go b/cmd/copy.go index 1dc1cc0b3..24525e28d 100644 --- a/cmd/copy.go +++ b/cmd/copy.go @@ -1607,6 +1607,11 @@ func (cca *CookedCopyCmdArgs) processCopyJobPartOrders() (err error) { cca.StripTopDir = true } + // TODO: Remove this check when FileBlob w/ File OAuth works. + if cca.FromTo.IsS2S() && cca.FromTo.From() == common.ELocation.File() && srcCredInfo.CredentialType.IsAzureOAuth() && cca.FromTo.To() != common.ELocation.File() { + return fmt.Errorf("S2S copy from Azure File authenticated with Azure AD to Blob/BlobFS is not supported") + } + switch { case cca.FromTo.IsUpload(), cca.FromTo.IsDownload(), cca.FromTo.IsS2S(): // Execute a standard copy command diff --git a/cmd/sync.go b/cmd/sync.go index e98891384..50923c74c 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -705,6 +705,11 @@ func (cca *cookedSyncCmdArgs) process() (err error) { } } + // TODO: Remove this check when FileBlob w/ File OAuth works. + if cca.fromTo.IsS2S() && cca.fromTo.From() == common.ELocation.File() && srcCredInfo.CredentialType.IsAzureOAuth() && cca.fromTo.To() != common.ELocation.File() { + return fmt.Errorf("S2S sync from Azure File authenticated with Azure AD to Blob/BlobFS is not supported") + } + enumerator, err := cca.initEnumerator(ctx) if err != nil { return err diff --git a/e2etest/newe2e_resource_managers_file.go b/e2etest/newe2e_resource_managers_file.go index ba7bbd4e7..7d235f203 100644 --- a/e2etest/newe2e_resource_managers_file.go +++ b/e2etest/newe2e_resource_managers_file.go @@ -54,8 +54,7 @@ func (s *FileServiceResourceManager) DefaultAuthType() ExplicitCredentialTypes { } func (s *FileServiceResourceManager) ValidAuthTypes() ExplicitCredentialTypes { - // OAuth isn't supported *quite yet* in azcopy! - return EExplicitCredentialType.With(EExplicitCredentialType.SASToken()) + return EExplicitCredentialType.With(EExplicitCredentialType.SASToken(), EExplicitCredentialType.OAuth()) } func (s *FileServiceResourceManager) WithSpecificAuthType(cred ExplicitCredentialTypes, a Asserter, opts ...CreateAzCopyTargetOptions) AzCopyTarget { diff --git a/e2etest/newe2e_task_resourcemanagement.go b/e2etest/newe2e_task_resourcemanagement.go index 4e1965679..0154f717b 100644 --- a/e2etest/newe2e_task_resourcemanagement.go +++ b/e2etest/newe2e_task_resourcemanagement.go @@ -6,6 +6,7 @@ import ( "github.com/Azure/azure-storage-azcopy/v10/cmd" "github.com/Azure/azure-storage-azcopy/v10/common" "io" + "strings" ) // ResourceTracker tracks resources @@ -216,3 +217,15 @@ func ValidateListOutput(a Asserter, stdout AzCopyStdout, expectedObjects map[AzC return KVPair[AzCopyOutputKey, cmd.AzCopyListObject]{Key: AzCopyOutputKey{Path: obj.Path, VersionId: obj.VersionId}, Value: obj} }}, o...) } + +func ValidateErrorOutput(a Asserter, stdout AzCopyStdout, errorMsg string) { + if dryrunner, ok := a.(DryrunAsserter); ok && dryrunner.Dryrun() { + return + } + for _, line := range stdout.(*AzCopyRawStdout).RawOutput { + if strings.Contains(line, errorMsg) { + return + } + } + a.Error("expected error message not found in azcopy output") +} diff --git a/e2etest/newe2e_task_runazcopy.go b/e2etest/newe2e_task_runazcopy.go index 3931e0649..0efa4eaf5 100644 --- a/e2etest/newe2e_task_runazcopy.go +++ b/e2etest/newe2e_task_runazcopy.go @@ -313,7 +313,7 @@ func RunAzCopy(a ScenarioAsserter, commandSpec AzCopyCommand) (AzCopyStdout, *Az } err = command.Wait() - a.Assert("wait for finalize", IsNil{}, err) + a.Assert("wait for finalize", common.Iff[Assertion](commandSpec.ShouldFail, Not{IsNil{}}, IsNil{}), err) a.Assert("expected exit code", common.Iff[Assertion](commandSpec.ShouldFail, Not{Equal{}}, Equal{}), 0, command.ProcessState.ExitCode()) diff --git a/e2etest/zt_newe2e_file_oauth_test.go b/e2etest/zt_newe2e_file_oauth_test.go new file mode 100644 index 000000000..95c1de0c7 --- /dev/null +++ b/e2etest/zt_newe2e_file_oauth_test.go @@ -0,0 +1,39 @@ +package e2etest + +import ( + "fmt" + "github.com/Azure/azure-storage-azcopy/v10/common" +) + +func init() { + suiteManager.RegisterSuite(&FileOAuthTestSuite{}) +} + +type FileOAuthTestSuite struct{} + +func (s *FileOAuthTestSuite) Scenario_FileBlobOAuthError(svm *ScenarioVariationManager) { + azCopyVerb := ResolveVariation(svm, []AzCopyVerb{AzCopyVerbCopy, AzCopyVerbSync}) // Calculate verb early to create the destination object early + + srcContainer := CreateResource[ContainerResourceManager](svm, GetRootResource(svm, common.ELocation.File()), ResourceDefinitionContainer{}) + dstContainer := CreateResource[ContainerResourceManager](svm, GetRootResource(svm, common.ELocation.Blob()), ResourceDefinitionContainer{}) + + dstAuth := ResolveVariation(svm, []ExplicitCredentialTypes{EExplicitCredentialType.SASToken(), EExplicitCredentialType.OAuth()}) + + stdout, _ := RunAzCopy( + svm, + AzCopyCommand{ + Verb: azCopyVerb, + Targets: []ResourceManager{ + TryApplySpecificAuthType(srcContainer, EExplicitCredentialType.OAuth(), svm, CreateAzCopyTargetOptions{}), + TryApplySpecificAuthType(dstContainer, dstAuth, svm, CreateAzCopyTargetOptions{}), + }, + Flags: CopyFlags{ + CopySyncCommonFlags: CopySyncCommonFlags{ + Recursive: pointerTo(true), + }, + }, + ShouldFail: true, + }) + + ValidateErrorOutput(svm, stdout, fmt.Sprintf("S2S %s from Azure File authenticated with Azure AD to Blob/BlobFS is not supported", azCopyVerb)) +}