// ClientSetup loads various TLS certificates and creates a // gRPC TransportCredentials that presents the client certificate // and validates the certificate presented by the server is for a // specific hostname and issued by the provided issuer certificate // thens dials and returns a grpc.ClientConn to the remote service. func ClientSetup(c *cmd.GRPCClientConfig, stats metrics.Scope) (*grpc.ClientConn, error) { if len(c.ServerAddresses) == 0 { return nil, fmt.Errorf("boulder/grpc: ServerAddresses is empty") } if stats == nil { return nil, errNilScope } serverIssuerBytes, err := ioutil.ReadFile(c.ServerIssuerPath) if err != nil { return nil, err } rootCAs := x509.NewCertPool() if ok := rootCAs.AppendCertsFromPEM(serverIssuerBytes); !ok { return nil, fmt.Errorf("Failed to parse server issues from '%s'", c.ServerIssuerPath) } clientCert, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath) if err != nil { return nil, err } grpc_prometheus.EnableHandlingTimeHistogram() ci := clientInterceptor{stats.NewScope("gRPCClient"), clock.Default(), c.Timeout.Duration} creds := bcreds.NewClientCredentials(rootCAs, []tls.Certificate{clientCert}) return grpc.Dial( "", // Since our staticResolver provides addresses we don't need to pass an address here grpc.WithTransportCredentials(creds), grpc.WithBalancer(grpc.RoundRobin(newStaticResolver(c.ServerAddresses))), grpc.WithUnaryInterceptor(ci.intercept), ) }
func TestDropRequestFailedNonFailFast(t *testing.T) { // Start a backend. beLis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen %v", err) } beAddr := strings.Split(beLis.Addr().String(), ":") bePort, err := strconv.Atoi(beAddr[1]) backends := startBackends(t, besn, beLis) defer stopBackends(backends) // Start a load balancer. lbLis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to create the listener for the load balancer %v", err) } lbCreds := &serverNameCheckCreds{ sn: lbsn, } lb := grpc.NewServer(grpc.Creds(lbCreds)) if err != nil { t.Fatalf("Failed to generate the port number %v", err) } be := &lbpb.Server{ IpAddress: []byte(beAddr[0]), Port: int32(bePort), LoadBalanceToken: lbToken, DropRequest: true, } var bes []*lbpb.Server bes = append(bes, be) sl := &lbpb.ServerList{ Servers: bes, } ls := newRemoteBalancer(sl) lbpb.RegisterLoadBalancerServer(lb, ls) go func() { lb.Serve(lbLis) }() defer func() { ls.stop() lb.Stop() }() creds := serverNameCheckCreds{ expected: besn, } ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{ addr: lbLis.Addr().String(), })), grpc.WithBlock(), grpc.WithTransportCredentials(&creds)) if err != nil { t.Fatalf("Failed to dial to the backend %v", err) } helloC := hwpb.NewGreeterClient(cc) ctx, _ = context.WithTimeout(context.Background(), 10*time.Millisecond) if _, err := helloC.SayHello(ctx, &hwpb.HelloRequest{Name: "grpc"}, grpc.FailFast(false)); grpc.Code(err) != codes.DeadlineExceeded { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, %s", helloC, err, codes.DeadlineExceeded) } cc.Close() }
func newClient(cfg *Config) (*Client, error) { if cfg == nil { cfg = &Config{} } var creds *credentials.TransportCredentials if cfg.TLS != nil { c := credentials.NewTLS(cfg.TLS) creds = &c } // use a temporary skeleton client to bootstrap first connection ctx, cancel := context.WithCancel(context.TODO()) client := &Client{ conn: nil, cfg: *cfg, creds: creds, ctx: ctx, cancel: cancel, } if cfg.Username != "" && cfg.Password != "" { client.Username = cfg.Username client.Password = cfg.Password } client.balancer = newSimpleBalancer(cfg.Endpoints) conn, err := client.dial(cfg.Endpoints[0], grpc.WithBalancer(client.balancer)) if err != nil { return nil, err } client.conn = conn client.retryWrapper = client.newRetryWrapper() // wait for a connection if cfg.DialTimeout > 0 { hasConn := false waitc := time.After(cfg.DialTimeout) select { case <-client.balancer.readyc: hasConn = true case <-ctx.Done(): case <-waitc: } if !hasConn { client.cancel() conn.Close() return nil, grpc.ErrClientConnTimeout } } client.Cluster = NewCluster(client) client.KV = NewKV(client) client.Lease = NewLease(client) client.Watcher = NewWatcher(client) client.Auth = NewAuth(client) client.Maintenance = NewMaintenance(client) go client.autoSync() return client, nil }
func TestGRPCLB(t *testing.T) { // Start a backend. beLis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen %v", err) } backends := startBackends(beLis) defer stopBackends(backends) // Start a load balancer. lis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to create the listener for the load balancer %v", err) } lb := grpc.NewServer() addr := strings.Split(lis.Addr().String(), ":") port, err := strconv.Atoi(addr[1]) if err != nil { t.Fatalf("Failed to generate the port number %v", err) } be := &lbpb.Server{ IpAddress: []byte(addr[0]), Port: int32(port), } var bes []*lbpb.Server bes = append(bes, be) sl := &lbpb.ServerList{ Servers: bes, } ls := newRemoteBalancer(sl) lbpb.RegisterLoadBalancerServer(lb, ls) go func() { lb.Serve(lis) }() defer func() { ls.stop() lb.Stop() }() cc, err := grpc.Dial("foo.bar.com", grpc.WithBalancer(Balancer(&testNameResolver{ addr: lis.Addr().String(), })), grpc.WithInsecure(), grpc.WithBlock()) if err != nil { t.Fatalf("Failed to dial to the backend %v", err) } // Issue an unimplemented RPC and expect codes.Unimplemented. var ( req, reply lbpb.Duration ) if err := grpc.Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || grpc.Code(err) != codes.Unimplemented { t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want error code %s", err, codes.Unimplemented) } cc.Close() }
func (r *RPCRegistry) Connect() { // We want the connection operation to block and constantly reconnect using grpc backoff log.Info("Starting gRPC connection to fleet-engine...") ep_engines := []string{":fleet-engine:"} r.balancer = newSimpleBalancer(ep_engines) connection, err := grpc.Dial(ep_engines[0], grpc.WithTimeout(12*time.Second), grpc.WithInsecure(), grpc.WithDialer(r.dialer), grpc.WithBlock(), grpc.WithBalancer(r.balancer)) if err != nil { log.Fatalf("Unable to dial to registry: %s", err) } r.registryConn = connection r.registryClient = pb.NewRegistryClient(r.registryConn) log.Info("Connected succesfully to fleet-engine via grpc!") }
func newClient(cfg *Config) (*Client, error) { if cfg == nil { cfg = &Config{} } var creds *credentials.TransportCredentials if cfg.TLS != nil { c := credentials.NewTLS(cfg.TLS) creds = &c } // use a temporary skeleton client to bootstrap first connection ctx, cancel := context.WithCancel(context.TODO()) client := &Client{ conn: nil, cfg: *cfg, creds: creds, ctx: ctx, cancel: cancel, } if cfg.Username != "" && cfg.Password != "" { client.Username = cfg.Username client.Password = cfg.Password } b := newSimpleBalancer(cfg.Endpoints) conn, err := client.dial(cfg.Endpoints[0], grpc.WithBalancer(b)) if err != nil { return nil, err } client.conn = conn client.Cluster = NewCluster(client) client.KV = NewKV(client) client.Lease = NewLease(client) client.Watcher = NewWatcher(client) client.Auth = NewAuth(client) client.Maintenance = NewMaintenance(client) if cfg.Logger != nil { logger.Set(cfg.Logger) } else { // disable client side grpc by default logger.Set(log.New(ioutil.Discard, "", 0)) } return client, nil }
// ClientSetup loads various TLS certificates and creates a // gRPC TransportCredentials that presents the client certificate // and validates the certificate presented by the server is for a // specific hostname and issued by the provided issuer certificate // thens dials and returns a grpc.ClientConn to the remote service. func ClientSetup(c *cmd.GRPCClientConfig) (*grpc.ClientConn, error) { if len(c.ServerAddresses) == 0 { return nil, fmt.Errorf("boulder/grpc: ServerAddresses is empty") } serverIssuerBytes, err := ioutil.ReadFile(c.ServerIssuerPath) if err != nil { return nil, err } rootCAs := x509.NewCertPool() if ok := rootCAs.AppendCertsFromPEM(serverIssuerBytes); !ok { return nil, fmt.Errorf("Failed to parse server issues from '%s'", c.ServerIssuerPath) } clientCert, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath) if err != nil { return nil, err } return grpc.Dial( "", // Since our staticResolver provides addresses we don't need to pass an address here grpc.WithTransportCredentials(bcreds.New(rootCAs, []tls.Certificate{clientCert})), grpc.WithBalancer(grpc.RoundRobin(newStaticResolver(c.ServerAddresses))), ) }
func TestRPCRegistryClientCreation(t *testing.T) { lis, err := net.Listen("tcp", ":0") if err != nil { t.Fatalf("failed to listen: %v", err) } _, port, err := net.SplitHostPort(lis.Addr().String()) if err != nil { t.Fatalf("failed to parse listener address: %v", err) } addr := "localhost:" + port b := newSimpleBalancer([]string{addr}) conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second), grpc.WithBlock(), grpc.WithBalancer(b)) if err != nil { t.Fatalf("failed to dial to the server %q: %v", addr, err) } // Close unaccepted connection (i.e., conn). lis.Close() registryClient := pb.NewRegistryClient(conn) if registryClient == nil { t.Fatalf("failed to create a new grpc registry to the server %q: %v", addr, err) } }
func (w withGRPCConnectionPool) Apply(o *internal.DialSettings) { balancer := grpc.RoundRobin(internal.NewPoolResolver(int(w), o)) o.GRPCDialOpts = append(o.GRPCDialOpts, grpc.WithBalancer(balancer)) }
func TestDropRequest(t *testing.T) { // Start 2 backends. beLis1, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen %v", err) } beAddr1 := strings.Split(beLis1.Addr().String(), ":") bePort1, err := strconv.Atoi(beAddr1[1]) beLis2, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen %v", err) } beAddr2 := strings.Split(beLis2.Addr().String(), ":") bePort2, err := strconv.Atoi(beAddr2[1]) backends := startBackends(t, besn, beLis1, beLis2) defer stopBackends(backends) // Start a load balancer. lbLis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to create the listener for the load balancer %v", err) } lbCreds := &serverNameCheckCreds{ sn: lbsn, } lb := grpc.NewServer(grpc.Creds(lbCreds)) if err != nil { t.Fatalf("Failed to generate the port number %v", err) } var bes []*lbpb.Server be := &lbpb.Server{ IpAddress: []byte(beAddr1[0]), Port: int32(bePort1), LoadBalanceToken: lbToken, DropRequest: true, } bes = append(bes, be) be = &lbpb.Server{ IpAddress: []byte(beAddr2[0]), Port: int32(bePort2), LoadBalanceToken: lbToken, DropRequest: false, } bes = append(bes, be) sl := &lbpb.ServerList{ Servers: bes, } ls := newRemoteBalancer(sl) lbpb.RegisterLoadBalancerServer(lb, ls) go func() { lb.Serve(lbLis) }() defer func() { ls.stop() lb.Stop() }() creds := serverNameCheckCreds{ expected: besn, } ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{ addr: lbLis.Addr().String(), })), grpc.WithBlock(), grpc.WithTransportCredentials(&creds)) if err != nil { t.Fatalf("Failed to dial to the backend %v", err) } // The 1st fail-fast RPC should fail because the 1st backend has DropRequest set to true. helloC := hwpb.NewGreeterClient(cc) if _, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); grpc.Code(err) != codes.Unavailable { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, %s", helloC, err, codes.Unavailable) } // The 2nd fail-fast RPC should succeed since it chooses the non-drop-request backend according // to the round robin policy. if _, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); err != nil { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err) } // The 3nd non-fail-fast RPC should succeed. if _, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}, grpc.FailFast(false)); err != nil { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err) } cc.Close() }
func TestServerExpiration(t *testing.T) { // Start a backend. beLis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen %v", err) } beAddr := strings.Split(beLis.Addr().String(), ":") bePort, err := strconv.Atoi(beAddr[1]) backends := startBackends(t, besn, beLis) defer stopBackends(backends) // Start a load balancer. lbLis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to create the listener for the load balancer %v", err) } lbCreds := &serverNameCheckCreds{ sn: lbsn, } lb := grpc.NewServer(grpc.Creds(lbCreds)) if err != nil { t.Fatalf("Failed to generate the port number %v", err) } be := &lbpb.Server{ IpAddress: []byte(beAddr[0]), Port: int32(bePort), LoadBalanceToken: lbToken, } var bes []*lbpb.Server bes = append(bes, be) exp := &lbpb.Duration{ Seconds: 0, Nanos: 100000000, // 100ms } var sls []*lbpb.ServerList sl := &lbpb.ServerList{ Servers: bes, ExpirationInterval: exp, } sls = append(sls, sl) sl = &lbpb.ServerList{ Servers: bes, } sls = append(sls, sl) var intervals []time.Duration intervals = append(intervals, 0) intervals = append(intervals, 500*time.Millisecond) ls := newRemoteBalancer(sls, intervals) lbpb.RegisterLoadBalancerServer(lb, ls) go func() { lb.Serve(lbLis) }() defer func() { ls.stop() lb.Stop() }() creds := serverNameCheckCreds{ expected: besn, } ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) cc, err := grpc.DialContext(ctx, besn, grpc.WithBalancer(Balancer(&testNameResolver{ addr: lbLis.Addr().String(), })), grpc.WithBlock(), grpc.WithTransportCredentials(&creds)) if err != nil { t.Fatalf("Failed to dial to the backend %v", err) } helloC := hwpb.NewGreeterClient(cc) if _, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); err != nil { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err) } // Sleep and wake up when the first server list gets expired. time.Sleep(150 * time.Millisecond) if _, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}); grpc.Code(err) != codes.Unavailable { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, %s", helloC, err, codes.Unavailable) } // A non-failfast rpc should be succeeded after the second server list is received from // the remote load balancer. if _, err := helloC.SayHello(context.Background(), &hwpb.HelloRequest{Name: "grpc"}, grpc.FailFast(false)); err != nil { t.Fatalf("%v.SayHello(_, _) = _, %v, want _, <nil>", helloC, err) } cc.Close() }