diff --git a/packages/envd/debug.Dockerfile b/packages/envd/debug.Dockerfile index 54386c1f3..3b79abfe2 100644 --- a/packages/envd/debug.Dockerfile +++ b/packages/envd/debug.Dockerfile @@ -3,11 +3,22 @@ FROM golang:1.23 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -RUN apt-get install systemd ca-certificates make python-is-python3 python3 nodejs chrony sudo -y +RUN apt-get install systemd ca-certificates make python-is-python3 python3 nodejs npm chrony sudo -y RUN update-ca-certificates +# Remove password for root. +RUN passwd -d root + +# Create default user. RUN useradd -ms /bin/bash user +RUN usermod -aG sudo user +RUN passwd -d user +RUN echo "user ALL=(ALL:ALL) NOPASSWD: ALL" >>/etc/sudoers + +RUN mkdir -p /home/user +RUN chmod 777 -R /home/user +RUN chmod 777 -R /usr/local RUN go install github.com/go-delve/delve/cmd/dlv@latest diff --git a/packages/envd/internal/services/process/handler/handler.go b/packages/envd/internal/services/process/handler/handler.go index e520aa2f4..fb7d070f7 100644 --- a/packages/envd/internal/services/process/handler/handler.go +++ b/packages/envd/internal/services/process/handler/handler.go @@ -44,7 +44,9 @@ type Handler struct { cmd *exec.Cmd tty *os.File - outWg *sync.WaitGroup + ctx context.Context + cancel context.CancelFunc + stdin io.WriteCloser DataEvent *MultiplexedChannel[rpc.ProcessEvent_Data] @@ -56,7 +58,14 @@ func (p *Handler) Pid() uint32 { return uint32(p.cmd.Process.Pid) } -func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *zerolog.Logger, envVars *utils.Map[string, string]) (*Handler, error) { +func New( + ctx context.Context, + user *user.User, + req *rpc.StartRequest, + logger *zerolog.Logger, + envVars *utils.Map[string, string], + cancel context.CancelFunc, +) (*Handler, error) { cmd := exec.CommandContext(ctx, req.GetProcess().GetCmd(), req.GetProcess().GetArgs()...) uid, gid, err := permissions.GetUserIds(user) @@ -104,6 +113,7 @@ func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *ze cmd.Env = formattedVars outMultiplex := NewMultiplexedChannel[rpc.ProcessEvent_Data](outputBufferSize) + var outWg sync.WaitGroup if req.GetPty() != nil { @@ -118,7 +128,6 @@ func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *ze } outWg.Add(1) - go func() { defer outWg.Done() @@ -155,7 +164,8 @@ func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *ze tty: tty, Tag: req.Tag, DataEvent: outMultiplex, - outWg: &outWg, + ctx: ctx, + cancel: cancel, EndEvent: NewMultiplexedChannel[rpc.ProcessEvent_End](0), logger: logger, }, nil @@ -174,7 +184,6 @@ func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *ze outWg.Add(1) go func() { defer outWg.Done() - stdoutLogs := make(chan []byte, outputBufferSize) defer close(stdoutLogs) @@ -219,7 +228,6 @@ func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *ze outWg.Add(1) go func() { defer outWg.Done() - stderrLogs := make(chan []byte, outputBufferSize) defer close(stderrLogs) @@ -256,13 +264,20 @@ func New(ctx context.Context, user *user.User, req *rpc.StartRequest, logger *ze } }() + go func() { + outWg.Wait() + close(outMultiplex.Source) + cancel() + }() + return &Handler{ Config: req.GetProcess(), cmd: cmd, stdin: stdin, Tag: req.Tag, DataEvent: outMultiplex, - outWg: &outWg, + ctx: ctx, + cancel: cancel, EndEvent: NewMultiplexedChannel[rpc.ProcessEvent_End](0), logger: logger, }, nil @@ -273,6 +288,10 @@ func (p *Handler) SendSignal(signal syscall.Signal) error { return fmt.Errorf("process not started") } + if signal == syscall.SIGKILL || signal == syscall.SIGTERM { + p.cancel() + } + return p.cmd.Process.Signal(signal) } @@ -335,14 +354,12 @@ func (p *Handler) Start() (uint32, error) { } func (p *Handler) Wait() { - p.outWg.Wait() + <-p.ctx.Done() - close(p.DataEvent.Source) + err := p.cmd.Wait() p.tty.Close() - err := p.cmd.Wait() - var errMsg *string if err != nil { diff --git a/packages/envd/internal/services/process/signal.go b/packages/envd/internal/services/process/signal.go index 9a89733ea..25994f8ac 100644 --- a/packages/envd/internal/services/process/signal.go +++ b/packages/envd/internal/services/process/signal.go @@ -11,7 +11,7 @@ import ( ) func (s *Service) SendSignal(ctx context.Context, req *connect.Request[rpc.SendSignalRequest]) (*connect.Response[rpc.SendSignalResponse], error) { - proc, err := s.getProcess(req.Msg.Process) + handler, err := s.getProcess(req.Msg.Process) if err != nil { return nil, err } @@ -26,7 +26,7 @@ func (s *Service) SendSignal(ctx context.Context, req *connect.Request[rpc.SendS return nil, connect.NewError(connect.CodeUnimplemented, fmt.Errorf("invalid signal: %s", req.Msg.GetSignal())) } - err = proc.SendSignal(signal) + err = handler.SendSignal(signal) if err != nil { return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("error sending signal: %w", err)) } diff --git a/packages/envd/internal/services/process/start.go b/packages/envd/internal/services/process/start.go index 5b7f06a95..0d24e9b5c 100644 --- a/packages/envd/internal/services/process/start.go +++ b/packages/envd/internal/services/process/start.go @@ -30,7 +30,8 @@ func (s *Service) InitializeStartProcess(ctx context.Context, user *user.User, r handlerL := s.logger.With().Str(string(logs.OperationIDKey), ctx.Value(logs.OperationIDKey).(string)).Logger() - proc, err := handler.New(ctx, user, req, &handlerL, nil) + startProcCtx, startProcCancel := context.WithCancel(ctx) + proc, err := handler.New(startProcCtx, user, req, &handlerL, nil, startProcCancel) if err != nil { return err } @@ -77,11 +78,12 @@ func (s *Service) handleStart(ctx context.Context, req *connect.Request[rpc.Star // Create a new context with a timeout if provided. // We do not want the command to be killed if the request context is cancelled - procCtx := context.Background() + procCtx, cancelProc := context.WithCancel(context.Background()) if timeout > 0 { // zero timeout means no timeout - procCtx, _ = context.WithTimeout(procCtx, timeout) + procCtx, cancelProc = context.WithTimeout(procCtx, timeout) } - proc, err := handler.New(procCtx, u, req.Msg, &handlerL, s.envs) + + proc, err := handler.New(procCtx, u, req.Msg, &handlerL, s.envs, cancelProc) if err != nil { return err }