From 5429512779bd5f25b88ff728ea91efdef7dfafa0 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Fri, 3 Jan 2025 07:48:47 -0700 Subject: [PATCH] K8s: Restores: Put behind a feature toggle (#98472) --- .../feature-toggles/index.md | 1 + .../src/types/featureToggles.gen.ts | 1 + .../apis/dashboard/v0alpha1/register.go | 26 +++++++++++-------- .../apis/dashboard/v1alpha1/register.go | 26 +++++++++++-------- .../apis/dashboard/v2alpha1/register.go | 26 +++++++++++-------- .../dashboards/service/dashboard_service.go | 4 +-- pkg/services/featuremgmt/registry.go | 6 +++++ pkg/services/featuremgmt/toggles_gen.csv | 1 + pkg/services/featuremgmt/toggles_gen.go | 4 +++ pkg/services/featuremgmt/toggles_gen.json | 25 ++++++++++++++++++ 10 files changed, 85 insertions(+), 35 deletions(-) diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 6c848e443d3..a798f18778f 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -170,6 +170,7 @@ Experimental features might be changed or removed without prior notice. | `kubernetesSnapshots` | Routes snapshot requests from /api to the /apis endpoint | | `kubernetesDashboards` | Use the kubernetes API in the frontend for dashboards | | `kubernetesCliDashboards` | Use the k8s client to retrieve dashboards internally | +| `kubernetesRestore` | Allow restoring objects in k8s | | `kubernetesFolders` | Use the kubernetes API in the frontend for folders, and route /api/folders requests to k8s | | `grafanaAPIServerTestingWithExperimentalAPIs` | Facilitate integration testing of experimental APIs | | `datasourceQueryTypes` | Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus) | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 498ddd5ea60..29dd5d1ffae 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -111,6 +111,7 @@ export interface FeatureToggles { kubernetesSnapshots?: boolean; kubernetesDashboards?: boolean; kubernetesCliDashboards?: boolean; + kubernetesRestore?: boolean; kubernetesFolders?: boolean; grafanaAPIServerTestingWithExperimentalAPIs?: boolean; datasourceQueryTypes?: boolean; diff --git a/pkg/registry/apis/dashboard/v0alpha1/register.go b/pkg/registry/apis/dashboard/v0alpha1/register.go index f09f0015efc..d2fbf1ff485 100644 --- a/pkg/registry/apis/dashboard/v0alpha1/register.go +++ b/pkg/registry/apis/dashboard/v0alpha1/register.go @@ -41,6 +41,7 @@ var ( // This is used just so wire has something unique to return type DashboardsAPIBuilder struct { dashboardService dashboards.DashboardService + features featuremgmt.FeatureToggles accessControl accesscontrol.AccessControl legacy *dashboard.DashboardStorage @@ -69,6 +70,7 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, log: log.New("grafana-apiserver.dashboards.v0alpha1"), dashboardService: dashboardService, + features: features, accessControl: accessControl, unified: unified, search: dashboard.NewSearchHandler(unified, tracing), @@ -157,18 +159,20 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver } } - storage[dash.StoragePath("restore")] = dashboard.NewRestoreConnector( - b.unified, - dashboardv0alpha1.DashboardResourceInfo.GroupResource(), - defaultOpts, - ) + if b.features.IsEnabledGlobally(featuremgmt.FlagKubernetesRestore) { + storage[dash.StoragePath("restore")] = dashboard.NewRestoreConnector( + b.unified, + dashboardv0alpha1.DashboardResourceInfo.GroupResource(), + defaultOpts, + ) - storage[dash.StoragePath("latest")] = dashboard.NewLatestConnector( - b.unified, - dashboardv0alpha1.DashboardResourceInfo.GroupResource(), - defaultOpts, - scheme, - ) + storage[dash.StoragePath("latest")] = dashboard.NewLatestConnector( + b.unified, + dashboardv0alpha1.DashboardResourceInfo.GroupResource(), + defaultOpts, + scheme, + ) + } // Register the DTO endpoint that will consolidate all dashboard bits storage[dash.StoragePath("dto")], err = dashboard.NewDTOConnector( diff --git a/pkg/registry/apis/dashboard/v1alpha1/register.go b/pkg/registry/apis/dashboard/v1alpha1/register.go index 506b71f6d48..84a8a0aab96 100644 --- a/pkg/registry/apis/dashboard/v1alpha1/register.go +++ b/pkg/registry/apis/dashboard/v1alpha1/register.go @@ -38,6 +38,7 @@ var ( // This is used just so wire has something unique to return type DashboardsAPIBuilder struct { dashboardService dashboards.DashboardService + features featuremgmt.FeatureToggles accessControl accesscontrol.AccessControl legacy *dashboard.DashboardStorage @@ -65,6 +66,7 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, log: log.New("grafana-apiserver.dashboards.v1alpha1"), dashboardService: dashboardService, + features: features, accessControl: accessControl, unified: unified, @@ -148,18 +150,20 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver } } - storage[dash.StoragePath("restore")] = dashboard.NewRestoreConnector( - b.unified, - dashboardv1alpha1.DashboardResourceInfo.GroupResource(), - defaultOpts, - ) + if b.features.IsEnabledGlobally(featuremgmt.FlagKubernetesRestore) { + storage[dash.StoragePath("restore")] = dashboard.NewRestoreConnector( + b.unified, + dashboardv1alpha1.DashboardResourceInfo.GroupResource(), + defaultOpts, + ) - storage[dash.StoragePath("latest")] = dashboard.NewLatestConnector( - b.unified, - dashboardv1alpha1.DashboardResourceInfo.GroupResource(), - defaultOpts, - scheme, - ) + storage[dash.StoragePath("latest")] = dashboard.NewLatestConnector( + b.unified, + dashboardv1alpha1.DashboardResourceInfo.GroupResource(), + defaultOpts, + scheme, + ) + } // Register the DTO endpoint that will consolidate all dashboard bits storage[dash.StoragePath("dto")], err = dashboard.NewDTOConnector( diff --git a/pkg/registry/apis/dashboard/v2alpha1/register.go b/pkg/registry/apis/dashboard/v2alpha1/register.go index d852f9a3f4d..7fdc34ebaf6 100644 --- a/pkg/registry/apis/dashboard/v2alpha1/register.go +++ b/pkg/registry/apis/dashboard/v2alpha1/register.go @@ -38,6 +38,7 @@ var ( // This is used just so wire has something unique to return type DashboardsAPIBuilder struct { dashboardService dashboards.DashboardService + features featuremgmt.FeatureToggles accessControl accesscontrol.AccessControl legacy *dashboard.DashboardStorage @@ -65,6 +66,7 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, log: log.New("grafana-apiserver.dashboards.v2alpha1"), dashboardService: dashboardService, + features: features, accessControl: accessControl, unified: unified, @@ -148,18 +150,20 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver } } - storage[dash.StoragePath("restore")] = dashboard.NewRestoreConnector( - b.unified, - dashboardv2alpha1.DashboardResourceInfo.GroupResource(), - defaultOpts, - ) + if b.features.IsEnabledGlobally(featuremgmt.FlagKubernetesRestore) { + storage[dash.StoragePath("restore")] = dashboard.NewRestoreConnector( + b.unified, + dashboardv2alpha1.DashboardResourceInfo.GroupResource(), + defaultOpts, + ) - storage[dash.StoragePath("latest")] = dashboard.NewLatestConnector( - b.unified, - dashboardv2alpha1.DashboardResourceInfo.GroupResource(), - defaultOpts, - scheme, - ) + storage[dash.StoragePath("latest")] = dashboard.NewLatestConnector( + b.unified, + dashboardv2alpha1.DashboardResourceInfo.GroupResource(), + defaultOpts, + scheme, + ) + } // Register the DTO endpoint that will consolidate all dashboard bits storage[dash.StoragePath("dto")], err = dashboard.NewDTOConnector( diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index e0f7dfd9f18..57bf2339d72 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -1109,9 +1109,9 @@ func (dr *DashboardServiceImpl) getDashboardThroughK8s(ctx context.Context, quer return nil, nil } - // if including deleted dashboards, use the /latest subresource + // if including deleted dashboards for restore, use the /latest subresource subresource := "" - if query.IncludeDeleted { + if query.IncludeDeleted && dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesRestore) { subresource = "latest" } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index a2a0a84f160..27bae82dafe 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -702,6 +702,12 @@ var ( Stage: FeatureStageExperimental, Owner: grafanaAppPlatformSquad, }, + { + Name: "kubernetesRestore", + Description: "Allow restoring objects in k8s", + Stage: FeatureStageExperimental, + Owner: grafanaAppPlatformSquad, + }, { Name: "kubernetesFolders", Description: "Use the kubernetes API in the frontend for folders, and route /api/folders requests to k8s", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 2f69909c040..ad5a9881e02 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -92,6 +92,7 @@ kubernetesPlaylists,GA,@grafana/grafana-app-platform-squad,false,true,false kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,false kubernetesDashboards,experimental,@grafana/grafana-app-platform-squad,false,false,true kubernetesCliDashboards,experimental,@grafana/grafana-app-platform-squad,false,false,false +kubernetesRestore,experimental,@grafana/grafana-app-platform-squad,false,false,false kubernetesFolders,experimental,@grafana/search-and-storage,false,false,false grafanaAPIServerTestingWithExperimentalAPIs,experimental,@grafana/search-and-storage,false,false,false datasourceQueryTypes,experimental,@grafana/grafana-app-platform-squad,false,true,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 737798e721d..a35ee0e089f 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -379,6 +379,10 @@ const ( // Use the k8s client to retrieve dashboards internally FlagKubernetesCliDashboards = "kubernetesCliDashboards" + // FlagKubernetesRestore + // Allow restoring objects in k8s + FlagKubernetesRestore = "kubernetesRestore" + // FlagKubernetesFolders // Use the kubernetes API in the frontend for folders, and route /api/folders requests to k8s FlagKubernetesFolders = "kubernetesFolders" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index e429e4838ca..90a15c08bba 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1940,6 +1940,19 @@ "expression": "false" } }, + { + "metadata": { + "name": "kuberenetesRestore", + "resourceVersion": "1735880172453", + "creationTimestamp": "2025-01-03T04:56:12Z", + "deletionTimestamp": "2025-01-03T05:01:38Z" + }, + "spec": { + "description": "Allow restoring objects in k8s", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad" + } + }, { "metadata": { "name": "kubernetesAggregator", @@ -2028,6 +2041,18 @@ "expression": "true" } }, + { + "metadata": { + "name": "kubernetesRestore", + "resourceVersion": "1735880498698", + "creationTimestamp": "2025-01-03T05:01:38Z" + }, + "spec": { + "description": "Allow restoring objects in k8s", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad" + } + }, { "metadata": { "name": "kubernetesSnapshots",