diff --git a/internal/devserver/server.go b/internal/devserver/server.go index 5a3cf44c0..452f8b6dd 100644 --- a/internal/devserver/server.go +++ b/internal/devserver/server.go @@ -60,8 +60,28 @@ const ( ) type namespaceFeatureOverride struct { - setting dynamicconfig.NamespaceBoolSetting - enabled bool + setting dynamicconfig.NamespaceBoolSetting + enabled bool + feature string + designation featureDesignation +} + +type featureDesignation string + +const ( + featureDesignationExperimental featureDesignation = "Experimental" + featureDesignationPreRelease featureDesignation = "Pre-release" + featureDesignationPublicPreview featureDesignation = "Public preview" +) + +// StartDevFeatureOverride describes feature-specific dynamic config shown in the +// `temporal server start-dev` banner. +type StartDevFeatureOverride struct { + Feature string + Designation string + Key string + Value any + UserConfigured bool } // startDevServerFeatureOverrides contains temporary feature-specific dynamic config for @@ -72,8 +92,40 @@ type namespaceFeatureOverride struct { // These values are applied without constraints and therefore affect every namespace. // NamespaceBoolSetting describes the server setting's lookup precedence, not override scope. var startDevServerFeatureOverrides = []namespaceFeatureOverride{ - {setting: activity.EnableStandaloneActivityOperatorCommands, enabled: true}, - {setting: dynamicconfig.FrontendEnableBatchOperationsForStandaloneActivities, enabled: true}, + { + setting: activity.EnableStandaloneActivityOperatorCommands, + enabled: true, + feature: "Standalone Activity operator commands", + designation: featureDesignationPublicPreview, + }, + { + setting: dynamicconfig.FrontendEnableBatchOperationsForStandaloneActivities, + enabled: true, + feature: "Standalone Activity batch operations", + designation: featureDesignationPublicPreview, + }, +} + +// StartDevFeatureOverrides returns the effective feature-specific configuration for display. +func (s *StartOptions) StartDevFeatureOverrides() []StartDevFeatureOverride { + overrides := make([]StartDevFeatureOverride, 0, len(startDevServerFeatureOverrides)) + for _, override := range startDevServerFeatureOverrides { + key := override.setting.Key().String() + value := any(override.enabled) + userConfigured := false + if configuredValue, ok := s.DynamicConfigValues[key]; ok { + value = configuredValue + userConfigured = true + } + overrides = append(overrides, StartDevFeatureOverride{ + Feature: override.feature, + Designation: string(override.designation), + Key: key, + Value: value, + UserConfigured: userConfigured, + }) + } + return overrides } type StartOptions struct { diff --git a/internal/devserver/server_test.go b/internal/devserver/server_test.go index 4b47950b3..d6d3dbed2 100644 --- a/internal/devserver/server_test.go +++ b/internal/devserver/server_test.go @@ -11,6 +11,14 @@ func TestStartDevServerFeatureOverridesMatchServerDefaults(t *testing.T) { for _, override := range startDevServerFeatureOverrides { key := override.setting.Key().String() t.Run(key, func(t *testing.T) { + if override.feature == "" { + t.Fatal("feature name must not be empty") + } + switch override.designation { + case featureDesignationExperimental, featureDesignationPreRelease, featureDesignationPublicPreview: + default: + t.Fatalf("invalid feature designation %q", override.designation) + } if serverDefault := override.setting.Get(dc)("default"); serverDefault == override.enabled { t.Fatalf("%q now defaults to %t; remove its feature override", key, override.enabled) } diff --git a/internal/temporalcli/commands.server.go b/internal/temporalcli/commands.server.go index d0aa7d97a..7f9701511 100644 --- a/internal/temporalcli/commands.server.go +++ b/internal/temporalcli/commands.server.go @@ -175,6 +175,15 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string) cctx.Printer.Printlnf("%-21s http://%v:%v%v", "Temporal UI:", toFriendlyIp(opts.UIIP), opts.UIPort, opts.PublicPath) } cctx.Printer.Printlnf("%-21s http://%v:%v/metrics", "Temporal Metrics:", toFriendlyIp(opts.FrontendIP), opts.MetricsPort) + cctx.Printer.Printlnf("Temporal Features:") + for _, override := range opts.StartDevFeatureOverrides() { + source := "built-in" + if override.UserConfigured { + source = "user override" + } + cctx.Printer.Printlnf(" %-14s %-37s %s=%v (%s)", + override.Designation, override.Feature, override.Key, override.Value, source) + } <-cctx.Done() if !t.Parent.Parent.LogLevel.ChangedFromDefault { // The server routinely emits various warnings on shutdown. diff --git a/internal/temporalcli/commands.server_test.go b/internal/temporalcli/commands.server_test.go index be7e1df62..7b4114853 100644 --- a/internal/temporalcli/commands.server_test.go +++ b/internal/temporalcli/commands.server_test.go @@ -19,6 +19,8 @@ import ( "go.temporal.io/api/operatorservice/v1" "go.temporal.io/sdk/client" "go.temporal.io/sdk/temporal" + serveractivity "go.temporal.io/server/chasm/lib/activity" + "go.temporal.io/server/common/dynamicconfig" ) // TODO(cretz): To test: @@ -328,7 +330,10 @@ func TestServer_StartDev_BannerPersistenceInMemory(t *testing.T) { httpPort := strconv.Itoa(devserver.MustGetFreePort("127.0.0.1")) resCh := make(chan *CommandResult, 1) go func() { - resCh <- h.Execute("server", "start-dev", "-p", port, "--http-port", httpPort, "--headless") + resCh <- h.Execute( + "server", "start-dev", "-p", port, "--http-port", httpPort, "--headless", + "--dynamic-config-value", serveractivity.EnableStandaloneActivityOperatorCommands.Key().String()+"=false", + ) }() // Wait until the server is dial-able, then cancel @@ -357,6 +362,10 @@ func TestServer_StartDev_BannerPersistenceInMemory(t *testing.T) { out := res.Stdout.String() h.Contains(out, "Temporal Persistence:") h.Contains(out, "in-memory") + h.Contains(out, "Temporal Features:") + h.Contains(out, "Public preview") + h.Contains(out, serveractivity.EnableStandaloneActivityOperatorCommands.Key().String()+"=false (user override)") + h.Contains(out, dynamicconfig.FrontendEnableBatchOperationsForStandaloneActivities.Key().String()+"=true (built-in)") } func TestServer_StartDev_BannerPersistenceFile(t *testing.T) {