Skip to content

Commit 3a2d7f2

Browse files
committed
Revert "fix: pass through signals to inner container (#83)"
This reverts commit c07d2c2.
1 parent 45addf8 commit 3a2d7f2

File tree

10 files changed

+41
-247
lines changed

10 files changed

+41
-247
lines changed

cli/clitest/cli.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func New(t *testing.T, cmd string, args ...string) (context.Context, *cobra.Comm
5656
ctx = ctx(t, fs, execer, mnt, client)
5757
)
5858

59-
root := cli.Root(nil)
59+
root := cli.Root()
6060
// This is the one thing that isn't really mocked for the tests.
6161
// I cringe at the thought of introducing yet another mock so
6262
// let's avoid it for now.

cli/docker.go

Lines changed: 27 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"net/url"
99
"os"
10-
"os/exec"
1110
"path"
1211
"path/filepath"
1312
"sort"
@@ -146,7 +145,7 @@ type flags struct {
146145
ethlink string
147146
}
148147

149-
func dockerCmd(ch chan func() error) *cobra.Command {
148+
func dockerCmd() *cobra.Command {
150149
var flags flags
151150

152151
cmd := &cobra.Command{
@@ -287,7 +286,7 @@ func dockerCmd(ch chan func() error) *cobra.Command {
287286
return xerrors.Errorf("wait for dockerd: %w", err)
288287
}
289288

290-
err = runDockerCVM(ctx, log, client, blog, ch, flags)
289+
err = runDockerCVM(ctx, log, client, blog, flags)
291290
if err != nil {
292291
// It's possible we failed because we ran out of disk while
293292
// pulling the image. We should restart the daemon and use
@@ -316,7 +315,7 @@ func dockerCmd(ch chan func() error) *cobra.Command {
316315
}()
317316

318317
log.Debug(ctx, "reattempting container creation")
319-
err = runDockerCVM(ctx, log, client, blog, ch, flags)
318+
err = runDockerCVM(ctx, log, client, blog, flags)
320319
}
321320
if err != nil {
322321
blog.Errorf("Failed to run envbox: %v", err)
@@ -359,7 +358,7 @@ func dockerCmd(ch chan func() error) *cobra.Command {
359358
return cmd
360359
}
361360

362-
func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.DockerClient, blog buildlog.Logger, shutdownCh chan func() error, flags flags) error {
361+
func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.DockerClient, blog buildlog.Logger, flags flags) error {
363362
fs := xunix.GetFS(ctx)
364363

365364
// Set our OOM score to something really unfavorable to avoid getting killed
@@ -679,71 +678,31 @@ func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.Docker
679678
}
680679

681680
blog.Info("Envbox startup complete!")
682-
if flags.boostrapScript == "" {
683-
return nil
684-
}
685-
686-
bootstrapExec, err := client.ContainerExecCreate(ctx, containerID, dockertypes.ExecConfig{
687-
User: imgMeta.UID,
688-
Cmd: []string{"/bin/sh", "-s"},
689-
Env: []string{fmt.Sprintf("BINARY_DIR=%s", bootDir)},
690-
AttachStdin: true,
691-
AttachStdout: true,
692-
AttachStderr: true,
693-
Detach: true,
694-
})
695-
if err != nil {
696-
return xerrors.Errorf("create exec: %w", err)
697-
}
698-
699-
resp, err := client.ContainerExecAttach(ctx, bootstrapExec.ID, dockertypes.ExecStartCheck{})
700-
if err != nil {
701-
return xerrors.Errorf("attach exec: %w", err)
702-
}
703-
704-
_, err = io.Copy(resp.Conn, strings.NewReader(flags.boostrapScript))
705-
if err != nil {
706-
return xerrors.Errorf("copy stdin: %w", err)
707-
}
708-
err = resp.CloseWrite()
709-
if err != nil {
710-
return xerrors.Errorf("close write: %w", err)
711-
}
712-
713-
go func() {
714-
defer resp.Close()
715-
rd := io.LimitReader(resp.Reader, 1<<10)
716-
_, err := io.Copy(blog, rd)
717-
if err != nil {
718-
log.Error(ctx, "copy bootstrap output", slog.Error(err))
719-
}
720-
}()
721681

722-
// We can't just call ExecInspect because there's a race where the cmd
723-
// hasn't been assigned a PID yet.
724-
bootstrapPID, err := dockerutil.GetExecPID(ctx, client, bootstrapExec.ID)
682+
// The bootstrap script doesn't return since it execs the agent
683+
// meaning that it can get pretty noisy if we were to log by default.
684+
// In order to allow users to discern issues getting the bootstrap script
685+
// to complete successfully we pipe the output to stdout if
686+
// CODER_DEBUG=true.
687+
debugWriter := io.Discard
688+
if flags.debug {
689+
debugWriter = os.Stdout
690+
}
691+
// Bootstrap the container if a script has been provided.
692+
blog.Infof("Bootstrapping workspace...")
693+
err = dockerutil.BootstrapContainer(ctx, client, dockerutil.BootstrapConfig{
694+
ContainerID: containerID,
695+
User: imgMeta.UID,
696+
Script: flags.boostrapScript,
697+
// We set this because the default behavior is to download the agent
698+
// to /tmp/coder.XXXX. This causes a race to happen where we finish
699+
// downloading the binary but before we can execute systemd remounts
700+
// /tmp.
701+
Env: []string{fmt.Sprintf("BINARY_DIR=%s", bootDir)},
702+
StdOutErr: debugWriter,
703+
})
725704
if err != nil {
726-
return xerrors.Errorf("exec inspect: %w", err)
727-
}
728-
729-
shutdownCh <- func() error {
730-
log.Debug(ctx, "killing container", slog.F("bootstrap_pid", bootstrapPID))
731-
732-
// The PID returned is the PID _outside_ the container...
733-
//nolint:gosec
734-
out, err := exec.Command("kill", "-TERM", strconv.Itoa(bootstrapPID)).CombinedOutput()
735-
if err != nil {
736-
return xerrors.Errorf("kill bootstrap process (%s): %w", out, err)
737-
}
738-
739-
log.Debug(ctx, "sent kill signal waiting for process to exit")
740-
err = dockerutil.WaitForExit(ctx, client, bootstrapExec.ID)
741-
if err != nil {
742-
return xerrors.Errorf("wait for exit: %w", err)
743-
}
744-
745-
log.Debug(ctx, "bootstrap process successfully exited")
746-
return nil
705+
return xerrors.Errorf("boostrap container: %w", err)
747706
}
748707

749708
return nil

cli/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"github.com/spf13/cobra"
55
)
66

7-
func Root(ch chan func() error) *cobra.Command {
7+
func Root() *cobra.Command {
88
cmd := &cobra.Command{
99
Use: "envbox",
1010
SilenceErrors: true,
@@ -15,6 +15,6 @@ func Root(ch chan func() error) *cobra.Command {
1515
},
1616
}
1717

18-
cmd.AddCommand(dockerCmd(ch))
18+
cmd.AddCommand(dockerCmd())
1919
return cmd
2020
}

cmd/envbox/main.go

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,20 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"os"
7-
"os/signal"
86
"runtime"
9-
"syscall"
107

11-
"cdr.dev/slog"
12-
"cdr.dev/slog/sloggers/slogjson"
138
"github.com/coder/envbox/cli"
149
)
1510

1611
func main() {
17-
ch := make(chan func() error, 1)
18-
sigs := make(chan os.Signal, 1)
19-
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT, syscall.SIGWINCH)
20-
go func() {
21-
ctx := context.Background()
22-
log := slog.Make(slogjson.Sink(os.Stderr))
23-
log.Info(ctx, "waiting for signal")
24-
<-sigs
25-
log.Info(ctx, "got signal")
26-
select {
27-
case fn := <-ch:
28-
log.Info(ctx, "running shutdown function")
29-
err := fn()
30-
if err != nil {
31-
log.Error(ctx, "shutdown function failed", slog.Error(err))
32-
os.Exit(1)
33-
}
34-
default:
35-
log.Info(ctx, "no shutdown function")
36-
}
37-
log.Info(ctx, "exiting")
38-
os.Exit(0)
39-
}()
40-
_, err := cli.Root(ch).ExecuteC()
12+
_, err := cli.Root().ExecuteC()
4113
if err != nil {
4214
_, _ = fmt.Fprintln(os.Stderr, err.Error())
4315
os.Exit(1)
4416
}
17+
18+
// We exit the main thread while keepin all the other procs goin strong.
4519
runtime.Goexit()
4620
}

dockerutil/container.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func BootstrapContainer(ctx context.Context, client DockerClient, conf Bootstrap
113113

114114
var err error
115115
for r, n := retry.New(time.Second, time.Second*2), 0; r.Wait(ctx) && n < 10; n++ {
116-
var out io.Reader
116+
var out []byte
117117
out, err = ExecContainer(ctx, client, ExecConfig{
118118
ContainerID: conf.ContainerID,
119119
User: conf.User,
@@ -122,16 +122,9 @@ func BootstrapContainer(ctx context.Context, client DockerClient, conf Bootstrap
122122
Stdin: strings.NewReader(conf.Script),
123123
Env: conf.Env,
124124
StdOutErr: conf.StdOutErr,
125-
Detach: conf.Detach,
126125
})
127126
if err != nil {
128-
output, rerr := io.ReadAll(out)
129-
if rerr != nil {
130-
err = xerrors.Errorf("read all: %w", err)
131-
continue
132-
}
133-
134-
err = xerrors.Errorf("boostrap container (%s): %w", output, err)
127+
err = xerrors.Errorf("boostrap container (%s): %w", out, err)
135128
continue
136129
}
137130
break

dockerutil/dockerfake/client.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,7 @@ func (m MockClient) ContainerExecCreate(ctx context.Context, name string, config
162162

163163
func (m MockClient) ContainerExecInspect(ctx context.Context, id string) (dockertypes.ContainerExecInspect, error) {
164164
if m.ContainerExecInspectFn == nil {
165-
return dockertypes.ContainerExecInspect{
166-
Pid: 1,
167-
}, nil
165+
return dockertypes.ContainerExecInspect{}, nil
168166
}
169167

170168
return m.ContainerExecInspectFn(ctx, id)

dockerutil/exec.go

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ import (
44
"bytes"
55
"context"
66
"io"
7-
"time"
87

98
dockertypes "github.com/docker/docker/api/types"
109
"golang.org/x/xerrors"
1110

1211
"github.com/coder/envbox/xio"
13-
"github.com/coder/retry"
1412
)
1513

1614
type ExecConfig struct {
@@ -26,9 +24,9 @@ type ExecConfig struct {
2624

2725
// ExecContainer runs a command in a container. It returns the output and any error.
2826
// If an error occurs during the execution of the command, the output is appended to the error.
29-
func ExecContainer(ctx context.Context, client DockerClient, config ExecConfig) (io.Reader, error) {
27+
func ExecContainer(ctx context.Context, client DockerClient, config ExecConfig) ([]byte, error) {
3028
exec, err := client.ContainerExecCreate(ctx, config.ContainerID, dockertypes.ExecConfig{
31-
Detach: config.Detach,
29+
Detach: true,
3230
Cmd: append([]string{config.Cmd}, config.Args...),
3331
User: config.User,
3432
AttachStderr: true,
@@ -92,41 +90,5 @@ func ExecContainer(ctx context.Context, client DockerClient, config ExecConfig)
9290
return nil, xerrors.Errorf("%s: exit code %d", buf.Bytes(), inspect.ExitCode)
9391
}
9492

95-
return &buf, nil
96-
}
97-
98-
func GetExecPID(ctx context.Context, client DockerClient, execID string) (int, error) {
99-
for r := retry.New(time.Second, time.Second); r.Wait(ctx); {
100-
inspect, err := client.ContainerExecInspect(ctx, execID)
101-
if err != nil {
102-
return 0, xerrors.Errorf("exec inspect: %w", err)
103-
}
104-
105-
if inspect.Pid == 0 {
106-
continue
107-
}
108-
return inspect.Pid, nil
109-
}
110-
111-
return 0, ctx.Err()
112-
}
113-
114-
func WaitForExit(ctx context.Context, client DockerClient, execID string) error {
115-
for r := retry.New(time.Second, time.Second); r.Wait(ctx); {
116-
inspect, err := client.ContainerExecInspect(ctx, execID)
117-
if err != nil {
118-
return xerrors.Errorf("exec inspect: %w", err)
119-
}
120-
121-
if inspect.Running {
122-
continue
123-
}
124-
125-
if inspect.ExitCode > 0 {
126-
return xerrors.Errorf("exit code %d", inspect.ExitCode)
127-
}
128-
129-
return nil
130-
}
131-
return ctx.Err()
93+
return buf.Bytes(), nil
13294
}

dockerutil/image.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dockerutil
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"fmt"
@@ -207,7 +208,7 @@ func GetImageMetadata(ctx context.Context, client DockerClient, image, username
207208
return ImageMetadata{}, xerrors.Errorf("get /etc/passwd entry for %s: %w", username, err)
208209
}
209210

210-
users, err := xunix.ParsePasswd(out)
211+
users, err := xunix.ParsePasswd(bytes.NewReader(out))
211212
if err != nil {
212213
return ImageMetadata{}, xerrors.Errorf("parse passwd entry for (%s): %w", out, err)
213214
}

0 commit comments

Comments
 (0)