func actionMemberList(c *cli.Context) { if len(c.Args()) != 0 { fmt.Fprintln(os.Stderr, "No arguments accepted") os.Exit(1) } mAPI := mustNewMembersAPI(c) ctx, cancel := contextWithTotalTimeout(c) defer cancel() members, err := mAPI.List(ctx) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } leader, err := mAPI.Leader(ctx) if err != nil { fmt.Fprintln(os.Stderr, "Failed to get leader: ", err) os.Exit(1) } for _, m := range members { isLeader := false if m.ID == leader.ID { isLeader = true } if len(m.Name) == 0 { fmt.Printf("%s[unstarted]: peerURLs=%s\n", m.ID, strings.Join(m.PeerURLs, ",")) } else { fmt.Printf("%s: name=%s peerURLs=%s clientURLs=%s isLeader=%v\n", m.ID, m.Name, strings.Join(m.PeerURLs, ","), strings.Join(m.ClientURLs, ","), isLeader) } } }
// txnCommandFunc executes the "txn" command. func txnCommandFunc(c *cli.Context) { if len(c.Args()) != 0 { panic("unexpected args") } reader := bufio.NewReader(os.Stdin) next := compareState txn := &pb.TxnRequest{} for next != nil { next = next(txn, reader) } conn, err := grpc.Dial("127.0.0.1:12379") if err != nil { panic(err) } etcd := pb.NewEtcdClient(conn) resp, err := etcd.Txn(context.Background(), txn) if err != nil { fmt.Println(err) } if resp.Succeeded { fmt.Println("executed success request list") } else { fmt.Println("executed failure request list") } }
// removeDirCommandFunc executes the "rmdir" command. func removeDirCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] return client.DeleteDir(key) }
// 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 }
// setDirCommandFunc executes the "setDir" command. func setDirCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] ttl := c.Int("ttl") return client.SetDir(key, uint64(ttl)) }
// 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) }
func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) { args := c.Args() if len(args) != 1 { fmt.Fprintln(os.Stderr, "Please provide a username") os.Exit(1) } api := mustNewAuthUserAPI(c) username := args[0] return api, username }
func mustRoleAPIAndName(c *cli.Context) (client.AuthRoleAPI, string) { args := c.Args() if len(args) != 1 { fmt.Fprintln(os.Stderr, "Please provide a role name") os.Exit(1) } name := args[0] api := mustNewAuthRoleAPI(c) return api, name }
// updatedirCommandFunc executes the "updatedir" command. func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] ttl := c.Int("ttl") ctx, cancel := contextWithTotalTimeout(c) _, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist}) cancel() if err != nil { handleError(ExitServerError, err) } }
// mkdirCommandFunc executes the "mkdir" command. func mkdirCommandFunc(c *cli.Context, ki client.KeysAPI, prevExist client.PrevExistType) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] ttl := c.Int("ttl") _, err := ki.Set(context.TODO(), key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: prevExist}) if err != nil { handleError(ExitServerError, err) } }
// deleteRangeCommandFunc executes the "delegeRange" command. func deleteRangeCommandFunc(c *cli.Context) { if len(c.Args()) == 0 { panic("bad arg") } var rangeEnd []byte key := []byte(c.Args()[0]) if len(c.Args()) > 1 { rangeEnd = []byte(c.Args()[1]) } conn, err := grpc.Dial(c.GlobalString("endpoint")) if err != nil { panic(err) } etcd := pb.NewEtcdClient(conn) req := &pb.DeleteRangeRequest{Key: key, RangeEnd: rangeEnd} etcd.DeleteRange(context.Background(), req) if rangeEnd != nil { fmt.Printf("range [%s, %s) is deleted\n", string(key), string(rangeEnd)) } else { fmt.Printf("key %s is deleted\n", string(key)) } }
// rmdirCommandFunc executes the "rmdir" command. func rmdirCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] resp, err := ki.Delete(context.TODO(), key, &client.DeleteOptions{Dir: true}) if err != nil { handleError(ExitServerError, err) } if !resp.Node.Dir { printResponseKey(resp, c.GlobalString("output")) } }
// 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) }
func actionMemberAdd(c *cli.Context) { args := c.Args() if len(args) != 2 { fmt.Fprintln(os.Stderr, "Provide a name and a single member peerURL") os.Exit(1) } mAPI := mustNewMembersAPI(c) url := args[1] ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) m, err := mAPI.Add(ctx, url) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } newID := m.ID newName := args[0] fmt.Printf("Added member named %s with ID %s to cluster\n", newName, newID) ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout) members, err := mAPI.List(ctx) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } conf := []string{} for _, memb := range members { for _, u := range memb.PeerURLs { n := memb.Name if memb.ID == newID { n = newName } conf = append(conf, fmt.Sprintf("%s=%s", n, u)) } } fmt.Print("\n") fmt.Printf("ETCD_NAME=%q\n", newName) fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ",")) fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n") }
func actionMemberRemove(c *cli.Context) { args := c.Args() if len(args) != 1 { fmt.Fprintln(os.Stderr, "Provide a single member ID") os.Exit(1) } removalID := args[0] mAPI := mustNewMembersAPI(c) // Get the list of members. listctx, listCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) members, err := mAPI.List(listctx) listCancel() if err != nil { fmt.Fprintln(os.Stderr, "Error while verifying ID against known members:", err.Error()) os.Exit(1) } // Sanity check the input. foundID := false for _, m := range members { if m.ID == removalID { foundID = true } if m.Name == removalID { // Note that, so long as it's not ambiguous, we *could* do the right thing by name here. fmt.Fprintf(os.Stderr, "Found a member named %s; if this is correct, please use its ID, eg:\n\tetcdctl member remove %s\n", m.Name, m.ID) fmt.Fprintf(os.Stderr, "For more details, read the documentation at https://github.com/coreos/etcd/blob/master/Documentation/runtime-configuration.md#remove-a-member\n\n") } } if !foundID { fmt.Fprintf(os.Stderr, "Couldn't find a member in the cluster with an ID of %s.\n", removalID) os.Exit(1) } // Actually attempt to remove the member. ctx, removeCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) err = mAPI.Remove(ctx, removalID) removeCancel() if err != nil { fmt.Fprintf(os.Stderr, "Recieved an error trying to remove member %s: %s", removalID, err.Error()) os.Exit(1) } fmt.Printf("Removed member %s from cluster\n", removalID) }
func actionMemberList(c *cli.Context) { if len(c.Args()) != 0 { fmt.Fprintln(os.Stderr, "No arguments accepted") os.Exit(1) } mAPI := mustNewMembersAPI(c) ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) members, err := mAPI.List(ctx) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } for _, m := range members { fmt.Printf("%s: name=%s peerURLs=%s clientURLs=%s\n", m.ID, m.Name, strings.Join(m.PeerURLs, ","), strings.Join(m.ClientURLs, ",")) } }
func actionUserList(c *cli.Context) { if len(c.Args()) != 0 { fmt.Fprintln(os.Stderr, "No arguments accepted") os.Exit(1) } u := mustNewAuthUserAPI(c) ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) users, err := u.ListUsers(ctx) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } for _, user := range users { fmt.Printf("%s\n", user) } }
// 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) }
func actionRoleList(c *cli.Context) { if len(c.Args()) != 0 { fmt.Fprintln(os.Stderr, "No arguments accepted") os.Exit(1) } r := mustNewAuthRoleAPI(c) ctx, cancel := contextWithTotalTimeout(c) roles, err := r.ListRoles(ctx) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } for _, role := range roles { fmt.Printf("%s\n", role) } }
// compactionCommandFunc executes the "compaction" command. func compactionCommandFunc(c *cli.Context) { if len(c.Args()) != 1 { panic("bad arg") } rev, err := strconv.ParseInt(c.Args()[0], 10, 64) if err != nil { panic("bad arg") } conn, err := grpc.Dial(c.GlobalString("endpoint"), grpc.WithInsecure()) if err != nil { panic(err) } etcd := pb.NewEtcdClient(conn) req := &pb.CompactionRequest{Revision: rev} etcd.Compact(context.Background(), req) }
func actionMemberRemove(c *cli.Context) { args := c.Args() if len(args) != 1 { fmt.Fprintln(os.Stderr, "Provide a single member ID") os.Exit(1) } mAPI := mustNewMembersAPI(c) mID := args[0] ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) err := mAPI.Remove(ctx, mID) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } fmt.Printf("Removed member %s from cluster\n", mID) }
// 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) }
// 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 } } }
// 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")) } }
// 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")) }
func actionMemberUpdate(c *cli.Context) { args := c.Args() if len(args) != 2 { fmt.Fprintln(os.Stderr, "Provide an ID and a list of comma separated peerURL (0xabcd http://example.com,http://example1.com)") os.Exit(1) } mAPI := mustNewMembersAPI(c) mid := args[0] urls := args[1] ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) err := mAPI.Update(ctx, mid, strings.Split(urls, ",")) cancel() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } fmt.Printf("Updated member with ID %s in cluster\n", mid) }
// 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")) }
// 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") ctx, cancel := contextWithTotalTimeout(c) // 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}) cancel() if err != nil { handleError(ExitServerError, err) } 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) }