| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | package featuretoggle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/runtime" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/runtime/schema" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/runtime/serializer" | 
					
						
							|  |  |  | 	"k8s.io/apiserver/pkg/authorization/authorizer" | 
					
						
							|  |  |  | 	"k8s.io/apiserver/pkg/registry/generic" | 
					
						
							|  |  |  | 	"k8s.io/apiserver/pkg/registry/rest" | 
					
						
							|  |  |  | 	genericapiserver "k8s.io/apiserver/pkg/server" | 
					
						
							|  |  |  | 	common "k8s.io/kube-openapi/pkg/common" | 
					
						
							|  |  |  | 	"k8s.io/kube-openapi/pkg/spec3" | 
					
						
							|  |  |  | 	"k8s.io/kube-openapi/pkg/validation/spec" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1" | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | 	grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" | 
					
						
							| 
									
										
										
										
											2024-02-02 12:52:02 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							| 
									
										
										
										
											2024-07-01 23:42:34 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/apiserver/builder" | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							| 
									
										
										
										
											2024-02-10 00:48:56 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2024-06-14 17:01:49 +08:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | var _ builder.APIGroupBuilder = (*FeatureFlagAPIBuilder)(nil) | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | var gv = v0alpha1.SchemeGroupVersion | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is used just so wire has something unique to return
 | 
					
						
							|  |  |  | type FeatureFlagAPIBuilder struct { | 
					
						
							| 
									
										
										
										
											2024-02-02 12:52:02 +08:00
										 |  |  | 	features      *featuremgmt.FeatureManager | 
					
						
							|  |  |  | 	accessControl accesscontrol.AccessControl | 
					
						
							| 
									
										
										
										
											2024-02-10 00:48:56 +08:00
										 |  |  | 	cfg           *setting.Cfg | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-10 00:48:56 +08:00
										 |  |  | func NewFeatureFlagAPIBuilder(features *featuremgmt.FeatureManager, accessControl accesscontrol.AccessControl, cfg *setting.Cfg) *FeatureFlagAPIBuilder { | 
					
						
							|  |  |  | 	return &FeatureFlagAPIBuilder{features, accessControl, cfg} | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func RegisterAPIService(features *featuremgmt.FeatureManager, | 
					
						
							| 
									
										
										
										
											2024-02-02 12:52:02 +08:00
										 |  |  | 	accessControl accesscontrol.AccessControl, | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	apiregistration builder.APIRegistrar, | 
					
						
							| 
									
										
										
										
											2024-02-10 00:48:56 +08:00
										 |  |  | 	cfg *setting.Cfg, | 
					
						
							| 
									
										
										
										
											2024-06-14 17:01:49 +08:00
										 |  |  | 	registerer prometheus.Registerer, | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | ) *FeatureFlagAPIBuilder { | 
					
						
							| 
									
										
										
										
											2024-02-10 00:48:56 +08:00
										 |  |  | 	builder := NewFeatureFlagAPIBuilder(features, accessControl, cfg) | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 	apiregistration.RegisterAPI(builder) | 
					
						
							|  |  |  | 	return builder | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *FeatureFlagAPIBuilder) GetGroupVersion() schema.GroupVersion { | 
					
						
							|  |  |  | 	return gv | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | func (b *FeatureFlagAPIBuilder) GetDesiredDualWriterMode(dualWrite bool, modeMap map[string]grafanarest.DualWriterMode) grafanarest.DualWriterMode { | 
					
						
							|  |  |  | 	// Add required configuration support in order to enable other modes. For an example, see pkg/registry/apis/playlist/register.go
 | 
					
						
							|  |  |  | 	return grafanarest.Mode0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) { | 
					
						
							|  |  |  | 	scheme.AddKnownTypes(gv, | 
					
						
							|  |  |  | 		&v0alpha1.Feature{}, | 
					
						
							|  |  |  | 		&v0alpha1.FeatureList{}, | 
					
						
							|  |  |  | 		&v0alpha1.FeatureToggles{}, | 
					
						
							|  |  |  | 		&v0alpha1.FeatureTogglesList{}, | 
					
						
							|  |  |  | 		&v0alpha1.ResolvedToggleState{}, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *FeatureFlagAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { | 
					
						
							|  |  |  | 	addKnownTypes(scheme, gv) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Link this version to the internal representation.
 | 
					
						
							|  |  |  | 	// This is used for server-side-apply (PATCH), and avoids the error:
 | 
					
						
							|  |  |  | 	//   "no kind is registered for the type"
 | 
					
						
							|  |  |  | 	addKnownTypes(scheme, schema.GroupVersion{ | 
					
						
							|  |  |  | 		Group:   gv.Group, | 
					
						
							|  |  |  | 		Version: runtime.APIVersionInternal, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If multiple versions exist, then register conversions from zz_generated.conversion.go
 | 
					
						
							|  |  |  | 	// if err := playlist.RegisterConversions(scheme); err != nil {
 | 
					
						
							|  |  |  | 	//   return err
 | 
					
						
							|  |  |  | 	// }
 | 
					
						
							|  |  |  | 	metav1.AddToGroupVersion(scheme, gv) | 
					
						
							|  |  |  | 	return scheme.SetVersionPriority(gv) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *FeatureFlagAPIBuilder) GetAPIGroupInfo( | 
					
						
							|  |  |  | 	scheme *runtime.Scheme, | 
					
						
							|  |  |  | 	codecs serializer.CodecFactory, // pointer?
 | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	_ generic.RESTOptionsGetter, | 
					
						
							| 
									
										
										
										
											2024-06-01 01:29:59 +08:00
										 |  |  | 	_ grafanarest.DualWriterMode, | 
					
						
							| 
									
										
										
										
											2024-06-14 17:01:49 +08:00
										 |  |  | 	_ prometheus.Registerer, | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | ) (*genericapiserver.APIGroupInfo, error) { | 
					
						
							|  |  |  | 	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v0alpha1.GROUP, scheme, metav1.ParameterCodec, codecs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-10 07:34:12 +08:00
										 |  |  | 	featureStore := NewFeaturesStorage() | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 	toggleStore := NewTogglesStorage(b.features) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	storage := map[string]rest.Storage{} | 
					
						
							|  |  |  | 	storage[featureStore.resource.StoragePath()] = featureStore | 
					
						
							|  |  |  | 	storage[toggleStore.resource.StoragePath()] = toggleStore | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	apiGroupInfo.VersionedResourcesStorageMap[v0alpha1.VERSION] = storage | 
					
						
							|  |  |  | 	return &apiGroupInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *FeatureFlagAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { | 
					
						
							|  |  |  | 	return v0alpha1.GetOpenAPIDefinitions | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (b *FeatureFlagAPIBuilder) GetAuthorizer() authorizer.Authorizer { | 
					
						
							|  |  |  | 	return nil // default authorizer is fine
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Register additional routes with the server
 | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | func (b *FeatureFlagAPIBuilder) GetAPIRoutes() *builder.APIRoutes { | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 	defs := v0alpha1.GetOpenAPIDefinitions(func(path string) spec.Ref { return spec.Ref{} }) | 
					
						
							|  |  |  | 	stateSchema := defs["github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1.ResolvedToggleState"].Schema | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tags := []string{"Editor"} | 
					
						
							| 
									
										
										
										
											2024-02-02 06:27:30 +08:00
										 |  |  | 	return &builder.APIRoutes{ | 
					
						
							|  |  |  | 		Root: []builder.APIRouteHandler{ | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				Path: "current", | 
					
						
							|  |  |  | 				Spec: &spec3.PathProps{ | 
					
						
							|  |  |  | 					Get: &spec3.Operation{ | 
					
						
							|  |  |  | 						OperationProps: spec3.OperationProps{ | 
					
						
							|  |  |  | 							Tags:        tags, | 
					
						
							|  |  |  | 							Summary:     "Current configuration with details", | 
					
						
							|  |  |  | 							Description: "Show details about the current flags and where they come from", | 
					
						
							|  |  |  | 							Responses: &spec3.Responses{ | 
					
						
							|  |  |  | 								ResponsesProps: spec3.ResponsesProps{ | 
					
						
							|  |  |  | 									StatusCodeResponses: map[int]*spec3.Response{ | 
					
						
							|  |  |  | 										200: { | 
					
						
							|  |  |  | 											ResponseProps: spec3.ResponseProps{ | 
					
						
							|  |  |  | 												Content: map[string]*spec3.MediaType{ | 
					
						
							|  |  |  | 													"application/json": { | 
					
						
							|  |  |  | 														MediaTypeProps: spec3.MediaTypeProps{ | 
					
						
							|  |  |  | 															Schema: &stateSchema, | 
					
						
							|  |  |  | 														}, | 
					
						
							|  |  |  | 													}, | 
					
						
							|  |  |  | 												}, | 
					
						
							|  |  |  | 												Description: "OK", | 
					
						
							|  |  |  | 											}, | 
					
						
							|  |  |  | 										}, | 
					
						
							|  |  |  | 									}, | 
					
						
							|  |  |  | 								}, | 
					
						
							|  |  |  | 							}, | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					Patch: &spec3.Operation{ | 
					
						
							|  |  |  | 						OperationProps: spec3.OperationProps{ | 
					
						
							|  |  |  | 							Tags:        tags, | 
					
						
							|  |  |  | 							Summary:     "Update individual toggles", | 
					
						
							|  |  |  | 							Description: "Patch some of the toggles (keyed by the toggle name)", | 
					
						
							|  |  |  | 							RequestBody: &spec3.RequestBody{ | 
					
						
							|  |  |  | 								RequestBodyProps: spec3.RequestBodyProps{ | 
					
						
							|  |  |  | 									Required:    true, | 
					
						
							|  |  |  | 									Description: "flags to change", | 
					
						
							|  |  |  | 									Content: map[string]*spec3.MediaType{ | 
					
						
							|  |  |  | 										"application/json": { | 
					
						
							|  |  |  | 											MediaTypeProps: spec3.MediaTypeProps{ | 
					
						
							|  |  |  | 												Schema: &stateSchema, | 
					
						
							|  |  |  | 												Example: &v0alpha1.ResolvedToggleState{ | 
					
						
							|  |  |  | 													Enabled: map[string]bool{ | 
					
						
							|  |  |  | 														featuremgmt.FlagAutoMigrateOldPanels: true, | 
					
						
							|  |  |  | 														featuremgmt.FlagAngularDeprecationUI: false, | 
					
						
							|  |  |  | 													}, | 
					
						
							|  |  |  | 												}, | 
					
						
							|  |  |  | 												Examples: map[string]*spec3.Example{ | 
					
						
							|  |  |  | 													"enable-auto-migrate": { | 
					
						
							|  |  |  | 														ExampleProps: spec3.ExampleProps{ | 
					
						
							|  |  |  | 															Summary:     "enable auto-migrate panels", | 
					
						
							| 
									
										
										
										
											2024-02-02 12:52:02 +08:00
										 |  |  | 															Description: "enable description", | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 															Value: &v0alpha1.ResolvedToggleState{ | 
					
						
							|  |  |  | 																Enabled: map[string]bool{ | 
					
						
							|  |  |  | 																	featuremgmt.FlagAutoMigrateOldPanels: true, | 
					
						
							|  |  |  | 																}, | 
					
						
							|  |  |  | 															}, | 
					
						
							|  |  |  | 														}, | 
					
						
							|  |  |  | 													}, | 
					
						
							|  |  |  | 													"disable-auto-migrate": { | 
					
						
							|  |  |  | 														ExampleProps: spec3.ExampleProps{ | 
					
						
							|  |  |  | 															Summary:     "disable auto-migrate panels", | 
					
						
							| 
									
										
										
										
											2024-02-02 12:52:02 +08:00
										 |  |  | 															Description: "disable description", | 
					
						
							| 
									
										
										
										
											2024-01-18 13:32:44 +08:00
										 |  |  | 															Value: &v0alpha1.ResolvedToggleState{ | 
					
						
							|  |  |  | 																Enabled: map[string]bool{ | 
					
						
							|  |  |  | 																	featuremgmt.FlagAutoMigrateOldPanels: false, | 
					
						
							|  |  |  | 																}, | 
					
						
							|  |  |  | 															}, | 
					
						
							|  |  |  | 														}, | 
					
						
							|  |  |  | 													}, | 
					
						
							|  |  |  | 												}, | 
					
						
							|  |  |  | 											}, | 
					
						
							|  |  |  | 										}, | 
					
						
							|  |  |  | 									}, | 
					
						
							|  |  |  | 								}, | 
					
						
							|  |  |  | 							}, | 
					
						
							|  |  |  | 							Responses: &spec3.Responses{ | 
					
						
							|  |  |  | 								ResponsesProps: spec3.ResponsesProps{ | 
					
						
							|  |  |  | 									StatusCodeResponses: map[int]*spec3.Response{ | 
					
						
							|  |  |  | 										200: { | 
					
						
							|  |  |  | 											ResponseProps: spec3.ResponseProps{ | 
					
						
							|  |  |  | 												Content: map[string]*spec3.MediaType{ | 
					
						
							|  |  |  | 													"application/json": {}, | 
					
						
							|  |  |  | 												}, | 
					
						
							|  |  |  | 												Description: "OK", | 
					
						
							|  |  |  | 											}, | 
					
						
							|  |  |  | 										}, | 
					
						
							|  |  |  | 									}, | 
					
						
							|  |  |  | 								}, | 
					
						
							|  |  |  | 							}, | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				Handler: b.handleCurrentStatus, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |