mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 18:32:23 +08:00
FeatureFlags: manage creation/modification times automatically (#82131)
This commit is contained in:
@ -3,7 +3,7 @@ package featuretoggle
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -27,18 +27,16 @@ var (
|
||||
type featuresStorage struct {
|
||||
resource *common.ResourceInfo
|
||||
tableConverter rest.TableConvertor
|
||||
features []featuremgmt.FeatureFlag
|
||||
startup int64
|
||||
features *v0alpha1.FeatureList
|
||||
featuresOnce sync.Once
|
||||
}
|
||||
|
||||
// NOTE! this does not depend on config or any system state!
|
||||
// In the future, the existence of features (and their properties) can be defined dynamically
|
||||
func NewFeaturesStorage(features []featuremgmt.FeatureFlag) *featuresStorage {
|
||||
func NewFeaturesStorage() *featuresStorage {
|
||||
resourceInfo := v0alpha1.FeatureResourceInfo
|
||||
return &featuresStorage{
|
||||
startup: time.Now().UnixMilli(),
|
||||
resource: &resourceInfo,
|
||||
features: features,
|
||||
tableConverter: utils.NewTableConverter(
|
||||
resourceInfo.GroupResource(),
|
||||
[]metav1.TableColumnDefinition{
|
||||
@ -82,44 +80,35 @@ func (s *featuresStorage) ConvertToTable(ctx context.Context, object runtime.Obj
|
||||
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (s *featuresStorage) init() {
|
||||
s.featuresOnce.Do(func() {
|
||||
rv := "1"
|
||||
features, _ := featuremgmt.GetEmbeddedFeatureList()
|
||||
for _, feature := range features.Items {
|
||||
if feature.ResourceVersion > rv {
|
||||
rv = feature.ResourceVersion
|
||||
}
|
||||
}
|
||||
features.ResourceVersion = rv
|
||||
s.features = &features
|
||||
})
|
||||
}
|
||||
|
||||
func (s *featuresStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
flags := &v0alpha1.FeatureList{
|
||||
ListMeta: metav1.ListMeta{
|
||||
ResourceVersion: fmt.Sprintf("%d", s.startup),
|
||||
},
|
||||
s.init()
|
||||
if s.features == nil {
|
||||
return nil, fmt.Errorf("error loading embedded features")
|
||||
}
|
||||
for _, flag := range s.features {
|
||||
flags.Items = append(flags.Items, toK8sForm(flag))
|
||||
}
|
||||
return flags, nil
|
||||
return s.features, nil
|
||||
}
|
||||
|
||||
func (s *featuresStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
for _, flag := range s.features {
|
||||
if name == flag.Name {
|
||||
obj := toK8sForm(flag)
|
||||
return &obj, nil
|
||||
s.init()
|
||||
|
||||
for idx, flag := range s.features.Items {
|
||||
if flag.Name == name {
|
||||
return &s.features.Items[idx], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func toK8sForm(flag featuremgmt.FeatureFlag) v0alpha1.Feature {
|
||||
return v0alpha1.Feature{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: flag.Name,
|
||||
CreationTimestamp: metav1.NewTime(flag.Created),
|
||||
},
|
||||
Spec: v0alpha1.FeatureSpec{
|
||||
Description: flag.Description,
|
||||
Stage: flag.Stage.String(),
|
||||
Owner: string(flag.Owner),
|
||||
AllowSelfServe: flag.AllowSelfServe,
|
||||
HideFromAdminPage: flag.HideFromAdminPage,
|
||||
HideFromDocs: flag.HideFromDocs,
|
||||
FrontendOnly: flag.FrontendOnly,
|
||||
RequiresDevMode: flag.RequiresDevMode,
|
||||
RequiresRestart: flag.RequiresRestart,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func (b *FeatureFlagAPIBuilder) GetAPIGroupInfo(
|
||||
) (*genericapiserver.APIGroupInfo, error) {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v0alpha1.GROUP, scheme, metav1.ParameterCodec, codecs)
|
||||
|
||||
featureStore := NewFeaturesStorage(b.features.GetFlags())
|
||||
featureStore := NewFeaturesStorage()
|
||||
toggleStore := NewTogglesStorage(b.features)
|
||||
|
||||
storage := map[string]rest.Storage{}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FeatureToggles interface {
|
||||
@ -115,8 +114,7 @@ type FeatureFlag struct {
|
||||
Name string `json:"name" yaml:"name"` // Unique name
|
||||
Description string `json:"description"`
|
||||
Stage FeatureFlagStage `json:"stage,omitempty"`
|
||||
Created time.Time `json:"created,omitempty"` // when the flag was introduced
|
||||
Owner codeowner `json:"-"` // Owner person or team that owns this feature flag
|
||||
Owner codeowner `json:"-"` // Owner person or team that owns this feature flag
|
||||
|
||||
// Recommended properties - control behavior of the feature toggle management page in the UI
|
||||
AllowSelfServe bool `json:"allowSelfServe,omitempty"` // allow users with the right privileges to toggle this from the UI (GeneralAvailability, PublicPreview, and Deprecated toggles only)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,158 +1,158 @@
|
||||
Name,Stage,Owner,Created,requiresDevMode,RequiresRestart,FrontendOnly
|
||||
disableEnvelopeEncryption,GA,@grafana/grafana-as-code,2022-05-24,false,false,false
|
||||
live-service-web-worker,experimental,@grafana/grafana-app-platform-squad,2021-11-09,false,false,true
|
||||
queryOverLive,experimental,@grafana/grafana-app-platform-squad,2022-01-05,false,false,true
|
||||
panelTitleSearch,preview,@grafana/grafana-app-platform-squad,2022-02-15,false,false,false
|
||||
publicDashboards,GA,@grafana/sharing-squad,2022-04-07,false,false,false
|
||||
publicDashboardsEmailSharing,preview,@grafana/sharing-squad,2022-12-21,false,false,false
|
||||
lokiExperimentalStreaming,experimental,@grafana/observability-logs,2023-06-19,false,false,false
|
||||
featureHighlights,GA,@grafana/grafana-as-code,2022-02-03,false,false,false
|
||||
migrationLocking,preview,@grafana/backend-platform,2022-02-15,false,false,false
|
||||
storage,experimental,@grafana/grafana-app-platform-squad,2022-03-17,false,false,false
|
||||
correlations,GA,@grafana/explore-squad,2022-09-16,false,false,false
|
||||
exploreContentOutline,GA,@grafana/explore-squad,2023-11-03,false,false,true
|
||||
datasourceQueryMultiStatus,experimental,@grafana/plugins-platform-backend,2022-05-03,false,false,false
|
||||
traceToMetrics,experimental,@grafana/observability-traces-and-profiling,2022-03-07,false,false,true
|
||||
autoMigrateOldPanels,preview,@grafana/dataviz-squad,2022-06-11,false,false,true
|
||||
autoMigrateGraphPanel,preview,@grafana/dataviz-squad,2023-12-11,false,false,true
|
||||
disableAngular,preview,@grafana/dataviz-squad,2023-03-23,false,false,true
|
||||
canvasPanelNesting,experimental,@grafana/dataviz-squad,2022-05-31,false,false,true
|
||||
newVizTooltips,preview,@grafana/dataviz-squad,2023-11-03,false,false,true
|
||||
scenes,experimental,@grafana/dashboards-squad,2022-07-07,false,false,true
|
||||
disableSecretsCompatibility,experimental,@grafana/hosted-grafana-team,2022-07-13,false,true,false
|
||||
logRequestsInstrumentedAsUnknown,experimental,@grafana/hosted-grafana-team,2022-06-10,false,false,false
|
||||
dataConnectionsConsole,GA,@grafana/plugins-platform-backend,2022-06-01,false,false,false
|
||||
topnav,deprecated,@grafana/grafana-frontend-platform,2022-06-20,false,false,false
|
||||
returnToPrevious,experimental,@grafana/grafana-frontend-platform,2024-01-09,false,false,true
|
||||
grpcServer,preview,@grafana/grafana-app-platform-squad,2022-09-27,false,false,false
|
||||
unifiedStorage,experimental,@grafana/grafana-app-platform-squad,2022-12-01,true,true,false
|
||||
cloudWatchCrossAccountQuerying,GA,@grafana/aws-datasources,2022-11-28,false,false,false
|
||||
redshiftAsyncQueryDataSupport,GA,@grafana/aws-datasources,2022-08-27,false,false,false
|
||||
athenaAsyncQueryDataSupport,GA,@grafana/aws-datasources,2022-08-27,false,false,true
|
||||
showDashboardValidationWarnings,experimental,@grafana/dashboards-squad,2022-10-14,false,false,false
|
||||
mysqlAnsiQuotes,experimental,@grafana/backend-platform,2022-10-12,false,false,false
|
||||
accessControlOnCall,preview,@grafana/identity-access-team,2022-10-19,false,false,false
|
||||
nestedFolders,preview,@grafana/backend-platform,2022-10-22,false,false,false
|
||||
nestedFolderPicker,GA,@grafana/grafana-frontend-platform,2023-07-24,false,false,true
|
||||
alertingBacktesting,experimental,@grafana/alerting-squad,2022-10-20,false,false,false
|
||||
editPanelCSVDragAndDrop,experimental,@grafana/grafana-bi-squad,2022-12-20,false,false,true
|
||||
alertingNoNormalState,preview,@grafana/alerting-squad,2023-01-14,false,false,false
|
||||
logsContextDatasourceUi,GA,@grafana/observability-logs,2023-01-27,false,false,true
|
||||
lokiQuerySplitting,GA,@grafana/observability-logs,2023-02-09,false,false,true
|
||||
lokiQuerySplittingConfig,experimental,@grafana/observability-logs,2023-03-20,false,false,true
|
||||
individualCookiePreferences,experimental,@grafana/backend-platform,2023-02-23,false,false,false
|
||||
prometheusMetricEncyclopedia,GA,@grafana/observability-metrics,2023-03-07,false,false,true
|
||||
influxdbBackendMigration,GA,@grafana/observability-metrics,2023-03-15,false,false,true
|
||||
influxqlStreamingParser,experimental,@grafana/observability-metrics,2023-11-29,false,false,false
|
||||
influxdbRunQueriesInParallel,privatePreview,@grafana/observability-metrics,2024-01-29,false,false,false
|
||||
clientTokenRotation,GA,@grafana/identity-access-team,2023-03-23,false,false,false
|
||||
prometheusDataplane,GA,@grafana/observability-metrics,2023-03-29,false,false,false
|
||||
lokiMetricDataplane,GA,@grafana/observability-logs,2023-04-13,false,false,false
|
||||
lokiLogsDataplane,experimental,@grafana/observability-logs,2023-07-13,false,false,false
|
||||
dataplaneFrontendFallback,GA,@grafana/observability-metrics,2023-04-24,false,false,true
|
||||
disableSSEDataplane,experimental,@grafana/observability-metrics,2023-04-24,false,false,false
|
||||
alertStateHistoryLokiSecondary,experimental,@grafana/alerting-squad,2023-03-30,false,false,false
|
||||
alertStateHistoryLokiPrimary,experimental,@grafana/alerting-squad,2023-03-30,false,false,false
|
||||
alertStateHistoryLokiOnly,experimental,@grafana/alerting-squad,2023-03-30,false,false,false
|
||||
unifiedRequestLog,experimental,@grafana/backend-platform,2023-03-31,false,false,false
|
||||
renderAuthJWT,preview,@grafana/grafana-as-code,2023-04-03,false,false,false
|
||||
externalServiceAuth,experimental,@grafana/identity-access-team,2023-04-11,true,false,false
|
||||
refactorVariablesTimeRange,preview,@grafana/dashboards-squad,2023-06-06,false,false,false
|
||||
enableElasticsearchBackendQuerying,GA,@grafana/observability-logs,2023-04-14,false,false,false
|
||||
faroDatasourceSelector,preview,@grafana/app-o11y,2023-05-04,false,false,true
|
||||
enableDatagridEditing,preview,@grafana/grafana-bi-squad,2023-04-24,false,false,true
|
||||
extraThemes,experimental,@grafana/grafana-frontend-platform,2023-05-10,false,false,true
|
||||
lokiPredefinedOperations,experimental,@grafana/observability-logs,2023-06-02,false,false,true
|
||||
pluginsFrontendSandbox,experimental,@grafana/plugins-platform-backend,2023-06-05,false,false,true
|
||||
dashboardEmbed,experimental,@grafana/grafana-as-code,2023-07-06,false,false,true
|
||||
frontendSandboxMonitorOnly,experimental,@grafana/plugins-platform-backend,2023-07-05,false,false,true
|
||||
sqlDatasourceDatabaseSelection,preview,@grafana/grafana-bi-squad,2023-06-06,false,false,true
|
||||
lokiFormatQuery,experimental,@grafana/observability-logs,2023-06-21,false,false,true
|
||||
cloudWatchLogsMonacoEditor,GA,@grafana/aws-datasources,2023-06-12,false,false,true
|
||||
exploreScrollableLogsContainer,experimental,@grafana/observability-logs,2023-06-15,false,false,true
|
||||
recordedQueriesMulti,GA,@grafana/observability-metrics,2023-06-14,false,false,false
|
||||
pluginsDynamicAngularDetectionPatterns,experimental,@grafana/plugins-platform-backend,2023-06-26,false,false,false
|
||||
vizAndWidgetSplit,experimental,@grafana/dashboards-squad,2023-06-27,false,false,true
|
||||
prometheusIncrementalQueryInstrumentation,experimental,@grafana/observability-metrics,2023-07-05,false,false,true
|
||||
logsExploreTableVisualisation,experimental,@grafana/observability-logs,2023-07-12,false,false,true
|
||||
awsDatasourcesTempCredentials,experimental,@grafana/aws-datasources,2023-07-06,false,false,false
|
||||
transformationsRedesign,GA,@grafana/observability-metrics,2023-07-12,false,false,true
|
||||
mlExpressions,experimental,@grafana/alerting-squad,2023-07-13,false,false,false
|
||||
traceQLStreaming,experimental,@grafana/observability-traces-and-profiling,2023-07-26,false,false,true
|
||||
metricsSummary,experimental,@grafana/observability-traces-and-profiling,2023-08-28,false,false,true
|
||||
grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,2023-10-06,true,true,false
|
||||
grafanaAPIServerEnsureKubectlAccess,experimental,@grafana/grafana-app-platform-squad,2023-12-06,true,true,false
|
||||
featureToggleAdminPage,experimental,@grafana/grafana-operator-experience-squad,2023-07-18,false,true,false
|
||||
awsAsyncQueryCaching,GA,@grafana/aws-datasources,2023-07-21,false,false,false
|
||||
splitScopes,deprecated,@grafana/identity-access-team,2023-07-21,false,true,false
|
||||
permissionsFilterRemoveSubquery,experimental,@grafana/backend-platform,2023-08-02,false,false,false
|
||||
prometheusConfigOverhaulAuth,GA,@grafana/observability-metrics,2023-07-21,false,false,false
|
||||
configurableSchedulerTick,experimental,@grafana/alerting-squad,2023-07-26,false,true,false
|
||||
influxdbSqlSupport,GA,@grafana/observability-metrics,2023-08-02,false,true,false
|
||||
alertingNoDataErrorExecution,GA,@grafana/alerting-squad,2023-08-15,false,true,false
|
||||
angularDeprecationUI,experimental,@grafana/plugins-platform-backend,2023-08-29,false,false,true
|
||||
dashgpt,preview,@grafana/dashboards-squad,2023-11-17,false,false,true
|
||||
reportingRetries,preview,@grafana/sharing-squad,2023-08-31,false,true,false
|
||||
sseGroupByDatasource,experimental,@grafana/observability-metrics,2023-09-07,false,false,false
|
||||
libraryPanelRBAC,experimental,@grafana/dashboards-squad,2023-10-11,false,true,false
|
||||
lokiRunQueriesInParallel,privatePreview,@grafana/observability-logs,2023-09-19,false,false,false
|
||||
wargamesTesting,experimental,@grafana/hosted-grafana-team,2023-09-13,false,false,false
|
||||
alertingInsights,GA,@grafana/alerting-squad,2023-09-14,false,false,true
|
||||
externalCorePlugins,experimental,@grafana/plugins-platform-backend,2023-09-22,false,false,false
|
||||
pluginsAPIMetrics,experimental,@grafana/plugins-platform-backend,2023-09-21,false,false,true
|
||||
idForwarding,experimental,@grafana/identity-access-team,2023-09-25,false,false,false
|
||||
cloudWatchWildCardDimensionValues,GA,@grafana/aws-datasources,2023-09-27,false,false,false
|
||||
externalServiceAccounts,preview,@grafana/identity-access-team,2023-09-28,false,false,false
|
||||
panelMonitoring,experimental,@grafana/dataviz-squad,2023-10-08,false,false,true
|
||||
enableNativeHTTPHistogram,experimental,@grafana/hosted-grafana-team,2023-10-03,false,false,false
|
||||
formatString,preview,@grafana/grafana-bi-squad,2023-10-13,false,false,true
|
||||
transformationsVariableSupport,preview,@grafana/grafana-bi-squad,2023-10-04,false,false,true
|
||||
kubernetesPlaylists,experimental,@grafana/grafana-app-platform-squad,2023-11-08,false,true,false
|
||||
kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,2023-12-04,false,true,false
|
||||
kubernetesQueryServiceRewrite,experimental,@grafana/grafana-app-platform-squad,2024-01-28,true,true,false
|
||||
cloudWatchBatchQueries,preview,@grafana/aws-datasources,2023-10-20,false,false,false
|
||||
recoveryThreshold,GA,@grafana/alerting-squad,2023-10-10,false,true,false
|
||||
lokiStructuredMetadata,experimental,@grafana/observability-logs,2023-11-16,false,false,false
|
||||
teamHttpHeaders,experimental,@grafana/identity-access-team,2023-10-17,false,false,false
|
||||
awsDatasourcesNewFormStyling,preview,@grafana/aws-datasources,2023-10-12,false,false,true
|
||||
cachingOptimizeSerializationMemoryUsage,experimental,@grafana/grafana-operator-experience-squad,2023-10-12,false,false,false
|
||||
panelTitleSearchInV1,experimental,@grafana/backend-platform,2023-10-13,true,false,false
|
||||
pluginsInstrumentationStatusSource,experimental,@grafana/plugins-platform-backend,2023-10-17,false,false,false
|
||||
managedPluginsInstall,preview,@grafana/plugins-platform-backend,2023-10-18,false,false,false
|
||||
prometheusPromQAIL,experimental,@grafana/observability-metrics,2023-10-19,false,false,true
|
||||
addFieldFromCalculationStatFunctions,preview,@grafana/grafana-bi-squad,2023-11-03,false,false,true
|
||||
alertmanagerRemoteSecondary,experimental,@grafana/alerting-squad,2023-10-30,false,false,false
|
||||
alertmanagerRemotePrimary,experimental,@grafana/alerting-squad,2023-10-30,false,false,false
|
||||
alertmanagerRemoteOnly,experimental,@grafana/alerting-squad,2023-10-30,false,false,false
|
||||
annotationPermissionUpdate,experimental,@grafana/identity-access-team,2023-10-31,false,false,false
|
||||
extractFieldsNameDeduplication,experimental,@grafana/grafana-bi-squad,2023-11-02,false,false,true
|
||||
dashboardSceneForViewers,experimental,@grafana/dashboards-squad,2023-11-02,false,false,true
|
||||
dashboardScene,experimental,@grafana/dashboards-squad,2023-11-13,false,false,true
|
||||
panelFilterVariable,experimental,@grafana/dashboards-squad,2023-11-03,false,false,true
|
||||
pdfTables,preview,@grafana/sharing-squad,2023-11-06,false,false,false
|
||||
ssoSettingsApi,experimental,@grafana/identity-access-team,2023-11-08,false,false,false
|
||||
canvasPanelPanZoom,preview,@grafana/dataviz-squad,2023-12-27,false,false,true
|
||||
logsInfiniteScrolling,experimental,@grafana/observability-logs,2023-11-09,false,false,true
|
||||
flameGraphItemCollapsing,experimental,@grafana/observability-traces-and-profiling,2023-11-09,false,false,true
|
||||
alertingDetailsViewV2,experimental,@grafana/alerting-squad,2023-11-09,false,false,true
|
||||
datatrails,experimental,@grafana/dashboards-squad,2023-11-15,false,false,true
|
||||
alertingSimplifiedRouting,experimental,@grafana/alerting-squad,2023-11-10,false,false,false
|
||||
logRowsPopoverMenu,GA,@grafana/observability-logs,2023-11-16,false,false,true
|
||||
pluginsSkipHostEnvVars,experimental,@grafana/plugins-platform-backend,2023-11-15,false,false,false
|
||||
tableSharedCrosshair,experimental,@grafana/grafana-bi-squad,2023-12-12,false,false,true
|
||||
regressionTransformation,preview,@grafana/grafana-bi-squad,2023-11-24,false,false,true
|
||||
displayAnonymousStats,GA,@grafana/identity-access-team,2023-11-29,false,false,true
|
||||
lokiQueryHints,GA,@grafana/observability-logs,2023-12-18,false,false,true
|
||||
kubernetesFeatureToggles,experimental,@grafana/grafana-operator-experience-squad,2023-12-22,false,false,true
|
||||
alertingPreviewUpgrade,GA,@grafana/alerting-squad,2024-01-03,false,true,false
|
||||
enablePluginsTracingByDefault,experimental,@grafana/plugins-platform-backend,2024-01-09,false,true,false
|
||||
cloudRBACRoles,experimental,@grafana/identity-access-team,2024-01-10,false,true,false
|
||||
alertingQueryOptimization,GA,@grafana/alerting-squad,2024-01-10,false,false,false
|
||||
newFolderPicker,experimental,@grafana/grafana-frontend-platform,2024-01-12,false,false,true
|
||||
jitterAlertRulesWithinGroups,preview,@grafana/alerting-squad,2024-01-17,false,true,false
|
||||
onPremToCloudMigrations,experimental,@grafana/grafana-operator-experience-squad,2024-01-22,false,false,false
|
||||
alertingSaveStatePeriodic,privatePreview,@grafana/alerting-squad,2024-01-22,false,false,false
|
||||
promQLScope,experimental,@grafana/observability-metrics,2024-01-29,false,false,false
|
||||
nodeGraphDotLayout,experimental,@grafana/observability-traces-and-profiling,2024-01-02,false,false,true
|
||||
groupToNestedTableTransformation,preview,@grafana/dataviz-squad,2024-02-05,false,false,true
|
||||
newPDFRendering,experimental,@grafana/sharing-squad,2024-02-08,false,false,false
|
||||
Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
|
||||
disableEnvelopeEncryption,GA,@grafana/grafana-as-code,false,false,false
|
||||
live-service-web-worker,experimental,@grafana/grafana-app-platform-squad,false,false,true
|
||||
queryOverLive,experimental,@grafana/grafana-app-platform-squad,false,false,true
|
||||
panelTitleSearch,preview,@grafana/grafana-app-platform-squad,false,false,false
|
||||
publicDashboards,GA,@grafana/sharing-squad,false,false,false
|
||||
publicDashboardsEmailSharing,preview,@grafana/sharing-squad,false,false,false
|
||||
lokiExperimentalStreaming,experimental,@grafana/observability-logs,false,false,false
|
||||
featureHighlights,GA,@grafana/grafana-as-code,false,false,false
|
||||
migrationLocking,preview,@grafana/backend-platform,false,false,false
|
||||
storage,experimental,@grafana/grafana-app-platform-squad,false,false,false
|
||||
correlations,GA,@grafana/explore-squad,false,false,false
|
||||
exploreContentOutline,GA,@grafana/explore-squad,false,false,true
|
||||
datasourceQueryMultiStatus,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
traceToMetrics,experimental,@grafana/observability-traces-and-profiling,false,false,true
|
||||
autoMigrateOldPanels,preview,@grafana/dataviz-squad,false,false,true
|
||||
autoMigrateGraphPanel,preview,@grafana/dataviz-squad,false,false,true
|
||||
disableAngular,preview,@grafana/dataviz-squad,false,false,true
|
||||
canvasPanelNesting,experimental,@grafana/dataviz-squad,false,false,true
|
||||
newVizTooltips,preview,@grafana/dataviz-squad,false,false,true
|
||||
scenes,experimental,@grafana/dashboards-squad,false,false,true
|
||||
disableSecretsCompatibility,experimental,@grafana/hosted-grafana-team,false,true,false
|
||||
logRequestsInstrumentedAsUnknown,experimental,@grafana/hosted-grafana-team,false,false,false
|
||||
dataConnectionsConsole,GA,@grafana/plugins-platform-backend,false,false,false
|
||||
topnav,deprecated,@grafana/grafana-frontend-platform,false,false,false
|
||||
returnToPrevious,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
grpcServer,preview,@grafana/grafana-app-platform-squad,false,false,false
|
||||
unifiedStorage,experimental,@grafana/grafana-app-platform-squad,true,true,false
|
||||
cloudWatchCrossAccountQuerying,GA,@grafana/aws-datasources,false,false,false
|
||||
redshiftAsyncQueryDataSupport,GA,@grafana/aws-datasources,false,false,false
|
||||
athenaAsyncQueryDataSupport,GA,@grafana/aws-datasources,false,false,true
|
||||
showDashboardValidationWarnings,experimental,@grafana/dashboards-squad,false,false,false
|
||||
mysqlAnsiQuotes,experimental,@grafana/backend-platform,false,false,false
|
||||
accessControlOnCall,preview,@grafana/identity-access-team,false,false,false
|
||||
nestedFolders,preview,@grafana/backend-platform,false,false,false
|
||||
nestedFolderPicker,GA,@grafana/grafana-frontend-platform,false,false,true
|
||||
alertingBacktesting,experimental,@grafana/alerting-squad,false,false,false
|
||||
editPanelCSVDragAndDrop,experimental,@grafana/grafana-bi-squad,false,false,true
|
||||
alertingNoNormalState,preview,@grafana/alerting-squad,false,false,false
|
||||
logsContextDatasourceUi,GA,@grafana/observability-logs,false,false,true
|
||||
lokiQuerySplitting,GA,@grafana/observability-logs,false,false,true
|
||||
lokiQuerySplittingConfig,experimental,@grafana/observability-logs,false,false,true
|
||||
individualCookiePreferences,experimental,@grafana/backend-platform,false,false,false
|
||||
prometheusMetricEncyclopedia,GA,@grafana/observability-metrics,false,false,true
|
||||
influxdbBackendMigration,GA,@grafana/observability-metrics,false,false,true
|
||||
influxqlStreamingParser,experimental,@grafana/observability-metrics,false,false,false
|
||||
influxdbRunQueriesInParallel,privatePreview,@grafana/observability-metrics,false,false,false
|
||||
clientTokenRotation,GA,@grafana/identity-access-team,false,false,false
|
||||
prometheusDataplane,GA,@grafana/observability-metrics,false,false,false
|
||||
lokiMetricDataplane,GA,@grafana/observability-logs,false,false,false
|
||||
lokiLogsDataplane,experimental,@grafana/observability-logs,false,false,false
|
||||
dataplaneFrontendFallback,GA,@grafana/observability-metrics,false,false,true
|
||||
disableSSEDataplane,experimental,@grafana/observability-metrics,false,false,false
|
||||
alertStateHistoryLokiSecondary,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertStateHistoryLokiPrimary,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertStateHistoryLokiOnly,experimental,@grafana/alerting-squad,false,false,false
|
||||
unifiedRequestLog,experimental,@grafana/backend-platform,false,false,false
|
||||
renderAuthJWT,preview,@grafana/grafana-as-code,false,false,false
|
||||
externalServiceAuth,experimental,@grafana/identity-access-team,true,false,false
|
||||
refactorVariablesTimeRange,preview,@grafana/dashboards-squad,false,false,false
|
||||
enableElasticsearchBackendQuerying,GA,@grafana/observability-logs,false,false,false
|
||||
faroDatasourceSelector,preview,@grafana/app-o11y,false,false,true
|
||||
enableDatagridEditing,preview,@grafana/grafana-bi-squad,false,false,true
|
||||
extraThemes,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
lokiPredefinedOperations,experimental,@grafana/observability-logs,false,false,true
|
||||
pluginsFrontendSandbox,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
dashboardEmbed,experimental,@grafana/grafana-as-code,false,false,true
|
||||
frontendSandboxMonitorOnly,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
sqlDatasourceDatabaseSelection,preview,@grafana/grafana-bi-squad,false,false,true
|
||||
lokiFormatQuery,experimental,@grafana/observability-logs,false,false,true
|
||||
cloudWatchLogsMonacoEditor,GA,@grafana/aws-datasources,false,false,true
|
||||
exploreScrollableLogsContainer,experimental,@grafana/observability-logs,false,false,true
|
||||
recordedQueriesMulti,GA,@grafana/observability-metrics,false,false,false
|
||||
pluginsDynamicAngularDetectionPatterns,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
vizAndWidgetSplit,experimental,@grafana/dashboards-squad,false,false,true
|
||||
prometheusIncrementalQueryInstrumentation,experimental,@grafana/observability-metrics,false,false,true
|
||||
logsExploreTableVisualisation,experimental,@grafana/observability-logs,false,false,true
|
||||
awsDatasourcesTempCredentials,experimental,@grafana/aws-datasources,false,false,false
|
||||
transformationsRedesign,GA,@grafana/observability-metrics,false,false,true
|
||||
mlExpressions,experimental,@grafana/alerting-squad,false,false,false
|
||||
traceQLStreaming,experimental,@grafana/observability-traces-and-profiling,false,false,true
|
||||
metricsSummary,experimental,@grafana/observability-traces-and-profiling,false,false,true
|
||||
grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,true,true,false
|
||||
grafanaAPIServerEnsureKubectlAccess,experimental,@grafana/grafana-app-platform-squad,true,true,false
|
||||
featureToggleAdminPage,experimental,@grafana/grafana-operator-experience-squad,false,true,false
|
||||
awsAsyncQueryCaching,GA,@grafana/aws-datasources,false,false,false
|
||||
splitScopes,deprecated,@grafana/identity-access-team,false,true,false
|
||||
permissionsFilterRemoveSubquery,experimental,@grafana/backend-platform,false,false,false
|
||||
prometheusConfigOverhaulAuth,GA,@grafana/observability-metrics,false,false,false
|
||||
configurableSchedulerTick,experimental,@grafana/alerting-squad,false,true,false
|
||||
influxdbSqlSupport,GA,@grafana/observability-metrics,false,true,false
|
||||
alertingNoDataErrorExecution,GA,@grafana/alerting-squad,false,true,false
|
||||
angularDeprecationUI,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
dashgpt,preview,@grafana/dashboards-squad,false,false,true
|
||||
reportingRetries,preview,@grafana/sharing-squad,false,true,false
|
||||
sseGroupByDatasource,experimental,@grafana/observability-metrics,false,false,false
|
||||
libraryPanelRBAC,experimental,@grafana/dashboards-squad,false,true,false
|
||||
lokiRunQueriesInParallel,privatePreview,@grafana/observability-logs,false,false,false
|
||||
wargamesTesting,experimental,@grafana/hosted-grafana-team,false,false,false
|
||||
alertingInsights,GA,@grafana/alerting-squad,false,false,true
|
||||
externalCorePlugins,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
pluginsAPIMetrics,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
idForwarding,experimental,@grafana/identity-access-team,false,false,false
|
||||
cloudWatchWildCardDimensionValues,GA,@grafana/aws-datasources,false,false,false
|
||||
externalServiceAccounts,preview,@grafana/identity-access-team,false,false,false
|
||||
panelMonitoring,experimental,@grafana/dataviz-squad,false,false,true
|
||||
enableNativeHTTPHistogram,experimental,@grafana/hosted-grafana-team,false,false,false
|
||||
formatString,preview,@grafana/grafana-bi-squad,false,false,true
|
||||
transformationsVariableSupport,preview,@grafana/grafana-bi-squad,false,false,true
|
||||
kubernetesPlaylists,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
kubernetesQueryServiceRewrite,experimental,@grafana/grafana-app-platform-squad,true,true,false
|
||||
cloudWatchBatchQueries,preview,@grafana/aws-datasources,false,false,false
|
||||
recoveryThreshold,GA,@grafana/alerting-squad,false,true,false
|
||||
lokiStructuredMetadata,experimental,@grafana/observability-logs,false,false,false
|
||||
teamHttpHeaders,experimental,@grafana/identity-access-team,false,false,false
|
||||
awsDatasourcesNewFormStyling,preview,@grafana/aws-datasources,false,false,true
|
||||
cachingOptimizeSerializationMemoryUsage,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
panelTitleSearchInV1,experimental,@grafana/backend-platform,true,false,false
|
||||
pluginsInstrumentationStatusSource,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
managedPluginsInstall,preview,@grafana/plugins-platform-backend,false,false,false
|
||||
prometheusPromQAIL,experimental,@grafana/observability-metrics,false,false,true
|
||||
addFieldFromCalculationStatFunctions,preview,@grafana/grafana-bi-squad,false,false,true
|
||||
alertmanagerRemoteSecondary,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertmanagerRemotePrimary,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertmanagerRemoteOnly,experimental,@grafana/alerting-squad,false,false,false
|
||||
annotationPermissionUpdate,experimental,@grafana/identity-access-team,false,false,false
|
||||
extractFieldsNameDeduplication,experimental,@grafana/grafana-bi-squad,false,false,true
|
||||
dashboardSceneForViewers,experimental,@grafana/dashboards-squad,false,false,true
|
||||
dashboardScene,experimental,@grafana/dashboards-squad,false,false,true
|
||||
panelFilterVariable,experimental,@grafana/dashboards-squad,false,false,true
|
||||
pdfTables,preview,@grafana/sharing-squad,false,false,false
|
||||
ssoSettingsApi,experimental,@grafana/identity-access-team,false,false,false
|
||||
canvasPanelPanZoom,preview,@grafana/dataviz-squad,false,false,true
|
||||
logsInfiniteScrolling,experimental,@grafana/observability-logs,false,false,true
|
||||
flameGraphItemCollapsing,experimental,@grafana/observability-traces-and-profiling,false,false,true
|
||||
alertingDetailsViewV2,experimental,@grafana/alerting-squad,false,false,true
|
||||
datatrails,experimental,@grafana/dashboards-squad,false,false,true
|
||||
alertingSimplifiedRouting,experimental,@grafana/alerting-squad,false,false,false
|
||||
logRowsPopoverMenu,GA,@grafana/observability-logs,false,false,true
|
||||
pluginsSkipHostEnvVars,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
tableSharedCrosshair,experimental,@grafana/grafana-bi-squad,false,false,true
|
||||
regressionTransformation,preview,@grafana/grafana-bi-squad,false,false,true
|
||||
displayAnonymousStats,GA,@grafana/identity-access-team,false,false,true
|
||||
lokiQueryHints,GA,@grafana/observability-logs,false,false,true
|
||||
kubernetesFeatureToggles,experimental,@grafana/grafana-operator-experience-squad,false,false,true
|
||||
alertingPreviewUpgrade,GA,@grafana/alerting-squad,false,true,false
|
||||
enablePluginsTracingByDefault,experimental,@grafana/plugins-platform-backend,false,true,false
|
||||
cloudRBACRoles,experimental,@grafana/identity-access-team,false,true,false
|
||||
alertingQueryOptimization,GA,@grafana/alerting-squad,false,false,false
|
||||
newFolderPicker,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
jitterAlertRulesWithinGroups,preview,@grafana/alerting-squad,false,true,false
|
||||
onPremToCloudMigrations,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
alertingSaveStatePeriodic,privatePreview,@grafana/alerting-squad,false,false,false
|
||||
promQLScope,experimental,@grafana/observability-metrics,false,false,false
|
||||
nodeGraphDotLayout,experimental,@grafana/observability-traces-and-profiling,false,false,true
|
||||
groupToNestedTableTransformation,preview,@grafana/dataviz-squad,false,false,true
|
||||
newPDFRendering,experimental,@grafana/sharing-squad,false,false,false
|
||||
|
|
2028
pkg/services/featuremgmt/toggles_gen.json
Normal file
2028
pkg/services/featuremgmt/toggles_gen.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@ package featuremgmt
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
@ -16,7 +17,10 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
featuretoggleapi "github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt/strcase"
|
||||
)
|
||||
|
||||
@ -26,6 +30,9 @@ func TestFeatureToggleFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("check registry constraints", func(t *testing.T) {
|
||||
invalidNames := make([]string, 0)
|
||||
|
||||
// Check that all flags set in code are valid
|
||||
for _, flag := range standardFeatureFlags {
|
||||
if flag.Expression == "true" && !(flag.Stage == FeatureStageGeneralAvailability || flag.Stage == FeatureStageDeprecated) {
|
||||
t.Errorf("only FeatureStageGeneralAvailability or FeatureStageDeprecated features can be enabled by default. See: %s", flag.Name)
|
||||
@ -45,19 +52,105 @@ func TestFeatureToggleFiles(t *testing.T) {
|
||||
if flag.AllowSelfServe && flag.Stage != FeatureStageGeneralAvailability {
|
||||
t.Errorf("only allow self-serving GA toggles")
|
||||
}
|
||||
if flag.Created.Year() < 2021 {
|
||||
t.Errorf("flag requires a reasonable created date. See: %s (%s)",
|
||||
flag.Name, flag.Created.Format(time.DateOnly))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("all new features should have an owner", func(t *testing.T) {
|
||||
for _, flag := range standardFeatureFlags {
|
||||
if flag.Owner == "" {
|
||||
t.Errorf("feature %s does not have an owner. please fill the FeatureFlag.Owner property", flag.Name)
|
||||
}
|
||||
// Check camel case names
|
||||
if flag.Name != strcase.ToLowerCamel(flag.Name) && !legacyNames[flag.Name] {
|
||||
invalidNames = append(invalidNames, flag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the names are valid
|
||||
require.Empty(t, invalidNames, "%s feature names should be camel cased", invalidNames)
|
||||
// acronyms can be configured as needed via `ConfigureAcronym` function from `./strcase/camel.go`
|
||||
|
||||
// Now that we know they are valid, update the json database
|
||||
t.Run("update k8s resource list", func(t *testing.T) {
|
||||
created := v1.NewTime(time.Now().UTC())
|
||||
resourceVersion := fmt.Sprintf("%d", created.UnixMilli())
|
||||
|
||||
featuresFile := "toggles_gen.json"
|
||||
current := featuretoggleapi.FeatureList{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "FeatureList",
|
||||
APIVersion: featuretoggleapi.APIVERSION,
|
||||
},
|
||||
}
|
||||
existing := featuretoggleapi.FeatureList{}
|
||||
body, err := os.ReadFile(featuresFile)
|
||||
if err == nil {
|
||||
_ = json.Unmarshal(body, &existing)
|
||||
current.ListMeta = existing.ListMeta
|
||||
}
|
||||
|
||||
lookup := map[string]featuretoggleapi.FeatureSpec{}
|
||||
for _, flag := range standardFeatureFlags {
|
||||
lookup[flag.Name] = featuretoggleapi.FeatureSpec{
|
||||
Description: flag.Description,
|
||||
Stage: flag.Stage.String(),
|
||||
Owner: string(flag.Owner),
|
||||
RequiresDevMode: flag.RequiresDevMode,
|
||||
FrontendOnly: flag.FrontendOnly,
|
||||
RequiresRestart: flag.RequiresRestart,
|
||||
AllowSelfServe: flag.AllowSelfServe,
|
||||
HideFromAdminPage: flag.HideFromAdminPage,
|
||||
HideFromDocs: flag.HideFromDocs,
|
||||
// EnabledVersion: ???,
|
||||
}
|
||||
|
||||
// Replace them all
|
||||
// current.Items = append(current.Items, featuretoggleapi.Feature{
|
||||
// ObjectMeta: v1.ObjectMeta{
|
||||
// Name: flag.Name,
|
||||
// CreationTimestamp: v1.NewTime(flag.Created),
|
||||
// ResourceVersion: fmt.Sprintf("%d", flag.Created.UnixMilli()),
|
||||
// },
|
||||
// Spec: lookup[flag.Name],
|
||||
// })
|
||||
// current.ListMeta.ResourceVersion = resourceVersion
|
||||
}
|
||||
|
||||
// Check for changes in any existing values
|
||||
for _, item := range existing.Items {
|
||||
v, ok := lookup[item.Name]
|
||||
if ok {
|
||||
delete(lookup, item.Name)
|
||||
a, e1 := json.Marshal(v)
|
||||
b, e2 := json.Marshal(item.Spec)
|
||||
if e1 != nil || e2 != nil || !bytes.Equal(a, b) {
|
||||
item.ResourceVersion = resourceVersion
|
||||
if item.Annotations == nil {
|
||||
item.Annotations = make(map[string]string)
|
||||
}
|
||||
item.Annotations[utils.AnnoKeyUpdatedTimestamp] = created.String()
|
||||
item.Spec = v // the current value
|
||||
}
|
||||
} else {
|
||||
item.DeletionTimestamp = &created
|
||||
fmt.Printf("mark feature as deleted")
|
||||
}
|
||||
current.Items = append(current.Items, item)
|
||||
}
|
||||
|
||||
// New flags not in the existing list
|
||||
for k, v := range lookup {
|
||||
current.Items = append(current.Items, featuretoggleapi.Feature{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: k,
|
||||
CreationTimestamp: created,
|
||||
ResourceVersion: fmt.Sprintf("%d", created.UnixMilli()),
|
||||
},
|
||||
Spec: v,
|
||||
})
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(current, "", " ")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = os.WriteFile(featuresFile, out, 0644)
|
||||
require.NoError(t, err, "error writing file")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("verify files", func(t *testing.T) {
|
||||
@ -85,22 +178,6 @@ func TestFeatureToggleFiles(t *testing.T) {
|
||||
generateCSV(),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("check feature naming convention", func(t *testing.T) {
|
||||
invalidNames := make([]string, 0)
|
||||
for _, f := range standardFeatureFlags {
|
||||
if legacyNames[f.Name] {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Name != strcase.ToLowerCamel(f.Name) {
|
||||
invalidNames = append(invalidNames, f.Name)
|
||||
}
|
||||
}
|
||||
|
||||
require.Empty(t, invalidNames, "%s feature names should be camel cased", invalidNames)
|
||||
// acronyms can be configured as needed via `ConfigureAcronym` function from `./strcase/camel.go`
|
||||
})
|
||||
}
|
||||
|
||||
func verifyAndGenerateFile(t *testing.T, fpath string, gen string) {
|
||||
@ -214,9 +291,8 @@ func generateCSV() string {
|
||||
w := csv.NewWriter(&buf)
|
||||
if err := w.Write([]string{
|
||||
"Name",
|
||||
"Stage", //flag.Stage.String(),
|
||||
"Owner", //string(flag.Owner),
|
||||
"Created",
|
||||
"Stage", //flag.Stage.String(),
|
||||
"Owner", //string(flag.Owner),
|
||||
"requiresDevMode", //strconv.FormatBool(flag.RequiresDevMode),
|
||||
"RequiresRestart", //strconv.FormatBool(flag.RequiresRestart),
|
||||
"FrontendOnly", //strconv.FormatBool(flag.FrontendOnly),
|
||||
@ -224,19 +300,11 @@ func generateCSV() string {
|
||||
log.Fatalln("error writing record to csv:", err)
|
||||
}
|
||||
|
||||
dateFormatter := func(t time.Time) string {
|
||||
if t.Year() < 2020 { // fake year
|
||||
return ""
|
||||
}
|
||||
return t.Format(time.DateOnly)
|
||||
}
|
||||
|
||||
for _, flag := range standardFeatureFlags {
|
||||
if err := w.Write([]string{
|
||||
flag.Name,
|
||||
flag.Stage.String(),
|
||||
string(flag.Owner),
|
||||
dateFormatter(flag.Created),
|
||||
strconv.FormatBool(flag.RequiresDevMode),
|
||||
strconv.FormatBool(flag.RequiresRestart),
|
||||
strconv.FormatBool(flag.FrontendOnly),
|
||||
|
Reference in New Issue
Block a user