From da8237e495018ec94835d173f75675f092fcbcc1 Mon Sep 17 00:00:00 2001 From: John Lago <750845+Lagoja@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:02:53 -0700 Subject: [PATCH 1/4] Initial services attach functionality --- internal/boxcli/services.go | 24 ++++++++++++++++++++++++ internal/devbox/services.go | 26 ++++++++++++++++++++++++++ internal/services/manager.go | 24 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/internal/boxcli/services.go b/internal/boxcli/services.go index f3ed049d150..b14365f37ed 100644 --- a/internal/boxcli/services.go +++ b/internal/boxcli/services.go @@ -69,6 +69,15 @@ func servicesCmd(persistentPreRunE ...cobraFunc) *cobra.Command { }, } + attachCommand := &cobra.Command{ + Use: "attach", + Short: "Attach to a running process-compose for the current project", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return attachServices(cmd, flags) + }, + } + lsCommand := &cobra.Command{ Use: "ls", Short: "List available services", @@ -123,6 +132,7 @@ func servicesCmd(persistentPreRunE ...cobraFunc) *cobra.Command { servicesCommand.Flag("run-in-current-shell").Hidden = true serviceUpFlags.register(upCommand) serviceStopFlags.register(stopCommand) + servicesCommand.AddCommand(attachCommand) servicesCommand.AddCommand(lsCommand) servicesCommand.AddCommand(upCommand) servicesCommand.AddCommand(restartCommand) @@ -131,6 +141,20 @@ func servicesCmd(persistentPreRunE ...cobraFunc) *cobra.Command { return servicesCommand } +func attachServices(cmd *cobra.Command, flags servicesCmdFlags) error { + + box, err := devbox.Open(&devopt.Opts{ + Dir: flags.config.path, + Environment: flags.config.environment, + Stderr: cmd.ErrOrStderr(), + }) + if err != nil { + return errors.WithStack(err) + } + + return box.AttachToProcessManager(cmd.Context()) +} + func listServices(cmd *cobra.Command, flags servicesCmdFlags) error { box, err := devbox.Open(&devopt.Opts{ Dir: flags.config.path, diff --git a/internal/devbox/services.go b/internal/devbox/services.go index cd9eb02430d..84b09de3825 100644 --- a/internal/devbox/services.go +++ b/internal/devbox/services.go @@ -170,6 +170,32 @@ func (d *Devbox) RestartServices( return nil } +func (d *Devbox) AttachToProcessManager(ctx context.Context) error { + if !services.ProcessManagerIsRunning(d.projectDir) { + return usererr.New("Process manager is not running. Run `devbox services up` to start it.") + } + + err := initDevboxUtilityProject(ctx, d.stderr) + if err != nil { + return err + } + + processComposeBinPath, err := utilityLookPath("process-compose") + if err != nil { + return err + } + + return services.AttachToProcessManager( + ctx, + d.stderr, + d.projectDir, + services.ProcessComposeOpts{ + BinPath: processComposeBinPath, + }, + ) + +} + func (d *Devbox) StartProcessManager( ctx context.Context, runInCurrentShell bool, diff --git a/internal/services/manager.go b/internal/services/manager.go index 775307d581e..201bf4ab9ed 100644 --- a/internal/services/manager.go +++ b/internal/services/manager.go @@ -293,6 +293,30 @@ func StopAllProcessManagers(ctx context.Context, w io.Writer) error { return nil } +func AttachToProcessManager(ctx context.Context, w io.Writer, projectDir string, processComposeConfig ProcessComposeOpts) error { + configFile, err := openGlobalConfigFile() + if err != nil { + return err + } + + defer configFile.Close() + config := readGlobalProcessComposeJSON(configFile) + + project, ok := config.Instances[projectDir] + if !ok { + return fmt.Errorf("Process-compose is not running for this project. To start it, run `devbox services up`") + } + + flags := []string{"attach", "-p", strconv.Itoa(project.Port)} + cmd := exec.Command(processComposeConfig.BinPath, flags...) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + return cmd.Run() +} + func ProcessManagerIsRunning(projectDir string) bool { configFile, err := openGlobalConfigFile() if err != nil { From 761593d595b1b0c70ed5d4d488b8a276710be30f Mon Sep 17 00:00:00 2001 From: John Lago <750845+Lagoja@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:18:48 -0700 Subject: [PATCH 2/4] Keep process-compose alive in background --- internal/services/manager.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/services/manager.go b/internal/services/manager.go index 201bf4ab9ed..8c466ff7847 100644 --- a/internal/services/manager.go +++ b/internal/services/manager.go @@ -216,6 +216,11 @@ func runProcessManagerInBackground(cmd *exec.Cmd, config *globalProcessComposeCo cmd.Stdout = logfile cmd.Stderr = logfile + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + Pgid: 0, + } + if err := cmd.Start(); err != nil { return fmt.Errorf("failed to start process-compose: %w", err) } From dd338951e3ff39bc865cf512000562be636a5d0b Mon Sep 17 00:00:00 2001 From: John Lago <750845+Lagoja@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:42:50 -0700 Subject: [PATCH 3/4] Lint + comments and helpful output --- internal/boxcli/services.go | 1 - internal/devbox/services.go | 1 - internal/services/manager.go | 11 ++++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/boxcli/services.go b/internal/boxcli/services.go index b14365f37ed..e1eb493afe1 100644 --- a/internal/boxcli/services.go +++ b/internal/boxcli/services.go @@ -142,7 +142,6 @@ func servicesCmd(persistentPreRunE ...cobraFunc) *cobra.Command { } func attachServices(cmd *cobra.Command, flags servicesCmdFlags) error { - box, err := devbox.Open(&devopt.Opts{ Dir: flags.config.path, Environment: flags.config.environment, diff --git a/internal/devbox/services.go b/internal/devbox/services.go index 84b09de3825..ead19ded6e9 100644 --- a/internal/devbox/services.go +++ b/internal/devbox/services.go @@ -193,7 +193,6 @@ func (d *Devbox) AttachToProcessManager(ctx context.Context) error { BinPath: processComposeBinPath, }, ) - } func (d *Devbox) StartProcessManager( diff --git a/internal/services/manager.go b/internal/services/manager.go index 8c466ff7847..f2e486a8d8f 100644 --- a/internal/services/manager.go +++ b/internal/services/manager.go @@ -159,7 +159,7 @@ func StartProcessManager( if processComposeConfig.Background { flags = append(flags, "-t=false") cmd := exec.Command(processComposeConfig.BinPath, flags...) - return runProcessManagerInBackground(cmd, config, port, projectDir) + return runProcessManagerInBackground(cmd, config, port, projectDir, w) } cmd := exec.Command(processComposeConfig.BinPath, flags...) @@ -206,7 +206,7 @@ func runProcessManagerInForeground(cmd *exec.Cmd, config *globalProcessComposeCo return writeGlobalProcessComposeJSON(config, configFile) } -func runProcessManagerInBackground(cmd *exec.Cmd, config *globalProcessComposeConfig, port int, projectDir string) error { +func runProcessManagerInBackground(cmd *exec.Cmd, config *globalProcessComposeConfig, port int, projectDir string, w io.Writer) error { logdir := filepath.Join(projectDir, processComposeLogfile) logfile, err := os.OpenFile(logdir, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_TRUNC, 0o664) if err != nil { @@ -216,6 +216,8 @@ func runProcessManagerInBackground(cmd *exec.Cmd, config *globalProcessComposeCo cmd.Stdout = logfile cmd.Stderr = logfile + // These attributes set the process group ID to the process ID of process-compose + // Starting in it's own process group means it won't be terminated if the shell crashes cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, Pgid: 0, @@ -225,6 +227,9 @@ func runProcessManagerInBackground(cmd *exec.Cmd, config *globalProcessComposeCo return fmt.Errorf("failed to start process-compose: %w", err) } + fmt.Fprintf(w, "Process-compose is now running on port %d\n",port) + fmt.Fprintf(w, "To stop your services, run `devbox services stop`\n") + projectConfig := instance{ Pid: cmd.Process.Pid, Port: port, @@ -309,7 +314,7 @@ func AttachToProcessManager(ctx context.Context, w io.Writer, projectDir string, project, ok := config.Instances[projectDir] if !ok { - return fmt.Errorf("Process-compose is not running for this project. To start it, run `devbox services up`") + return fmt.Errorf("process-compose is not running for this project. To start it, run `devbox services up`") } flags := []string{"attach", "-p", strconv.Itoa(project.Port)} From 55f358bddfed77a4117b1ce3d0289d1ec1f310fe Mon Sep 17 00:00:00 2001 From: John Lago <750845+Lagoja@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:57:53 -0700 Subject: [PATCH 4/4] format --- internal/services/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/manager.go b/internal/services/manager.go index f2e486a8d8f..df38f4c4bf6 100644 --- a/internal/services/manager.go +++ b/internal/services/manager.go @@ -227,7 +227,7 @@ func runProcessManagerInBackground(cmd *exec.Cmd, config *globalProcessComposeCo return fmt.Errorf("failed to start process-compose: %w", err) } - fmt.Fprintf(w, "Process-compose is now running on port %d\n",port) + fmt.Fprintf(w, "Process-compose is now running on port %d\n", port) fmt.Fprintf(w, "To stop your services, run `devbox services stop`\n") projectConfig := instance{