blob: d4916a505f99b245c2b27d2b8d84ddc52f050240 [file] [log] [blame]
// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"flag"
"fmt"
"os"
"os/exec"
"path"
"regexp"
"time"
)
var (
etcdctlPath string
etcdPath string
etcdRestoreDir string
etcdName string
etcdPeerUrls string
)
func main() {
flag.StringVar(&etcdctlPath, "etcdctl-path", "/usr/bin/etcdctl", "absolute path to etcdctl executable")
flag.StringVar(&etcdPath, "etcd-path", "/usr/bin/etcd2", "absolute path to etcd2 executable")
flag.StringVar(&etcdRestoreDir, "etcd-restore-dir", "/var/lib/etcd2-restore", "absolute path to etcd2 restore dir")
flag.StringVar(&etcdName, "etcd-name", "default", "name of etcd2 node")
flag.StringVar(&etcdPeerUrls, "etcd-peer-urls", "", "advertise peer urls")
flag.Parse()
if etcdPeerUrls == "" {
panic("must set -etcd-peer-urls")
}
if finfo, err := os.Stat(etcdRestoreDir); err != nil {
panic(err)
} else {
if !finfo.IsDir() {
panic(fmt.Errorf("%s is not a directory", etcdRestoreDir))
}
}
if !path.IsAbs(etcdctlPath) {
panic(fmt.Sprintf("etcdctl-path %s is not absolute", etcdctlPath))
}
if !path.IsAbs(etcdPath) {
panic(fmt.Sprintf("etcd-path %s is not absolute", etcdPath))
}
if err := restoreEtcd(); err != nil {
panic(err)
}
}
func restoreEtcd() error {
etcdCmd := exec.Command(etcdPath, "--force-new-cluster", "--data-dir", etcdRestoreDir)
etcdCmd.Stdout = os.Stdout
etcdCmd.Stderr = os.Stderr
if err := etcdCmd.Start(); err != nil {
return fmt.Errorf("Could not start etcd2: %s", err)
}
defer etcdCmd.Wait()
defer etcdCmd.Process.Kill()
return runCommands(10, 2*time.Second)
}
var (
clusterHealthRegex = regexp.MustCompile(".*cluster is healthy.*")
lineSplit = regexp.MustCompile("\n+")
colonSplit = regexp.MustCompile(`\:`)
)
func runCommands(maxRetry int, interval time.Duration) error {
var retryCnt int
for retryCnt = 1; retryCnt <= maxRetry; retryCnt++ {
out, err := exec.Command(etcdctlPath, "cluster-health").CombinedOutput()
if err == nil && clusterHealthRegex.Match(out) {
break
}
fmt.Printf("Error: %s: %s\n", err, string(out))
time.Sleep(interval)
}
if retryCnt > maxRetry {
return fmt.Errorf("Timed out waiting for healthy cluster\n")
}
var (
memberID string
out []byte
err error
)
if out, err = exec.Command(etcdctlPath, "member", "list").CombinedOutput(); err != nil {
return fmt.Errorf("Error calling member list: %s", err)
}
members := lineSplit.Split(string(out), 2)
if len(members) < 1 {
return fmt.Errorf("Could not find a cluster member from: \"%s\"", members)
}
parts := colonSplit.Split(members[0], 2)
if len(parts) < 2 {
return fmt.Errorf("Could not parse member id from: \"%s\"", members[0])
}
memberID = parts[0]
out, err = exec.Command(etcdctlPath, "member", "update", memberID, etcdPeerUrls).CombinedOutput()
fmt.Printf("member update result: %s\n", string(out))
return err
}