Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for delaying webhook shutdown #3170

Closed
31 changes: 30 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"

"github.com/go-logr/zapr"
Expand Down Expand Up @@ -115,6 +117,7 @@ var (
disabledBuiltins = util.NewFlagSet()
enableK8sCel = flag.Bool("experimental-enable-k8s-native-validation", false, "PROTOTYPE (not stable): enable the validating admission policy driver")
externaldataProviderResponseCacheTTL = flag.Duration("external-data-provider-response-cache-ttl", 3*time.Minute, "TTL for the external data provider response cache. Specify the duration in 'h', 'm', or 's' for hours, minutes, or seconds respectively. Defaults to 3 minutes if unspecified. Setting the TTL to 0 disables the cache.")
shutdownDelayDuration = flag.Duration("shutdown-delay-duration", 10*time.Second, "The amount of time to wait before shutting down gracefully. This can be used to delay webhook shutdown until the Kubernetes API Server stops trying to make new connections")
)

func init() {
Expand Down Expand Up @@ -309,7 +312,7 @@ func innerMain() int {

// Setup controllers asynchronously, they will block for certificate generation if needed.
setupErr := make(chan error)
ctx := ctrl.SetupSignalHandler()
ctx := setupSignalHandler(*shutdownDelayDuration)
go func() {
setupErr <- setupControllers(ctx, mgr, sw, tracker, setupFinished)
}()
Expand Down Expand Up @@ -579,3 +582,29 @@ func setLoggerForProduction(encoder zapcore.LevelEncoder, dest io.Writer) {
ctrl.SetLogger(newlogger)
klog.SetLogger(newlogger)
}

// setupSignalHandler registers a signal handler for SIGTERM and SIGINT and
// returns a context which is canceled when one of these signals is caught after
// waiting for the specified duration. If a second signal is caught then we
// terminate immediately with exit code 1. The implementation has been stolen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid misinterpretation, let's avoid "stolen". "This augments controller-runtime's model to support the delay".

Also, have you considered submitting this to controller-runtime? Generally it's better to avoid forks when possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxsmythe Thanks for the review, good point! I have created a controller-runtime PR here: kubernetes-sigs/controller-runtime#2601

Happy for this PR to be closed for now and I will create a new one if/when the controller-runtime PR is merged

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contributing!

// from controller-runtime and then extended to support delay:
// https://github.com/kubernetes-sigs/controller-runtime/blob/2154ffbc22e26ffd8c3b713927f0df2fa40841f2/pkg/manager/signals/signal.go#L27-L45
func setupSignalHandler(delayDuration time.Duration) context.Context {
ctx, cancel := context.WithCancel(context.Background())

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
// Cancel the context after delaying for the specified duration but
// avoid blocking if a second signal is received
go func() {
<-time.After(delayDuration)
cancel()
}()
<-c
os.Exit(1) // Second signal received, exit directly...
}()

return ctx
}
Loading