blob: 2bf76967757499b00407903695a998427d23141d [file] [log] [blame]
Scott Baker2c1c4822019-10-16 11:02:41 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package grpc
17
18import (
19 "context"
Scott Baker2c1c4822019-10-16 11:02:41 -070020 "google.golang.org/grpc"
Scott Baker104b67d2019-10-29 15:56:27 -070021 "google.golang.org/grpc/codes"
Scott Baker2c1c4822019-10-16 11:02:41 -070022 "google.golang.org/grpc/credentials"
Scott Baker104b67d2019-10-29 15:56:27 -070023 "google.golang.org/grpc/status"
Scott Baker2c1c4822019-10-16 11:02:41 -070024 "net"
25)
26
27/*
28To add a GRPC server to your existing component simply follow these steps:
29
301. Create a server instance by passing the host and port where it should run and optionally add certificate information
31
32 e.g.
33 s.server = server.NewGrpcServer(s.config.GrpcHost, s.config.GrpcPort, nil, false)
34
352. Create a function that will register your service with the GRPC server
36
37 e.g.
38 f := func(gs *grpc.Server) {
39 voltha.RegisterVolthaReadOnlyServiceServer(
40 gs,
41 core.NewReadOnlyServiceHandler(s.root),
42 )
43 }
44
453. Add the service to the server
46
47 e.g.
48 s.server.AddService(f)
49
504. Start the server
51
52 s.server.Start(ctx)
53*/
54
Scott Baker104b67d2019-10-29 15:56:27 -070055// Interface allows probes to be attached to server
56// A probe must support the IsReady() method
57type ReadyProbe interface {
58 IsReady() bool
59}
60
Scott Baker2c1c4822019-10-16 11:02:41 -070061type GrpcServer struct {
62 gs *grpc.Server
63 address string
Scott Baker2c1c4822019-10-16 11:02:41 -070064 secure bool
65 services []func(*grpc.Server)
Scott Baker104b67d2019-10-29 15:56:27 -070066 probe ReadyProbe // optional
Scott Baker2c1c4822019-10-16 11:02:41 -070067
68 *GrpcSecurity
69}
70
71/*
72Instantiate a GRPC server data structure
73*/
74func NewGrpcServer(
75 address string,
Scott Baker2c1c4822019-10-16 11:02:41 -070076 certs *GrpcSecurity,
77 secure bool,
Scott Bakerdefa2bf2019-11-08 12:03:56 -080078 probe ReadyProbe,
Scott Baker2c1c4822019-10-16 11:02:41 -070079) *GrpcServer {
80 server := &GrpcServer{
81 address: address,
Scott Baker2c1c4822019-10-16 11:02:41 -070082 secure: secure,
83 GrpcSecurity: certs,
Scott Bakerdefa2bf2019-11-08 12:03:56 -080084 probe: probe,
Scott Baker2c1c4822019-10-16 11:02:41 -070085 }
86 return server
87}
88
89/*
90Start prepares the GRPC server and starts servicing requests
91*/
92func (s *GrpcServer) Start(ctx context.Context) {
93
Neha Sharmadd9af392020-04-28 09:03:57 +000094 lis, err := net.Listen("tcp", s.address)
Scott Baker2c1c4822019-10-16 11:02:41 -070095 if err != nil {
Neha Sharma3c425fb2020-06-08 16:42:32 +000096 logger.Fatalf(ctx, "failed to listen: %v", err)
Scott Baker2c1c4822019-10-16 11:02:41 -070097 }
98
99 if s.secure && s.GrpcSecurity != nil {
100 creds, err := credentials.NewServerTLSFromFile(s.CertFile, s.KeyFile)
101 if err != nil {
Neha Sharma3c425fb2020-06-08 16:42:32 +0000102 logger.Fatalf(ctx, "could not load TLS keys: %s", err)
Scott Baker2c1c4822019-10-16 11:02:41 -0700103 }
Scott Baker104b67d2019-10-29 15:56:27 -0700104 s.gs = grpc.NewServer(grpc.Creds(creds),
105 withServerUnaryInterceptor(s))
Scott Baker2c1c4822019-10-16 11:02:41 -0700106
107 } else {
Neha Sharma3c425fb2020-06-08 16:42:32 +0000108 logger.Info(ctx, "starting-insecure-grpc-server")
Scott Baker104b67d2019-10-29 15:56:27 -0700109 s.gs = grpc.NewServer(withServerUnaryInterceptor(s))
Scott Baker2c1c4822019-10-16 11:02:41 -0700110 }
111
112 // Register all required services
113 for _, service := range s.services {
114 service(s.gs)
115 }
116
117 if err := s.gs.Serve(lis); err != nil {
Neha Sharma3c425fb2020-06-08 16:42:32 +0000118 logger.Fatalf(ctx, "failed to serve: %v\n", err)
Scott Baker2c1c4822019-10-16 11:02:41 -0700119 }
120}
121
Scott Baker104b67d2019-10-29 15:56:27 -0700122func withServerUnaryInterceptor(s *GrpcServer) grpc.ServerOption {
123 return grpc.UnaryInterceptor(mkServerInterceptor(s))
124}
125
126// Make a serverInterceptor for the given GrpcServer
127// This interceptor will check whether there is an attached probe,
128// and if that probe indicates NotReady, then an UNAVAILABLE
129// response will be returned.
130func mkServerInterceptor(s *GrpcServer) func(ctx context.Context,
131 req interface{},
132 info *grpc.UnaryServerInfo,
133 handler grpc.UnaryHandler) (interface{}, error) {
134
135 return func(ctx context.Context,
136 req interface{},
137 info *grpc.UnaryServerInfo,
138 handler grpc.UnaryHandler) (interface{}, error) {
139
140 if (s.probe != nil) && (!s.probe.IsReady()) {
Neha Sharma3c425fb2020-06-08 16:42:32 +0000141 logger.Warnf(ctx, "Grpc request received while not ready %v", req)
Scott Baker104b67d2019-10-29 15:56:27 -0700142 return nil, status.Error(codes.Unavailable, "system is not ready")
143 }
144
145 // Calls the handler
146 h, err := handler(ctx, req)
147
148 return h, err
149 }
150}
151
Scott Baker2c1c4822019-10-16 11:02:41 -0700152/*
153Stop servicing GRPC requests
154*/
155func (s *GrpcServer) Stop() {
156 if s.gs != nil {
157 s.gs.Stop()
158 }
159}
160
161/*
162AddService appends a generic service request function
163*/
164func (s *GrpcServer) AddService(
165 registerFunction func(*grpc.Server),
166) {
167 s.services = append(s.services, registerFunction)
168}