Skip to content

Commit

Permalink
Merge pull request #73292 from steffengy/master
Browse files Browse the repository at this point in the history
windows/service: implement graceful shutdown when run as windows service
  • Loading branch information
k8s-ci-robot committed Feb 20, 2019
2 parents a782adf + c2b771d commit 296985c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 7 deletions.
1 change: 1 addition & 0 deletions pkg/windows/service/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/windows/service",
deps = select({
"@io_bazel_rules_go//go/platform:windows": [
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/golang.org/x/sys/windows:go_default_library",
"//vendor/golang.org/x/sys/windows/svc:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
Expand Down
30 changes: 27 additions & 3 deletions pkg/windows/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ package service

import (
"os"
"time"

"k8s.io/apiserver/pkg/server"
"k8s.io/klog"

"golang.org/x/sys/windows"
Expand Down Expand Up @@ -80,9 +82,31 @@ 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.
graceful := 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}

// If we cannot exit gracefully, we really only can exit our process, so atleast the
// service manager will think that we gracefully exited. At the time of writing this comment this is
// needed for applications that do not use signals (e.g. kube-proxy)
if !graceful {
go func() {
// Ensure the SCM was notified (The operation above (send to s) was received and communicated to the
// service control manager - so it doesn't look like the service crashes)
time.Sleep(1 * time.Second)
os.Exit(0)
}()
}
break Loop
}
}
}
Expand Down
24 changes: 20 additions & 4 deletions staging/src/k8s.io/apiserver/pkg/server/signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,38 @@ import (
)

var onlyOneSignalHandler = make(chan struct{})
var shutdownHandler chan os.Signal

// 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
// is terminated with exit code 1.
func SetupSignalHandler() <-chan struct{} {
close(onlyOneSignalHandler) // panics when called twice

shutdownHandler = make(chan os.Signal, 2)

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)
// This returns whether a handler was notified
func RequestShutdown() bool {
if shutdownHandler != nil {
select {
case shutdownHandler <- shutdownSignals[0]:
return true
default:
}
}

return false
}

0 comments on commit 296985c

Please sign in to comment.