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 }