From 474c796a1e499d200e44bbe7aaf7fe4fc0a6fae3 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Fri, 6 May 2022 17:45:45 +0000 Subject: [PATCH 1/2] feat: make it harder to skip graceful shutdown accidentally Signed-off-by: Spike Curtis --- cli/server.go | 13 ++++++++++--- cli/server_test.go | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cli/server.go b/cli/server.go index be2ddab647374..d7b2f4ef079d3 100644 --- a/cli/server.go +++ b/cli/server.go @@ -342,7 +342,13 @@ func server() *cobra.Command { return xerrors.Errorf("notify systemd: %w", err) } - stopChan := make(chan os.Signal, 1) + // Because the graceful shutdown includes cleaning up workspaces in dev mode, we're + // going to make it harder to accidentally skip the graceful shutdown by hitting ctrl+c + // two or more times. So the stopChan is unlimited in size and we don't call + // signal.Stop() until graceful shutdown finished--this means we swallow additional + // SIGINT after the first. To get out of a graceful shutdown, the user can send SIGQUIT + // with ctrl+\ or SIGTERM with `kill`. + stopChan := make(chan os.Signal) defer signal.Stop(stopChan) signal.Notify(stopChan, os.Interrupt) select { @@ -358,12 +364,13 @@ func server() *cobra.Command { return err case <-stopChan: } - signal.Stop(stopChan) _, err = daemon.SdNotify(false, daemon.SdNotifyStopping) if err != nil { return xerrors.Errorf("notify systemd: %w", err) } - _, _ = fmt.Fprintln(cmd.OutOrStdout(), "\n\n"+cliui.Styles.Bold.Render("Interrupt caught. Gracefully exiting...")) + _, _ = fmt.Fprintln(cmd.OutOrStdout(), "\n\n"+ + cliui.Styles.Bold.Render( + "Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit")) if dev { organizations, err := client.OrganizationsByUser(cmd.Context(), codersdk.Me) diff --git a/cli/server_test.go b/cli/server_test.go index 369334c48ec14..ddefefd18f476 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -271,6 +271,9 @@ func TestServer(t *testing.T) { require.NoError(t, err) err = currentProcess.Signal(os.Interrupt) require.NoError(t, err) + // Send a second signal, which should be ignored + err = currentProcess.Signal(os.Interrupt) + require.NoError(t, err) <-done }) t.Run("DatadogTracerNoLeak", func(t *testing.T) { From ff7e74cf6c05d418ab0b3ac730687e4de0ccfc71 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Fri, 6 May 2022 18:05:55 +0000 Subject: [PATCH 2/2] fixup: don't use unbuffered signal channel Signed-off-by: Spike Curtis --- cli/server.go | 2 +- cli/server_test.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cli/server.go b/cli/server.go index d7b2f4ef079d3..8a6dc99492816 100644 --- a/cli/server.go +++ b/cli/server.go @@ -348,7 +348,7 @@ func server() *cobra.Command { // signal.Stop() until graceful shutdown finished--this means we swallow additional // SIGINT after the first. To get out of a graceful shutdown, the user can send SIGQUIT // with ctrl+\ or SIGTERM with `kill`. - stopChan := make(chan os.Signal) + stopChan := make(chan os.Signal, 1) defer signal.Stop(stopChan) signal.Notify(stopChan, os.Interrupt) select { diff --git a/cli/server_test.go b/cli/server_test.go index ddefefd18f476..4066cb1e86ea2 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -271,7 +271,10 @@ func TestServer(t *testing.T) { require.NoError(t, err) err = currentProcess.Signal(os.Interrupt) require.NoError(t, err) - // Send a second signal, which should be ignored + // Send a two more signal, which should be ignored. Send 2 because the channel has a buffer + // of 1 and we want to make sure that nothing strange happens if we exceed the buffer. + err = currentProcess.Signal(os.Interrupt) + require.NoError(t, err) err = currentProcess.Signal(os.Interrupt) require.NoError(t, err) <-done