github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/cmd/testserver/testserver.go (about)

     1  package testserver
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/rs/zerolog"
     9  	"golang.org/x/sync/errgroup"
    10  	"google.golang.org/grpc"
    11  
    12  	"github.com/authzed/spicedb/internal/dispatch/graph"
    13  	"github.com/authzed/spicedb/internal/gateway"
    14  	log "github.com/authzed/spicedb/internal/logging"
    15  	consistencymw "github.com/authzed/spicedb/internal/middleware/consistency"
    16  	dispatchmw "github.com/authzed/spicedb/internal/middleware/dispatcher"
    17  	"github.com/authzed/spicedb/internal/middleware/pertoken"
    18  	"github.com/authzed/spicedb/internal/middleware/readonly"
    19  	"github.com/authzed/spicedb/internal/middleware/servicespecific"
    20  	"github.com/authzed/spicedb/internal/services"
    21  	"github.com/authzed/spicedb/internal/services/health"
    22  	v1svc "github.com/authzed/spicedb/internal/services/v1"
    23  	"github.com/authzed/spicedb/pkg/cmd/util"
    24  	"github.com/authzed/spicedb/pkg/datastore"
    25  )
    26  
    27  const maxDepth = 50
    28  
    29  //go:generate go run github.com/ecordell/optgen -output zz_generated.options.go . Config
    30  type Config struct {
    31  	GRPCServer                      util.GRPCServerConfig `debugmap:"visible"`
    32  	ReadOnlyGRPCServer              util.GRPCServerConfig `debugmap:"visible"`
    33  	HTTPGateway                     util.HTTPServerConfig `debugmap:"visible"`
    34  	ReadOnlyHTTPGateway             util.HTTPServerConfig `debugmap:"visible"`
    35  	LoadConfigs                     []string              `debugmap:"visible"`
    36  	MaximumUpdatesPerWrite          uint16                `debugmap:"visible"`
    37  	MaximumPreconditionCount        uint16                `debugmap:"visible"`
    38  	MaxCaveatContextSize            int                   `debugmap:"visible"`
    39  	MaxRelationshipContextSize      int                   `debugmap:"visible"`
    40  	MaxReadRelationshipsLimit       uint32                `debugmap:"visible"`
    41  	MaxDeleteRelationshipsLimit     uint32                `debugmap:"visible"`
    42  	MaxLookupResourcesLimit         uint32                `debugmap:"visible"`
    43  	MaxBulkExportRelationshipsLimit uint32                `debugmap:"visible"`
    44  }
    45  
    46  type RunnableTestServer interface {
    47  	Run(ctx context.Context) error
    48  	GRPCDialContext(ctx context.Context, opts ...grpc.DialOption) (*grpc.ClientConn, error)
    49  	ReadOnlyGRPCDialContext(ctx context.Context, opts ...grpc.DialOption) (*grpc.ClientConn, error)
    50  }
    51  
    52  type datastoreReady struct{}
    53  
    54  func (dr datastoreReady) ReadyState(_ context.Context) (datastore.ReadyState, error) {
    55  	return datastore.ReadyState{IsReady: true}, nil
    56  }
    57  
    58  func (c *Config) Complete() (RunnableTestServer, error) {
    59  	dispatcher := graph.NewLocalOnlyDispatcher(10)
    60  
    61  	datastoreMiddleware := pertoken.NewMiddleware(c.LoadConfigs)
    62  
    63  	healthManager := health.NewHealthManager(dispatcher, &datastoreReady{})
    64  
    65  	registerServices := func(srv *grpc.Server) {
    66  		services.RegisterGrpcServices(
    67  			srv,
    68  			healthManager,
    69  			dispatcher,
    70  			services.V1SchemaServiceEnabled,
    71  			services.WatchServiceEnabled,
    72  			v1svc.PermissionsServerConfig{
    73  				MaxPreconditionsCount:           c.MaximumPreconditionCount,
    74  				MaxUpdatesPerWrite:              c.MaximumUpdatesPerWrite,
    75  				MaximumAPIDepth:                 maxDepth,
    76  				MaxCaveatContextSize:            c.MaxCaveatContextSize,
    77  				MaxReadRelationshipsLimit:       c.MaxReadRelationshipsLimit,
    78  				MaxDeleteRelationshipsLimit:     c.MaxDeleteRelationshipsLimit,
    79  				MaxLookupResourcesLimit:         c.MaxLookupResourcesLimit,
    80  				MaxBulkExportRelationshipsLimit: c.MaxBulkExportRelationshipsLimit,
    81  			},
    82  			1*time.Second,
    83  		)
    84  	}
    85  	gRPCSrv, err := c.GRPCServer.Complete(zerolog.InfoLevel, registerServices,
    86  		grpc.ChainUnaryInterceptor(
    87  			datastoreMiddleware.UnaryServerInterceptor(),
    88  			dispatchmw.UnaryServerInterceptor(dispatcher),
    89  			consistencymw.UnaryServerInterceptor(),
    90  			servicespecific.UnaryServerInterceptor,
    91  		),
    92  		grpc.ChainStreamInterceptor(
    93  			datastoreMiddleware.StreamServerInterceptor(),
    94  			dispatchmw.StreamServerInterceptor(dispatcher),
    95  			consistencymw.StreamServerInterceptor(),
    96  			servicespecific.StreamServerInterceptor,
    97  		),
    98  	)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	readOnlyGRPCSrv, err := c.ReadOnlyGRPCServer.Complete(zerolog.InfoLevel, registerServices,
   104  		grpc.ChainUnaryInterceptor(
   105  			datastoreMiddleware.UnaryServerInterceptor(),
   106  			readonly.UnaryServerInterceptor(),
   107  			dispatchmw.UnaryServerInterceptor(dispatcher),
   108  			consistencymw.UnaryServerInterceptor(),
   109  			servicespecific.UnaryServerInterceptor,
   110  		),
   111  		grpc.ChainStreamInterceptor(
   112  			datastoreMiddleware.StreamServerInterceptor(),
   113  			readonly.StreamServerInterceptor(),
   114  			dispatchmw.StreamServerInterceptor(dispatcher),
   115  			consistencymw.StreamServerInterceptor(),
   116  			servicespecific.StreamServerInterceptor,
   117  		),
   118  	)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	gatewayHandler, err := gateway.NewHandler(context.TODO(), c.GRPCServer.Address, c.GRPCServer.TLSCertPath)
   124  	if err != nil {
   125  		log.Fatal().Err(err).Msg("failed to initialize rest gateway")
   126  	}
   127  
   128  	if c.HTTPGateway.HTTPEnabled {
   129  		log.Info().Msg("starting REST gateway")
   130  	}
   131  
   132  	gatewayServer, err := c.HTTPGateway.Complete(zerolog.InfoLevel, gatewayHandler)
   133  	if err != nil {
   134  		return nil, fmt.Errorf("failed to initialize rest gateway: %w", err)
   135  	}
   136  
   137  	readOnlyGatewayHandler, err := gateway.NewHandler(context.TODO(), c.ReadOnlyGRPCServer.Address, c.ReadOnlyGRPCServer.TLSCertPath)
   138  	if err != nil {
   139  		log.Fatal().Err(err).Msg("failed to initialize rest gateway")
   140  	}
   141  
   142  	if c.ReadOnlyHTTPGateway.HTTPEnabled {
   143  		log.Info().Msg("starting REST gateway")
   144  	}
   145  
   146  	readOnlyGatewayServer, err := c.ReadOnlyHTTPGateway.Complete(zerolog.InfoLevel, readOnlyGatewayHandler)
   147  	if err != nil {
   148  		return nil, fmt.Errorf("failed to initialize rest gateway: %w", err)
   149  	}
   150  
   151  	return &completedTestServer{
   152  		gRPCServer:            gRPCSrv,
   153  		readOnlyGRPCServer:    readOnlyGRPCSrv,
   154  		gatewayServer:         gatewayServer,
   155  		readOnlyGatewayServer: readOnlyGatewayServer,
   156  		healthManager:         healthManager,
   157  	}, nil
   158  }
   159  
   160  type completedTestServer struct {
   161  	gRPCServer         util.RunnableGRPCServer
   162  	readOnlyGRPCServer util.RunnableGRPCServer
   163  
   164  	gatewayServer         util.RunnableHTTPServer
   165  	readOnlyGatewayServer util.RunnableHTTPServer
   166  
   167  	healthManager health.Manager
   168  }
   169  
   170  func (c *completedTestServer) Run(ctx context.Context) error {
   171  	g, ctx := errgroup.WithContext(ctx)
   172  
   173  	stopOnCancel := func(stopFn func()) func() error {
   174  		return func() error {
   175  			<-ctx.Done()
   176  			stopFn()
   177  			return nil
   178  		}
   179  	}
   180  
   181  	g.Go(c.healthManager.Checker(ctx))
   182  
   183  	g.Go(c.gRPCServer.Listen(ctx))
   184  	g.Go(stopOnCancel(c.gRPCServer.GracefulStop))
   185  
   186  	g.Go(c.readOnlyGRPCServer.Listen(ctx))
   187  	g.Go(stopOnCancel(c.readOnlyGRPCServer.GracefulStop))
   188  
   189  	g.Go(c.gatewayServer.ListenAndServe)
   190  	g.Go(stopOnCancel(c.gatewayServer.Close))
   191  
   192  	g.Go(c.readOnlyGatewayServer.ListenAndServe)
   193  	g.Go(stopOnCancel(c.readOnlyGatewayServer.Close))
   194  
   195  	if err := g.Wait(); err != nil {
   196  		log.Ctx(ctx).Warn().Err(err).Msg("error shutting down servers")
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  func (c *completedTestServer) GRPCDialContext(ctx context.Context, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
   203  	return c.gRPCServer.DialContext(ctx, opts...)
   204  }
   205  
   206  func (c *completedTestServer) ReadOnlyGRPCDialContext(ctx context.Context, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
   207  	return c.readOnlyGRPCServer.DialContext(ctx, opts...)
   208  }