service.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // +build windows
  2. /*
  3. Copyright 2018 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package service
  15. import (
  16. "os"
  17. "time"
  18. "k8s.io/apiserver/pkg/server"
  19. "k8s.io/klog"
  20. "golang.org/x/sys/windows"
  21. "golang.org/x/sys/windows/svc"
  22. )
  23. var (
  24. service *handler
  25. )
  26. type handler struct {
  27. tosvc chan bool
  28. fromsvc chan error
  29. }
  30. // InitService is the entry point for running the daemon as a Windows
  31. // service. It returns an indication of whether it is running as a service;
  32. // and an error.
  33. func InitService(serviceName string) error {
  34. h := &handler{
  35. tosvc: make(chan bool),
  36. fromsvc: make(chan error),
  37. }
  38. service = h
  39. var err error
  40. go func() {
  41. err = svc.Run(serviceName, h)
  42. h.fromsvc <- err
  43. }()
  44. // Wait for the first signal from the service handler.
  45. err = <-h.fromsvc
  46. if err != nil {
  47. return err
  48. }
  49. klog.Infof("Running %s as a Windows service!", serviceName)
  50. return nil
  51. }
  52. func (h *handler) Execute(_ []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
  53. s <- svc.Status{State: svc.StartPending, Accepts: 0}
  54. // Unblock initService()
  55. h.fromsvc <- nil
  56. s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)}
  57. klog.Infof("Service running")
  58. Loop:
  59. for {
  60. select {
  61. case <-h.tosvc:
  62. break Loop
  63. case c := <-r:
  64. switch c.Cmd {
  65. case svc.Cmd(windows.SERVICE_CONTROL_PARAMCHANGE):
  66. s <- c.CurrentStatus
  67. case svc.Interrogate:
  68. s <- c.CurrentStatus
  69. case svc.Stop, svc.Shutdown:
  70. klog.Infof("Service stopping")
  71. // We need to translate this request into a signal that can be handled by the signal handler
  72. // handling shutdowns normally (currently apiserver/pkg/server/signal.go).
  73. // If we do not do this, our main threads won't be notified of the upcoming shutdown.
  74. // Since Windows services do not use any console, we cannot simply generate a CTRL_BREAK_EVENT
  75. // but need a dedicated notification mechanism.
  76. graceful := server.RequestShutdown()
  77. // Free up the control handler and let us terminate as gracefully as possible.
  78. // If that takes too long, the service controller will kill the remaining threads.
  79. // As per https://docs.microsoft.com/en-us/windows/desktop/services/service-control-handler-function
  80. s <- svc.Status{State: svc.StopPending}
  81. // If we cannot exit gracefully, we really only can exit our process, so atleast the
  82. // service manager will think that we gracefully exited. At the time of writing this comment this is
  83. // needed for applications that do not use signals (e.g. kube-proxy)
  84. if !graceful {
  85. go func() {
  86. // Ensure the SCM was notified (The operation above (send to s) was received and communicated to the
  87. // service control manager - so it doesn't look like the service crashes)
  88. time.Sleep(1 * time.Second)
  89. os.Exit(0)
  90. }()
  91. }
  92. break Loop
  93. }
  94. }
  95. }
  96. return false, 0
  97. }