diff --git a/pkg/api/featuremgmt.go b/pkg/api/featuremgmt.go index da34568366f..dbed785ae0a 100644 --- a/pkg/api/featuremgmt.go +++ b/pkg/api/featuremgmt.go @@ -98,7 +98,7 @@ func isFeatureHidden(flag featuremgmt.FeatureFlag, hideCfg map[string]struct{}) if _, ok := hideCfg[flag.Name]; ok { return true } - return flag.Stage == featuremgmt.FeatureStageUnknown || flag.Stage == featuremgmt.FeatureStageExperimental || flag.Stage == featuremgmt.FeatureStagePrivatePreview + return flag.Stage == featuremgmt.FeatureStageUnknown || flag.Stage == featuremgmt.FeatureStageExperimental || flag.Stage == featuremgmt.FeatureStagePrivatePreview || flag.HideFromAdminPage } // isFeatureWriteable returns whether a toggle on the admin page can be updated by the user. @@ -110,7 +110,8 @@ func isFeatureWriteable(flag featuremgmt.FeatureFlag, readOnlyCfg map[string]str if flag.Name == featuremgmt.FlagFeatureToggleAdminPage { return false } - return flag.Stage == featuremgmt.FeatureStageGeneralAvailability || flag.Stage == featuremgmt.FeatureStageDeprecated + allowSelfServe := flag.AllowSelfServe != nil && *flag.AllowSelfServe + return flag.Stage == featuremgmt.FeatureStageGeneralAvailability && allowSelfServe || flag.Stage == featuremgmt.FeatureStageDeprecated } // isFeatureEditingAllowed checks if the backend is properly configured to allow feature toggle changes from the UI diff --git a/pkg/api/featuremgmt_test.go b/pkg/api/featuremgmt_test.go index 120680dc78b..459f7dafa1b 100644 --- a/pkg/api/featuremgmt_test.go +++ b/pkg/api/featuremgmt_test.go @@ -20,6 +20,15 @@ import ( "github.com/stretchr/testify/require" ) +func boolPtr(b bool) *bool { + return &b +} + +var ( + truePtr = boolPtr(true) + falsePtr = boolPtr(false) +) + func TestGetFeatureToggles(t *testing.T) { readPermissions := []accesscontrol.Permission{{Action: accesscontrol.ActionFeatureManagementRead}} @@ -107,20 +116,27 @@ func TestGetFeatureToggles(t *testing.T) { Name: "toggle3", Stage: featuremgmt.FeatureStagePrivatePreview, }, { - Name: "toggle4", - Stage: featuremgmt.FeatureStagePublicPreview, + Name: "toggle4", + Stage: featuremgmt.FeatureStagePublicPreview, + AllowSelfServe: truePtr, }, { - Name: "toggle5", - Stage: featuremgmt.FeatureStageGeneralAvailability, + Name: "toggle5", + Stage: featuremgmt.FeatureStageGeneralAvailability, + AllowSelfServe: truePtr, }, { - Name: "toggle6", - Stage: featuremgmt.FeatureStageDeprecated, + Name: "toggle6", + Stage: featuremgmt.FeatureStageDeprecated, + AllowSelfServe: truePtr, + }, { + Name: "toggle7", + Stage: featuremgmt.FeatureStageGeneralAvailability, + AllowSelfServe: falsePtr, }, } t.Run("unknown, experimental, and private preview toggles are hidden by default", func(t *testing.T) { result := runGetScenario(t, features, setting.FeatureMgmtSettings{}, readPermissions, http.StatusOK) - assert.Len(t, result, 3) + assert.Len(t, result, 4) _, ok := findResult(t, result, "toggle1") assert.False(t, ok) @@ -130,13 +146,13 @@ func TestGetFeatureToggles(t *testing.T) { assert.False(t, ok) }) - t.Run("only public preview and GA are writeable by default", func(t *testing.T) { + t.Run("only public preview and GA with AllowSelfServe are writeable", func(t *testing.T) { settings := setting.FeatureMgmtSettings{ AllowEditing: true, UpdateWebhook: "bogus", } result := runGetScenario(t, features, settings, readPermissions, http.StatusOK) - assert.Len(t, result, 3) + assert.Len(t, result, 4) t4, ok := findResult(t, result, "toggle4") assert.True(t, ok) @@ -155,7 +171,7 @@ func TestGetFeatureToggles(t *testing.T) { UpdateWebhook: "", } result := runGetScenario(t, features, settings, readPermissions, http.StatusOK) - assert.Len(t, result, 3) + assert.Len(t, result, 4) t4, ok := findResult(t, result, "toggle4") assert.True(t, ok) @@ -305,13 +321,15 @@ func TestSetFeatureToggles(t *testing.T) { Enabled: false, Stage: featuremgmt.FeatureStageGeneralAvailability, }, { - Name: "toggle4", - Enabled: false, - Stage: featuremgmt.FeatureStageGeneralAvailability, + Name: "toggle4", + Enabled: false, + Stage: featuremgmt.FeatureStageGeneralAvailability, + AllowSelfServe: truePtr, }, { - Name: "toggle5", - Enabled: false, - Stage: featuremgmt.FeatureStageDeprecated, + Name: "toggle5", + Enabled: false, + Stage: featuremgmt.FeatureStageDeprecated, + AllowSelfServe: truePtr, }, } diff --git a/pkg/services/featuremgmt/models.go b/pkg/services/featuremgmt/models.go index f2aaa7b1c19..54bc36766a5 100644 --- a/pkg/services/featuremgmt/models.go +++ b/pkg/services/featuremgmt/models.go @@ -110,10 +110,12 @@ type FeatureFlag struct { // Special behavior flags RequiresDevMode bool `json:"requiresDevMode,omitempty"` // can not be enabled in production // This flag is currently unused. - RequiresRestart bool `json:"requiresRestart,omitempty"` // The server must be initialized with the value - RequiresLicense bool `json:"requiresLicense,omitempty"` // Must be enabled in the license - FrontendOnly bool `json:"frontend,omitempty"` // change is only seen in the frontend - HideFromDocs bool `json:"hideFromDocs,omitempty"` // don't add the values to docs + RequiresRestart bool `json:"requiresRestart,omitempty"` // The server must be initialized with the value + RequiresLicense bool `json:"requiresLicense,omitempty"` // Must be enabled in the license + FrontendOnly bool `json:"frontend,omitempty"` // change is only seen in the frontend + HideFromDocs bool `json:"hideFromDocs,omitempty"` // don't add the values to docs + HideFromAdminPage bool `json:"hideFromAdminPage,omitempty"` // don't display the feature in the admin page - add a comment with the reasoning + AllowSelfServe *bool `json:"allowSelfServe,omitempty"` // allow admin users to toggle the feature state from the admin page; this is required for GA toggles only // This field is only for the feature management API. To enable your feature toggle by default, use `Expression`. Enabled bool `json:"enabled,omitempty"` diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 4b8ab1b6221..02e4f0b7c0e 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -7,13 +7,16 @@ package featuremgmt var ( + falsePtr = boolPtr(false) + truePtr = boolPtr(true) // Register each toggle here standardFeatureFlags = []FeatureFlag{ { - Name: "disableEnvelopeEncryption", - Description: "Disable envelope encryption (emergency only)", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaAsCodeSquad, + Name: "disableEnvelopeEncryption", + Description: "Disable envelope encryption (emergency only)", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaAsCodeSquad, + AllowSelfServe: falsePtr, }, { Name: "live-service-web-worker", @@ -36,11 +39,12 @@ var ( Owner: grafanaAppPlatformSquad, }, { - Name: "publicDashboards", - Description: "Enables public access to dashboards", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaSharingSquad, - Expression: "true", // enabled by default + Name: "publicDashboards", + Description: "Enables public access to dashboards", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaSharingSquad, + Expression: "true", // enabled by default + AllowSelfServe: truePtr, }, { Name: "publicDashboardsEmailSharing", @@ -57,10 +61,11 @@ var ( Owner: grafanaObservabilityLogsSquad, }, { - Name: "featureHighlights", - Description: "Highlight Grafana Enterprise features", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaAsCodeSquad, + Name: "featureHighlights", + Description: "Highlight Grafana Enterprise features", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaAsCodeSquad, + AllowSelfServe: falsePtr, }, { Name: "migrationLocking", @@ -81,12 +86,13 @@ var ( Owner: grafanaExploreSquad, }, { - Name: "exploreContentOutline", - Description: "Content outline sidebar", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaExploreSquad, - Expression: "true", // enabled by default - FrontendOnly: true, + Name: "exploreContentOutline", + Description: "Content outline sidebar", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaExploreSquad, + Expression: "true", // enabled by default + FrontendOnly: true, + AllowSelfServe: falsePtr, }, { Name: "datasourceQueryMultiStatus", @@ -149,11 +155,12 @@ var ( Owner: hostedGrafanaTeam, }, { - Name: "dataConnectionsConsole", - Description: "Enables a new top-level page called Connections. This page is an experiment that provides a better experience when you install and configure data sources and other plugins.", - Stage: FeatureStageGeneralAvailability, - Expression: "true", // turned on by default - Owner: grafanaPluginsPlatformSquad, + Name: "dataConnectionsConsole", + Description: "Enables a new top-level page called Connections. This page is an experiment that provides a better experience when you install and configure data sources and other plugins.", + Stage: FeatureStageGeneralAvailability, + Expression: "true", // turned on by default + Owner: grafanaPluginsPlatformSquad, + AllowSelfServe: falsePtr, }, { // Some plugins rely on topnav feature flag being enabled, so we cannot remove this until we @@ -185,26 +192,29 @@ var ( Owner: grafanaAppPlatformSquad, }, { - Name: "cloudWatchCrossAccountQuerying", - Description: "Enables cross-account querying in CloudWatch datasources", - Stage: FeatureStageGeneralAvailability, - Expression: "true", // enabled by default - Owner: awsDatasourcesSquad, + Name: "cloudWatchCrossAccountQuerying", + Description: "Enables cross-account querying in CloudWatch datasources", + Stage: FeatureStageGeneralAvailability, + Expression: "true", // enabled by default + Owner: awsDatasourcesSquad, + AllowSelfServe: falsePtr, }, { - Name: "redshiftAsyncQueryDataSupport", - Description: "Enable async query data support for Redshift", - Stage: FeatureStageGeneralAvailability, - Expression: "true", // enabled by default - Owner: awsDatasourcesSquad, + Name: "redshiftAsyncQueryDataSupport", + Description: "Enable async query data support for Redshift", + Stage: FeatureStageGeneralAvailability, + Expression: "true", // enabled by default + Owner: awsDatasourcesSquad, + AllowSelfServe: falsePtr, }, { - Name: "athenaAsyncQueryDataSupport", - Description: "Enable async query data support for Athena", - Stage: FeatureStageGeneralAvailability, - Expression: "true", // enabled by default - FrontendOnly: true, - Owner: awsDatasourcesSquad, + Name: "athenaAsyncQueryDataSupport", + Description: "Enable async query data support for Athena", + Stage: FeatureStageGeneralAvailability, + Expression: "true", // enabled by default + FrontendOnly: true, + Owner: awsDatasourcesSquad, + AllowSelfServe: falsePtr, }, { Name: "cloudwatchNewRegionsHandler", @@ -237,32 +247,36 @@ var ( Owner: grafanaBackendPlatformSquad, }, { - Name: "nestedFolderPicker", - Description: "Enables the new folder picker to work with nested folders. Requires the nestedFolders feature toggle", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaFrontendPlatformSquad, - FrontendOnly: true, - Expression: "true", // enabled by default + Name: "nestedFolderPicker", + Description: "Enables the new folder picker to work with nested folders. Requires the nestedFolders feature toggle", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaFrontendPlatformSquad, + FrontendOnly: true, + Expression: "true", // enabled by default + AllowSelfServe: falsePtr, }, { - Name: "accessTokenExpirationCheck", - Description: "Enable OAuth access_token expiration check and token refresh using the refresh_token", - Stage: FeatureStageGeneralAvailability, - Owner: identityAccessTeam, + Name: "accessTokenExpirationCheck", + Description: "Enable OAuth access_token expiration check and token refresh using the refresh_token", + Stage: FeatureStageGeneralAvailability, + Owner: identityAccessTeam, + AllowSelfServe: falsePtr, }, { - Name: "emptyDashboardPage", - Description: "Enable the redesigned user interface of a dashboard page that includes no panels", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: grafanaDashboardsSquad, + Name: "emptyDashboardPage", + Description: "Enable the redesigned user interface of a dashboard page that includes no panels", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Expression: "true", // enabled by default + Owner: grafanaDashboardsSquad, + AllowSelfServe: falsePtr, }, { - Name: "disablePrometheusExemplarSampling", - Description: "Disable Prometheus exemplar sampling", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaObservabilityMetricsSquad, + Name: "disablePrometheusExemplarSampling", + Description: "Disable Prometheus exemplar sampling", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaObservabilityMetricsSquad, + AllowSelfServe: falsePtr, }, { Name: "alertingBacktesting", @@ -285,20 +299,22 @@ var ( Owner: grafanaAlertingSquad, }, { - Name: "logsContextDatasourceUi", - Description: "Allow datasource to provide custom UI for context view", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Owner: grafanaObservabilityLogsSquad, - Expression: "true", // turned on by default + Name: "logsContextDatasourceUi", + Description: "Allow datasource to provide custom UI for context view", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Owner: grafanaObservabilityLogsSquad, + Expression: "true", // turned on by default + AllowSelfServe: falsePtr, }, { - Name: "lokiQuerySplitting", - Description: "Split large interval queries into subqueries with smaller time intervals", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Owner: grafanaObservabilityLogsSquad, - Expression: "true", // turned on by default + Name: "lokiQuerySplitting", + Description: "Split large interval queries into subqueries with smaller time intervals", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Owner: grafanaObservabilityLogsSquad, + Expression: "true", // turned on by default + AllowSelfServe: falsePtr, }, { Name: "lokiQuerySplittingConfig", @@ -314,26 +330,29 @@ var ( Owner: grafanaBackendPlatformSquad, }, { - Name: "gcomOnlyExternalOrgRoleSync", - Description: "Prohibits a user from changing organization roles synced with Grafana Cloud auth provider", - Stage: FeatureStageGeneralAvailability, - Owner: identityAccessTeam, + Name: "gcomOnlyExternalOrgRoleSync", + Description: "Prohibits a user from changing organization roles synced with Grafana Cloud auth provider", + Stage: FeatureStageGeneralAvailability, + Owner: identityAccessTeam, + AllowSelfServe: falsePtr, }, { - Name: "prometheusMetricEncyclopedia", - Description: "Adds the metrics explorer component to the Prometheus query builder as an option in metric select", - Expression: "true", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Owner: grafanaObservabilityMetricsSquad, + Name: "prometheusMetricEncyclopedia", + Description: "Adds the metrics explorer component to the Prometheus query builder as an option in metric select", + Expression: "true", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Owner: grafanaObservabilityMetricsSquad, + AllowSelfServe: falsePtr, }, { - Name: "influxdbBackendMigration", - Description: "Query InfluxDB InfluxQL without the proxy", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Owner: grafanaObservabilityMetricsSquad, - Expression: "true", // enabled by default + Name: "influxdbBackendMigration", + Description: "Query InfluxDB InfluxQL without the proxy", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Owner: grafanaObservabilityMetricsSquad, + Expression: "true", // enabled by default + AllowSelfServe: falsePtr, }, { Name: "clientTokenRotation", @@ -342,18 +361,20 @@ var ( Owner: identityAccessTeam, }, { - Name: "prometheusDataplane", - Description: "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.", - Expression: "true", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaObservabilityMetricsSquad, + Name: "prometheusDataplane", + Description: "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.", + Expression: "true", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaObservabilityMetricsSquad, + AllowSelfServe: falsePtr, }, { - Name: "lokiMetricDataplane", - Description: "Changes metric responses from Loki to be compliant with the dataplane specification.", - Stage: FeatureStageGeneralAvailability, - Expression: "true", - Owner: grafanaObservabilityLogsSquad, + Name: "lokiMetricDataplane", + Description: "Changes metric responses from Loki to be compliant with the dataplane specification.", + Stage: FeatureStageGeneralAvailability, + Expression: "true", + Owner: grafanaObservabilityLogsSquad, + AllowSelfServe: falsePtr, }, { Name: "lokiLogsDataplane", @@ -362,12 +383,13 @@ var ( Owner: grafanaObservabilityLogsSquad, }, { - Name: "dataplaneFrontendFallback", - Description: "Support dataplane contract field name change for transformations and field name matchers where the name is different", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", - Owner: grafanaObservabilityMetricsSquad, + Name: "dataplaneFrontendFallback", + Description: "Support dataplane contract field name change for transformations and field name matchers where the name is different", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Expression: "true", + Owner: grafanaObservabilityMetricsSquad, + AllowSelfServe: falsePtr, }, { Name: "disableSSEDataplane", @@ -382,12 +404,13 @@ var ( Owner: grafanaAlertingSquad, }, { - Name: "alertingNotificationsPoliciesMatchingInstances", - Description: "Enables the preview of matching instances for notification policies", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: grafanaAlertingSquad, + Name: "alertingNotificationsPoliciesMatchingInstances", + Description: "Enables the preview of matching instances for notification policies", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Expression: "true", // enabled by default + Owner: grafanaAlertingSquad, + AllowSelfServe: falsePtr, }, { Name: "alertStateHistoryLokiPrimary", @@ -433,21 +456,24 @@ var ( Owner: grafanaOperatorExperienceSquad, RequiresRestart: true, Expression: "true", // enabled by default + AllowSelfServe: falsePtr, }, { - Name: "enableElasticsearchBackendQuerying", - Description: "Enable the processing of queries and responses in the Elasticsearch data source through backend", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaObservabilityLogsSquad, - Expression: "true", // enabled by default + Name: "enableElasticsearchBackendQuerying", + Description: "Enable the processing of queries and responses in the Elasticsearch data source through backend", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaObservabilityLogsSquad, + Expression: "true", // enabled by default + AllowSelfServe: falsePtr, }, { - Name: "advancedDataSourcePicker", - Description: "Enable a new data source picker with contextual information, recently used order and advanced mode", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: grafanaDashboardsSquad, + Name: "advancedDataSourcePicker", + Description: "Enable a new data source picker with contextual information, recently used order and advanced mode", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Expression: "true", // enabled by default + Owner: grafanaDashboardsSquad, + AllowSelfServe: falsePtr, }, { Name: "faroDatasourceSelector", @@ -520,12 +546,13 @@ var ( Owner: grafanaObservabilityLogsSquad, }, { - Name: "cloudWatchLogsMonacoEditor", - Description: "Enables the Monaco editor for CloudWatch Logs queries", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: awsDatasourcesSquad, + Name: "cloudWatchLogsMonacoEditor", + Description: "Enables the Monaco editor for CloudWatch Logs queries", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Expression: "true", // enabled by default + Owner: awsDatasourcesSquad, + AllowSelfServe: falsePtr, }, { Name: "exploreScrollableLogsContainer", @@ -535,11 +562,12 @@ var ( Owner: grafanaObservabilityLogsSquad, }, { - Name: "recordedQueriesMulti", - Description: "Enables writing multiple items from a single query within Recorded Queries", - Stage: FeatureStageGeneralAvailability, - Expression: "true", - Owner: grafanaObservabilityMetricsSquad, + Name: "recordedQueriesMulti", + Description: "Enables writing multiple items from a single query within Recorded Queries", + Stage: FeatureStageGeneralAvailability, + Expression: "true", + Owner: grafanaObservabilityMetricsSquad, + AllowSelfServe: falsePtr, }, { Name: "pluginsDynamicAngularDetectionPatterns", @@ -576,12 +604,13 @@ var ( Owner: awsDatasourcesSquad, }, { - Name: "transformationsRedesign", - Description: "Enables the transformations redesign", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: grafanaObservabilityMetricsSquad, + Name: "transformationsRedesign", + Description: "Enables the transformations redesign", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Expression: "true", // enabled by default + Owner: grafanaObservabilityMetricsSquad, + AllowSelfServe: falsePtr, }, { Name: "mlExpressions", @@ -641,11 +670,12 @@ var ( RequiresRestart: true, }, { - Name: "azureMonitorDataplane", - Description: "Adds dataplane compliant frame metadata in the Azure Monitor datasource", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaPartnerPluginsSquad, - Expression: "true", // on by default + Name: "azureMonitorDataplane", + Description: "Adds dataplane compliant frame metadata in the Azure Monitor datasource", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaPartnerPluginsSquad, + Expression: "true", // on by default + AllowSelfServe: falsePtr, }, { Name: "traceToProfiles", @@ -661,11 +691,12 @@ var ( Owner: grafanaBackendPlatformSquad, }, { - Name: "prometheusConfigOverhaulAuth", - Description: "Update the Prometheus configuration page with the new auth component", - Owner: grafanaObservabilityMetricsSquad, - Stage: FeatureStageGeneralAvailability, - Expression: "true", // on by default + Name: "prometheusConfigOverhaulAuth", + Description: "Update the Prometheus configuration page with the new auth component", + Owner: grafanaObservabilityMetricsSquad, + Stage: FeatureStageGeneralAvailability, + Expression: "true", // on by default + AllowSelfServe: falsePtr, }, { Name: "configurableSchedulerTick", @@ -701,12 +732,13 @@ var ( Owner: grafanaPluginsPlatformSquad, }, { - Name: "dashgpt", - Description: "Enable AI powered features in dashboards", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Owner: grafanaDashboardsSquad, - Expression: "true", // on by default + Name: "dashgpt", + Description: "Enable AI powered features in dashboards", + Stage: FeatureStageGeneralAvailability, + FrontendOnly: true, + Owner: grafanaDashboardsSquad, + Expression: "true", // on by default + AllowSelfServe: falsePtr, }, { Name: "reportingRetries", @@ -717,12 +749,13 @@ var ( RequiresRestart: true, }, { - Name: "newBrowseDashboards", - Description: "New browse/manage dashboards UI", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaFrontendPlatformSquad, - FrontendOnly: true, - Expression: "true", // on by default + Name: "newBrowseDashboards", + Description: "New browse/manage dashboards UI", + Stage: FeatureStageGeneralAvailability, + Owner: grafanaFrontendPlatformSquad, + FrontendOnly: true, + Expression: "true", // on by default + AllowSelfServe: falsePtr, }, { Name: "sseGroupByDatasource", @@ -760,12 +793,13 @@ var ( Owner: hostedGrafanaTeam, }, { - Name: "alertingInsights", - Description: "Show the new alerting insights landing page", - FrontendOnly: true, - Stage: FeatureStageGeneralAvailability, - Owner: grafanaAlertingSquad, - Expression: "true", // enabled by default + Name: "alertingInsights", + Description: "Show the new alerting insights landing page", + FrontendOnly: true, + Stage: FeatureStageGeneralAvailability, + Owner: grafanaAlertingSquad, + Expression: "true", // enabled by default + AllowSelfServe: falsePtr, }, { Name: "alertingContactPointsV2", @@ -803,11 +837,12 @@ var ( RequiresDevMode: true, }, { - Name: "cloudWatchWildCardDimensionValues", - Description: "Fetches dimension values from CloudWatch to correctly label wildcard dimensions", - Stage: FeatureStageGeneralAvailability, - Expression: "true", // enabled by default - Owner: awsDatasourcesSquad, + Name: "cloudWatchWildCardDimensionValues", + Description: "Fetches dimension values from CloudWatch to correctly label wildcard dimensions", + Stage: FeatureStageGeneralAvailability, + Expression: "true", // enabled by default + Owner: awsDatasourcesSquad, + AllowSelfServe: falsePtr, }, { Name: "externalServiceAccounts", @@ -991,3 +1026,7 @@ var ( }, } ) + +func boolPtr(b bool) *bool { + return &b +} diff --git a/pkg/services/featuremgmt/toggles_gen_test.go b/pkg/services/featuremgmt/toggles_gen_test.go index 5f7798195e6..7d950b048bc 100644 --- a/pkg/services/featuremgmt/toggles_gen_test.go +++ b/pkg/services/featuremgmt/toggles_gen_test.go @@ -45,6 +45,12 @@ func TestFeatureToggleFiles(t *testing.T) { if flag.Name != strings.TrimSpace(flag.Name) { t.Errorf("flag Name should not start/end with spaces. See: %s", flag.Name) } + if flag.Stage == FeatureStageGeneralAvailability && flag.AllowSelfServe == nil { + t.Errorf("feature stage FeatureStageGeneralAvailability should have the AllowSelfServe field defined") + } + if flag.AllowSelfServe != nil && flag.Stage != FeatureStageGeneralAvailability { + t.Errorf("only allow self-serving GA toggles") + } } })