Commit afdfe8d5 authored by Steffen Butzer's avatar Steffen Butzer

windows/svc: workaround-exit mechanism that works for signal-less binaries

parent f243c887
...@@ -19,6 +19,9 @@ limitations under the License. ...@@ -19,6 +19,9 @@ limitations under the License.
package service package service
import ( import (
"os"
"time"
"k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server"
"k8s.io/klog" "k8s.io/klog"
...@@ -85,12 +88,24 @@ Loop: ...@@ -85,12 +88,24 @@ Loop:
// If we do not do this, our main threads won't be notified of the upcoming shutdown. // 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 // Since Windows services do not use any console, we cannot simply generate a CTRL_BREAK_EVENT
// but need a dedicated notification mechanism. // but need a dedicated notification mechanism.
server.RequestShutdown() graceful := server.RequestShutdown()
// Free up the control handler and let us terminate as gracefully as possible. // 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. // 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 // As per https://docs.microsoft.com/en-us/windows/desktop/services/service-control-handler-function
s <- svc.Status{State: svc.StopPending} 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 break Loop
} }
} }
......
...@@ -22,7 +22,7 @@ import ( ...@@ -22,7 +22,7 @@ import (
) )
var onlyOneSignalHandler = make(chan struct{}) var onlyOneSignalHandler = make(chan struct{})
var shutdownHandler = make(chan os.Signal, 2) var shutdownHandler chan os.Signal
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned // 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 // which is closed on one of these signals. If a second signal is caught, the program
...@@ -30,6 +30,8 @@ var shutdownHandler = make(chan os.Signal, 2) ...@@ -30,6 +30,8 @@ var shutdownHandler = make(chan os.Signal, 2)
func SetupSignalHandler() <-chan struct{} { func SetupSignalHandler() <-chan struct{} {
close(onlyOneSignalHandler) // panics when called twice close(onlyOneSignalHandler) // panics when called twice
shutdownHandler = make(chan os.Signal, 2)
stop := make(chan struct{}) stop := make(chan struct{})
signal.Notify(shutdownHandler, shutdownSignals...) signal.Notify(shutdownHandler, shutdownSignals...)
go func() { go func() {
...@@ -43,9 +45,15 @@ func SetupSignalHandler() <-chan struct{} { ...@@ -43,9 +45,15 @@ func SetupSignalHandler() <-chan struct{} {
} }
// RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT) // RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT)
func RequestShutdown() { // This returns whether a handler was notified
select { func RequestShutdown() bool {
case shutdownHandler <- shutdownSignals[0]: if shutdownHandler != nil {
default: select {
case shutdownHandler <- shutdownSignals[0]:
return true
default:
}
} }
return false
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment