get.porter.sh/porter@v1.3.0/pkg/plugins/server.go (about) 1 package plugins 2 3 import ( 4 "context" 5 "fmt" 6 7 "get.porter.sh/porter/pkg/portercontext" 8 "get.porter.sh/porter/pkg/tracing" 9 grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" 10 "github.com/hashicorp/go-plugin" 11 "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" 12 "go.opentelemetry.io/otel/attribute" 13 "go.opentelemetry.io/otel/baggage" 14 "google.golang.org/grpc" 15 ) 16 17 // Serve a single named plugin. 18 func Serve(c *portercontext.Context, interfaceName string, pluginImplementation plugin.Plugin, version int) { 19 pluginMap := map[int]plugin.PluginSet{ 20 version: {interfaceName: pluginImplementation}, 21 } 22 ServeMany(c, pluginMap) 23 } 24 25 // ServeMany plugins that the client will select by named interface. 26 func ServeMany(c *portercontext.Context, pluginMap map[int]plugin.PluginSet) { 27 plugin.Serve(&plugin.ServeConfig{ 28 HandshakeConfig: HandshakeConfig, 29 VersionedPlugins: pluginMap, 30 GRPCServer: func(opts []grpc.ServerOption) *grpc.Server { 31 opts = append(opts, 32 grpc.StatsHandler(otelgrpc.NewServerHandler()), 33 // These handlers are called from left to right. The right-most handler is the one that calls the actual implementation 34 // the grpc_recovery handler should always be last so that it can recover from a panic, and then the other handlers only get 35 // a nice error (created from the panic) to deal with 36 grpc.ChainUnaryInterceptor( 37 makeLogUnaryHandler(c), 38 makePanicHandler()), 39 ) 40 return grpc.NewServer(opts...) 41 }, 42 }) 43 } 44 45 // makeLogUnaryHandler creates a span for each RPC method called 46 func makeLogUnaryHandler(c *portercontext.Context) func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 47 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 48 bags := baggage.FromContext(ctx) 49 ctx, rootLog := c.StartRootSpan(ctx, info.FullMethod, attribute.String("baggage", bags.String())) 50 defer func() { 51 rootLog.EndSpan() 52 }() 53 54 resp, err := handler(ctx, req) 55 return resp, rootLog.Error(err) 56 } 57 } 58 59 // makePanicHandler recovers from a panic, logs the error and returns it 60 func makePanicHandler() grpc.UnaryServerInterceptor { 61 recoveryOpts := grpc_recovery.WithRecoveryHandlerContext(func(ctx context.Context, p interface{}) (err error) { 62 rootLog := tracing.LoggerFromContext(ctx) 63 return rootLog.Error(fmt.Errorf("%s", p)) 64 }) 65 return grpc_recovery.UnaryServerInterceptor(recoveryOpts) 66 }