github.com/google/cloudprober@v0.11.3/servers/grpc/grpc.go (about)

     1  // Copyright 2018 The Cloudprober Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package grpc provides a simple gRPC server that acts as a probe target.
    16  package grpc
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"net"
    23  	"time"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/health"
    28  	"google.golang.org/grpc/reflection"
    29  
    30  	"github.com/google/cloudprober/config/runconfig"
    31  	"github.com/google/cloudprober/logger"
    32  	"github.com/google/cloudprober/metrics"
    33  	"github.com/google/cloudprober/probes/probeutils"
    34  	configpb "github.com/google/cloudprober/servers/grpc/proto"
    35  	grpcpb "github.com/google/cloudprober/servers/grpc/proto"
    36  	spb "github.com/google/cloudprober/servers/grpc/proto"
    37  	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    38  )
    39  
    40  // Server implements a gRPCServer.
    41  type Server struct {
    42  	c            *configpb.ServerConf
    43  	ln           net.Listener
    44  	grpcSrv      *grpc.Server
    45  	healthSrv    *health.Server
    46  	l            *logger.Logger
    47  	startTime    time.Time
    48  	dedicatedSrv bool
    49  	msg          []byte
    50  }
    51  
    52  var (
    53  	maxMsgSize = 1 * 1024 * 1024 // 1MB
    54  	msgPattern = []byte("cloudprober")
    55  )
    56  
    57  // Echo reflects back the incoming message.
    58  // TODO: return error if EchoMessage is greater than maxMsgSize.
    59  func (s *Server) Echo(ctx context.Context, req *spb.EchoMessage) (*spb.EchoMessage, error) {
    60  	return req, nil
    61  }
    62  
    63  // BlobRead returns a blob of data.
    64  func (s *Server) BlobRead(ctx context.Context, req *spb.BlobReadRequest) (*spb.BlobReadResponse, error) {
    65  	reqSize := req.GetSize()
    66  	if reqSize > int32(maxMsgSize) {
    67  		return nil, fmt.Errorf("read request size (%d) exceeds max size (%d)", reqSize, maxMsgSize)
    68  	}
    69  	return &spb.BlobReadResponse{
    70  		Blob: s.msg[0:reqSize],
    71  	}, nil
    72  }
    73  
    74  // ServerStatus returns the current server status.
    75  func (s *Server) ServerStatus(ctx context.Context, req *spb.StatusRequest) (*spb.StatusResponse, error) {
    76  	return &spb.StatusResponse{
    77  		UptimeUs: proto.Int64(time.Since(s.startTime).Nanoseconds() / 1000),
    78  	}, nil
    79  }
    80  
    81  // BlobWrite returns the size of blob in the WriteRequest. It does not operate
    82  // on the blob.
    83  func (s *Server) BlobWrite(ctx context.Context, req *spb.BlobWriteRequest) (*spb.BlobWriteResponse, error) {
    84  	reqSize := int32(len(req.Blob))
    85  	if reqSize > int32(maxMsgSize) {
    86  		return nil, fmt.Errorf("write request size (%d) exceeds max size (%d)", reqSize, maxMsgSize)
    87  	}
    88  	return &spb.BlobWriteResponse{
    89  		Size: proto.Int32(reqSize),
    90  	}, nil
    91  }
    92  
    93  // New returns a Server.
    94  func New(initCtx context.Context, c *configpb.ServerConf, l *logger.Logger) (*Server, error) {
    95  	srv := &Server{
    96  		c: c,
    97  		l: l,
    98  	}
    99  	srv.msg = make([]byte, maxMsgSize)
   100  	probeutils.PatternPayload(srv.msg, msgPattern)
   101  	if c.GetUseDedicatedServer() {
   102  		if err := srv.newGRPCServer(initCtx); err != nil {
   103  			return nil, err
   104  		}
   105  		srv.dedicatedSrv = true
   106  		return srv, nil
   107  	}
   108  
   109  	defGRPCSrv := runconfig.DefaultGRPCServer()
   110  	if defGRPCSrv == nil {
   111  		return nil, errors.New("initialization of gRPC server failed as default gRPC server is not configured")
   112  	}
   113  	l.Warningf("Reusing global gRPC server %v to handle gRPC probes", defGRPCSrv)
   114  	srv.grpcSrv = defGRPCSrv
   115  	srv.dedicatedSrv = false
   116  	srv.startTime = time.Now()
   117  	grpcpb.RegisterProberServer(defGRPCSrv, srv)
   118  	return srv, nil
   119  }
   120  
   121  func (s *Server) newGRPCServer(ctx context.Context) error {
   122  	grpcSrv := grpc.NewServer()
   123  	healthSrv := health.NewServer()
   124  	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", s.c.GetPort()))
   125  	if err != nil {
   126  		return err
   127  	}
   128  	// Cleanup listener if ctx is canceled.
   129  	go func() {
   130  		<-ctx.Done()
   131  		ln.Close()
   132  	}()
   133  
   134  	s.ln = ln
   135  	s.grpcSrv = grpcSrv
   136  	s.healthSrv = healthSrv
   137  	s.startTime = time.Now()
   138  
   139  	grpcpb.RegisterProberServer(grpcSrv, s)
   140  	healthpb.RegisterHealthServer(grpcSrv, healthSrv)
   141  	return nil
   142  }
   143  
   144  // Start starts the gRPC server and serves requests until the context is
   145  // canceled or the gRPC server panics.
   146  func (s *Server) Start(ctx context.Context, dataChan chan<- *metrics.EventMetrics) error {
   147  	if !s.dedicatedSrv {
   148  		// Nothing to do as caller owns server. Wait till context is done.
   149  		<-ctx.Done()
   150  		return nil
   151  	}
   152  
   153  	s.l.Infof("Starting gRPC server at %s", s.ln.Addr().String())
   154  	go func() {
   155  		<-ctx.Done()
   156  		s.l.Infof("Context canceled. Shutting down the gRPC server at: %s", s.ln.Addr().String())
   157  		for svc := range s.grpcSrv.GetServiceInfo() {
   158  			s.healthSrv.SetServingStatus(svc, healthpb.HealthCheckResponse_NOT_SERVING)
   159  		}
   160  		s.grpcSrv.Stop()
   161  	}()
   162  	for si := range s.grpcSrv.GetServiceInfo() {
   163  		s.healthSrv.SetServingStatus(si, healthpb.HealthCheckResponse_SERVING)
   164  	}
   165  	if s.c.GetEnableReflection() {
   166  		s.l.Infof("Enabling reflection for gRPC server at %s", s.ln.Addr().String())
   167  		reflection.Register(s.grpcSrv)
   168  	}
   169  	return s.grpcSrv.Serve(s.ln)
   170  }