func RunAzCopy()

in e2etest/newe2e_task_runazcopy.go [317:518]


func RunAzCopy(a ScenarioAsserter, commandSpec AzCopyCommand) (AzCopyStdout, *AzCopyJobPlan) {
	if a.Dryrun() {
		return nil, &AzCopyJobPlan{}
	}
	a.HelperMarker().Helper()
	var flagMap map[string]string
	var envMap map[string]string

	// we have no need to update our context manager, Fetch should do it for us.
	envCtx := FetchAzCopyEnvironmentContext(a)
	envCtx.SetupCleanup(a) // Make sure we add the cleanup hook; the sync.Once ensures idempotency.

	// register our environment, or create a new one if needed.
	var runNum uint
	if env := commandSpec.Environment; env == nil {
		commandSpec.Environment = envCtx.CreateEnvironment()
	} else {
		runNum = envCtx.RegisterEnvironment(env)
	}

	ctx := context.WithValue(envCtx, AzCopyRunNumKey{}, runNum)
	ctx = context.WithValue(ctx, AzCopyEnvironmentKey{}, commandSpec.Environment)

	// separate these from the struct so their execution order is fixed
	// Setup the positional args
	args := func() []string {
		if commandSpec.Environment == nil {
			commandSpec.Environment = &AzCopyEnvironment{}
		}

		out := []string{GlobalConfig.AzCopyExecutableConfig.ExecutablePath}
		out = append(out, strings.Split(string(commandSpec.Verb), " ")...)

		for _, v := range commandSpec.PositionalArgs {
			out = append(out, v)
		}

		for _, v := range commandSpec.Targets {
			out = append(out, commandSpec.applyTargetAuth(a, v))
		}

		if commandSpec.Flags == nil {
			switch commandSpec.Verb {
			case AzCopyVerbCopy:
				commandSpec.Flags = CopyFlags{}
			case AzCopyVerbSync:
				commandSpec.Flags = SyncFlags{}
			case AzCopyVerbList:
				commandSpec.Flags = ListFlags{}
			case AzCopyVerbLogin:
				commandSpec.Flags = LoginFlags{}
			case AzCopyVerbLoginStatus:
				commandSpec.Flags = LoginStatusFlags{}
			case AzCopyVerbRemove:
				commandSpec.Flags = RemoveFlags{}
			default:
				commandSpec.Flags = GlobalFlags{}
			}
		}

		flagMap = MapFromTags(reflect.ValueOf(commandSpec.Flags), "flag", a, ctx)
		for k, v := range flagMap {
			out = append(out, fmt.Sprintf("--%s=%s", k, v))
		}

		return out
	}()
	// Setup the env vars
	env := func() []string {
		out := make([]string, 0)

		envMap = MapFromTags(reflect.ValueOf(commandSpec.Environment), "env", a, ctx)

		for k, v := range envMap {
			out = append(out, fmt.Sprintf("%s=%s", k, v))
		}

		if commandSpec.Environment.InheritEnvironment != nil {
			ieMap := commandSpec.Environment.InheritEnvironment
			if ieMap["*"] {
				out = append(out, os.Environ()...)
			} else {
				for _, v := range os.Environ() {
					key := v[:strings.Index(v, "=")]

					if ieMap[strings.ToLower(key)] {
						out = append(out, v)
					}
				}
			}
		}

		return out
	}()

	var out = commandSpec.Stdout
	if out == nil { // Select the correct stdoutput parser
		switch {
		// Dry-run parser
		case strings.EqualFold(flagMap["dry-run"], "true") && (strings.EqualFold(flagMap["output-type"], "json") || strings.EqualFold(flagMap["output-type"], "text") || flagMap["output-type"] == ""): //  Dryrun has its own special sort of output, that supports non-json output.
			jsonMode := strings.EqualFold(flagMap["output-type"], "json")
			var fromTo common.FromTo
			if !jsonMode && len(commandSpec.Targets) >= 2 {
				fromTo = common.FromTo(commandSpec.Targets[0].Location())<<8 | common.FromTo(commandSpec.Targets[1].Location())
			}
			out = &AzCopyParsedDryrunStdout{
				JsonMode: jsonMode,
				fromTo:   fromTo,
				Raw:      make(map[string]bool),
			}

		// Text formats don't get parsed usually
		case !strings.EqualFold(flagMap["output-type"], "json"):
			out = &AzCopyRawStdout{}

		// Copy/sync/remove share the same output format
		case commandSpec.Verb == AzCopyVerbCopy || commandSpec.Verb == AzCopyVerbSync || commandSpec.Verb == AzCopyVerbRemove:
			out = &AzCopyParsedCopySyncRemoveStdout{
				JobPlanFolder: *commandSpec.Environment.JobPlanLocation,
				LogFolder:     *commandSpec.Environment.LogLocation,
			}

		// List
		case commandSpec.Verb == AzCopyVerbList:
			out = &AzCopyParsedListStdout{}

		// Jobs list
		case commandSpec.Verb == AzCopyVerbJobsList:
			out = &AzCopyParsedJobsListStdout{}

		// Jobs resume
		case commandSpec.Verb == AzCopyVerbJobsResume:
			out = &AzCopyParsedCopySyncRemoveStdout{ // Resume command treated the same as copy/sync/remove
				JobPlanFolder: *commandSpec.Environment.JobPlanLocation,
				LogFolder:     *commandSpec.Environment.LogLocation,
			}

		// Login status
		case commandSpec.Verb == AzCopyVerbLoginStatus:
			out = &AzCopyParsedLoginStatusStdout{}

		// Login (interactive)
		case commandSpec.Verb == AzCopyVerbLogin:
			var lType common.AutoLoginType
			if ltStr := flagMap["login-type"]; ltStr != "" {
				_ = lType.Parse(ltStr)
			}

			if lType.IsInteractive() {
				out = NewAzCopyInteractiveStdout(a)
				break
			}

			fallthrough

		default: // We don't know how to parse this.
			out = &AzCopyRawStdout{}
		}
	}

	stderr := &bytes.Buffer{}
	command := exec.Cmd{
		Path: GlobalConfig.AzCopyExecutableConfig.ExecutablePath,
		Args: args,
		Env:  env,

		Stdout: out, // todo
		Stderr: stderr,
	}
	in, err := command.StdinPipe()
	a.NoError("get stdin pipe", err)

	err = command.Start()
	a.Assert("run command", IsNil{}, err)

	if isLaunchedByDebugger {
		beginAzCopyDebugging(in)
	}

	err = command.Wait()

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

	// validate log file retention for jobs clean command before the job logs are cleaned up and uploaded
	if !a.Failed() && commandSpec.Verb == AzCopyVerbJobsClean {
		ValidateLogFileRetention(a, *commandSpec.Environment.LogLocation, 1)
	}

	// The environment manager will handle cleanup for us-- All we need to do at this point is register our stdout.
	envCtx.RegisterLogUpload(LogUpload{
		EnvironmentID: *commandSpec.Environment.EnvironmentId,
		RunID:         runNum,

		Stdout: out.String(),
		Stderr: stderr.String(),
	})

	return out, &AzCopyJobPlan{}
}