From 1794cbed468c23f6a1f712f2f264cd1a9c43b069 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Thu, 11 Nov 2021 11:22:49 +0800 Subject: [PATCH] refactor(*): feature flag --- pkg/apiserver/conprof/module.go | 8 ++- pkg/apiserver/conprof/service.go | 2 +- pkg/apiserver/info/info.go | 26 ++++---- pkg/apiserver/nonrootlogin/feature_support.go | 34 ---------- .../feature_support.go => user/module.go} | 20 +----- .../semver_check.go => utils/feature_flag.go} | 26 +++++++- pkg/utils/feature_flag_test.go | 63 +++++++++++++++++++ 7 files changed, 113 insertions(+), 66 deletions(-) delete mode 100644 pkg/apiserver/nonrootlogin/feature_support.go rename pkg/apiserver/{conprof/feature_support.go => user/module.go} (53%) rename pkg/{apiserver/utils/semver_check.go => utils/feature_flag.go} (67%) create mode 100644 pkg/utils/feature_flag_test.go diff --git a/pkg/apiserver/conprof/module.go b/pkg/apiserver/conprof/module.go index a88e4263df..9c72969771 100644 --- a/pkg/apiserver/conprof/module.go +++ b/pkg/apiserver/conprof/module.go @@ -13,7 +13,13 @@ package conprof -import "go.uber.org/fx" +import ( + "go.uber.org/fx" + + "github.com/pingcap/tidb-dashboard/pkg/utils" +) + +var FeatureFlagConprof = utils.NewFeatureFlag("conprof", []string{">= 5.3.0"}) var Module = fx.Options( fx.Provide(newService), diff --git a/pkg/apiserver/conprof/service.go b/pkg/apiserver/conprof/service.go index 740fc1320d..ca290bf9b1 100644 --- a/pkg/apiserver/conprof/service.go +++ b/pkg/apiserver/conprof/service.go @@ -81,7 +81,7 @@ func newService(lc fx.Lifecycle, p ServiceParams) *Service { func registerRouter(r *gin.RouterGroup, auth *user.AuthService, s *Service) { endpoint := r.Group("/continuous_profiling") - endpoint.Use(utils.MWForbidByFeatureSupport(IsFeatureSupport(s.params.Config))) + endpoint.Use(utils.MWForbidByFeatureSupport(FeatureFlagConprof.IsSupport(s.params.Config.FeatureVersion))) { endpoint.GET("/config", auth.MWAuthRequired(), s.reverseProxy("/config"), s.conprofConfig) endpoint.POST("/config", auth.MWAuthRequired(), auth.MWRequireWritePriv(), s.reverseProxy("/config"), s.updateConprofConfig) diff --git a/pkg/apiserver/info/info.go b/pkg/apiserver/info/info.go index 05a4e423f0..38de24b3d8 100644 --- a/pkg/apiserver/info/info.go +++ b/pkg/apiserver/info/info.go @@ -23,12 +23,12 @@ import ( "go.uber.org/fx" "github.com/pingcap/tidb-dashboard/pkg/apiserver/conprof" - "github.com/pingcap/tidb-dashboard/pkg/apiserver/nonrootlogin" "github.com/pingcap/tidb-dashboard/pkg/apiserver/user" - "github.com/pingcap/tidb-dashboard/pkg/apiserver/utils" + apiutils "github.com/pingcap/tidb-dashboard/pkg/apiserver/utils" "github.com/pingcap/tidb-dashboard/pkg/config" "github.com/pingcap/tidb-dashboard/pkg/dbstore" "github.com/pingcap/tidb-dashboard/pkg/tidb" + "github.com/pingcap/tidb-dashboard/pkg/utils" "github.com/pingcap/tidb-dashboard/pkg/utils/version" ) @@ -53,7 +53,7 @@ func RegisterRouter(r *gin.RouterGroup, auth *user.AuthService, s *Service) { endpoint.Use(auth.MWAuthRequired()) endpoint.GET("/whoami", s.whoamiHandler) - endpoint.Use(utils.MWConnectTiDB(s.params.TiDBClient)) + endpoint.Use(apiutils.MWConnectTiDB(s.params.TiDBClient)) endpoint.GET("/databases", s.databasesHandler) endpoint.GET("/tables", s.tablesHandler) } @@ -72,12 +72,16 @@ type InfoResponse struct { //nolint // @Security JwtAuth // @Failure 401 {object} utils.APIError "Unauthorized failure" func (s *Service) infoHandler(c *gin.Context) { - supportedFeatures := []string{} - if conprof.IsFeatureSupport(s.params.Config) { - supportedFeatures = append(supportedFeatures, "conprof") + featureFlags := []*utils.FeatureFlag{ + conprof.FeatureFlagConprof, + user.FeatureFlagNonRootLogin, } - if nonrootlogin.IsFeatureSupport(s.params.Config) { - supportedFeatures = append(supportedFeatures, "nonRootLogin") + supportedFeatures := []string{} + for _, ff := range featureFlags { + if !ff.IsSupport(s.params.Config.FeatureVersion) { + continue + } + supportedFeatures = append(supportedFeatures, ff.Name) } resp := InfoResponse{ @@ -102,7 +106,7 @@ type WhoAmIResponse struct { // @Security JwtAuth // @Failure 401 {object} utils.APIError "Unauthorized failure" func (s *Service) whoamiHandler(c *gin.Context) { - sessionUser := utils.GetSession(c) + sessionUser := apiutils.GetSession(c) resp := WhoAmIResponse{ DisplayName: sessionUser.DisplayName, IsShareable: sessionUser.IsShareable, @@ -122,7 +126,7 @@ func (s *Service) databasesHandler(c *gin.Context) { Databases string `gorm:"column:Database"` } var result []databaseSchemas - db := utils.GetTiDBConnection(c) + db := apiutils.GetTiDBConnection(c) err := db.Raw("SHOW DATABASES").Scan(&result).Error if err != nil { _ = c.Error(err) @@ -150,7 +154,7 @@ type tableSchema struct { // @Failure 401 {object} utils.APIError "Unauthorized failure" func (s *Service) tablesHandler(c *gin.Context) { var result []tableSchema - db := utils.GetTiDBConnection(c) + db := apiutils.GetTiDBConnection(c) tx := db.Select([]string{"TABLE_NAME", "TIDB_TABLE_ID"}).Table("INFORMATION_SCHEMA.TABLES") databaseName := c.Query("database_name") diff --git a/pkg/apiserver/nonrootlogin/feature_support.go b/pkg/apiserver/nonrootlogin/feature_support.go deleted file mode 100644 index cd958db7c2..0000000000 --- a/pkg/apiserver/nonrootlogin/feature_support.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package nonrootlogin - -import ( - "github.com/pingcap/tidb-dashboard/pkg/apiserver/utils" - "github.com/pingcap/tidb-dashboard/pkg/config" -) - -var ( - supportedTiDBVersions = []string{">= 5.3.0"} - featureSupported *bool -) - -func IsFeatureSupport(config *config.Config) (supported bool) { - if featureSupported != nil { - return *featureSupported - } - - supported = utils.IsVersionSupport(config.FeatureVersion, supportedTiDBVersions) - featureSupported = &supported - return -} diff --git a/pkg/apiserver/conprof/feature_support.go b/pkg/apiserver/user/module.go similarity index 53% rename from pkg/apiserver/conprof/feature_support.go rename to pkg/apiserver/user/module.go index 51f015dddd..6818410b11 100644 --- a/pkg/apiserver/conprof/feature_support.go +++ b/pkg/apiserver/user/module.go @@ -11,24 +11,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package conprof +package user import ( - "github.com/pingcap/tidb-dashboard/pkg/apiserver/utils" - "github.com/pingcap/tidb-dashboard/pkg/config" + "github.com/pingcap/tidb-dashboard/pkg/utils" ) -var ( - supportedTiDBVersions = []string{">= 5.3.0"} - featureSupported *bool -) - -func IsFeatureSupport(config *config.Config) (supported bool) { - if featureSupported != nil { - return *featureSupported - } - - supported = utils.IsVersionSupport(config.FeatureVersion, supportedTiDBVersions) - featureSupported = &supported - return -} +var FeatureFlagNonRootLogin = utils.NewFeatureFlag("nonRootLogin", []string{">= 5.3.0"}) diff --git a/pkg/apiserver/utils/semver_check.go b/pkg/utils/feature_flag.go similarity index 67% rename from pkg/apiserver/utils/semver_check.go rename to pkg/utils/feature_flag.go index 31626e49c2..92f773e4ec 100644 --- a/pkg/apiserver/utils/semver_check.go +++ b/pkg/utils/feature_flag.go @@ -1,4 +1,4 @@ -// Copyright 2020 PingCAP, Inc. +// Copyright 2021 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -8,6 +8,7 @@ // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. @@ -21,10 +22,31 @@ import ( "github.com/pingcap/tidb-dashboard/pkg/utils/version" ) +type FeatureFlag struct { + Name string + + supportedVersions []string + supported *bool +} + +func NewFeatureFlag(name string, supportedVersions []string) *FeatureFlag { + return &FeatureFlag{Name: name, supportedVersions: supportedVersions} +} + +func (ff *FeatureFlag) IsSupport(targetVersion string) bool { + if ff.supported != nil { + return *ff.supported + } + + supported := isVersionSupport(targetVersion, ff.supportedVersions) + ff.supported = &supported + return supported +} + // IsVersionSupport checks if a semantic version fits within a set of constraints // pdVersion, standaloneVersion examples: "v5.2.2", "v5.3.0", "v5.4.0-alpha-xxx", "5.3.0" (semver can handle `v` prefix by itself) // constraints examples: "~5.2.2", ">= 5.3.0", see semver docs to get more information -func IsVersionSupport(standaloneVersion string, constraints []string) bool { +func isVersionSupport(standaloneVersion string, constraints []string) bool { curVersion := standaloneVersion if version.Standalone == "No" { curVersion = version.PDVersion diff --git a/pkg/utils/feature_flag_test.go b/pkg/utils/feature_flag_test.go new file mode 100644 index 0000000000..c614a589e8 --- /dev/null +++ b/pkg/utils/feature_flag_test.go @@ -0,0 +1,63 @@ +// Copyright 2021 Suhaha +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "testing" + + "github.com/pingcap/tidb-dashboard/pkg/utils/version" + "github.com/stretchr/testify/require" +) + +func Test_IsSupport(t *testing.T) { + ff := NewFeatureFlag("testFeature", []string{">= 5.3.0"}) + + require.Equal(t, false, ff.IsSupport("v5.2.2")) + // The results do not change because of caching. This is expected. + require.Equal(t, false, ff.IsSupport("v5.3.0")) + + ff2 := NewFeatureFlag("testFeature", []string{">= 5.3.0"}) + require.Equal(t, true, ff2.IsSupport("v5.3.0")) + + ff3 := NewFeatureFlag("testFeature", []string{">= 5.3.0"}) + require.Equal(t, true, ff3.IsSupport("v5.3.2")) +} + +func Test_isVersionSupport(t *testing.T) { + type Args struct { + target string + supported []string + } + tests := []struct { + want bool + args Args + }{ + {want: false, args: Args{target: "v4.2.0", supported: []string{">= 5.3.0"}}}, + {want: false, args: Args{target: "v5.2.0", supported: []string{">= 5.3.0"}}}, + {want: true, args: Args{target: "v5.3.0", supported: []string{">= 5.3.0"}}}, + {want: false, args: Args{target: "v5.2.0-alpha-xxx", supported: []string{">= 5.3.0"}}}, + {want: true, args: Args{target: "v5.3.0-alpha-xxx", supported: []string{">= 5.3.0"}}}, + {want: true, args: Args{target: "v5.3.0", supported: []string{"= 5.3.0"}}}, + {want: false, args: Args{target: "v5.3.1", supported: []string{"= 5.3.0"}}}, + } + + for _, tt := range tests { + isVersionSupport(tt.args.target, tt.args.supported) + } + + version.Standalone = "No" + version.PDVersion = "v5.3.0" + require.Equal(t, true, isVersionSupport("v100.0.0", []string{"= 5.3.0"})) +}