in e2etest/runner.go [260:408]
func (t *TestRunner) ExecuteAzCopyCommand(operation Operation, src, dst string, needsOAuth bool, oauthMode string, needsFromTo bool, fromTo common.FromTo, afterStart func() string, chToStdin <-chan string, logDir string) (CopyOrSyncCommandResult, bool, error) {
capLen := func(b []byte) []byte {
if len(b) < 1024 {
return b
} else {
return append(b[:1024], byte('\n'))
}
}
verb := ""
switch operation {
case eOperation.Copy():
verb = "copy"
case eOperation.Sync():
verb = "sync"
case eOperation.Remove():
verb = "remove"
case eOperation.Resume():
verb = "jobs resume"
case eOperation.Cancel():
verb = "cancel"
case eOperation.Benchmark():
verb = "bench"
default:
panic("unsupported operation type")
}
args := strings.Split(verb, " ")
args = append(args, src)
if operation.NeedsDst() {
args = append(args, dst)
}
args = append(args, t.computeArgs()...)
if needsFromTo {
args = append(args, "--from-to="+fromTo.String())
}
// pass along existing environment variables (because $HOME doesn't come along if we just use the OAuth vars, that can be troublesome!)
env := make([]string, len(os.Environ()))
copy(env, os.Environ())
if needsOAuth {
switch strings.ToLower(oauthMode) {
case common.EAutoLoginType.SPN().String():
tenId, appId, clientSecret := GlobalInputManager{}.GetServicePrincipalAuth()
env = append(env,
"AZCOPY_AUTO_LOGIN_TYPE="+common.Iff(oauthMode == "", common.EAutoLoginType.SPN().String(), oauthMode),
"AZCOPY_SPA_APPLICATION_ID="+appId,
"AZCOPY_SPA_CLIENT_SECRET="+clientSecret,
)
if tenId != "" {
env = append(env, "AZCOPY_TENANT_ID="+tenId)
}
case "", common.EAutoLoginType.AzCLI().String():
if os.Getenv("NEW_E2E_ENVIRONMENT") == AzurePipeline {
// We are already logged in with AzCLI in Azure Pipeline
} else {
tenId, appId, clientSecret := GlobalInputManager{}.GetServicePrincipalAuth()
args := []string{
"login",
"--service-principal",
"-u=" + appId,
"-p=" + clientSecret,
}
if tenId != "" {
args = append(args, "--tenant="+tenId)
env = append(env, "AZCOPY_TENANT_ID="+tenId)
}
out, err := exec.Command("az", args...).Output()
if err != nil {
e, ok := err.(*exec.ExitError)
if ok {
return CopyOrSyncCommandResult{}, false, fmt.Errorf("%s\n%s\nfailed to login with AzCli: %s", e.Stderr, out, err.Error())
} else {
return CopyOrSyncCommandResult{}, false, fmt.Errorf("failed to login with AzCli: %s", err.Error())
}
}
}
env = append(env, "AZCOPY_AUTO_LOGIN_TYPE=AzCLI")
case "pscred":
var script string
if os.Getenv("NEW_E2E_ENVIRONMENT") == AzurePipeline {
tenId, clientId, token := GlobalInputManager{}.GetWorkloadIdentity()
cmd := `Connect-AzAccount -ApplicationId %s -Tenant %s -FederatedToken %s`
script = fmt.Sprintf(cmd, clientId, tenId, token)
} else {
tenId, appId, clientSecret := GlobalInputManager{}.GetServicePrincipalAuth()
cmd := `$secret = ConvertTo-SecureString -String %s -AsPlainText -Force;
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList %s, $secret;
Connect-AzAccount -ServicePrincipal -Credential $cred`
if tenId != "" {
cmd += " -Tenant " + tenId
}
script = fmt.Sprintf(cmd, clientSecret, appId)
}
out, err := exec.Command("pwsh", "-Command", script).Output()
if err != nil {
e := err.(*exec.ExitError)
e, ok := err.(*exec.ExitError)
if ok {
return CopyOrSyncCommandResult{}, false, fmt.Errorf("%s\n%s\nfailed to login with Powershell: %s", e.Stderr, out, err.Error())
} else {
return CopyOrSyncCommandResult{}, false, fmt.Errorf("failed to login with Powershell: %s", err.Error())
}
}
env = append(env, "AZCOPY_AUTO_LOGIN_TYPE=PsCred")
default:
return CopyOrSyncCommandResult{}, false, errors.New("Unsupported OAuth mode " + oauthMode)
}
}
if logDir != "" {
env = append(env, "AZCOPY_LOG_LOCATION="+logDir)
env = append(env, "AZCOPY_JOB_PLAN_LOCATION="+filepath.Join(logDir, "plans"))
}
out, err := t.execDebuggableWithOutput(GlobalInputManager{}.GetExecutablePath(), args, env, afterStart, chToStdin)
wasClean := true
stdErr := make([]byte, 0)
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
stdErr = capLen(ee.Stderr) // cap length of this, because it can be a panic. But don't cap stdout, because we need its last line in newCopyOrSyncCommandResult
if len(stdErr) > 0 {
wasClean = false // something was written to stderr, probably a panic
}
}
}
if wasClean {
// either it succeeded, for it returned a failure code in a clean (non-panic) way.
// In both cases, we want out to be parsed, to get us the job ID. E.g. maybe 1 transfer out of several failed,
// and that's what we'er actually testing for (so can't treat this as a fatal error).
r, ok := newCopyOrSyncCommandResult(string(out))
if ok {
return r, true, err
} else {
err = fmt.Errorf("could not parse AzCopy output. Run error, if any, was '%w'", err)
}
}
return CopyOrSyncCommandResult{},
false,
fmt.Errorf("azcopy run error: %w\n with stderr: %s\n and stdout: %s\n from args %v", err, stdErr, out, args)
}