fix: separate signals for passive, active, and forced shutdown (#12358)

* fix: separate signals for passive, active, and forced shutdown

`SIGTERM`: Passive shutdown stopping provisioner daemons from accepting new
jobs but waiting for existing jobs to successfully complete.

`SIGINT` (old existing behavior): Notify provisioner daemons to cancel in-flight jobs, wait 5s for jobs to be exited, then force quit.

`SIGKILL`: Untouched from before, will force-quit.

* Revert dramatic signal changes

* Rename

* Fix shutdown behavior for provisioner daemons

* Add test for graceful shutdown
This commit is contained in:
Kyle Carberry
2024-03-15 13:16:36 +00:00
committed by GitHub
parent 2c947c1921
commit 895df54051
18 changed files with 136 additions and 35 deletions
+42 -1
View File
@@ -21,6 +21,7 @@ import (
"net/url"
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
@@ -1605,7 +1606,7 @@ func TestServer_Production(t *testing.T) {
}
//nolint:tparallel,paralleltest // This test cannot be run in parallel due to signal handling.
func TestServer_Shutdown(t *testing.T) {
func TestServer_InterruptShutdown(t *testing.T) {
t.Skip("This test issues an interrupt signal which will propagate to the test runner.")
if runtime.GOOS == "windows" {
@@ -1638,6 +1639,46 @@ func TestServer_Shutdown(t *testing.T) {
require.NoError(t, err)
}
func TestServer_GracefulShutdown(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
// Sending interrupt signal isn't supported on Windows!
t.SkipNow()
}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--http-address", ":0",
"--access-url", "http://example.com",
"--provisioner-daemons", "1",
"--cache-dir", t.TempDir(),
)
var stopFunc context.CancelFunc
root = root.WithTestSignalNotifyContext(t, func(parent context.Context, signals ...os.Signal) (context.Context, context.CancelFunc) {
if !reflect.DeepEqual(cli.StopSignalsNoInterrupt, signals) {
return context.WithCancel(ctx)
}
var ctx context.Context
ctx, stopFunc = context.WithCancel(parent)
return ctx, stopFunc
})
serverErr := make(chan error, 1)
pty := ptytest.New(t).Attach(root)
go func() {
serverErr <- root.WithContext(ctx).Run()
}()
_ = waitAccessURL(t, cfg)
// It's fair to assume `stopFunc` isn't nil here, because the server
// has started and access URL is propagated.
stopFunc()
pty.ExpectMatch("waiting for provisioner jobs to complete")
err := <-serverErr
require.NoError(t, err)
}
func BenchmarkServerHelp(b *testing.B) {
// server --help is a good proxy for measuring the
// constant overhead of each command.