Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganize serve flags into flagsets #2023

Merged
merged 14 commits into from
Aug 27, 2024

Conversation

tstirrat15
Copy link
Contributor

Description

The flags on spicedb serve --help are numerous. It makes parsing through them to figure out which ones are relevant and which ones you need pretty difficult. This is the first step towards addressing that - we organize the flags into flagsets which provides a bit of logical and visual grouping.

Changes

Gonna annotate.

Testing

Review. ./spicedb serve --help and see that all of the expected flags are present and that they are organized more nicely.

@tstirrat15 tstirrat15 requested a review from a team as a code owner August 15, 2024 00:04
@github-actions github-actions bot added the area/CLI Affects the command line label Aug 15, 2024
Copy link
Contributor Author

@tstirrat15 tstirrat15 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments

func RegisterDatastoreFlags(cmd *cobra.Command, opts *Config) error {
return RegisterDatastoreFlagsWithPrefix(cmd.Flags(), "", opts)
func RegisterDatastoreFlags(cmd *cobra.Command, flagset *pflag.FlagSet, opts *Config) error {
return RegisterDatastoreFlagsWithPrefix(flagset, "", opts)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in the flagset so we can get them properly grouped.

Comment on lines -25 to -33
ot := cobraotel.New(cmd.Use)
ot.RegisterFlags(cmd.PersistentFlags())
if err := ot.RegisterFlagCompletion(cmd); err != nil {
return fmt.Errorf("failed to register otel flag completion: %w", err)
}

releases.RegisterFlags(cmd.PersistentFlags())
termination.RegisterFlags(cmd.PersistentFlags())
runtime.RegisterFlags(cmd.PersistentFlags())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cribbing from #2022 - these flags are really more serve-specific than root flags, so we move them up into serve so that we can group them accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need these at least for migrate commands - maybe serve-testing too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... in #2022 they're moved into a function that registers them and then that function is reused. I can mimic that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and added them to migrate but I'm gonna push back on serve-testing for the time being.

pkg/cmd/serve.go Outdated
Comment on lines 58 to 66
grpcFlagSet := nfs.FlagSet("gRPC")
// Flags for logging
cmd.Flags().BoolVar(&config.EnableRequestLogs, "grpc-log-requests-enabled", false, "logs API request payloads")
cmd.Flags().BoolVar(&config.EnableResponseLogs, "grpc-log-responses-enabled", false, "logs API response payloads")
grpcFlagSet.BoolVar(&config.EnableRequestLogs, "grpc-log-requests-enabled", false, "logs API request payloads")
grpcFlagSet.BoolVar(&config.EnableResponseLogs, "grpc-log-responses-enabled", false, "logs API response payloads")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is basically the pattern you'll see - create a flagset on the flagsets with a name (and they're declared in the order they're displayed) and then attach a bunch of flags to the named flagset rather than the command's flagset.

pkg/cmd/serve.go Outdated
Comment on lines 95 to 96
// TODO: this one should maybe not live in API?
apiFlags.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")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't sure whether this should be api or datastore

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call this datastore since it affects how data is loaded from the backend

pkg/cmd/serve.go Outdated
Comment on lines 114 to 115
// TODO: should this be under datastore or api?
datastoreFlags.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.")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is datastore - this controls how spicedb pro-actively fills its cache with the schema from the backend

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this might fit better in the Namespace cache section, since it controls the automatic filling of that cache from Watch. But also that feature is still experimental 😓

Comment on lines +162 to +168
// TODO: these two could reasonably be put in either the Dispatch group or the Experimental group. Is there a preference?
experimentalFlags.StringToStringVar(&config.DispatchSecondaryUpstreamAddrs, "experimental-dispatch-secondary-upstream-addrs", nil, "secondary upstream addresses for dispatches, each with a name")
experimentalFlags.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")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question - I don't have a strong preference since once these are no longer experiments we'll put them in the right place anyway. I like what you've done here in grouping the experiments together.

@josephschorr or @jzelinskie sometimes have opinions about this kind of thing though.

pkg/cmd/serve.go Outdated
Comment on lines 166 to 174
observabilityFlags := nfs.FlagSet("Observability")
// Flags for observability and profiling
otel := cobraotel.New(cmd.Use)
otel.RegisterFlags(observabilityFlags)
runtime.RegisterFlags(observabilityFlags)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the flags that used to live in the root command landed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see my other comment - I think we at least need these on migrate, and maybe others as well

Copy link
Contributor

@ecordell ecordell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Much easier to scan flags now.

Screenshot 2024-08-15 at 9 22 50 AM

I noticed that our examples use colors (when supported) - I don't know how hard this would be, but it might be nice to add some color to the flagset headers so that gRPC Flags and HTTP Flags etc are different colors. If there's a simple way to do that I think it would help delineate the blocks even more than now (if it's a whole big project do make that happen don't worry about it).

@@ -159,8 +159,8 @@ type Config struct {
}

// RegisterDatastoreFlags adds datastore flags to a cobra command.
func RegisterDatastoreFlags(cmd *cobra.Command, opts *Config) error {
return RegisterDatastoreFlagsWithPrefix(cmd.Flags(), "", opts)
func RegisterDatastoreFlags(cmd *cobra.Command, flagset *pflag.FlagSet, opts *Config) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func RegisterDatastoreFlags(cmd *cobra.Command, flagset *pflag.FlagSet, opts *Config) error {
func RegisterDatastoreFlags(flagset *pflag.FlagSet, opts *Config) error {

cmd is unused now, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to nest flagsets? The datastore set is pretty big and I see some natural points we could break it up; i.e. connection pooling, bootstrapping, etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - the hierarchy seems to be cobra.Command -> nfs.NamedFlagSets -> nfs.FlagSet, and there isn't a recursive definition in there. I could add additional categories under datastore if you think that would help, though.

Comment on lines -25 to -33
ot := cobraotel.New(cmd.Use)
ot.RegisterFlags(cmd.PersistentFlags())
if err := ot.RegisterFlagCompletion(cmd); err != nil {
return fmt.Errorf("failed to register otel flag completion: %w", err)
}

releases.RegisterFlags(cmd.PersistentFlags())
termination.RegisterFlags(cmd.PersistentFlags())
runtime.RegisterFlags(cmd.PersistentFlags())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need these at least for migrate commands - maybe serve-testing too?

pkg/cmd/serve.go Outdated
Comment on lines 95 to 96
// TODO: this one should maybe not live in API?
apiFlags.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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call this datastore since it affects how data is loaded from the backend

pkg/cmd/serve.go Outdated
Comment on lines 114 to 115
// TODO: should this be under datastore or api?
datastoreFlags.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.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is datastore - this controls how spicedb pro-actively fills its cache with the schema from the backend

Comment on lines +162 to +168
// TODO: these two could reasonably be put in either the Dispatch group or the Experimental group. Is there a preference?
experimentalFlags.StringToStringVar(&config.DispatchSecondaryUpstreamAddrs, "experimental-dispatch-secondary-upstream-addrs", nil, "secondary upstream addresses for dispatches, each with a name")
experimentalFlags.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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question - I don't have a strong preference since once these are no longer experiments we'll put them in the right place anyway. I like what you've done here in grouping the experiments together.

@josephschorr or @jzelinskie sometimes have opinions about this kind of thing though.

apiFlags.Uint16Var(&config.MaximumPreconditionCount, "update-relationships-max-preconditions-per-call", 1000, "maximum number of preconditions allowed for WriteRelationships and DeleteRelationships calls")
// TODO: this one should maybe not live in API?
apiFlags.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")
apiFlags.BoolVar(&config.DisableV1SchemaAPI, "disable-v1-schema-api", false, "disables the V1 schema API")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can hide this flag, I don't think there's much utility for most users.

pkg/cmd/serve.go Outdated
Comment on lines 114 to 115
// TODO: should this be under datastore or api?
datastoreFlags.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.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this might fit better in the Namespace cache section, since it controls the automatic filling of that cache from Watch. But also that feature is still experimental 😓

pkg/cmd/serve.go Show resolved Hide resolved
pkg/cmd/serve.go Outdated Show resolved Hide resolved
util.RegisterGRPCServerFlags(cmd.Flags(), &config.DispatchServer, "dispatch-cluster", "dispatch", ":50053", false)
server.MustRegisterCacheFlags(cmd.Flags(), "dispatch-cache", &config.DispatchCacheConfig, dispatchCacheDefaults)
server.MustRegisterCacheFlags(cmd.Flags(), "dispatch-cluster-cache", &config.ClusterDispatchCacheConfig, dispatchClusterCacheDefaults)
util.RegisterGRPCServerFlags(dispatchFlags, &config.DispatchServer, "dispatch-cluster", "dispatch", ":50053", false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is good but I think we could further break down the dispatch flags if we wanted to: dispatch server, dispatch (client) cache, dispatch (server) cache, hashring config, etc.

@tstirrat15
Copy link
Contributor Author

@ecordell re: color: I think it's just on the far side of easy. I tried https://github.com/ivanpirog/coloredcobra and it isn't aware of flagsets, and I tried fatih/color on the flagset names but that only changes the label, not the entire string output:

gRPC Flags:
^ this is colored
      ^ this is not

I think it'd be possible to do it, but it'd require more manual intervention, so I'm gonna leave it for now and work on addressing your other comments.

cmd.Flags().StringVar(&config.TelemetryEndpoint, "telemetry-endpoint", telemetry.DefaultEndpoint, "endpoint to which telemetry is reported, empty string to disable")
cmd.Flags().StringVar(&config.TelemetryCAOverridePath, "telemetry-ca-override-path", "", "TODO")
cmd.Flags().DurationVar(&config.TelemetryInterval, "telemetry-interval", telemetry.DefaultInterval, "approximate period between telemetry reports, minimum 1 minute")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add this to the end in order for the flags to actually be registered with the command. This seems like an unintuitive DX for cobrautil:

nfs.AddFlagSets(cmd)

@tstirrat15 tstirrat15 force-pushed the 2022-reorganize-flags-into-flagsets branch from 2880c7f to 0e4105b Compare August 21, 2024 16:59
Copy link
Contributor

@ecordell ecordell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the changes to the otel flags are doing weird things:

On serve:

      --otel-service-name string       service name for trace data (default "serve")

Vs the previous:

      --otel-service-name string       service name for trace data (default "spicedb")

Also look at migrate:

      --otel-service-name string                     service name for trace data (default "migrate [revision]")

I also don't see --schema-prefixes-required in the new flagsets:

$ go run ./cmd/spicedb/... serve -h | grep prefixes

It was kind of tricky to review that the flags hadn't changed - I wonder if we should have a test that with a list of flags to confirm are in the output?

@tstirrat15 tstirrat15 added this pull request to the merge queue Aug 27, 2024
Merged via the queue into main with commit fd2978a Aug 27, 2024
22 checks passed
@tstirrat15 tstirrat15 deleted the 2022-reorganize-flags-into-flagsets branch August 27, 2024 21:20
@github-actions github-actions bot locked and limited conversation to collaborators Aug 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area/CLI Affects the command line
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants