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 }