func()

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)
}