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

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/spf13/cobra"
     9  
    10  	"github.com/authzed/spicedb/internal/telemetry"
    11  	"github.com/authzed/spicedb/pkg/cmd/datastore"
    12  	"github.com/authzed/spicedb/pkg/cmd/server"
    13  	"github.com/authzed/spicedb/pkg/cmd/termination"
    14  	"github.com/authzed/spicedb/pkg/cmd/util"
    15  )
    16  
    17  const PresharedKeyFlag = "grpc-preshared-key"
    18  
    19  var (
    20  	namespaceCacheDefaults = &server.CacheConfig{
    21  		Name:        "namespace",
    22  		Enabled:     true,
    23  		Metrics:     true,
    24  		NumCounters: 1_000,
    25  		MaxCost:     "32MiB",
    26  	}
    27  
    28  	dispatchCacheDefaults = &server.CacheConfig{
    29  		Name:        "dispatch",
    30  		Enabled:     true,
    31  		Metrics:     true,
    32  		NumCounters: 10_000,
    33  		MaxCost:     "30%",
    34  	}
    35  
    36  	dispatchClusterCacheDefaults = &server.CacheConfig{
    37  		Name:        "cluster_dispatch",
    38  		Enabled:     true,
    39  		Metrics:     true,
    40  		NumCounters: 100_000,
    41  		MaxCost:     "70%",
    42  	}
    43  )
    44  
    45  func RegisterServeFlags(cmd *cobra.Command, config *server.Config) error {
    46  	// sets default values, but does not expose it as CLI arguments
    47  	config.DispatchClusterMetricsEnabled = true
    48  	config.DispatchClientMetricsEnabled = true
    49  
    50  	// Flags for logging
    51  	cmd.Flags().BoolVar(&config.EnableRequestLogs, "grpc-log-requests-enabled", false, "logs API request payloads")
    52  	cmd.Flags().BoolVar(&config.EnableResponseLogs, "grpc-log-responses-enabled", false, "logs API response payloads")
    53  
    54  	// Flags for the gRPC API server
    55  	util.RegisterGRPCServerFlags(cmd.Flags(), &config.GRPCServer, "grpc", "gRPC", ":50051", true)
    56  	cmd.Flags().StringSliceVar(&config.PresharedSecureKey, PresharedKeyFlag, []string{}, "preshared key(s) to require for authenticated requests")
    57  	cmd.Flags().DurationVar(&config.ShutdownGracePeriod, "grpc-shutdown-grace-period", 0*time.Second, "amount of time after receiving sigint to continue serving")
    58  	if err := cmd.MarkFlagRequired(PresharedKeyFlag); err != nil {
    59  		return fmt.Errorf("failed to mark flag as required: %w", err)
    60  	}
    61  
    62  	// Flags for the datastore
    63  	if err := datastore.RegisterDatastoreFlags(cmd, &config.DatastoreConfig); err != nil {
    64  		return err
    65  	}
    66  
    67  	// Flags for configuring the API usage of the datastore
    68  	cmd.Flags().Uint64Var(&config.MaxDatastoreReadPageSize, "max-datastore-read-page-size", 1_000, "limit on the maximum page size that we will load into memory from the datastore at one time")
    69  
    70  	// Flags for the namespace cache
    71  	cmd.Flags().Duration("ns-cache-expiration", 1*time.Minute, "amount of time a namespace entry should remain cached")
    72  	if err := cmd.Flags().MarkHidden("ns-cache-expiration"); err != nil {
    73  		return fmt.Errorf("failed to mark flag as hidden: %w", err)
    74  	}
    75  	server.RegisterCacheFlags(cmd.Flags(), "ns-cache", &config.NamespaceCacheConfig, namespaceCacheDefaults)
    76  
    77  	cmd.Flags().BoolVar(&config.EnableExperimentalWatchableSchemaCache, "enable-experimental-watchable-schema-cache", false, "enables the experimental schema cache which makes use of the Watch API for automatic updates")
    78  	cmd.Flags().DurationVar(&config.SchemaWatchHeartbeat, "datastore-schema-watch-heartbeat", 1*time.Second, "heartbeat time on the schema watch in the datastore (if supported). 0 means to default to the datastore's minimum.")
    79  
    80  	// Flags for parsing and validating schemas.
    81  	cmd.Flags().BoolVar(&config.SchemaPrefixesRequired, "schema-prefixes-required", false, "require prefixes on all object definitions in schemas")
    82  
    83  	// Flags for HTTP gateway
    84  	util.RegisterHTTPServerFlags(cmd.Flags(), &config.HTTPGateway, "http", "gateway", ":8443", false)
    85  	cmd.Flags().StringVar(&config.HTTPGatewayUpstreamAddr, "http-upstream-override-addr", "", "Override the upstream to point to a different gRPC server")
    86  	if err := cmd.Flags().MarkHidden("http-upstream-override-addr"); err != nil {
    87  		return fmt.Errorf("failed to mark flag as hidden: %w", err)
    88  	}
    89  	cmd.Flags().StringVar(&config.HTTPGatewayUpstreamTLSCertPath, "http-upstream-override-tls-cert-path", "", "Override the upstream TLS certificate")
    90  	if err := cmd.Flags().MarkHidden("http-upstream-override-tls-cert-path"); err != nil {
    91  		return fmt.Errorf("failed to mark flag as hidden: %w", err)
    92  	}
    93  	cmd.Flags().BoolVar(&config.HTTPGatewayCorsEnabled, "http-cors-enabled", false, "DANGEROUS: Enable CORS on the http gateway")
    94  	if err := cmd.Flags().MarkHidden("http-cors-enabled"); err != nil {
    95  		return fmt.Errorf("failed to mark flag as hidden: %w", err)
    96  	}
    97  	cmd.Flags().StringSliceVar(&config.HTTPGatewayCorsAllowedOrigins, "http-cors-allowed-origins", []string{"*"}, "Set CORS allowed origins for http gateway, defaults to all origins")
    98  	if err := cmd.Flags().MarkHidden("http-cors-allowed-origins"); err != nil {
    99  		return fmt.Errorf("failed to mark flag as hidden: %w", err)
   100  	}
   101  
   102  	// Flags for configuring the dispatch server
   103  	util.RegisterGRPCServerFlags(cmd.Flags(), &config.DispatchServer, "dispatch-cluster", "dispatch", ":50053", false)
   104  	server.RegisterCacheFlags(cmd.Flags(), "dispatch-cache", &config.DispatchCacheConfig, dispatchCacheDefaults)
   105  	server.RegisterCacheFlags(cmd.Flags(), "dispatch-cluster-cache", &config.ClusterDispatchCacheConfig, dispatchClusterCacheDefaults)
   106  
   107  	// Flags for configuring dispatch requests
   108  	cmd.Flags().Uint32Var(&config.DispatchMaxDepth, "dispatch-max-depth", 50, "maximum recursion depth for nested calls")
   109  	cmd.Flags().StringVar(&config.DispatchUpstreamAddr, "dispatch-upstream-addr", "", "upstream grpc address to dispatch to")
   110  	cmd.Flags().StringVar(&config.DispatchUpstreamCAPath, "dispatch-upstream-ca-path", "", "local path to the TLS CA used when connecting to the dispatch cluster")
   111  	cmd.Flags().DurationVar(&config.DispatchUpstreamTimeout, "dispatch-upstream-timeout", 60*time.Second, "maximum duration of a dispatch call an upstream cluster before it times out")
   112  
   113  	cmd.Flags().Uint16Var(&config.GlobalDispatchConcurrencyLimit, "dispatch-concurrency-limit", 50, "maximum number of parallel goroutines to create for each request or subrequest")
   114  
   115  	cmd.Flags().Uint16Var(&config.DispatchConcurrencyLimits.Check, "dispatch-check-permission-concurrency-limit", 0, "maximum number of parallel goroutines to create for each check request or subrequest. defaults to --dispatch-concurrency-limit")
   116  	cmd.Flags().Uint16Var(&config.DispatchConcurrencyLimits.LookupResources, "dispatch-lookup-resources-concurrency-limit", 0, "maximum number of parallel goroutines to create for each lookup resources request or subrequest. defaults to --dispatch-concurrency-limit")
   117  	cmd.Flags().Uint16Var(&config.DispatchConcurrencyLimits.LookupSubjects, "dispatch-lookup-subjects-concurrency-limit", 0, "maximum number of parallel goroutines to create for each lookup subjects request or subrequest. defaults to --dispatch-concurrency-limit")
   118  	cmd.Flags().Uint16Var(&config.DispatchConcurrencyLimits.ReachableResources, "dispatch-reachable-resources-concurrency-limit", 0, "maximum number of parallel goroutines to create for each reachable resources request or subrequest. defaults to --dispatch-concurrency-limit")
   119  
   120  	cmd.Flags().Uint16Var(&config.DispatchHashringReplicationFactor, "dispatch-hashring-replication-factor", 100, "set the replication factor of the consistent hasher used for the dispatcher")
   121  	cmd.Flags().Uint8Var(&config.DispatchHashringSpread, "dispatch-hashring-spread", 1, "set the spread of the consistent hasher used for the dispatcher")
   122  
   123  	cmd.Flags().StringToStringVar(&config.DispatchSecondaryUpstreamAddrs, "experimental-dispatch-secondary-upstream-addrs", nil, "secondary upstream addresses for dispatches, each with a name")
   124  	cmd.Flags().StringToStringVar(&config.DispatchSecondaryUpstreamExprs, "experimental-dispatch-secondary-upstream-exprs", nil, "map from request type (currently supported: `check`) to its associated CEL expression, which returns the secondary upstream(s) to be used for the request")
   125  
   126  	// Flags for configuring API behavior
   127  	cmd.Flags().BoolVar(&config.DisableV1SchemaAPI, "disable-v1-schema-api", false, "disables the V1 schema API")
   128  	cmd.Flags().BoolVar(&config.DisableVersionResponse, "disable-version-response", false, "disables version response support in the API")
   129  	cmd.Flags().Uint16Var(&config.MaximumUpdatesPerWrite, "write-relationships-max-updates-per-call", 1000, "maximum number of updates allowed for WriteRelationships calls")
   130  	cmd.Flags().Uint16Var(&config.MaximumPreconditionCount, "update-relationships-max-preconditions-per-call", 1000, "maximum number of preconditions allowed for WriteRelationships and DeleteRelationships calls")
   131  	cmd.Flags().IntVar(&config.MaxCaveatContextSize, "max-caveat-context-size", 4096, "maximum allowed size of request caveat context in bytes. A value of zero or less means no limit")
   132  	cmd.Flags().IntVar(&config.MaxRelationshipContextSize, "max-relationship-context-size", 25000, "maximum allowed size of the context to be stored in a relationship")
   133  	cmd.Flags().DurationVar(&config.StreamingAPITimeout, "streaming-api-response-delay-timeout", 30*time.Second, "max duration time elapsed between messages sent by the server-side to the client (responses) before the stream times out")
   134  	cmd.Flags().DurationVar(&config.WatchHeartbeat, "watch-api-heartbeat", 1*time.Second, "heartbeat time on the watch in the API. 0 means to default to the datastore's minimum.")
   135  
   136  	cmd.Flags().Uint32Var(&config.MaxReadRelationshipsLimit, "max-read-relationships-limit", 1000, "maximum number of relationships that can be read in a single request")
   137  	cmd.Flags().Uint32Var(&config.MaxDeleteRelationshipsLimit, "max-delete-relationships-limit", 1000, "maximum number of relationships that can be deleted in a single request")
   138  	cmd.Flags().Uint32Var(&config.MaxLookupResourcesLimit, "max-lookup-resources-limit", 1000, "maximum number of resources that can be looked up in a single request")
   139  	cmd.Flags().Uint32Var(&config.MaxBulkExportRelationshipsLimit, "max-bulk-export-relationships-limit", 10_000, "maximum number of relationships that can be exported in a single request")
   140  
   141  	cmd.Flags().BoolVar(&config.V1SchemaAdditiveOnly, "testing-only-schema-additive-writes", false, "append new definitions to the existing schema, rather than overwriting it")
   142  	if err := cmd.Flags().MarkHidden("testing-only-schema-additive-writes"); err != nil {
   143  		return fmt.Errorf("failed to mark flag as required: %w", err)
   144  	}
   145  
   146  	// Flags for misc services
   147  	util.RegisterHTTPServerFlags(cmd.Flags(), &config.MetricsAPI, "metrics", "metrics", ":9090", true)
   148  
   149  	if err := util.RegisterDeprecatedHTTPServerFlags(cmd, "dashboard", "dashboard"); err != nil {
   150  		return err
   151  	}
   152  
   153  	// Flags for telemetry
   154  	cmd.Flags().StringVar(&config.TelemetryEndpoint, "telemetry-endpoint", telemetry.DefaultEndpoint, "endpoint to which telemetry is reported, empty string to disable")
   155  	cmd.Flags().StringVar(&config.TelemetryCAOverridePath, "telemetry-ca-override-path", "", "TODO")
   156  	cmd.Flags().DurationVar(&config.TelemetryInterval, "telemetry-interval", telemetry.DefaultInterval, "approximate period between telemetry reports, minimum 1 minute")
   157  
   158  	return nil
   159  }
   160  
   161  func NewServeCommand(programName string, config *server.Config) *cobra.Command {
   162  	return &cobra.Command{
   163  		Use:     "serve",
   164  		Short:   "serve the permissions database",
   165  		Long:    "A database that stores, computes, and validates application permissions",
   166  		PreRunE: server.DefaultPreRunE(programName),
   167  		RunE: termination.PublishError(func(cmd *cobra.Command, args []string) error {
   168  			server, err := config.Complete(cmd.Context())
   169  			if err != nil {
   170  				return err
   171  			}
   172  			signalctx := SignalContextWithGracePeriod(
   173  				context.Background(),
   174  				config.ShutdownGracePeriod,
   175  			)
   176  			return server.Run(signalctx)
   177  		}),
   178  		Example: server.ServeExample(programName),
   179  	}
   180  }