go-execute icon indicating copy to clipboard operation
go-execute copied to clipboard

[Feature request] Support optional timeouts for tasks

Open daychou opened this issue 4 years ago • 11 comments

If there is a problem in the execution process and the command execution has been blocked, is there any way to solve this problem? For example, setting the timeout period, but it does not seem to be supported currently.

daychou avatar Dec 25 '20 03:12 daychou

Can you give a code example that I can use to reproduce the issue you're facing?

alexellis avatar Dec 28 '20 17:12 alexellis

@daychou ? Let me know otherwise I'll close out the issue. I tend to use an issue template, which it appears I neglected to do here, it asks for the context of the issue you're facing, which we're missing here.

https://raw.githubusercontent.com/alexellis/registry-creds/master/.github/ISSUE_TEMPLATE/feature_request.md https://raw.githubusercontent.com/alexellis/registry-creds/master/.github/ISSUE_TEMPLATE/bug_report.md

alexellis avatar Dec 29 '20 09:12 alexellis

For example, exec("sleep 3600000"), sleep means the process is stuck, then it will never stop. I currently find a solution, using exec's CommandContext method

func (et ExecTask) Execute(timeout time.Duration) (ExecResult, error) {
	argsSt := ""
	if len(et.Args) > 0 {
		argsSt = strings.Join(et.Args, " ")
	}

	if et.PrintCommand {
		fmt.Println("exec: ", et.Command, argsSt)
	}

	var cmd *exec.Cmd

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	if et.Shell {
		var args []string
		if len(et.Args) == 0 {
			startArgs := strings.Split(et.Command, " ")
			script := strings.Join(startArgs, " ")
			args = append([]string{"-c"}, fmt.Sprintf("%s", script))

		} else {
			script := strings.Join(et.Args, " ")
			args = append([]string{"-c"}, fmt.Sprintf("%s %s", et.Command, script))

		}

		cmd = exec.CommandContext(ctx,"/bin/bash", args...)
	} else {
		if strings.Index(et.Command, " ") > 0 {
			parts := strings.Split(et.Command, " ")
			command := parts[0]
			args := parts[1:]
			cmd = exec.CommandContext(ctx, command, args...)

		} else {
			cmd = exec.CommandContext(ctx, et.Command, et.Args...)
		}
	}

	cmd.Dir = et.Cwd

	if len(et.Env) > 0 {
		overrides := map[string]bool{}
		for _, env := range et.Env {
			key := strings.Split(env, "=")[0]
			overrides[key] = true
			cmd.Env = append(cmd.Env, env)
		}

		for _, env := range os.Environ() {
			key := strings.Split(env, "=")[0]

			if _, ok := overrides[key]; !ok {
				cmd.Env = append(cmd.Env, env)
			}
		}
	}
	if et.Stdin != nil {
		cmd.Stdin = et.Stdin
	}

	stdoutBuff := bytes.Buffer{}
	stderrBuff := bytes.Buffer{}

	var stdoutWriters io.Writer
	var stderrWriters io.Writer

	if et.StreamStdio {
		stdoutWriters = io.MultiWriter(os.Stdout, &stdoutBuff)
		stderrWriters = io.MultiWriter(os.Stderr, &stderrBuff)
	} else {
		stdoutWriters = &stdoutBuff
		stderrWriters = &stderrBuff
	}

	cmd.Stdout = stdoutWriters
	cmd.Stderr = stderrWriters

	startErr := cmd.Start()

	if startErr != nil {
		return ExecResult{}, startErr
	}

	exitCode := 0
	if execErr := cmd.Wait(); execErr != nil {
		if exitError, ok := execErr.(*exec.ExitError); ok {
			exitCode = exitError.ExitCode()
		}
	}

	if ctx.Err() == context.DeadlineExceeded {
		return ExecResult{}, errors.New("command execution timeout")
	}

	return ExecResult{
		Stdout:   string(stdoutBuff.Bytes()),
		Stderr:   string(stderrBuff.Bytes()),
		ExitCode: exitCode,
	}, nil
}

daychou avatar Dec 29 '20 12:12 daychou

This feels like a contrived example. The sleep command isn't stuck, it's actually what you asked for.

Have you got another more realistic example? Maybe the actual task at hand that made you raise the issue?

alexellis avatar Dec 29 '20 13:12 alexellis

If there is an unpredictable accident on a task that causes it to get stuck, I have already encountered this problem.

I use go-execute to execute the java jmap command (jmap -dump:format=b,file=jvm.bin 1). Under normal circumstances, it should complete the task in about 10 minutes, but due to an abnormality in the java service, my The task has been stuck for a few days. This is obviously bad. My idea is that if the task has been executed for 1 hour and has not been completed, it will pop up a timeout and actively end the exec task. This is what I want.

daychou avatar Dec 29 '20 13:12 daychou

Using a context seems like a fair approach for the use-case. I would consider a PR for this, and a new example on the README showing how it works.

alexellis avatar Dec 29 '20 17:12 alexellis

/set title: [Feature request] Support optional timeouts for tasks

alexellis avatar Dec 29 '20 17:12 alexellis

Thanks, look forward to the release

daychou avatar Dec 30 '20 01:12 daychou

Are you going to work on the enhancement?

alexellis avatar Dec 30 '20 08:12 alexellis

Yes, sorry for closing this issues

daychou avatar Dec 30 '20 09:12 daychou

Do you have news ? :-)

henri9813 avatar Oct 08 '21 13:10 henri9813