Skip to content

Commit 38a4fbc

Browse files
authored
Merge pull request #220 from articulate/refactor/exec
refactor!: replace the current process instead of spawning a child process
2 parents ec7bec4 + 43c14cf commit 38a4fbc

File tree

3 files changed

+14
-79
lines changed

3 files changed

+14
-79
lines changed

go.mod

-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ require (
66
github.com/aws/aws-sdk-go-v2/config v1.27.10
77
github.com/aws/aws-sdk-go-v2/service/kms v1.30.1
88
github.com/hashicorp/consul/api v1.28.2
9-
github.com/hashicorp/go-reap v0.0.0-20230117204525-bf69c61a7b71
109
github.com/hashicorp/vault/api v1.12.0
1110
github.com/hashicorp/vault/api/auth/aws v0.6.0
1211
github.com/hashicorp/vault/api/auth/kubernetes v0.6.0
1312
github.com/samber/lo v1.39.0
1413
github.com/stretchr/testify v1.9.0
15-
golang.org/x/term v0.15.0
1614
)
1715

1816
require (

go.sum

-3
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,6 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh
108108
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
109109
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
110110
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
111-
github.com/hashicorp/go-reap v0.0.0-20230117204525-bf69c61a7b71 h1:ntMIobjNd0QLB/i6OQM/OV1B+k6RjmvtY84z/SUeYPA=
112-
github.com/hashicorp/go-reap v0.0.0-20230117204525-bf69c61a7b71/go.mod h1:qIFzeFcJU3OIFk/7JreWXcUjFmcCaeHTH9KoNyHYVCs=
113111
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
114112
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
115113
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
@@ -325,7 +323,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
325323
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
326324
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
327325
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
328-
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
329326
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
330327
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
331328
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=

main.go

+14-74
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,17 @@ package main
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"log/slog"
87
"os"
98
"os/exec"
10-
"os/signal"
119
"runtime"
1210
"strconv"
1311
"strings"
1412
"syscall"
15-
"time"
16-
17-
"github.com/hashicorp/go-reap"
18-
"golang.org/x/term"
1913
)
2014

21-
var (
22-
logLevel = new(slog.LevelVar)
23-
// killWait is the time to wait before forcefully terminating the child process
24-
killWait = 5 * time.Second
25-
)
15+
var logLevel = new(slog.LevelVar)
2616

2717
func main() {
2818
ctx := context.Background()
@@ -68,18 +58,17 @@ func main() {
6858
os.Exit(4)
6959
}
7060

71-
if os.Getpid() == 1 {
72-
go reap.ReapChildren(nil, nil, nil, nil)
73-
}
74-
7561
cmd := os.Args[1]
7662
args := os.Args[2:]
7763

7864
if cmd == "yarn" || cmd == "npm" {
7965
logger.WarnContext(ctx, cmd+" is not recommended. You might see unexpected behavior. Use node instead.")
8066
}
8167

82-
os.Exit(run(ctx, cmd, args, env.Environ(), logger))
68+
if err := run(cmd, args, env.Environ()); err != nil {
69+
logger.ErrorContext(ctx, "Could not run command", "error", err)
70+
os.Exit(1)
71+
}
8372
}
8473

8574
func loadEnvVars(ctx context.Context, cfg *Config, l *slog.Logger) (*EnvMap, error) {
@@ -108,7 +97,7 @@ func loadEnvVars(ctx context.Context, cfg *Config, l *slog.Logger) (*EnvMap, err
10897
}
10998

11099
func loadConsul(ctx context.Context, addr string, c *Config, l *slog.Logger) (Dict, error) {
111-
l.Debug("Loading values from Consul")
100+
l.DebugContext(ctx, "Loading values from Consul")
112101

113102
client, err := NewConsul(addr)
114103
if err != nil {
@@ -161,64 +150,15 @@ func loadVault(ctx context.Context, addr string, c *Config, l *slog.Logger) (Dic
161150
return values, err
162151
}
163152

164-
func run(ctx context.Context, name string, args, env []string, l *slog.Logger) int {
165-
ctx, cancel := context.WithCancel(ctx)
166-
defer cancel()
167-
168-
cmd := exec.CommandContext(ctx, name, args...) //nolint:gosec
169-
cmd.Env = append(os.Environ(), env...)
170-
cmd.Stdin = os.Stdin
171-
cmd.Stderr = os.Stderr
172-
cmd.Stdout = os.Stdout
173-
174-
isTerm := term.IsTerminal(int(os.Stdin.Fd()))
175-
cmd.SysProcAttr = &syscall.SysProcAttr{Foreground: isTerm, Setsid: !isTerm}
176-
177-
if err := cmd.Start(); err != nil {
178-
l.ErrorContext(ctx, "Could not start command", "error", err, "cmd", cmd.String())
179-
return 1
180-
}
181-
182-
sigch := make(chan os.Signal, 1)
183-
exitch := make(chan os.Signal, 1)
184-
signal.Notify(sigch)
185-
signal.Notify(exitch, syscall.SIGINT)
186-
defer signal.Stop(sigch)
187-
defer signal.Stop(exitch)
188-
189-
// forward signals to the child process
190-
go func() {
191-
for s := range sigch {
192-
if s == syscall.SIGCHLD {
193-
continue
194-
}
195-
196-
l.DebugContext(ctx, "Sending signal", "signal", s.String())
197-
if err := cmd.Process.Signal(s); err != nil && !errors.Is(err, os.ErrProcessDone) {
198-
l.ErrorContext(ctx, "Could not send signal to command", "error", err, "cmd", cmd.String(), "signal", s.String())
199-
}
200-
}
201-
}()
202-
203-
// handle forceful termination
204-
go func() {
205-
<-exitch
206-
time.Sleep(killWait)
207-
l.WarnContext(ctx, "Terminating unresponsive process", "cmd", cmd.String())
208-
cancel()
209-
}()
210-
211-
if err := cmd.Wait(); err != nil {
212-
var exit *exec.ExitError
213-
if errors.As(err, &exit) {
214-
return exit.ExitCode()
215-
}
216-
l.ErrorContext(ctx, "Unknown error while running command", "error", err, "cmd", cmd.String())
217-
return 3
153+
func run(name string, args, env []string) error {
154+
bin, err := exec.LookPath(name)
155+
if err != nil {
156+
return fmt.Errorf("could not find %s: %w", name, err)
218157
}
219158

220-
if code := cmd.ProcessState.ExitCode(); code != -1 {
221-
return code
159+
if err := syscall.Exec(bin, args, env); err != nil {
160+
return fmt.Errorf("could not execute %s %s: %w", name, strings.Join(args, " "), err)
222161
}
223-
return 0
162+
163+
return nil
224164
}

0 commit comments

Comments
 (0)