diff --git a/pkg/windows/service/service.go b/pkg/windows/service/service.go index a5bffa1822e..28b531ae77c 100644 --- a/pkg/windows/service/service.go +++ b/pkg/windows/service/service.go @@ -19,8 +19,7 @@ limitations under the License. package service import ( - "os" - + "k8s.io/apiserver/pkg/server" "k8s.io/klog" "golang.org/x/sys/windows" @@ -80,9 +79,19 @@ Loop: case svc.Interrogate: s <- c.CurrentStatus case svc.Stop, svc.Shutdown: - s <- svc.Status{State: svc.Stopped} - // TODO: Stop the kubelet gracefully instead of killing the process - os.Exit(0) + klog.Infof("Service stopping") + // We need to translate this request into a signal that can be handled by the the signal handler + // handling shutdowns normally (currently apiserver/pkg/server/signal.go). + // If we do not do this, our main threads won't be notified of the upcoming shutdown. + // Since Windows services do not use any console, we cannot simply generate a CTRL_BREAK_EVENT + // but need a dedicated notification mechanism. + server.RequestShutdown() + + // Free up the control handler and let us terminate as gracefully as possible. + // If that takes too long, the service controller will kill the remaining threads. + // As per https://docs.microsoft.com/en-us/windows/desktop/services/service-control-handler-function + s <- svc.Status{State: svc.StopPending} + break Loop } } } diff --git a/staging/src/k8s.io/apiserver/pkg/server/signal.go b/staging/src/k8s.io/apiserver/pkg/server/signal.go index 6f0cff4baed..6f3edbba470 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/signal.go +++ b/staging/src/k8s.io/apiserver/pkg/server/signal.go @@ -22,6 +22,7 @@ import ( ) var onlyOneSignalHandler = make(chan struct{}) +var shutdownHandler = make(chan os.Signal, 2) // SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned // which is closed on one of these signals. If a second signal is caught, the program @@ -30,14 +31,21 @@ func SetupSignalHandler() <-chan struct{} { close(onlyOneSignalHandler) // panics when called twice stop := make(chan struct{}) - c := make(chan os.Signal, 2) - signal.Notify(c, shutdownSignals...) + signal.Notify(shutdownHandler, shutdownSignals...) go func() { - <-c + <-shutdownHandler close(stop) - <-c + <-shutdownHandler os.Exit(1) // second signal. Exit directly. }() return stop } + +// RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT) +func RequestShutdown() { + select { + case shutdownHandler <- shutdownSignals[0]: + default: + } +}