// leaseCreateCommandFunc executes the "lease create" command. func leaseCreateCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 1 { ExitWithError(ExitBadArgs, fmt.Errorf("lease create command needs TTL argument.")) } ttl, err := strconv.ParseInt(args[0], 10, 64) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("bad TTL (%v)", err)) } endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } lease := pb.NewLeaseClient(conn) req := &pb.LeaseCreateRequest{TTL: ttl} resp, err := lease.LeaseCreate(context.Background(), req) if err != nil { fmt.Fprintf(os.Stderr, "failed to create lease (%v)\n", err) return } fmt.Printf("lease %016x created with TTL(%ds)\n", resp.ID, resp.TTL) }
// memberListCommandFunc executes the "member list" command. func memberListCommandFunc(cmd *cobra.Command, args []string) { endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } mc := pb.NewClusterClient(conn) resp, err := mc.MemberList(context.TODO(), &pb.MemberListRequest{}) if err != nil { ExitWithError(ExitError, err) } // use https://github.com/olekukonko/tablewriter to print out a pretty table? for _, m := range resp.Members { if len(m.Name) == 0 { fmt.Printf("%16x[unstarted]: peerURLs=%s\n", m.ID, strings.Join(m.PeerURLs, ",")) } else { fmt.Printf("%16x: name=%s peerURLs=%s clientURLs=%s\n", m.ID, m.Name, strings.Join(m.PeerURLs, ","), strings.Join(m.ClientURLs, ",")) } } }
// memberUpdateCommandFunc executes the "member update" command. func memberUpdateCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 1 { ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided")) } id, err := strconv.ParseUint(args[0], 16, 64) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err)) } if len(memberPeerURLs) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided.")) } urls := strings.Split(memberPeerURLs, ",") endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } mc := pb.NewClusterClient(conn) resp, err := mc.MemberUpdate(context.TODO(), &pb.MemberUpdateRequest{ID: uint64(id), PeerURLs: urls}) if err != nil { ExitWithError(ExitError, err) } fmt.Printf("Member %16x updated in cluster %16x\n", id, resp.Header.ClusterId) }
// memberRemoveCommandFunc executes the "member remove" command. func memberRemoveCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 1 { ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided")) } id, err := strconv.ParseUint(args[0], 16, 64) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err)) } endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } mc := pb.NewClusterClient(conn) resp, err := mc.MemberRemove(context.TODO(), &pb.MemberRemoveRequest{ID: uint64(id)}) if err != nil { ExitWithError(ExitError, err) } fmt.Printf("Member %16x removed from cluster %16x\n", id, resp.Header.ClusterId) }
// leaseRevokeCommandFunc executes the "lease create" command. func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 1 { ExitWithError(ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument")) } id, err := strconv.ParseInt(args[0], 16, 64) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err)) } endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } lease := pb.NewLeaseClient(conn) req := &pb.LeaseRevokeRequest{ID: id} _, err = lease.LeaseRevoke(context.Background(), req) if err != nil { fmt.Fprintf(os.Stderr, "failed to revoke lease (%v)\n", err) return } fmt.Printf("lease %016x revoked\n", id) }
func clientSetUp(t *testing.T, addr string, cg grpc.CompressorGenerator, dg grpc.DecompressorGenerator, ua string, e env) (cc *grpc.ClientConn) { var derr error if e.security == "tls" { creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com") if err != nil { t.Fatalf("Failed to create credentials %v", err) } cc, derr = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer), grpc.WithUserAgent(ua), grpc.WithCompressor(cg), grpc.WithDecompressor(dg)) } else { cc, derr = grpc.Dial(addr, grpc.WithDialer(e.dialer), grpc.WithInsecure(), grpc.WithUserAgent(ua), grpc.WithCompressor(cg), grpc.WithDecompressor(dg)) } if derr != nil { t.Fatalf("Dial(%q) = %v", addr, derr) } return }
// 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") } }
// txnCommandFunc executes the "txn" command. func txnCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 0 { ExitWithError(ExitBadArgs, fmt.Errorf("txn command does not accept argument.")) } reader := bufio.NewReader(os.Stdin) next := compareState txn := &pb.TxnRequest{} for next != nil { next = next(txn, reader) } endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } kv := pb.NewKVClient(conn) resp, err := kv.Txn(context.Background(), txn) if err != nil { ExitWithError(ExitError, err) } if resp.Succeeded { fmt.Println("executed success request list") } else { fmt.Println("executed failure request list") } }
// deleteRangeCommandFunc executes the "deleteRange" command. func deleteRangeCommandFunc(cmd *cobra.Command, args []string) { if len(args) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("delete-range command needs arguments.")) } var rangeEnd []byte key := []byte(args[0]) if len(args) > 1 { rangeEnd = []byte(args[1]) } endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } kv := pb.NewKVClient(conn) req := &pb.DeleteRangeRequest{Key: key, RangeEnd: rangeEnd} kv.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)) } }
// putCommandFunc executes the "put" command. func putCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 2 { ExitWithError(ExitBadArgs, fmt.Errorf("put command needs 2 arguments.")) } id, err := strconv.ParseInt(leaseStr, 16, 64) if err != nil { ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err)) } key := []byte(args[0]) value := []byte(args[1]) endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } kv := pb.NewKVClient(conn) req := &pb.PutRequest{Key: key, Value: value, Lease: id} kv.Put(context.Background(), req) fmt.Printf("%s %s\n", key, value) }
// NewClientConn creates a gRPC client connection to addr. func NewClientConn(addr string) *grpc.ClientConn { conn, err := grpc.Dial(addr) if err != nil { grpclog.Fatalf("NewClientConn(%q) failed to create a ClientConn %v", addr, err) } return conn }
// 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)) } }
// memberAddCommandFunc executes the "member add" command. func memberAddCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 1 { ExitWithError(ExitBadArgs, fmt.Errorf("member name not provided.")) } if len(memberPeerURLs) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided.")) } urls := strings.Split(memberPeerURLs, ",") endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } mc := pb.NewClusterClient(conn) resp, err := mc.MemberAdd(context.TODO(), &pb.MemberAddRequest{PeerURLs: urls}) if err != nil { ExitWithError(ExitError, err) } fmt.Printf("Member %16x added to cluster %16x\n", args[0], resp.Member.ID, resp.Header.ClusterId) }
// NewClientV3 creates a new grpc client connection to the member func NewClientV3(m *member) (*clientv3.Client, error) { if m.grpcAddr == "" { return nil, fmt.Errorf("member not configured for grpc") } f := func(a string, t time.Duration) (net.Conn, error) { return net.Dial("unix", a) } unixdialer := grpc.WithDialer(f) opts := []grpc.DialOption{ unixdialer, grpc.WithBlock(), grpc.WithTimeout(5 * time.Second)} if m.ClientTLSInfo != nil { tlscfg, err := m.ClientTLSInfo.ClientConfig() if err != nil { return nil, err } creds := credentials.NewTLS(tlscfg) opts = append(opts, grpc.WithTransportCredentials(creds)) } else { opts = append(opts, grpc.WithInsecure()) } conn, err := grpc.Dial(m.grpcAddr, opts...) if err != nil { return nil, err } return clientv3.NewFromConn(conn), nil }
// Dial establishes a connection for a given endpoint using the client's config func (c *Client) Dial(endpoint string) (*grpc.ClientConn, error) { opts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithTimeout(c.cfg.DialTimeout), } if c.creds != nil { opts = append(opts, grpc.WithTransportCredentials(*c.creds)) } else { opts = append(opts, grpc.WithInsecure()) } if url, uerr := url.Parse(endpoint); uerr == nil && url.Scheme == "unix" { f := func(a string, t time.Duration) (net.Conn, error) { return net.DialTimeout("unix", a, t) } // strip unix:// prefix so certs work endpoint = url.Host opts = append(opts, grpc.WithDialer(f)) } conn, err := grpc.Dial(endpoint, opts...) if err != nil { return nil, err } return conn, nil }
// rangeCommandFunc executes the "range" command. func rangeCommandFunc(cmd *cobra.Command, args []string) { if len(args) == 0 { ExitWithError(ExitBadArgs, fmt.Errorf("range command needs arguments.")) } var rangeEnd []byte key := []byte(args[0]) if len(args) > 1 { rangeEnd = []byte(args[1]) } endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitError, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } kv := pb.NewKVClient(conn) req := &pb.RangeRequest{Key: key, RangeEnd: rangeEnd} resp, err := kv.Range(context.Background(), req) for _, kv := range resp.Kvs { fmt.Printf("%s %s\n", string(kv.Key), string(kv.Value)) } }
// Dial establishes a connection for a given endpoint using the client's config func (c *Client) Dial(endpoint string) (*grpc.ClientConn, error) { opts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithTimeout(c.cfg.DialTimeout), } if c.creds != nil { opts = append(opts, grpc.WithTransportCredentials(*c.creds)) } else { opts = append(opts, grpc.WithInsecure()) } proto := "tcp" if url, uerr := url.Parse(endpoint); uerr == nil && url.Scheme == "unix" { proto = "unix" // strip unix:// prefix so certs work endpoint = url.Host } f := func(a string, t time.Duration) (net.Conn, error) { select { case <-c.ctx.Done(): return nil, c.ctx.Err() default: } return net.DialTimeout(proto, a, t) } opts = append(opts, grpc.WithDialer(f)) conn, err := grpc.Dial(endpoint, opts...) if err != nil { return nil, err } return conn, nil }
// checkConsistency stops the cluster for a moment and get the hashes of KV storages. func (c *cluster) checkConsistency() error { hashes := make(map[string]uint32) for _, u := range c.GRPCURLs { conn, err := grpc.Dial(u, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second)) if err != nil { return err } kvc := pb.NewKVClient(conn) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) resp, err := kvc.Hash(ctx, &pb.HashRequest{}) hv := resp.Hash if resp != nil && err != nil { return err } cancel() hashes[u] = hv } if !checkConsistency(hashes) { return fmt.Errorf("check consistency fails: %v", hashes) } return nil }
func main() { var ( c, n int url string size int ) flag.IntVar(&c, "c", 50, "number of connections") flag.IntVar(&n, "n", 200, "number of requests") flag.IntVar(&size, "s", 128, "size of put request") // TODO: config the number of concurrency in each connection flag.StringVar(&url, "u", "127.0.0.1:12379", "etcd server endpoint") flag.Parse() if flag.NArg() < 1 { flag.Usage() os.Exit(1) } var act string if act = flag.Args()[0]; act != "get" && act != "put" { fmt.Printf("unsupported action %v\n", act) os.Exit(1) } conn, err := grpc.Dial(url) if err != nil { fmt.Errorf("dial error: %v", err) os.Exit(1) } results = make(chan *result, n) bar = pb.New(n) bar.Format("Bom !") bar.Start() start := time.Now() if act == "get" { var rangeEnd []byte key := []byte(flag.Args()[1]) if len(flag.Args()) > 2 { rangeEnd = []byte(flag.Args()[2]) } benchGet(conn, key, rangeEnd, n, c) } else if act == "put" { key := []byte(flag.Args()[1]) // number of different keys to put into etcd kc, err := strconv.ParseInt(flag.Args()[2], 10, 32) if err != nil { panic(err) } benchPut(conn, key, int(kc), n, c, size) } wg.Wait() bar.Finish() printReport(n, results, time.Now().Sub(start)) }
func setUp(maxStream uint32, e env) (s *grpc.Server, cc *grpc.ClientConn) { sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream)} la := ":0" switch e.network { case "unix": la = "/tmp/testsock" + fmt.Sprintf("%d", time.Now()) syscall.Unlink(la) } lis, err := net.Listen(e.network, la) if err != nil { grpclog.Fatalf("Failed to listen: %v", err) } if e.security == "tls" { creds, err := credentials.NewServerTLSFromFile(tlsDir+"server1.pem", tlsDir+"server1.key") if err != nil { grpclog.Fatalf("Failed to generate credentials %v", err) } sopts = append(sopts, grpc.Creds(creds)) } s = grpc.NewServer(sopts...) testpb.RegisterTestServiceServer(s, &testServer{}) go s.Serve(lis) addr := la switch e.network { case "unix": default: _, port, err := net.SplitHostPort(lis.Addr().String()) if err != nil { grpclog.Fatalf("Failed to parse listener address: %v", err) } addr = "localhost:" + port } if e.security == "tls" { creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com") if err != nil { grpclog.Fatalf("Failed to create credentials %v", err) } cc, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer)) } else { cc, err = grpc.Dial(addr, grpc.WithDialer(e.dialer)) } if err != nil { grpclog.Fatalf("Dial(%q) = %v", addr, err) } return }
func mustCreateConn(endpoint string) *grpc.ClientConn { conn, err := grpc.Dial(endpoint) if err != nil { fmt.Fprintf(os.Stderr, "dial error: %v\n", err) os.Exit(1) } return conn }
// watchCommandFunc executes the "watch" command. func watchCommandFunc(cmd *cobra.Command, args []string) { endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitInvalidInput, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } wAPI := pb.NewWatchClient(conn) wStream, err := wAPI.Watch(context.TODO()) if err != nil { ExitWithError(ExitBadConnection, err) } go recvLoop(wStream) reader := bufio.NewReader(os.Stdin) for { l, err := reader.ReadString('\n') if err != nil { ExitWithError(ExitInvalidInput, fmt.Errorf("Error reading watch request line: %v", err)) } 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]\", \"watchprefix [prefix]\" or \"cancel [watcher ID]\"\n") continue } var r *pb.WatchRequest switch segs[0] { case "watch": r = &pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Key: []byte(segs[1])}} case "watchprefix": r = &pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Prefix: []byte(segs[1])}} case "cancel": id, perr := strconv.ParseInt(segs[1], 10, 64) if perr != nil { fmt.Fprintf(os.Stderr, "Invalid cancel ID (%v)\n", perr) continue } r = &pb.WatchRequest{CancelRequest: &pb.WatchCancelRequest{WatchId: id}} default: fmt.Fprintf(os.Stderr, "Invalid watch request type: use watch, watchprefix or cancel\n") continue } err = wStream.Send(r) if err != nil { fmt.Fprintf(os.Stderr, "Error sending request to server: %v\n", err) } } }
func TestDialTimeout(t *testing.T) { conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTimeout(time.Millisecond)) if err == nil { conn.Close() } if err != grpc.ErrClientConnTimeout { t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout) } }
// newGrpcClient creates a new grpc client connection to the member func NewGRPCClient(m *member) (*grpc.ClientConn, error) { if m.grpcAddr == "" { return nil, fmt.Errorf("member not configured for grpc") } f := func(a string, t time.Duration) (net.Conn, error) { return net.Dial("unix", a) } unixdialer := grpc.WithDialer(f) return grpc.Dial(m.grpcAddr, unixdialer) }
func (s *stresser) Stress() error { conn, err := grpc.Dial(s.Endpoint, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second)) if err != nil { return fmt.Errorf("%v (%s)", err, s.Endpoint) } defer conn.Close() ctx, cancel := context.WithCancel(context.Background()) wg := &sync.WaitGroup{} wg.Add(s.N) s.mu.Lock() s.conn = conn s.cancel = cancel s.wg = wg s.mu.Unlock() kvc := pb.NewKVClient(conn) for i := 0; i < s.N; i++ { go func(i int) { defer wg.Done() for { // TODO: 10-second is enough timeout to cover leader failure // and immediate leader election. Find out what other cases this // could be timed out. putctx, putcancel := context.WithTimeout(ctx, 10*time.Second) _, err := kvc.Put(putctx, &pb.PutRequest{ Key: []byte(fmt.Sprintf("foo%d", rand.Intn(s.KeySuffixRange))), Value: []byte(randStr(s.KeySize)), }) putcancel() if err != nil { if grpc.ErrorDesc(err) == context.DeadlineExceeded.Error() { // This retries when request is triggered at the same time as // leader failure. When we terminate the leader, the request to // that leader cannot be processed, and times out. Also requests // to followers cannot be forwarded to the old leader, so timing out // as well. We want to keep stressing until the cluster elects a // new leader and start processing requests again. continue } return } s.mu.Lock() s.success++ s.mu.Unlock() } }(i) } <-ctx.Done() return nil }
func mustCreateConn() *grpc.ClientConn { eps := strings.Split(endpoints, ",") endpoint := eps[dialTotal%len(eps)] dialTotal++ conn, err := grpc.Dial(endpoint) if err != nil { fmt.Fprintf(os.Stderr, "dial error: %v\n", err) os.Exit(1) } return conn }
// Dial establishes a connection for a given endpoint using the client's config func (c *Client) Dial(endpoint string) (*grpc.ClientConn, error) { // TODO: enable grpc.WithTransportCredentials(creds) conn, err := grpc.Dial( endpoint, grpc.WithBlock(), grpc.WithTimeout(c.cfg.DialTimeout), grpc.WithInsecure()) if err != nil { return nil, err } return conn, nil }
func main() { var c, n int var url string flag.IntVar(&c, "c", 50, "number of connections") flag.IntVar(&n, "n", 200, "number of requests") // TODO: config the number of concurrency in each connection flag.StringVar(&url, "u", "127.0.0.1:12379", "etcd server endpoint") flag.Parse() if flag.NArg() < 1 { flag.Usage() os.Exit(1) } if act := flag.Args()[0]; act != "get" { fmt.Errorf("unsupported action %v", act) os.Exit(1) } var rangeEnd []byte key := []byte(flag.Args()[1]) if len(flag.Args()) > 2 { rangeEnd = []byte(flag.Args()[2]) } results = make(chan *result, n) bar = pb.New(n) bar.Format("Bom !") bar.Start() start := time.Now() wg.Add(c) requests := make(chan struct{}, n) conn, err := grpc.Dial(url) if err != nil { fmt.Errorf("dial error: %v", err) os.Exit(1) } for i := 0; i < c; i++ { go get(etcdserverpb.NewEtcdClient(conn), key, rangeEnd, requests) } for i := 0; i < n; i++ { requests <- struct{}{} } close(requests) wg.Wait() bar.Finish() printReport(n, results, time.Now().Sub(start)) }
func TestTLSDialTimeout(t *testing.T) { creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com") if err != nil { t.Fatalf("Failed to create credentials %v", err) } conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTransportCredentials(creds), grpc.WithTimeout(time.Millisecond)) if err == nil { conn.Close() } if err != grpc.ErrClientConnTimeout { t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout) } }
// watchCommandFunc executes the "watch" command. func watchCommandFunc(cmd *cobra.Command, args []string) { endpoint, err := cmd.Flags().GetString("endpoint") if err != nil { ExitWithError(ExitInvalidInput, err) } conn, err := grpc.Dial(endpoint) if err != nil { ExitWithError(ExitBadConnection, err) } wAPI := pb.NewWatchClient(conn) wStream, err := wAPI.Watch(context.TODO()) if err != nil { ExitWithError(ExitBadConnection, err) } go recvLoop(wStream) reader := bufio.NewReader(os.Stdin) for { l, err := reader.ReadString('\n') if err != nil { ExitWithError(ExitInvalidInput, fmt.Errorf("Error reading watch request line: %v", err)) } 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) } } }