FeatureToggles: Removed deprecated code (#111750)
This commit is contained in:
@@ -125,11 +125,6 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Get("/admin/migrate-to-cloud", authorize(cloudmigration.MigrationAssistantAccess), hs.Index)
|
||||
}
|
||||
|
||||
// feature toggle admin page
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagFeatureToggleAdminPage) {
|
||||
r.Get("/admin/featuretoggles", authorize(ac.EvalPermission(ac.ActionFeatureManagementRead)), hs.Index)
|
||||
}
|
||||
|
||||
// secrets management page
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagSecretsManagementAppPlatform) && hs.Features.IsEnabledGlobally(featuremgmt.FlagSecretsManagementAppPlatformUI) {
|
||||
r.Get("/admin/secrets", authorize(ac.EvalAny(
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=featuretoggle.grafana.app
|
||||
|
||||
package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1"
|
||||
@@ -1,54 +0,0 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
GROUP = "featuretoggle.grafana.app"
|
||||
VERSION = "v0alpha1"
|
||||
APIVERSION = GROUP + "/" + VERSION
|
||||
)
|
||||
|
||||
// FeatureResourceInfo represents each feature that may have a toggle
|
||||
var FeatureResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"features", "feature", "Feature",
|
||||
func() runtime.Object { return &Feature{} },
|
||||
func() runtime.Object { return &FeatureList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Stage", Type: "string", Format: "string", Description: "Where is the flag in the dev cycle"},
|
||||
{Name: "Owner", Type: "string", Format: "string", Description: "Which team owns the feature"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
r, ok := obj.(*Feature)
|
||||
if ok {
|
||||
return []interface{}{
|
||||
r.Name,
|
||||
r.Spec.Stage,
|
||||
r.Spec.Owner,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("expected resource or info")
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// TogglesResourceInfo represents the actual configuration
|
||||
var TogglesResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"featuretoggles", "featuretoggle", "FeatureToggles",
|
||||
func() runtime.Object { return &FeatureToggles{} },
|
||||
func() runtime.Object { return &FeatureTogglesList{} },
|
||||
utils.TableColumns{}, // default table converter
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
|
||||
)
|
||||
@@ -1,215 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
commonv0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Feature) DeepCopyInto(out *Feature) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Feature.
|
||||
func (in *Feature) DeepCopy() *Feature {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Feature)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Feature) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FeatureList) DeepCopyInto(out *FeatureList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Feature, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureList.
|
||||
func (in *FeatureList) DeepCopy() *FeatureList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FeatureList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FeatureList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FeatureSpec) DeepCopyInto(out *FeatureSpec) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureSpec.
|
||||
func (in *FeatureSpec) DeepCopy() *FeatureSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FeatureSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FeatureToggles) DeepCopyInto(out *FeatureToggles) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
if in.Spec != nil {
|
||||
in, out := &in.Spec, &out.Spec
|
||||
*out = make(map[string]bool, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureToggles.
|
||||
func (in *FeatureToggles) DeepCopy() *FeatureToggles {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FeatureToggles)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FeatureToggles) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FeatureTogglesList) DeepCopyInto(out *FeatureTogglesList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]FeatureToggles, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureTogglesList.
|
||||
func (in *FeatureTogglesList) DeepCopy() *FeatureTogglesList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FeatureTogglesList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FeatureTogglesList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResolvedToggleState) DeepCopyInto(out *ResolvedToggleState) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Enabled != nil {
|
||||
in, out := &in.Enabled, &out.Enabled
|
||||
*out = make(map[string]bool, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Toggles != nil {
|
||||
in, out := &in.Toggles, &out.Toggles
|
||||
*out = make([]ToggleStatus, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedToggleState.
|
||||
func (in *ResolvedToggleState) DeepCopy() *ResolvedToggleState {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResolvedToggleState)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ResolvedToggleState) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ToggleStatus) DeepCopyInto(out *ToggleStatus) {
|
||||
*out = *in
|
||||
if in.Source != nil {
|
||||
in, out := &in.Source, &out.Source
|
||||
*out = new(commonv0alpha1.ObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToggleStatus.
|
||||
func (in *ToggleStatus) DeepCopy() *ToggleStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ToggleStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1,ResolvedToggleState,Toggles
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1,FeatureSpec,FrontendOnly
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1,FeatureSpec,Owner
|
||||
+7
-1
@@ -1,4 +1,4 @@
|
||||
package v0alpha1
|
||||
package feature_toggle_api
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -6,6 +6,12 @@ import (
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
GROUP = "featuretoggle.grafana.app"
|
||||
VERSION = "v0alpha1"
|
||||
APIVERSION = GROUP + "/" + VERSION
|
||||
)
|
||||
|
||||
// Feature represents a feature in development and information about that feature
|
||||
// It does *not* know the status, only defines properties about the feature itself
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -14,10 +13,7 @@ var (
|
||||
)
|
||||
|
||||
type FeatureManager struct {
|
||||
isDevMod bool
|
||||
restartRequired bool
|
||||
|
||||
Settings setting.FeatureMgmtSettings
|
||||
isDevMod bool
|
||||
|
||||
flags map[string]*FeatureFlag
|
||||
enabled map[string]bool // only the "on" values
|
||||
@@ -131,66 +127,6 @@ func (fm *FeatureManager) GetFlags() []FeatureFlag {
|
||||
return v
|
||||
}
|
||||
|
||||
// isFeatureEditingAllowed checks if the backend is properly configured to allow feature toggle changes from the UI
|
||||
func (fm *FeatureManager) IsFeatureEditingAllowed() bool {
|
||||
return fm.Settings.AllowEditing && fm.Settings.UpdateWebhook != ""
|
||||
}
|
||||
|
||||
// indicate if a change has been made (not that accurate, but better than nothing)
|
||||
func (fm *FeatureManager) IsRestartRequired() bool {
|
||||
return fm.restartRequired
|
||||
}
|
||||
|
||||
// Flags that can be edited
|
||||
func (fm *FeatureManager) IsEditableFromAdminPage(key string) bool {
|
||||
flag, ok := fm.flags[key]
|
||||
if !ok ||
|
||||
!fm.IsFeatureEditingAllowed() ||
|
||||
!flag.AllowSelfServe ||
|
||||
flag.Name == FlagFeatureToggleAdminPage {
|
||||
return false
|
||||
}
|
||||
return flag.Stage == FeatureStageGeneralAvailability ||
|
||||
flag.Stage == FeatureStagePublicPreview ||
|
||||
flag.Stage == FeatureStageDeprecated
|
||||
}
|
||||
|
||||
// Flags that should not be shown in the UI (regardless of their state)
|
||||
func (fm *FeatureManager) IsHiddenFromAdminPage(key string, lenient bool) bool {
|
||||
_, hide := fm.Settings.HiddenToggles[key]
|
||||
flag, ok := fm.flags[key]
|
||||
if !ok || flag.HideFromAdminPage || hide {
|
||||
return true // unknown flag (should we show it as a warning!)
|
||||
}
|
||||
|
||||
// Explicitly hidden from configs
|
||||
_, found := fm.Settings.HiddenToggles[key]
|
||||
if found {
|
||||
return true
|
||||
}
|
||||
if lenient {
|
||||
return false
|
||||
}
|
||||
|
||||
return flag.Stage == FeatureStageUnknown ||
|
||||
flag.Stage == FeatureStageExperimental ||
|
||||
flag.Stage == FeatureStagePrivatePreview
|
||||
}
|
||||
|
||||
// Get the flags that were explicitly set on startup
|
||||
func (fm *FeatureManager) GetStartupFlags() map[string]bool {
|
||||
return fm.startup
|
||||
}
|
||||
|
||||
// Perhaps expose the flag warnings
|
||||
func (fm *FeatureManager) GetWarning() map[string]string {
|
||||
return fm.warnings
|
||||
}
|
||||
|
||||
func (fm *FeatureManager) SetRestartRequired() {
|
||||
fm.restartRequired = true
|
||||
}
|
||||
|
||||
// ############# Test Functions #############
|
||||
|
||||
func WithFeatures(spec ...any) FeatureToggles {
|
||||
@@ -223,33 +159,3 @@ func WithManager(spec ...any) *FeatureManager {
|
||||
|
||||
return &FeatureManager{enabled: enabled, flags: features, startup: enabled, warnings: map[string]string{}}
|
||||
}
|
||||
|
||||
// WithFeatureManager is used to define feature toggle manager for testing.
|
||||
// It should be used when your test feature toggles require metadata beyond `Name` and `Enabled`.
|
||||
// You should provide a feature toggle Name at a minimum.
|
||||
func WithFeatureManager(cfg setting.FeatureMgmtSettings, flags []*FeatureFlag, disabled ...string) *FeatureManager {
|
||||
count := len(flags)
|
||||
features := make(map[string]*FeatureFlag, count)
|
||||
enabled := make(map[string]bool, count)
|
||||
|
||||
dis := make(map[string]bool)
|
||||
for _, v := range disabled {
|
||||
dis[v] = true
|
||||
}
|
||||
|
||||
for _, f := range flags {
|
||||
if f.Name == "" {
|
||||
continue
|
||||
}
|
||||
features[f.Name] = f
|
||||
enabled[f.Name] = !dis[f.Name]
|
||||
}
|
||||
|
||||
return &FeatureManager{
|
||||
Settings: cfg,
|
||||
enabled: enabled,
|
||||
flags: features,
|
||||
startup: enabled,
|
||||
warnings: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
|
||||
featuretoggle "github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1"
|
||||
featuretoggleapi "github.com/grafana/grafana/pkg/services/featuremgmt/feature_toggle_api"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -332,15 +332,6 @@ var (
|
||||
RequiresRestart: true,
|
||||
Owner: grafanaAppPlatformSquad,
|
||||
},
|
||||
{
|
||||
Name: "featureToggleAdminPage",
|
||||
Description: "Enable admin page for managing feature toggles from the Grafana front-end. Grafana Cloud only.",
|
||||
Stage: FeatureStageExperimental,
|
||||
FrontendOnly: false,
|
||||
Owner: grafanaBackendServicesSquad,
|
||||
RequiresRestart: true,
|
||||
HideFromDocs: true,
|
||||
},
|
||||
{
|
||||
Name: "awsAsyncQueryCaching",
|
||||
Description: "Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled",
|
||||
@@ -2073,8 +2064,8 @@ var (
|
||||
var f embed.FS
|
||||
|
||||
// Get the cached feature list (exposed as a k8s resource)
|
||||
func GetEmbeddedFeatureList() (featuretoggle.FeatureList, error) {
|
||||
features := featuretoggle.FeatureList{}
|
||||
func GetEmbeddedFeatureList() (featuretoggleapi.FeatureList, error) {
|
||||
features := featuretoggleapi.FeatureList{}
|
||||
body, err := f.ReadFile("toggles_gen.json")
|
||||
if err == nil {
|
||||
err = json.Unmarshal(body, &features)
|
||||
|
||||
@@ -27,7 +27,6 @@ func ProvideManagerService(cfg *setting.Cfg) (*FeatureManager, error) {
|
||||
enabled: make(map[string]bool),
|
||||
startup: make(map[string]bool),
|
||||
warnings: make(map[string]string),
|
||||
Settings: cfg.FeatureManagement,
|
||||
log: log.New("featuremgmt"),
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ datasourceAPIServers,experimental,@grafana/grafana-app-platform-squad,false,true
|
||||
grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,true,true,false
|
||||
provisioning,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
grafanaAPIServerEnsureKubectlAccess,experimental,@grafana/grafana-app-platform-squad,true,true,false
|
||||
featureToggleAdminPage,experimental,@grafana/grafana-backend-services-squad,false,true,false
|
||||
awsAsyncQueryCaching,GA,@grafana/aws-datasources,false,false,false
|
||||
queryCacheRequestDeduplication,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
permissionsFilterRemoveSubquery,experimental,@grafana/search-and-storage,false,false,false
|
||||
|
||||
|
@@ -179,10 +179,6 @@ const (
|
||||
// Start an additional https handler and write kubectl options
|
||||
FlagGrafanaAPIServerEnsureKubectlAccess = "grafanaAPIServerEnsureKubectlAccess"
|
||||
|
||||
// FlagFeatureToggleAdminPage
|
||||
// Enable admin page for managing feature toggles from the Grafana front-end. Grafana Cloud only.
|
||||
FlagFeatureToggleAdminPage = "featureToggleAdminPage"
|
||||
|
||||
// FlagAwsAsyncQueryCaching
|
||||
// Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled
|
||||
FlagAwsAsyncQueryCaching = "awsAsyncQueryCaching"
|
||||
|
||||
@@ -1577,6 +1577,7 @@
|
||||
"name": "featureToggleAdminPage",
|
||||
"resourceVersion": "1758022099771",
|
||||
"creationTimestamp": "2023-07-18T20:43:32Z",
|
||||
"deletionTimestamp": "2025-09-29T13:36:16Z",
|
||||
"annotations": {
|
||||
"grafana.app/updatedTimestamp": "2025-09-16 11:28:19.771156 +0000 UTC"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
featuretoggleapi "github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1"
|
||||
featuretoggleapi "github.com/grafana/grafana/pkg/services/featuremgmt/feature_toggle_api"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt/strcase"
|
||||
)
|
||||
|
||||
|
||||
@@ -44,15 +44,6 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
||||
Text: "Organizations", SubTitle: "Isolated instances of Grafana running on the same server", Id: "global-orgs", Url: s.cfg.AppSubURL + "/admin/orgs", Icon: "building",
|
||||
})
|
||||
}
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagFeatureToggleAdminPage) && hasAccess(ac.EvalPermission(ac.ActionFeatureManagementRead)) {
|
||||
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
|
||||
Text: "Feature toggles",
|
||||
SubTitle: "View and edit feature toggles",
|
||||
Id: "feature-toggles",
|
||||
Url: s.cfg.AppSubURL + "/admin/featuretoggles",
|
||||
Icon: "toggle-on",
|
||||
})
|
||||
}
|
||||
if hasAccess(cloudmigration.MigrationAssistantAccess) && s.features.IsEnabled(ctx, featuremgmt.FlagOnPremToCloudMigrations) {
|
||||
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
|
||||
Text: "Migrate to Grafana Cloud",
|
||||
|
||||
@@ -543,9 +543,6 @@ type Cfg struct {
|
||||
// Cloud Migration
|
||||
CloudMigration CloudMigrationSettings
|
||||
|
||||
// Feature Management Settings
|
||||
FeatureManagement FeatureMgmtSettings
|
||||
|
||||
// Alerting
|
||||
AlertingEvaluationTimeout time.Duration
|
||||
AlertingNotificationTimeout time.Duration
|
||||
@@ -1429,7 +1426,6 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
|
||||
logSection := iniFile.Section("log")
|
||||
cfg.UserFacingDefaultError = logSection.Key("user_facing_default_error").MustString("please inspect Grafana server log for details")
|
||||
|
||||
cfg.readFeatureManagementConfig()
|
||||
cfg.readPublicDashboardsSettings()
|
||||
cfg.readCloudMigrationSettings()
|
||||
cfg.readSecretsManagerSettings()
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type FeatureMgmtSettings struct {
|
||||
HiddenToggles map[string]struct{}
|
||||
ReadOnlyToggles map[string]struct{}
|
||||
AllowEditing bool
|
||||
UpdateWebhook string
|
||||
UpdateWebhookToken string
|
||||
}
|
||||
|
||||
func (cfg *Cfg) readFeatureManagementConfig() {
|
||||
section := cfg.Raw.Section("feature_management")
|
||||
|
||||
hiddenToggles := make(map[string]struct{})
|
||||
readOnlyToggles := make(map[string]struct{})
|
||||
|
||||
// parse the comma separated list in `hidden_toggles`.
|
||||
hiddenTogglesStr := valueAsString(section, "hidden_toggles", "")
|
||||
for _, feature := range util.SplitString(hiddenTogglesStr) {
|
||||
hiddenToggles[feature] = struct{}{}
|
||||
}
|
||||
|
||||
// parse the comma separated list in `read_only_toggles`.
|
||||
readOnlyTogglesStr := valueAsString(section, "read_only_toggles", "")
|
||||
for _, feature := range util.SplitString(readOnlyTogglesStr) {
|
||||
readOnlyToggles[feature] = struct{}{}
|
||||
}
|
||||
|
||||
cfg.FeatureManagement.HiddenToggles = hiddenToggles
|
||||
cfg.FeatureManagement.ReadOnlyToggles = readOnlyToggles
|
||||
cfg.FeatureManagement.AllowEditing = cfg.SectionWithEnvOverrides("feature_management").Key("allow_editing").MustBool(false)
|
||||
cfg.FeatureManagement.UpdateWebhook = cfg.SectionWithEnvOverrides("feature_management").Key("update_webhook").MustString("")
|
||||
cfg.FeatureManagement.UpdateWebhookToken = cfg.SectionWithEnvOverrides("feature_management").Key("update_webhook_token").MustString("")
|
||||
}
|
||||
Reference in New Issue
Block a user