func ExecuteCommand()

in sharedlibraries/commandlineexecutor/commandlineexecutor.go [133:206]


func ExecuteCommand(ctx context.Context, params Params) Result {
	if !exists(params.Executable) {
		log.CtxLogger(ctx).Debugw("Command executable not found", "executable", params.Executable)
		msg := fmt.Sprintf("Command executable: %q not found.", params.Executable)
		return Result{"", msg, 0, fmt.Errorf("command executable: %s not found", params.Executable), false, false}
	}

	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	// Timeout the command at 60 seconds by default.
	timeout := 60 * time.Second
	if params.Timeout > 0 {
		timeout = time.Duration(params.Timeout) * time.Second
	}
	// Context tctx has a Timeout while running the commands.
	tctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	args := params.Args
	if params.ArgsToSplit != "" {
		args = splitParams(params.ArgsToSplit)
	}
	exe := exec.CommandContext(tctx, params.Executable, args...)

	exe.Stdin = strings.NewReader(params.Stdin)
	exe.Stdout = stdout
	exe.Stderr = stderr
	var err error
	if exeForPlatform != nil {
		err = exeForPlatform(exe, params)
	} else {
		// We pass ctx because this calls back into ExecuteCommand which adds the timeout before running the command.
		err = setupExeForPlatform(ctx, exe, params, ExecuteCommand)
	}
	if err != nil {
		log.CtxLogger(ctx).Debugw("Could not setup the executable environment", "executable", params.Executable, "args", args, "error", err)
		return Result{stdout.String(), stderr.String(), 0, err, true, false}
	}

	log.CtxLogger(ctx).Debugw("Executing command", "executable", params.Executable, "args", args,
		"timeout", timeout, "user", params.User, "env", params.Env)

	if run != nil {
		err = run()
	} else {
		err = exe.Run()
	}
	if err != nil {
		// Set the exit code based on the error first, then see if we can get it from the error message.
		exitCode := exitCode(err)
		m := exitStatusPattern.FindStringSubmatch(err.Error())
		exitStatusParsed := false
		if len(m) > 0 {
			atoi, serr := strconv.Atoi(m[1])
			if serr != nil {
				log.CtxLogger(ctx).Debugw("Failed to get command exit code from string match", "executable", params.Executable,
					"args", args, "error", serr)
			} else {
				// This is the case where we expect to have an Error but want the exit code from the "exit status #" string
				exitCode = atoi
				exitStatusParsed = true
			}
		} else {
			log.CtxLogger(ctx).Debugw("Error encountered when executing command", "executable", params.Executable,
				"args", args, "exitcode", exitCode, "error", err, "stdout", stdout.String(),
				"stderr", stderr.String())
		}
		return Result{stdout.String(), stderr.String(), exitCode, err, true, exitStatusParsed}
	}

	// Exit code can assumed to be 0
	log.CtxLogger(ctx).Debugw("Successfully executed command", "executable", params.Executable, "args", args,
		"stdout", stdout.String(), "stderr", stderr.String())
	return Result{stdout.String(), stderr.String(), 0, nil, true, false}
}