Skip to content

Commit

Permalink
pkg/cmd: add serve package
Browse files Browse the repository at this point in the history
  • Loading branch information
jzelinskie committed Dec 8, 2021
1 parent a27ae2c commit 8592b71
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 92 deletions.
49 changes: 19 additions & 30 deletions cmd/spicedb/main.go
Expand Up @@ -2,21 +2,17 @@ package main

import (
"math/rand"
"net/http"
"net/http/pprof"
"time"

"github.com/cespare/xxhash"
"github.com/jzelinskie/cobrautil"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog"
"github.com/sercand/kuberesolver/v3"
"google.golang.org/grpc/balancer"

consistentbalancer "github.com/authzed/spicedb/pkg/balancer"
"github.com/authzed/spicedb/pkg/cmd/devsvc"
cmdutil "github.com/authzed/spicedb/pkg/cmd"
"github.com/authzed/spicedb/pkg/cmd/migrate"
"github.com/authzed/spicedb/pkg/cmd/root"
"github.com/authzed/spicedb/pkg/cmd/serve"
"github.com/authzed/spicedb/pkg/cmd/version"
)

Expand All @@ -25,23 +21,6 @@ const (
backendsPerKey = 1
)

var defaultPreRunE = cobrautil.CommandStack(
cobrautil.SyncViperPreRunE("spicedb"),
cobrautil.ZeroLogPreRunE("log", zerolog.InfoLevel),
cobrautil.OpenTelemetryPreRunE("otel", zerolog.InfoLevel),
)

func metricsHandler() http.Handler {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
return mux
}

func main() {
// Set up a seed for randomness
rand.Seed(time.Now().UnixNano())
Expand All @@ -50,7 +29,11 @@ func main() {
kuberesolver.RegisterInCluster()

// Enable consistent hashring gRPC load balancer
balancer.Register(consistentbalancer.NewConsistentHashringBuilder(xxhash.Sum64, hashringReplicationFactor, backendsPerKey))
balancer.Register(consistentbalancer.NewConsistentHashringBuilder(
xxhash.Sum64,
hashringReplicationFactor,
backendsPerKey,
))

// Create a root command
rootCmd := root.NewCommand()
Expand All @@ -71,12 +54,18 @@ func main() {
rootCmd.AddCommand(headCmd)

// Add server commands
devSvcCmd := devsvc.NewCommand(rootCmd.Use)
devsvc.RegisterFlags(devSvcCmd)
rootCmd.AddCommand(devSvcCmd)

registerServeCmd(rootCmd)
registerTestserverCmd(rootCmd)
var dsConfig cmdutil.DatastoreConfig
serveCmd := serve.NewServeCommand(rootCmd.Use, &dsConfig)
serve.RegisterServeFlags(serveCmd, &dsConfig)
rootCmd.AddCommand(serveCmd)

devtoolsCmd := serve.NewDevtoolsCommand(rootCmd.Use)
serve.RegisterDevtoolsFlags(devtoolsCmd)
rootCmd.AddCommand(devtoolsCmd)

testingCmd := serve.NewTestingCommand(rootCmd.Use)
serve.RegisterTestingFlags(testingCmd)
rootCmd.AddCommand(testingCmd)

_ = rootCmd.Execute()
}
File renamed without changes.
6 changes: 3 additions & 3 deletions pkg/cmd/devsvc/devsvc.go → pkg/cmd/serve/devtools.go
@@ -1,4 +1,4 @@
package devsvc
package serve

import (
"context"
Expand Down Expand Up @@ -28,7 +28,7 @@ import (
defaults "github.com/authzed/spicedb/pkg/cmd"
)

func RegisterFlags(cmd *cobra.Command) {
func RegisterDevtoolsFlags(cmd *cobra.Command) {
cobrautil.RegisterGrpcServerFlags(cmd.Flags(), "grpc", "gRPC", ":50051", true)
cobrautil.RegisterHttpServerFlags(cmd.Flags(), "metrics", "metrics", ":9090", true)
cobrautil.RegisterHttpServerFlags(cmd.Flags(), "http", "download", ":8443", false)
Expand All @@ -42,7 +42,7 @@ func RegisterFlags(cmd *cobra.Command) {
cmd.Flags().String("s3-region", "auto", "s3 region for s3 share store")
}

func NewCommand(programName string) *cobra.Command {
func NewDevtoolsCommand(programName string) *cobra.Command {
return &cobra.Command{
Use: "serve-devtools",
Short: "runs the developer tools service",
Expand Down
95 changes: 46 additions & 49 deletions cmd/spicedb/serve.go → pkg/cmd/serve/serve.go
@@ -1,4 +1,4 @@
package main
package serve

import (
"context"
Expand Down Expand Up @@ -29,87 +29,84 @@ import (
"github.com/authzed/spicedb/internal/namespace"
"github.com/authzed/spicedb/internal/services"
v1alpha1svc "github.com/authzed/spicedb/internal/services/v1alpha1"
cmdlib "github.com/authzed/spicedb/pkg/cmd"
defaults "github.com/authzed/spicedb/pkg/cmd"
logmw "github.com/authzed/spicedb/pkg/middleware/logging"
"github.com/authzed/spicedb/pkg/middleware/requestid"
"github.com/authzed/spicedb/pkg/validationfile"
)

func registerServeCmd(rootCmd *cobra.Command) {
var datastoreOptions cmdlib.DatastoreConfig
serveCmd := &cobra.Command{
Use: "serve",
Short: "serve the permissions database",
Long: "A database that stores, computes, and validates application permissions",
PreRunE: defaultPreRunE,
Run: func(cmd *cobra.Command, args []string) {
serveRun(cmd, args, datastoreOptions)
},
Example: fmt.Sprintf(` %s:
spicedb serve --grpc-preshared-key "somerandomkeyhere"
%s:
spicedb serve --grpc-preshared-key "realkeyhere" --grpc-tls-cert-path path/to/tls/cert --grpc-tls-key-path path/to/tls/key \
--http-tls-cert-path path/to/tls/cert --http-tls-key-path path/to/tls/key \
--datastore-engine postgres --datastore-conn-uri "postgres-connection-string-here"
`, color.YellowString("No TLS and in-memory"), color.GreenString("TLS and a real datastore")),
}

func RegisterServeFlags(cmd *cobra.Command, dsConfig *defaults.DatastoreConfig) {
// Flags for the gRPC API server
cobrautil.RegisterGrpcServerFlags(serveCmd.Flags(), "grpc", "gRPC", ":50051", true)
serveCmd.Flags().String("grpc-preshared-key", "", "preshared key to require for authenticated requests")
serveCmd.Flags().Duration("grpc-shutdown-grace-period", 0*time.Second, "amount of time after receiving sigint to continue serving")
if err := serveCmd.MarkFlagRequired("grpc-preshared-key"); err != nil {
cobrautil.RegisterGrpcServerFlags(cmd.Flags(), "grpc", "gRPC", ":50051", true)
cmd.Flags().String("grpc-preshared-key", "", "preshared key to require for authenticated requests")
cmd.Flags().Duration("grpc-shutdown-grace-period", 0*time.Second, "amount of time after receiving sigint to continue serving")
if err := cmd.MarkFlagRequired("grpc-preshared-key"); err != nil {
panic("failed to mark flag as required: " + err.Error())
}

// Flags for the datastore
cmdlib.RegisterDatastoreFlags(serveCmd, &datastoreOptions)
serveCmd.Flags().Bool("datastore-readonly", false, "set the service to read-only mode")
serveCmd.Flags().StringSlice("datastore-bootstrap-files", []string{}, "bootstrap data yaml files to load")
serveCmd.Flags().Bool("datastore-bootstrap-overwrite", false, "overwrite any existing data with bootstrap data")
defaults.RegisterDatastoreFlags(cmd, dsConfig)
cmd.Flags().Bool("datastore-readonly", false, "set the service to read-only mode")
cmd.Flags().StringSlice("datastore-bootstrap-files", []string{}, "bootstrap data yaml files to load")
cmd.Flags().Bool("datastore-bootstrap-overwrite", false, "overwrite any existing data with bootstrap data")

serveCmd.Flags().Bool("datastore-request-hedging", true, "enable request hedging")
serveCmd.Flags().Duration("datastore-request-hedging-initial-slow-value", 10*time.Millisecond, "initial value to use for slow datastore requests, before statistics have been collected")
serveCmd.Flags().Uint64("datastore-request-hedging-max-requests", 1_000_000, "maximum number of historical requests to consider")
serveCmd.Flags().Float64("datastore-request-hedging-quantile", 0.95, "quantile of historical datastore request time over which a request will be considered slow")
cmd.Flags().Bool("datastore-request-hedging", true, "enable request hedging")
cmd.Flags().Duration("datastore-request-hedging-initial-slow-value", 10*time.Millisecond, "initial value to use for slow datastore requests, before statistics have been collected")
cmd.Flags().Uint64("datastore-request-hedging-max-requests", 1_000_000, "maximum number of historical requests to consider")
cmd.Flags().Float64("datastore-request-hedging-quantile", 0.95, "quantile of historical datastore request time over which a request will be considered slow")

// Flags for the namespace manager
serveCmd.Flags().Duration("ns-cache-expiration", 1*time.Minute, "amount of time a namespace entry should remain cached")
cmd.Flags().Duration("ns-cache-expiration", 1*time.Minute, "amount of time a namespace entry should remain cached")

// Flags for parsing and validating schemas.
serveCmd.Flags().Bool("schema-prefixes-required", false, "require prefixes on all object definitions in schemas")
cmd.Flags().Bool("schema-prefixes-required", false, "require prefixes on all object definitions in schemas")

// Flags for HTTP gateway
cobrautil.RegisterHttpServerFlags(serveCmd.Flags(), "http", "http", ":8443", false)
cobrautil.RegisterHttpServerFlags(cmd.Flags(), "http", "http", ":8443", false)

// Flags for configuring the dispatch server
cobrautil.RegisterGrpcServerFlags(serveCmd.Flags(), "dispatch-cluster", "dispatch", ":50053", false)
cobrautil.RegisterGrpcServerFlags(cmd.Flags(), "dispatch-cluster", "dispatch", ":50053", false)

// Flags for configuring dispatch requests
serveCmd.Flags().Uint32("dispatch-max-depth", 50, "maximum recursion depth for nested calls")
serveCmd.Flags().String("dispatch-upstream-addr", "", "upstream grpc address to dispatch to")
serveCmd.Flags().String("dispatch-upstream-ca-path", "", "local path to the TLS CA used when connecting to the dispatch cluster")
cmd.Flags().Uint32("dispatch-max-depth", 50, "maximum recursion depth for nested calls")
cmd.Flags().String("dispatch-upstream-addr", "", "upstream grpc address to dispatch to")
cmd.Flags().String("dispatch-upstream-ca-path", "", "local path to the TLS CA used when connecting to the dispatch cluster")

// Flags for configuring API behavior
serveCmd.Flags().Bool("disable-v1-schema-api", false, "disables the V1 schema API")
cmd.Flags().Bool("disable-v1-schema-api", false, "disables the V1 schema API")

// Flags for misc services
cobrautil.RegisterHttpServerFlags(serveCmd.Flags(), "dashboard", "dashboard", ":8080", true)
cobrautil.RegisterHttpServerFlags(serveCmd.Flags(), "metrics", "metrics", ":9090", true)
cobrautil.RegisterHttpServerFlags(cmd.Flags(), "dashboard", "dashboard", ":8080", true)
cobrautil.RegisterHttpServerFlags(cmd.Flags(), "metrics", "metrics", ":9090", true)
}

// Required flags.
func NewServeCommand(programName string, dsConfig *defaults.DatastoreConfig) *cobra.Command {
return &cobra.Command{
Use: "serve",
Short: "serve the permissions database",
Long: "A database that stores, computes, and validates application permissions",
PreRunE: defaults.DefaultPreRunE(programName),
Run: func(cmd *cobra.Command, args []string) {
serveRun(cmd, args, dsConfig)
},
Example: fmt.Sprintf(` %s:
spicedb serve --grpc-preshared-key "somerandomkeyhere"
rootCmd.AddCommand(serveCmd)
%s:
spicedb serve --grpc-preshared-key "realkeyhere" --grpc-tls-cert-path path/to/tls/cert --grpc-tls-key-path path/to/tls/key \
--http-tls-cert-path path/to/tls/cert --http-tls-key-path path/to/tls/key \
--datastore-engine postgres --datastore-conn-uri "postgres-connection-string-here"
`, color.YellowString("No TLS and in-memory"), color.GreenString("TLS and a real datastore")),
}
}

func serveRun(cmd *cobra.Command, args []string, datastoreOpts cmdlib.DatastoreConfig) {
func serveRun(cmd *cobra.Command, args []string, datastoreOpts *defaults.DatastoreConfig) {
token := cobrautil.MustGetStringExpanded(cmd, "grpc-preshared-key")
if len(token) < 1 {
log.Fatal().Msg("a preshared key must be provided via --grpc-preshared-key to authenticate API requests")
}

ds, err := cmdlib.NewDatastore(datastoreOpts.ToOption())
ds, err := defaults.NewDatastore(datastoreOpts.ToOption())
if err != nil {
log.Fatal().Err(err).Msg("failed to init datastore")
}
Expand Down Expand Up @@ -259,7 +256,7 @@ func serveRun(cmd *cobra.Command, args []string, datastoreOpts cmdlib.DatastoreC

// Start the metrics endpoint.
metricsSrv := cobrautil.HttpServerFromFlags(cmd, "metrics")
metricsSrv.Handler = metricsHandler()
metricsSrv.Handler = defaults.MetricsHandler()
go func() {
if err := cobrautil.HttpListenFromFlags(cmd, "metrics", metricsSrv, zerolog.InfoLevel); err != nil {
log.Fatal().Err(err).Msg("failed while serving metrics")
Expand Down
21 changes: 11 additions & 10 deletions cmd/spicedb/testserver.go → pkg/cmd/serve/testing.go
@@ -1,4 +1,4 @@
package main
package serve

import (
"context"
Expand Down Expand Up @@ -33,6 +33,7 @@ import (
"github.com/authzed/spicedb/internal/namespace"
"github.com/authzed/spicedb/internal/services"
v1alpha1svc "github.com/authzed/spicedb/internal/services/v1alpha1"
defaults "github.com/authzed/spicedb/pkg/cmd"
"github.com/authzed/spicedb/pkg/validationfile"
)

Expand All @@ -43,20 +44,20 @@ const (
revisionFuzzingDuration = 10 * time.Millisecond
)

func registerTestserverCmd(rootCmd *cobra.Command) {
testserveCmd := &cobra.Command{
func RegisterTestingFlags(cmd *cobra.Command) {
cobrautil.RegisterGrpcServerFlags(cmd.Flags(), "grpc", "gRPC", ":50051", true)
cobrautil.RegisterGrpcServerFlags(cmd.Flags(), "readonly-grpc", "read-only gRPC", ":50052", true)
cmd.Flags().StringSlice("load-configs", []string{}, "configuration yaml files to load")
}

func NewTestingCommand(programName string) *cobra.Command {
return &cobra.Command{
Use: "serve-testing",
Short: "test server with an in-memory datastore",
Long: "An in-memory spicedb server which serves completely isolated datastores per client-supplied auth token used.",
PreRunE: defaultPreRunE,
PreRunE: defaults.DefaultPreRunE(programName),
Run: runTestServer,
}

cobrautil.RegisterGrpcServerFlags(testserveCmd.Flags(), "grpc", "gRPC", ":50051", true)
cobrautil.RegisterGrpcServerFlags(testserveCmd.Flags(), "readonly-grpc", "read-only gRPC", ":50052", true)
testserveCmd.Flags().StringSlice("load-configs", []string{}, "configuration yaml files to load")

rootCmd.AddCommand(testserveCmd)
}

func runTestServer(cmd *cobra.Command, args []string) {
Expand Down

0 comments on commit 8592b71

Please sign in to comment.