From ad8d9dd71a3e3672e78d48058f29bbe5b251bdfb Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Fri, 6 May 2022 13:45:18 -0700 Subject: [PATCH] feat: make it harder to skip graceful shutdown accidentally (#1327) * feat: make it harder to skip graceful shutdown accidentally Signed-off-by: Spike Curtis * fixup: don't use unbuffered signal channel Signed-off-by: Spike Curtis --- cli/server.go | 11 +++++++++-- cli/server_test.go | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cli/server.go b/cli/server.go index be2ddab647..8a6dc99492 100644 --- a/cli/server.go +++ b/cli/server.go @@ -342,6 +342,12 @@ func server() *cobra.Command { return xerrors.Errorf("notify systemd: %w", err) } + // 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, 1) defer signal.Stop(stopChan) signal.Notify(stopChan, os.Interrupt) @@ -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 369334c48e..4066cb1e86 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -271,6 +271,12 @@ func TestServer(t *testing.T) { require.NoError(t, err) err = currentProcess.Signal(os.Interrupt) require.NoError(t, err) + // 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 }) t.Run("DatadogTracerNoLeak", func(t *testing.T) {