// mkCommandFunc executes the "mk" command. func mkCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] value, err := argOrStdin(c.Args(), os.Stdin, 1) if err != nil { handleError(ExitBadArgs, errors.New("value required")) } ttl := c.Int("ttl") inorder := c.Bool("in-order") var resp *client.Response ctx, cancel := contextWithTotalTimeout(c) if !inorder { // Since PrevNoExist means that the Node must not exist previously, // this Set method always creates a new key. Therefore, mk command // succeeds only if the key did not previously exist, and the command // prevents one from overwriting values accidentally. resp, err = ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevNoExist}) } else { // If in-order flag is specified then create an inorder key under // the directory identified by the key argument. resp, err = ki.CreateInOrder(ctx, key, value, &client.CreateInOrderOptions{TTL: time.Duration(ttl) * time.Second}) } cancel() if err != nil { handleError(ExitServerError, err) } printResponseKey(resp, c.GlobalString("output")) }
// watchCommandFunc executes the "watch" command. func watchCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] recursive := c.Bool("recursive") forever := c.Bool("forever") index := 0 if c.Int("after-index") != 0 { index = c.Int("after-index") + 1 } if forever { sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) stop := make(chan bool) go func() { <-sigch os.Exit(0) }() receiver := make(chan *etcd.Response) errCh := make(chan error, 1) go func() { _, err := client.Watch(key, uint64(index), recursive, receiver, stop) errCh <- err }() for { select { case resp := <-receiver: printAll(resp, c.GlobalString("output")) case err := <-errCh: handleError(-1, err) } } } else { var resp *etcd.Response var err error resp, err = client.Watch(key, uint64(index), recursive, nil, nil) if err != nil { handleError(ErrorFromEtcd, err) } if err != nil { return nil, err } printAll(resp, c.GlobalString("output")) } return nil, nil }
// getCommandFunc executes the "get" command. func getCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] sorted := c.Bool("sort") // Retrieve the value from the server. return client.Get(key, sorted, false) }
// lsCommandFunc executes the "ls" command. func lsCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { key := "/" if len(c.Args()) != 0 { key = c.Args()[0] } recursive := c.Bool("recursive") // Retrieve the value from the server. return client.Get(key, false, recursive) }
// rPrint recursively prints out the nodes in the node structure. func rPrint(c *cli.Context, n *client.Node) { if n.Dir && c.Bool("p") { fmt.Println(fmt.Sprintf("%v/", n.Key)) } else { fmt.Println(n.Key) } for _, node := range n.Nodes { rPrint(c, node) } }
// lsCommandFunc executes the "ls" command. func lsCommandFunc(c *cli.Context, ki client.KeysAPI) { key := "/" if len(c.Args()) != 0 { key = c.Args()[0] } sort := c.Bool("sort") recursive := c.Bool("recursive") resp, err := ki.Get(context.TODO(), key, &client.GetOptions{Sort: sort, Recursive: recursive}) if err != nil { handleError(ExitServerError, err) } printLs(c, resp) }
// getCommandFunc executes the "get" command. func getCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] consistent := c.Bool("consistent") sorted := c.Bool("sort") // Setup consistency on the client. if consistent { client.SetConsistency(etcd.STRONG_CONSISTENCY) } else { client.SetConsistency(etcd.WEAK_CONSISTENCY) } // Retrieve the value from the server. return client.Get(key, sorted, false) }
// watchCommandFunc executes the "watch" command. func watchCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] recursive := c.Bool("recursive") forever := c.Bool("forever") index := 0 if c.Int("after-index") != 0 { index = c.Int("after-index") + 1 } stop := false w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive}) sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) go func() { <-sigch os.Exit(0) }() for !stop { resp, err := w.Next(context.TODO()) if err != nil { handleError(ExitServerError, err) } if resp.Node.Dir { continue } if recursive { fmt.Printf("[%s] %s\n", resp.Action, resp.Node.Key) } printResponseKey(resp, c.GlobalString("output")) if !forever { stop = true } } }
// getCommandFunc executes the "get" command. func getCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] sorted := c.Bool("sort") resp, err := ki.Get(context.TODO(), key, &client.GetOptions{Sort: sorted}) if err != nil { handleError(ExitServerError, err) } if resp.Node.Dir { fmt.Fprintln(os.Stderr, fmt.Sprintf("%s: is a directory", resp.Node.Key)) os.Exit(1) } printResponseKey(resp, c.GlobalString("output")) }
// rmCommandFunc executes the "rm" command. func rmCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] recursive := c.Bool("recursive") dir := c.Bool("dir") prevValue := c.String("with-value") prevIndex := c.Int("with-index") // TODO: handle transport timeout resp, err := ki.Delete(context.TODO(), key, &client.DeleteOptions{PrevIndex: uint64(prevIndex), PrevValue: prevValue, Dir: dir, Recursive: recursive}) if err != nil { handleError(ExitServerError, err) } if !resp.Node.Dir { printResponseKey(resp, c.GlobalString("output")) } }
// removeCommandFunc executes the "rm" command. func removeCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] recursive := c.Bool("recursive") dir := c.Bool("dir") // TODO: distinguish with flag is not set and empty flag // the cli pkg need to provide this feature prevValue := c.String("with-value") prevIndex := uint64(c.Int("with-index")) if prevValue != "" || prevIndex != 0 { return client.CompareAndDelete(key, prevValue, prevIndex) } if recursive || !dir { return client.Delete(key, recursive) } return client.DeleteDir(key) }
// lsCommandFunc executes the "ls" command. func lsCommandFunc(c *cli.Context, ki client.KeysAPI) { key := "/" if len(c.Args()) != 0 { key = c.Args()[0] } sort := c.Bool("sort") recursive := c.Bool("recursive") quorum := c.Bool("quorum") ctx, cancel := contextWithTotalTimeout(c) resp, err := ki.Get(ctx, key, &client.GetOptions{Sort: sort, Recursive: recursive, Quorum: quorum}) cancel() if err != nil { handleError(ExitServerError, err) } printLs(c, resp) }
func handleClusterHealth(c *cli.Context) { forever := c.Bool("forever") if forever { sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) go func() { <-sigch os.Exit(0) }() } tr, err := getTransport(c) if err != nil { handleError(ExitServerError, err) } hc := http.Client{ Transport: tr, } cln := mustNewClientNoSync(c) mi := client.NewMembersAPI(cln) ms, err := mi.List(context.TODO()) if err != nil { fmt.Println("cluster may be unhealthy: failed to list members") handleError(ExitServerError, err) } for { health := false for _, m := range ms { if len(m.ClientURLs) == 0 { fmt.Printf("member %s is unreachable: no available published client urls\n", m.ID) continue } checked := false for _, url := range m.ClientURLs { resp, err := hc.Get(url + "/health") if err != nil { fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err) continue } result := struct{ Health string }{} nresult := struct{ Health bool }{} bytes, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err) continue } resp.Body.Close() err = json.Unmarshal(bytes, &result) if err != nil { err = json.Unmarshal(bytes, &nresult) } if err != nil { fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err) continue } checked = true if result.Health == "true" || nresult.Health == true { health = true fmt.Printf("member %s is healthy: got healthy result from %s\n", m.ID, url) } else { fmt.Printf("member %s is unhealthy: got unhealthy result from %s\n", m.ID, url) } break } if !checked { fmt.Printf("member %s is unreachable: %v are all unreachable\n", m.ID, m.ClientURLs) } } if health { fmt.Println("cluster is healthy") } else { fmt.Println("cluster is unhealthy") } if !forever { if health { os.Exit(ExitSuccess) } else { os.Exit(ExitClusterNotHealthy) } } fmt.Printf("\nnext check after 10 second...\n\n") time.Sleep(10 * time.Second) } }
func handleClusterHealth(c *cli.Context) { forever := c.Bool("forever") if forever { sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) go func() { <-sigch os.Exit(0) }() } tr, err := getTransport(c) if err != nil { handleError(ExitServerError, err) } // TODO: update members when forever is set. mi := mustNewMembersAPI(c) ms, err := mi.List(context.TODO()) if err != nil { fmt.Println("cluster may be unhealthy: failed to list members") handleError(ExitServerError, err) } cl := make([]string, 0) for _, m := range ms { cl = append(cl, m.ClientURLs...) } for { // check the /health endpoint of all members first ep, rs0, err := getLeaderStatus(tr, cl) if err != nil { fmt.Println("cluster may be unhealthy: failed to connect", cl) if forever { time.Sleep(10 * time.Second) continue } os.Exit(1) } time.Sleep(time.Second) // are all the members makeing progress? _, rs1, err := getLeaderStatus(tr, []string{ep}) if err != nil { fmt.Println("cluster is unhealthy") if forever { time.Sleep(10 * time.Second) continue } os.Exit(1) } if rs1.Commit > rs0.Commit { fmt.Printf("cluster is healthy: raft is making progress [commit index: %v->%v]\n", rs0.Commit, rs1.Commit) } else { fmt.Printf("cluster is unhealthy: raft is not making progress [commit index: %v]\n", rs0.Commit) } fmt.Printf("leader is %v\n", rs0.Lead) var prints []string for id, pr0 := range rs0.Progress { pr1, ok := rs1.Progress[id] if !ok { // TODO: forever should handle configuration change. fmt.Println("Cluster configuration changed during health checking. Please retry.") os.Exit(1) } if pr1.Match <= pr0.Match { prints = append(prints, fmt.Sprintf("member %s is unhealthy: raft is not making progress [match: %v->%v]\n", id, pr0.Match, pr1.Match)) } else { prints = append(prints, fmt.Sprintf("member %s is healthy: raft is making progress [match: %v->%v]\n", id, pr0.Match, pr1.Match)) } } sort.Strings(prints) for _, p := range prints { fmt.Print(p) } if !forever { return } time.Sleep(10 * time.Second) } }
func roleGrantRevoke(c *cli.Context, grant bool) { path := c.String("path") if path == "" { fmt.Fprintln(os.Stderr, "No path specified; please use `-path`") os.Exit(1) } if pathutil.CanonicalURLPath(path) != path { fmt.Fprintf(os.Stderr, "Not canonical path; please use `-path=%s`\n", pathutil.CanonicalURLPath(path)) os.Exit(1) } read := c.Bool("read") write := c.Bool("write") rw := c.Bool("readwrite") permcount := 0 for _, v := range []bool{read, write, rw} { if v { permcount++ } } if permcount != 1 { fmt.Fprintln(os.Stderr, "Please specify exactly one of -read, -write or -readwrite") os.Exit(1) } var permType client.PermissionType switch { case read: permType = client.ReadPermission case write: permType = client.WritePermission case rw: permType = client.ReadWritePermission } api, role := mustRoleAPIAndName(c) ctx, cancel := contextWithTotalTimeout(c) defer cancel() currentRole, err := api.GetRole(ctx, role) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } var newRole *client.Role if grant { newRole, err = api.GrantRoleKV(ctx, role, []string{path}, permType) } else { newRole, err = api.RevokeRoleKV(ctx, role, []string{path}, permType) } if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } if reflect.DeepEqual(newRole, currentRole) { if grant { fmt.Printf("Role unchanged; already granted") } else { fmt.Printf("Role unchanged; already revoked") } } fmt.Printf("Role %s updated\n", role) }
// execWatchCommandFunc executes the "exec-watch" command. func execWatchCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { _ = io.Copy _ = exec.Command args := c.Args() argsLen := len(args) if argsLen < 2 { return nil, errors.New("Key and command to exec required") } key := args[argsLen-1] cmdArgs := args[:argsLen-1] index := 0 if c.Int("after-index") != 0 { index = c.Int("after-index") + 1 key = args[0] cmdArgs = args[2:] } recursive := c.Bool("recursive") if recursive != false { key = args[0] cmdArgs = args[2:] } sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) stop := make(chan bool) go func() { <-sigch stop <- true os.Exit(0) }() receiver := make(chan *etcd.Response) client.SetConsistency(etcd.WEAK_CONSISTENCY) go client.Watch(key, uint64(index), recursive, receiver, stop) for { resp := <-receiver cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) cmd.Env = environResponse(resp, os.Environ()) stdout, err := cmd.StdoutPipe() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } stderr, err := cmd.StderrPipe() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } err = cmd.Start() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } go io.Copy(os.Stdout, stdout) go io.Copy(os.Stderr, stderr) cmd.Wait() } }
func handleClusterHealth(c *cli.Context) { forever := c.Bool("forever") if forever { sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) go func() { <-sigch os.Exit(0) }() } tr, err := getTransport(c) if err != nil { handleError(ExitServerError, err) } hc := http.Client{ Transport: tr, } mi := mustNewMembersAPI(c) ms, err := mi.List(context.TODO()) if err != nil { fmt.Println("cluster may be unhealthy: failed to list members") handleError(ExitServerError, err) } for { health := false for _, m := range ms { checked := false for _, url := range m.ClientURLs { resp, err := hc.Get(url + "/health") if err != nil { fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err) continue } result := struct{ Health string }{} d := json.NewDecoder(resp.Body) err = d.Decode(&result) resp.Body.Close() if err != nil { fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err) continue } checked = true if result.Health == "true" { checked = true fmt.Printf("member %s is healthy: got healthy result from %s\n", m.ID, url) } else { fmt.Printf("member %s is unhealthy: got unhealthy result from %s\n", m.ID, url) } break } if !checked { fmt.Printf("member %s is unreachable: %v are all unreachable\n", m.ID, m.ClientURLs) } } if health { fmt.Println("cluster is healthy") } else { fmt.Println("cluster is unhealthy") } if !forever { break } fmt.Printf("\nnext check after 10 second...\n\n") time.Sleep(10 * time.Second) } }
// execWatchCommandFunc executes the "exec-watch" command. func execWatchCommandFunc(c *cli.Context, ki client.KeysAPI) { args := c.Args() argslen := len(args) if argslen < 2 { handleError(ExitBadArgs, errors.New("key and command to exec required")) } var ( key string cmdArgs []string ) foundSep := false for i := range args { if args[i] == "--" && i != 0 { foundSep = true break } } if foundSep { key = args[0] cmdArgs = args[2:] } else { // If no flag is parsed, the order of key and cmdArgs will be switched and // args will not contain `--`. key = args[argslen-1] cmdArgs = args[:argslen-1] } index := 0 if c.Int("after-index") != 0 { index = c.Int("after-index") + 1 } recursive := c.Bool("recursive") sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) go func() { <-sigch os.Exit(0) }() w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive}) for { resp, err := w.Next(context.TODO()) if err != nil { handleError(ExitServerError, err) } if resp.Node.Dir { fmt.Fprintf(os.Stderr, "Ignored dir %s change", resp.Node.Key) continue } cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) cmd.Env = environResponse(resp, os.Environ()) stdout, err := cmd.StdoutPipe() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } stderr, err := cmd.StderrPipe() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } go func() { err := cmd.Start() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } go io.Copy(os.Stdout, stdout) go io.Copy(os.Stderr, stderr) cmd.Wait() }() } }