github.com/hashicorp/go-plugin@v1.6.0/grpc_server.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package plugin 5 6 import ( 7 "bytes" 8 "crypto/tls" 9 "encoding/json" 10 "fmt" 11 "io" 12 "net" 13 14 hclog "github.com/hashicorp/go-hclog" 15 "github.com/hashicorp/go-plugin/internal/grpcmux" 16 "github.com/hashicorp/go-plugin/internal/plugin" 17 "google.golang.org/grpc" 18 "google.golang.org/grpc/credentials" 19 "google.golang.org/grpc/health" 20 "google.golang.org/grpc/health/grpc_health_v1" 21 "google.golang.org/grpc/reflection" 22 ) 23 24 // GRPCServiceName is the name of the service that the health check should 25 // return as passing. 26 const GRPCServiceName = "plugin" 27 28 // DefaultGRPCServer can be used with the "GRPCServer" field for Server 29 // as a default factory method to create a gRPC server with no extra options. 30 func DefaultGRPCServer(opts []grpc.ServerOption) *grpc.Server { 31 return grpc.NewServer(opts...) 32 } 33 34 // GRPCServer is a ServerType implementation that serves plugins over 35 // gRPC. This allows plugins to easily be written for other languages. 36 // 37 // The GRPCServer outputs a custom configuration as a base64-encoded 38 // JSON structure represented by the GRPCServerConfig config structure. 39 type GRPCServer struct { 40 // Plugins are the list of plugins to serve. 41 Plugins map[string]Plugin 42 43 // Server is the actual server that will accept connections. This 44 // will be used for plugin registration as well. 45 Server func([]grpc.ServerOption) *grpc.Server 46 47 // TLS should be the TLS configuration if available. If this is nil, 48 // the connection will not have transport security. 49 TLS *tls.Config 50 51 // DoneCh is the channel that is closed when this server has exited. 52 DoneCh chan struct{} 53 54 // Stdout/StderrLis are the readers for stdout/stderr that will be copied 55 // to the stdout/stderr connection that is output. 56 Stdout io.Reader 57 Stderr io.Reader 58 59 config GRPCServerConfig 60 server *grpc.Server 61 broker *GRPCBroker 62 stdioServer *grpcStdioServer 63 64 logger hclog.Logger 65 66 muxer *grpcmux.GRPCServerMuxer 67 } 68 69 // ServerProtocol impl. 70 func (s *GRPCServer) Init() error { 71 // Create our server 72 var opts []grpc.ServerOption 73 if s.TLS != nil { 74 opts = append(opts, grpc.Creds(credentials.NewTLS(s.TLS))) 75 } 76 s.server = s.Server(opts) 77 78 // Register the health service 79 healthCheck := health.NewServer() 80 healthCheck.SetServingStatus( 81 GRPCServiceName, grpc_health_v1.HealthCheckResponse_SERVING) 82 grpc_health_v1.RegisterHealthServer(s.server, healthCheck) 83 84 // Register the reflection service 85 reflection.Register(s.server) 86 87 // Register the broker service 88 brokerServer := newGRPCBrokerServer() 89 plugin.RegisterGRPCBrokerServer(s.server, brokerServer) 90 s.broker = newGRPCBroker(brokerServer, s.TLS, unixSocketConfigFromEnv(), nil, s.muxer) 91 go s.broker.Run() 92 93 // Register the controller 94 controllerServer := &grpcControllerServer{server: s} 95 plugin.RegisterGRPCControllerServer(s.server, controllerServer) 96 97 // Register the stdio service 98 s.stdioServer = newGRPCStdioServer(s.logger, s.Stdout, s.Stderr) 99 plugin.RegisterGRPCStdioServer(s.server, s.stdioServer) 100 101 // Register all our plugins onto the gRPC server. 102 for k, raw := range s.Plugins { 103 p, ok := raw.(GRPCPlugin) 104 if !ok { 105 return fmt.Errorf("%q is not a GRPC-compatible plugin", k) 106 } 107 108 if err := p.GRPCServer(s.broker, s.server); err != nil { 109 return fmt.Errorf("error registering %q: %s", k, err) 110 } 111 } 112 113 return nil 114 } 115 116 // Stop calls Stop on the underlying grpc.Server and Close on the underlying 117 // grpc.Broker if present. 118 func (s *GRPCServer) Stop() { 119 s.server.Stop() 120 121 if s.broker != nil { 122 s.broker.Close() 123 s.broker = nil 124 } 125 } 126 127 // GracefulStop calls GracefulStop on the underlying grpc.Server and Close on 128 // the underlying grpc.Broker if present. 129 func (s *GRPCServer) GracefulStop() { 130 s.server.GracefulStop() 131 132 if s.broker != nil { 133 s.broker.Close() 134 s.broker = nil 135 } 136 } 137 138 // Config is the GRPCServerConfig encoded as JSON then base64. 139 func (s *GRPCServer) Config() string { 140 // Create a buffer that will contain our final contents 141 var buf bytes.Buffer 142 143 // Wrap the base64 encoding with JSON encoding. 144 if err := json.NewEncoder(&buf).Encode(s.config); err != nil { 145 // We panic since ths shouldn't happen under any scenario. We 146 // carefully control the structure being encoded here and it should 147 // always be successful. 148 panic(err) 149 } 150 151 return buf.String() 152 } 153 154 func (s *GRPCServer) Serve(lis net.Listener) { 155 defer close(s.DoneCh) 156 err := s.server.Serve(lis) 157 if err != nil { 158 s.logger.Error("grpc server", "error", err) 159 } 160 } 161 162 // GRPCServerConfig is the extra configuration passed along for consumers 163 // to facilitate using GRPC plugins. 164 type GRPCServerConfig struct { 165 StdoutAddr string `json:"stdout_addr"` 166 StderrAddr string `json:"stderr_addr"` 167 }