go.etcd.io/etcd@v3.3.27+incompatible/contrib/systemd/etcd2-backup-coreos/etcd2-restore.go (about) 1 // Copyright 2015 The etcd Authors 2 // 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 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "flag" 19 "fmt" 20 "os" 21 "os/exec" 22 "path" 23 "regexp" 24 "time" 25 ) 26 27 var ( 28 etcdctlPath string 29 etcdPath string 30 etcdRestoreDir string 31 etcdName string 32 etcdPeerUrls string 33 ) 34 35 func main() { 36 flag.StringVar(&etcdctlPath, "etcdctl-path", "/usr/bin/etcdctl", "absolute path to etcdctl executable") 37 flag.StringVar(&etcdPath, "etcd-path", "/usr/bin/etcd2", "absolute path to etcd2 executable") 38 flag.StringVar(&etcdRestoreDir, "etcd-restore-dir", "/var/lib/etcd2-restore", "absolute path to etcd2 restore dir") 39 flag.StringVar(&etcdName, "etcd-name", "default", "name of etcd2 node") 40 flag.StringVar(&etcdPeerUrls, "etcd-peer-urls", "", "advertise peer urls") 41 42 flag.Parse() 43 44 if etcdPeerUrls == "" { 45 panic("must set -etcd-peer-urls") 46 } 47 48 if finfo, err := os.Stat(etcdRestoreDir); err != nil { 49 panic(err) 50 } else { 51 if !finfo.IsDir() { 52 panic(fmt.Errorf("%s is not a directory", etcdRestoreDir)) 53 } 54 } 55 56 if !path.IsAbs(etcdctlPath) { 57 panic(fmt.Sprintf("etcdctl-path %s is not absolute", etcdctlPath)) 58 } 59 60 if !path.IsAbs(etcdPath) { 61 panic(fmt.Sprintf("etcd-path %s is not absolute", etcdPath)) 62 } 63 64 if err := restoreEtcd(); err != nil { 65 panic(err) 66 } 67 } 68 69 func restoreEtcd() error { 70 etcdCmd := exec.Command(etcdPath, "--force-new-cluster", "--data-dir", etcdRestoreDir) 71 72 etcdCmd.Stdout = os.Stdout 73 etcdCmd.Stderr = os.Stderr 74 75 if err := etcdCmd.Start(); err != nil { 76 return fmt.Errorf("Could not start etcd2: %s", err) 77 } 78 defer etcdCmd.Wait() 79 defer etcdCmd.Process.Kill() 80 81 return runCommands(10, 2*time.Second) 82 } 83 84 var ( 85 clusterHealthRegex = regexp.MustCompile(".*cluster is healthy.*") 86 lineSplit = regexp.MustCompile("\n+") 87 colonSplit = regexp.MustCompile(`\:`) 88 ) 89 90 func runCommands(maxRetry int, interval time.Duration) error { 91 var retryCnt int 92 for retryCnt = 1; retryCnt <= maxRetry; retryCnt++ { 93 out, err := exec.Command(etcdctlPath, "cluster-health").CombinedOutput() 94 if err == nil && clusterHealthRegex.Match(out) { 95 break 96 } 97 fmt.Printf("Error: %s: %s\n", err, string(out)) 98 time.Sleep(interval) 99 } 100 101 if retryCnt > maxRetry { 102 return fmt.Errorf("Timed out waiting for healthy cluster\n") 103 } 104 105 var ( 106 memberID string 107 out []byte 108 err error 109 ) 110 if out, err = exec.Command(etcdctlPath, "member", "list").CombinedOutput(); err != nil { 111 return fmt.Errorf("Error calling member list: %s", err) 112 } 113 members := lineSplit.Split(string(out), 2) 114 if len(members) < 1 { 115 return fmt.Errorf("Could not find a cluster member from: \"%s\"", members) 116 } 117 parts := colonSplit.Split(members[0], 2) 118 if len(parts) < 2 { 119 return fmt.Errorf("Could not parse member id from: \"%s\"", members[0]) 120 } 121 memberID = parts[0] 122 123 out, err = exec.Command(etcdctlPath, "member", "update", memberID, etcdPeerUrls).CombinedOutput() 124 fmt.Printf("member update result: %s\n", string(out)) 125 return err 126 }