github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/engine/docker/exec.go (about)

     1  package docker
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  
     7  	dockertypes "github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/pkg/stdcopy"
     9  
    10  	enginetypes "github.com/projecteru2/core/engine/types"
    11  	"github.com/projecteru2/core/log"
    12  )
    13  
    14  // ExecCreate create a exec
    15  func (e *Engine) execCreate(ctx context.Context, target string, config *enginetypes.ExecConfig) (string, error) {
    16  	execConfig := dockertypes.ExecConfig{
    17  		User:         config.User,
    18  		Privileged:   config.Privileged,
    19  		Cmd:          config.Cmd,
    20  		WorkingDir:   config.WorkingDir,
    21  		Env:          config.Env,
    22  		AttachStderr: config.AttachStderr,
    23  		AttachStdout: config.AttachStdout,
    24  		AttachStdin:  config.AttachStdin,
    25  		Tty:          config.Tty,
    26  	}
    27  
    28  	// TODO should timeout
    29  	// Fuck docker, ctx will not use inside funcs!!
    30  	idResp, err := e.client.ContainerExecCreate(ctx, target, execConfig)
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  	return idResp.ID, nil
    35  }
    36  
    37  // ExecAttach attach a exec
    38  func (e *Engine) execAttach(ctx context.Context, execID string, tty bool) (io.ReadCloser, io.WriteCloser, error) {
    39  	execStartCheck := dockertypes.ExecStartCheck{
    40  		Tty: tty,
    41  	}
    42  	resp, err := e.client.ContainerExecAttach(ctx, execID, execStartCheck)
    43  	if err != nil {
    44  		return nil, nil, err
    45  	}
    46  	return io.NopCloser(resp.Reader), resp.Conn, nil
    47  }
    48  
    49  // Execute executes a workload
    50  func (e *Engine) Execute(ctx context.Context, ID string, config *enginetypes.ExecConfig) (execID string, stdout, stderr io.ReadCloser, stdin io.WriteCloser, err error) {
    51  	if execID, err = e.execCreate(ctx, ID, config); err != nil {
    52  		return
    53  	}
    54  
    55  	reader, writer, err := e.execAttach(ctx, execID, config.Tty)
    56  	if err != nil {
    57  		return
    58  	}
    59  	if config.AttachStdin {
    60  		return execID, reader, nil, writer, err
    61  	}
    62  
    63  	stdout, stderr = e.demultiplexStdStream(ctx, reader)
    64  	return execID, stdout, stderr, nil, err
    65  }
    66  
    67  func (e *Engine) demultiplexStdStream(ctx context.Context, stdStream io.Reader) (stdout, stderr io.ReadCloser) {
    68  	stdout, stdoutW := io.Pipe()
    69  	stderr, stderrW := io.Pipe()
    70  	go func() {
    71  		defer stdoutW.Close()
    72  		defer stderrW.Close()
    73  		if _, err := stdcopy.StdCopy(stdoutW, stderrW, stdStream); err != nil {
    74  			log.WithFunc("engine.docker.demultiplexStdStream").Error(ctx, err, "StdCopy failed")
    75  		}
    76  	}()
    77  	return stdout, stderr
    78  }
    79  
    80  // ExecExitCode get exec return code
    81  func (e *Engine) ExecExitCode(ctx context.Context, _, execID string) (int, error) {
    82  	r, err := e.client.ContainerExecInspect(ctx, execID)
    83  	if err != nil {
    84  		return -1, err
    85  	}
    86  	return r.ExitCode, nil
    87  }
    88  
    89  // ExecResize resize exec tty
    90  func (e *Engine) ExecResize(ctx context.Context, execID string, height, width uint) error {
    91  	opts := dockertypes.ResizeOptions{
    92  		Height: height,
    93  		Width:  width,
    94  	}
    95  
    96  	return e.client.ContainerExecResize(ctx, execID, opts)
    97  }