// 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)) } }
// 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(c.GlobalString("endpoint")) 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") } }
// 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")) }
func newClient(c *cli.Context) (client.Client, error) { eps, err := getEndpoints(c) if err != nil { return nil, err } tr, err := getTransport(c) if err != nil { return nil, err } cfg := client.Config{ Transport: tr, Endpoints: eps, HeaderTimeoutPerRequest: c.GlobalDuration("timeout"), } uFlag := c.GlobalString("username") if uFlag != "" { username, password, err := getUsernamePasswordFromFlag(uFlag) if err != nil { return nil, err } cfg.Username = username cfg.Password = password } return client.New(cfg) }
// 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 }
// Just like handlePrint but also passed the context of the command func handleContextualPrint(c *cli.Context, fn handlerFunc, pFn contextualPrintFunc) { resp, err := rawhandle(c, fn) if err != nil { handleError(ErrorFromEtcd, err) } if resp != nil && pFn != nil { pFn(c, resp, c.GlobalString("output")) } }
// handlePrint wraps the command function handlers to parse global flags // into a client and to properly format the response objects. func handlePrint(c *cli.Context, fn handlerFunc, pFn printFunc) { resp, err := rawhandle(c, fn) // Print error and exit, if necessary. if err != nil { handleError(ErrorFromEtcd, err) } if resp != nil && pFn != nil { pFn(resp, c.GlobalString("output")) } }
func mustNewClient(c *cli.Context) client.Client { eps, err := getEndpoints(c) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } tr, err := getTransport(c) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } cfg := client.Config{ Transport: tr, Endpoints: eps, HeaderTimeoutPerRequest: c.GlobalDuration("timeout"), } uFlag := c.GlobalString("username") if uFlag != "" { username, password, err := getUsernamePasswordFromFlag(uFlag) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } cfg.Username = username cfg.Password = password } hc, err := client.New(cfg) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } if !c.GlobalBool("no-sync") { ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) err := hc.Sync(ctx) cancel() if err != nil { handleError(ExitServerError, err) os.Exit(1) } } if c.GlobalBool("debug") { fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", ")) client.EnablecURLDebug() } return hc }
// rawhandle wraps the command function handlers and sets up the // environment but performs no output formatting. func rawhandle(c *cli.Context, fn handlerFunc) (*etcd.Response, error) { sync := !c.GlobalBool("no-sync") peerstr := c.GlobalString("peers") // Use an environment variable if nothing was supplied on the // command line if peerstr == "" { peerstr = os.Getenv("ETCDCTL_PEERS") } // If we still don't have peers, use a default if peerstr == "" { peerstr = "127.0.0.1:4001" } peers := strings.Split(peerstr, ",") // If no sync, create http path for each peer address if !sync { revisedPeers := make([]string, 0) for _, peer := range peers { if revisedPeer, err := createHttpPath(peer); err != nil { fmt.Fprintf(os.Stderr, "Unsupported url %v: %v\n", peer, err) } else { revisedPeers = append(revisedPeers, revisedPeer) } } peers = revisedPeers } client := etcd.NewClient(peers) if c.GlobalBool("debug") { go dumpCURL(client) } // Sync cluster. if sync { if ok := client.SyncCluster(); !ok { handleError(FailedToConnectToHost, errors.New("Cannot sync with the cluster using peers "+strings.Join(peers, ", "))) } } if c.GlobalBool("debug") { fmt.Fprintf(os.Stderr, "Cluster-Peers: %s\n", strings.Join(client.GetCluster(), " ")) } // Execute handler function. return fn(c, client) }
// 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")) } }
// watchCommandFunc executes the "watch" command. func watchCommandFunc(c *cli.Context) { conn, err := grpc.Dial(c.GlobalString("endpoint")) if err != nil { panic(err) } wAPI := pb.NewWatchClient(conn) wStream, err := wAPI.Watch(context.TODO()) if err != nil { panic(err) } go recvLoop(wStream) reader := bufio.NewReader(os.Stdin) for { l, err := reader.ReadString('\n') if err != nil { fmt.Fprintf(os.Stderr, "Error reading watch request line: %v", err) os.Exit(1) } l = strings.TrimSuffix(l, "\n") // TODO: support start and end revision segs := strings.Split(l, " ") if len(segs) != 2 { fmt.Fprintf(os.Stderr, "Invalid watch request format: use watch key or watchprefix prefix\n") continue } var r *pb.WatchRequest switch segs[0] { case "watch": r = &pb.WatchRequest{Key: []byte(segs[1])} case "watchprefix": r = &pb.WatchRequest{Prefix: []byte(segs[1])} default: fmt.Fprintf(os.Stderr, "Invalid watch request format: use watch key or watchprefix prefix\n") continue } err = wStream.Send(r) if err != nil { fmt.Fprintf(os.Stderr, "Error sending request to server: %v\n", err) } } }
func getPeersFlagValue(c *cli.Context) []string { peerstr := c.GlobalString("peers") // Use an environment variable if nothing was supplied on the // command line if peerstr == "" { peerstr = os.Getenv("ETCDCTL_PEERS") } // If we still don't have peers, use a default if peerstr == "" { peerstr = "127.0.0.1:4001,127.0.0.1:2379" } return strings.Split(peerstr, ",") }
// putCommandFunc executes the "put" command. func putCommandFunc(c *cli.Context) { if len(c.Args()) != 2 { panic("bad arg") } key := []byte(c.Args()[0]) value := []byte(c.Args()[1]) conn, err := grpc.Dial(c.GlobalString("endpoint"), grpc.WithInsecure()) if err != nil { panic(err) } etcd := pb.NewEtcdClient(conn) req := &pb.PutRequest{Key: key, Value: value} etcd.Put(context.Background(), req) fmt.Printf("%s %s\n", key, value) }
// 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) }
// updateCommandFunc executes the "update" command. func updateCommandFunc(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") resp, err := ki.Set(context.TODO(), key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevExist}) if err != nil { handleError(ExitServerError, err) } printResponseKey(resp, c.GlobalString("output")) }
// 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 getPeersFlagValue(c *cli.Context) []string { peerstr := c.GlobalString("endpoints") if peerstr == "" { peerstr = os.Getenv("ETCDCTL_ENDPOINTS") } if peerstr == "" { peerstr = c.GlobalString("endpoint") } if peerstr == "" { peerstr = os.Getenv("ETCDCTL_ENDPOINT") } if peerstr == "" { peerstr = c.GlobalString("peers") } if peerstr == "" { peerstr = os.Getenv("ETCDCTL_PEERS") } // If we still don't have peers, use a default if peerstr == "" { peerstr = "http://127.0.0.1:2379,http://127.0.0.1:4001" } return strings.Split(peerstr, ",") }
// rawhandle wraps the command function handlers and sets up the // environment but performs no output formatting. func rawhandle(c *cli.Context, fn handlerFunc) (*etcd.Response, error) { endpoints, err := getEndpoints(c) if err != nil { return nil, err } tr, err := getTransport(c) if err != nil { return nil, err } client := etcd.NewClient(endpoints) client.SetTransport(tr) username := c.GlobalString("username") if username != "" { err := prepAuth(client, username) if err != nil { return nil, err } } if c.GlobalBool("debug") { go dumpCURL(client) } // Sync cluster. if !c.GlobalBool("no-sync") { if ok := client.SyncCluster(); !ok { handleError(ExitBadConnection, errors.New("cannot sync with the cluster using endpoints "+strings.Join(endpoints, ", "))) } } if c.GlobalBool("debug") { fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(client.GetCluster(), ", ")) } // Execute handler function. return fn(c, client) }
func getTransport(c *cli.Context) (*http.Transport, error) { cafile := c.GlobalString("ca-file") certfile := c.GlobalString("cert-file") keyfile := c.GlobalString("key-file") // Use an environment variable if nothing was supplied on the // command line if cafile == "" { cafile = os.Getenv("ETCDCTL_CA_FILE") } if certfile == "" { certfile = os.Getenv("ETCDCTL_CERT_FILE") } if keyfile == "" { keyfile = os.Getenv("ETCDCTL_KEY_FILE") } tls := transport.TLSInfo{ CAFile: cafile, CertFile: certfile, KeyFile: keyfile, } return transport.NewTransport(tls) }
// setCommandFunc executes the "set" command. func setCommandFunc(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") prevValue := c.String("swap-with-value") prevIndex := c.Int("swap-with-index") // TODO: handle transport timeout resp, err := ki.Set(context.TODO(), key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevIndex: uint64(prevIndex), PrevValue: prevValue}) if err != nil { handleError(ExitServerError, err) } printResponseKey(resp, c.GlobalString("output")) }
// rangeCommandFunc executes the "range" command. func rangeCommandFunc(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.RangeRequest{Key: key, RangeEnd: rangeEnd} resp, err := etcd.Range(context.Background(), req) for _, kv := range resp.Kvs { fmt.Printf("%s %s\n", string(kv.Key), string(kv.Value)) } }
func getDomainDiscoveryFlagValue(c *cli.Context) ([]string, error) { domainstr := c.GlobalString("discovery-srv") // Use an environment variable if nothing was supplied on the // command line if domainstr == "" { domainstr = os.Getenv("ETCDCTL_DISCOVERY_SRV") } // If we still don't have domain discovery, return nothing if domainstr == "" { return []string{}, nil } discoverer := client.NewSRVDiscover() eps, err := discoverer.Discover(domainstr) if err != nil { return nil, err } return eps, err }
func getTransport(c *cli.Context) (*http.Transport, error) { tls := transport.TLSInfo{ CAFile: c.GlobalString("ca-file"), CertFile: c.GlobalString("cert-file"), KeyFile: c.GlobalString("key-file"), } return transport.NewTransport(tls) }
func mustNewClient(c *cli.Context) client.Client { eps, err := getEndpoints(c) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } tr, err := getTransport(c) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } cfg := client.Config{ Transport: tr, Endpoints: eps, } uFlag := c.GlobalString("username") if uFlag != "" { username, password, err := getUsernamePasswordFromFlag(uFlag) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } cfg.Username = username cfg.Password = password } hc, err := client.New(cfg) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } return hc }