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  }