BUGFIX: exit with correct exit codes
Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com>
This commit is contained in:
parent
99fc8fb5f8
commit
bfd7a51618
56
internal/cmd/util/exit_test.go
Normal file
56
internal/cmd/util/exit_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2020 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetExitCode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expCode int
|
||||
}{
|
||||
{"Test context.Canceled", context.Canceled, 0},
|
||||
{"Test wrapped context.Canceled", fmt.Errorf("wrapped: %w", context.Canceled), 0},
|
||||
{"Test context.DeadlineExceeded", context.DeadlineExceeded, 124},
|
||||
{"Test wrapped context.DeadlineExceeded", fmt.Errorf("wrapped: %w", context.DeadlineExceeded), 124},
|
||||
{"Test error", errors.New("error"), 1},
|
||||
{"Test nil", nil, 0},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Every testExitCode call has to be run in its own test, because
|
||||
// it calls the test again filtered by the name of the subtest with
|
||||
// the variable BE_CRASHER=1.
|
||||
exitCode := testExitCode(t, func(t *testing.T) {
|
||||
SetExitCode(tt.err)
|
||||
|
||||
_, complete := SetupExitHandler(context.Background(), AlwaysErrCode)
|
||||
complete()
|
||||
})
|
||||
|
||||
if exitCode != tt.expCode {
|
||||
t.Errorf("Test %s: expected exit code %d, got %d", tt.name, tt.expCode, exitCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -59,7 +59,7 @@ func SetupExitHandler(parentCtx context.Context, exitBehavior ExitBehavior) (con
|
||||
// first signal. Cancel context and pass exit code to errorExitCodeChannel.
|
||||
signalInt := int((<-c).(syscall.Signal))
|
||||
if exitBehavior == AlwaysErrCode {
|
||||
errorExitCodeChannel <- signalInt
|
||||
errorExitCodeChannel <- (128 + signalInt)
|
||||
}
|
||||
cancel(fmt.Errorf("received signal %d", signalInt))
|
||||
// second signal. Exit directly.
|
||||
@ -70,7 +70,7 @@ func SetupExitHandler(parentCtx context.Context, exitBehavior ExitBehavior) (con
|
||||
return ctx, func() {
|
||||
select {
|
||||
case signalInt := <-errorExitCodeChannel:
|
||||
os.Exit(128 + signalInt)
|
||||
os.Exit(signalInt)
|
||||
default:
|
||||
// Do not exit, there are no exit codes in the channel,
|
||||
// so just continue and let the main function go out of
|
||||
|
||||
126
internal/cmd/util/signal_test.go
Normal file
126
internal/cmd/util/signal_test.go
Normal file
@ -0,0 +1,126 @@
|
||||
//go:build !windows
|
||||
|
||||
/*
|
||||
Copyright 2020 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// based on https://go.dev/talks/2014/testing.slide#23 and
|
||||
// https://stackoverflow.com/a/33404435
|
||||
func testExitCode(
|
||||
t *testing.T,
|
||||
fn func(t *testing.T),
|
||||
) int {
|
||||
if os.Getenv("BE_CRASHER") == "1" {
|
||||
fn(t)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0], "-test.run="+t.Name())
|
||||
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
|
||||
if e, ok := err.(*exec.ExitError); ok {
|
||||
return e.ExitCode()
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func TestSetupExitHandlerAlwaysErrCodeSIGTERM(t *testing.T) {
|
||||
exitCode := testExitCode(t, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx, complete := SetupExitHandler(ctx, AlwaysErrCode)
|
||||
defer complete()
|
||||
|
||||
if err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM); err != nil {
|
||||
t.Fatal(err)
|
||||
os.Exit(99)
|
||||
}
|
||||
|
||||
// Wait for the program to shut down.
|
||||
<-ctx.Done()
|
||||
|
||||
if context.Cause(ctx).Error() != "received signal 15" {
|
||||
t.Errorf("expected signal 15, got %s", ctx.Err().Error())
|
||||
os.Exit(99)
|
||||
}
|
||||
})
|
||||
|
||||
if exitCode != 143 {
|
||||
t.Errorf("expected exit code 143, got %d", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupExitHandlerAlwaysErrCodeSIGINT(t *testing.T) {
|
||||
exitCode := testExitCode(t, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx, complete := SetupExitHandler(ctx, AlwaysErrCode)
|
||||
defer complete()
|
||||
|
||||
if err := syscall.Kill(syscall.Getpid(), syscall.SIGINT); err != nil {
|
||||
t.Fatal(err)
|
||||
os.Exit(99)
|
||||
}
|
||||
|
||||
// Wait for the program to shut down.
|
||||
<-ctx.Done()
|
||||
|
||||
if context.Cause(ctx).Error() != "received signal 2" {
|
||||
t.Errorf("expected signal 2, got %s", ctx.Err().Error())
|
||||
os.Exit(99)
|
||||
}
|
||||
})
|
||||
|
||||
if exitCode != 130 {
|
||||
t.Errorf("expected exit code 130, got %d", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupExitHandlerGracefulShutdownSIGINT(t *testing.T) {
|
||||
exitCode := testExitCode(t, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx, complete := SetupExitHandler(ctx, GracefulShutdown)
|
||||
defer complete()
|
||||
|
||||
if err := syscall.Kill(syscall.Getpid(), syscall.SIGINT); err != nil {
|
||||
t.Fatal(err)
|
||||
os.Exit(99)
|
||||
}
|
||||
|
||||
// Wait for the program to shut down.
|
||||
<-ctx.Done()
|
||||
|
||||
if context.Cause(ctx).Error() != "received signal 2" {
|
||||
t.Errorf("expected signal 2, got %s", ctx.Err().Error())
|
||||
os.Exit(99)
|
||||
}
|
||||
})
|
||||
|
||||
if exitCode != 0 {
|
||||
t.Errorf("expected exit code 0, got %d", exitCode)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user