Skip to content

Commit c083608

Browse files
committed
feat(agent): Handle signals and shutdown gracefully
This change allows the agent to handle common shutdown signals like interrupt, hangup and terminate and initiate a graceful shutdown. As long as terraform providers initiate graceful shutdowns via the aforementioned signals, things like SSH connections will be closed immediately on shutdown instead of being left hanging/timing out due to the agent being abruptly killed. Refs: #4677, #5901
1 parent 896158c commit c083608

File tree

1 file changed

+30
-10
lines changed

1 file changed

+30
-10
lines changed

cli/agent.go

+30-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http/pprof"
88
"net/url"
99
"os"
10+
"os/signal"
1011
"path/filepath"
1112
"runtime"
1213
"time"
@@ -35,12 +36,10 @@ func workspaceAgent() *cobra.Command {
3536
Use: "agent",
3637
// This command isn't useful to manually execute.
3738
Hidden: true,
38-
RunE: func(cmd *cobra.Command, args []string) error {
39+
RunE: func(cmd *cobra.Command, _ []string) error {
3940
ctx, cancel := context.WithCancel(cmd.Context())
4041
defer cancel()
4142

42-
go dumpHandler(ctx)
43-
4443
rawURL, err := cmd.Flags().GetString(varAgentURL)
4544
if err != nil {
4645
return xerrors.Errorf("CODER_AGENT_URL must be set: %w", err)
@@ -50,18 +49,18 @@ func workspaceAgent() *cobra.Command {
5049
return xerrors.Errorf("parse %q: %w", rawURL, err)
5150
}
5251

53-
logWriter := &lumberjack.Logger{
54-
Filename: filepath.Join(os.TempDir(), "coder-agent.log"),
55-
MaxSize: 5, // MB
56-
}
57-
defer logWriter.Close()
58-
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug)
59-
6052
isLinux := runtime.GOOS == "linux"
6153

6254
// Spawn a reaper so that we don't accumulate a ton
6355
// of zombie processes.
6456
if reaper.IsInitProcess() && !noReap && isLinux {
57+
logWriter := &lumberjack.Logger{
58+
Filename: filepath.Join(os.TempDir(), "coder-agent-init.log"),
59+
MaxSize: 5, // MB
60+
}
61+
defer logWriter.Close()
62+
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug)
63+
6564
logger.Info(ctx, "spawning reaper process")
6665
// Do not start a reaper on the child process. It's important
6766
// to do this else we fork bomb ourselves.
@@ -76,6 +75,27 @@ func workspaceAgent() *cobra.Command {
7675
return nil
7776
}
7877

78+
// Handle interrupt signals to allow for graceful shutdown,
79+
// note that calling stopNotify disables the signal handler
80+
// and the next interrupt will terminate the program.
81+
//
82+
// Note that we don't want to handle these signals in the
83+
// process that runs as PID 1, that's why we do this after
84+
// the reaper forked.
85+
ctx, stopNotify := signal.NotifyContext(ctx, InterruptSignals...)
86+
defer stopNotify()
87+
88+
// dumpHandler does signal handling, so we call it after the
89+
// reaper.
90+
go dumpHandler(ctx)
91+
92+
logWriter := &lumberjack.Logger{
93+
Filename: filepath.Join(os.TempDir(), "coder-agent.log"),
94+
MaxSize: 5, // MB
95+
}
96+
defer logWriter.Close()
97+
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug)
98+
7999
version := buildinfo.Version()
80100
logger.Info(ctx, "starting agent",
81101
slog.F("url", coderURL),

0 commit comments

Comments
 (0)