github.com/defang-io/defang/src@v0.0.0-20240505002154-bdf411911834/pkg/local/local.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package local 5 6 import ( 7 "context" 8 "errors" 9 "io" 10 "os" 11 "os/exec" 12 "strconv" 13 "syscall" 14 15 "github.com/defang-io/defang/src/pkg/types" 16 ) 17 18 type PID = types.TaskID 19 20 type Local struct { 21 entrypoint []string 22 cmd *exec.Cmd 23 outReader io.ReadCloser 24 errReader io.ReadCloser 25 } 26 27 var _ types.Driver = (*Local)(nil) 28 29 func New() *Local { 30 return &Local{} 31 } 32 33 func (l *Local) SetUp(ctx context.Context, containers []types.Container) error { 34 if len(containers) != 1 { 35 return errors.New("expected exactly one container") 36 } 37 if len(containers[0].EntryPoint) == 0 { 38 return errors.New("entrypoint not set") 39 } 40 l.entrypoint = containers[0].EntryPoint 41 return nil 42 } 43 44 func (l *Local) TearDown(ctx context.Context) error { 45 if l.cmd == nil { 46 return nil 47 } 48 // l.cmd.Process.Kill() 49 return l.cmd.Wait() 50 } 51 52 func (l *Local) Run(ctx context.Context, env map[string]string, args ...string) (PID, error) { 53 if l.cmd != nil { 54 return nil, errors.New("already running") 55 } 56 args = append(l.entrypoint[1:], args...) 57 cmd := exec.CommandContext(ctx, l.entrypoint[0], args...) 58 cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 59 or, err := cmd.StdoutPipe() 60 if err != nil { 61 return nil, err 62 } 63 er, err := cmd.StderrPipe() 64 if err != nil { 65 return nil, err 66 } 67 for k, v := range env { 68 cmd.Env = append(cmd.Env, k+"="+v) 69 } 70 if err := cmd.Start(); err != nil { 71 return nil, err 72 } 73 l.outReader = or 74 l.errReader = er 75 l.cmd = cmd 76 pid := strconv.Itoa(cmd.Process.Pid) 77 return &pid, nil 78 } 79 80 func (l *Local) Tail(ctx context.Context, taskID PID) error { 81 if l.cmd == nil { 82 return errors.New("not running") 83 } 84 if strconv.Itoa(l.cmd.Process.Pid) != *taskID { 85 return errors.New("task ID does not match") 86 } 87 go io.Copy(os.Stderr, l.errReader) 88 _, err := io.Copy(os.Stdout, l.outReader) 89 return err 90 } 91 92 func (l *Local) Stop(ctx context.Context, taskID PID) error { 93 pid, err := strconv.Atoi(*taskID) 94 if err != nil { 95 return err 96 } 97 return syscall.Kill(-pid, syscall.SIGTERM) // negative pid kills the process group 98 } 99 100 func (l *Local) GetInfo(ctx context.Context, taskID PID) (*types.TaskInfo, error) { 101 return nil, errors.New("not implemented for local driver") 102 } 103 104 func (l *Local) PutSecret(ctx context.Context, name, value string) error { 105 return errors.New("not implemented for local driver") 106 } 107 108 func (l *Local) ListSecrets(ctx context.Context) ([]string, error) { 109 return nil, errors.New("not implemented for local driver") 110 } 111 112 func (l *Local) CreateUploadURL(ctx context.Context, name string) (string, error) { 113 return "", errors.New("not implemented for local driver") 114 }