migrate_server.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package main
  14. import (
  15. "fmt"
  16. "os"
  17. "os/exec"
  18. "strings"
  19. "time"
  20. "k8s.io/klog"
  21. )
  22. // EtcdMigrateServer manages starting and stopping a versioned etcd server binary.
  23. type EtcdMigrateServer struct {
  24. cfg *EtcdMigrateCfg
  25. client EtcdMigrateClient
  26. cmd *exec.Cmd
  27. }
  28. // NewEtcdMigrateServer creates a EtcdMigrateServer for starting and stopping a etcd server at the given version.
  29. func NewEtcdMigrateServer(cfg *EtcdMigrateCfg, client EtcdMigrateClient) *EtcdMigrateServer {
  30. return &EtcdMigrateServer{cfg: cfg, client: client}
  31. }
  32. // Start starts an etcd server as a separate process, waits until it has started, and returns a exec.Cmd.
  33. func (r *EtcdMigrateServer) Start(version *EtcdVersion) error {
  34. etcdCmd := exec.Command(
  35. fmt.Sprintf("%s/etcd-%s", r.cfg.binPath, version),
  36. "--name", r.cfg.name,
  37. "--initial-cluster", r.cfg.initialCluster,
  38. "--debug",
  39. "--data-dir", r.cfg.dataDirectory,
  40. "--listen-client-urls", fmt.Sprintf("http://127.0.0.1:%d", r.cfg.port),
  41. "--advertise-client-urls", fmt.Sprintf("http://127.0.0.1:%d", r.cfg.port),
  42. "--listen-peer-urls", r.cfg.peerListenUrls,
  43. "--initial-advertise-peer-urls", r.cfg.peerAdvertiseUrls,
  44. )
  45. if r.cfg.etcdServerArgs != "" {
  46. extraArgs := strings.Fields(r.cfg.etcdServerArgs)
  47. etcdCmd.Args = append(etcdCmd.Args, extraArgs...)
  48. }
  49. fmt.Printf("Starting server %s: %+v\n", r.cfg.name, etcdCmd.Args)
  50. etcdCmd.Stdout = os.Stdout
  51. etcdCmd.Stderr = os.Stderr
  52. err := etcdCmd.Start()
  53. if err != nil {
  54. return err
  55. }
  56. interval := time.NewTicker(time.Millisecond * 500)
  57. defer interval.Stop()
  58. done := make(chan bool)
  59. go func() {
  60. time.Sleep(time.Minute * 2)
  61. done <- true
  62. }()
  63. for {
  64. select {
  65. case <-interval.C:
  66. err := r.client.SetEtcdVersionKeyValue(version)
  67. if err != nil {
  68. klog.Infof("Still waiting for etcd to start, current error: %v", err)
  69. // keep waiting
  70. } else {
  71. klog.Infof("Etcd on port %d is up.", r.cfg.port)
  72. r.cmd = etcdCmd
  73. return nil
  74. }
  75. case <-done:
  76. err = etcdCmd.Process.Kill()
  77. if err != nil {
  78. return fmt.Errorf("error killing etcd: %v", err)
  79. }
  80. return fmt.Errorf("Timed out waiting for etcd on port %d", r.cfg.port)
  81. }
  82. }
  83. }
  84. // Stop terminates the etcd server process. If the etcd server process has not been started
  85. // or is not still running, this returns an error.
  86. func (r *EtcdMigrateServer) Stop() error {
  87. if r.cmd == nil {
  88. return fmt.Errorf("cannot stop EtcdMigrateServer that has not been started")
  89. }
  90. err := r.cmd.Process.Signal(os.Interrupt)
  91. if err != nil {
  92. return fmt.Errorf("error sending SIGINT to etcd for graceful shutdown: %v", err)
  93. }
  94. gracefulWait := time.Minute * 2
  95. stopped := make(chan bool)
  96. timedout := make(chan bool)
  97. go func() {
  98. time.Sleep(gracefulWait)
  99. timedout <- true
  100. }()
  101. go func() {
  102. select {
  103. case <-stopped:
  104. return
  105. case <-timedout:
  106. klog.Infof("etcd server has not terminated gracefully after %s, killing it.", gracefulWait)
  107. r.cmd.Process.Kill()
  108. return
  109. }
  110. }()
  111. err = r.cmd.Wait()
  112. stopped <- true
  113. if exiterr, ok := err.(*exec.ExitError); ok {
  114. klog.Infof("etcd server stopped (signal: %s)", exiterr.Error())
  115. // stopped
  116. } else if err != nil {
  117. return fmt.Errorf("error waiting for etcd to stop: %v", err)
  118. }
  119. klog.Infof("Stopped etcd server %s", r.cfg.name)
  120. return nil
  121. }