diff --git a/server/embed/config_test.go b/server/embed/config_test.go index cf18a5ec55c..73769408cd9 100644 --- a/server/embed/config_test.go +++ b/server/embed/config_test.go @@ -97,6 +97,7 @@ func TestConfigFileFeatureGates(t *testing.T) { name string serverFeatureGatesJSON string experimentalStopGRPCServiceOnDefrag string + experimentalInitialCorruptCheck string expectErr bool expectedFeatures map[featuregate.Feature]bool }{ @@ -105,6 +106,7 @@ func TestConfigFileFeatureGates(t *testing.T) { expectedFeatures: map[featuregate.Feature]bool{ features.DistributedTracing: false, features.StopGRPCServiceOnDefrag: false, + features.InitialCorruptCheck: false, }, }, { @@ -113,6 +115,12 @@ func TestConfigFileFeatureGates(t *testing.T) { experimentalStopGRPCServiceOnDefrag: "false", expectErr: true, }, + { + name: "cannot set both experimental flag and feature gate flag for InitialCorruptCheck", + serverFeatureGatesJSON: "InitialCorruptCheck=true", + experimentalInitialCorruptCheck: "false", + expectErr: true, + }, { name: "ok to set different experimental flag and feature gate flag", serverFeatureGatesJSON: "DistributedTracing=true", @@ -120,6 +128,7 @@ func TestConfigFileFeatureGates(t *testing.T) { expectedFeatures: map[featuregate.Feature]bool{ features.DistributedTracing: true, features.StopGRPCServiceOnDefrag: true, + features.InitialCorruptCheck: false, }, }, { @@ -128,6 +137,7 @@ func TestConfigFileFeatureGates(t *testing.T) { expectedFeatures: map[featuregate.Feature]bool{ features.StopGRPCServiceOnDefrag: true, features.DistributedTracing: false, + features.InitialCorruptCheck: false, }, }, { @@ -136,14 +146,43 @@ func TestConfigFileFeatureGates(t *testing.T) { expectedFeatures: map[featuregate.Feature]bool{ features.StopGRPCServiceOnDefrag: false, features.DistributedTracing: false, + features.InitialCorruptCheck: false, + }, + }, + { + name: "can set feature gate experimentalInitialCorruptCheck to true from experimental flag", + experimentalInitialCorruptCheck: "true", + expectedFeatures: map[featuregate.Feature]bool{ + features.StopGRPCServiceOnDefrag: false, + features.DistributedTracing: false, + features.InitialCorruptCheck: true, + }, + }, + { + name: "can set feature gate experimentalInitialCorruptCheck to false from experimental flag", + experimentalInitialCorruptCheck: "false", + expectedFeatures: map[featuregate.Feature]bool{ + features.StopGRPCServiceOnDefrag: false, + features.DistributedTracing: false, + features.InitialCorruptCheck: false, }, }, { - name: "can set feature gate to true from feature gate flag", + name: "can set feature gate StopGRPCServiceOnDefrag to true from feature gate flag", serverFeatureGatesJSON: "StopGRPCServiceOnDefrag=true", expectedFeatures: map[featuregate.Feature]bool{ features.StopGRPCServiceOnDefrag: true, features.DistributedTracing: false, + features.InitialCorruptCheck: false, + }, + }, + { + name: "can set feature gate InitialCorruptCheck to true from feature gate flag", + serverFeatureGatesJSON: "InitialCorruptCheck=true", + expectedFeatures: map[featuregate.Feature]bool{ + features.StopGRPCServiceOnDefrag: false, + features.DistributedTracing: false, + features.InitialCorruptCheck: true, }, }, { @@ -152,6 +191,7 @@ func TestConfigFileFeatureGates(t *testing.T) { expectedFeatures: map[featuregate.Feature]bool{ features.StopGRPCServiceOnDefrag: false, features.DistributedTracing: false, + features.InitialCorruptCheck: false, }, }, } @@ -159,11 +199,20 @@ func TestConfigFileFeatureGates(t *testing.T) { t.Run(tc.name, func(t *testing.T) { yc := struct { ExperimentalStopGRPCServiceOnDefrag *bool `json:"experimental-stop-grpc-service-on-defrag,omitempty"` + ExperimentalInitialCorruptCheck *bool `json:"experimental-initial-corrupt-check,omitempty"` ServerFeatureGatesJSON string `json:"feature-gates"` }{ ServerFeatureGatesJSON: tc.serverFeatureGatesJSON, } + if tc.experimentalInitialCorruptCheck != "" { + experimentalInitialCorruptCheck, err := strconv.ParseBool(tc.experimentalInitialCorruptCheck) + if err != nil { + t.Fatal(err) + } + yc.ExperimentalInitialCorruptCheck = &experimentalInitialCorruptCheck + } + if tc.experimentalStopGRPCServiceOnDefrag != "" { experimentalStopGRPCServiceOnDefrag, err := strconv.ParseBool(tc.experimentalStopGRPCServiceOnDefrag) if err != nil { @@ -171,6 +220,7 @@ func TestConfigFileFeatureGates(t *testing.T) { } yc.ExperimentalStopGRPCServiceOnDefrag = &experimentalStopGRPCServiceOnDefrag } + b, err := yaml.Marshal(&yc) if err != nil { t.Fatal(err) diff --git a/server/embed/etcd.go b/server/embed/etcd.go index 2970a804d11..80cc1cc3bf2 100644 --- a/server/embed/etcd.go +++ b/server/embed/etcd.go @@ -48,6 +48,7 @@ import ( "go.etcd.io/etcd/server/v3/etcdserver" "go.etcd.io/etcd/server/v3/etcdserver/api/etcdhttp" "go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp" + "go.etcd.io/etcd/server/v3/features" "go.etcd.io/etcd/server/v3/storage" "go.etcd.io/etcd/server/v3/verify" ) @@ -203,7 +204,6 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { TokenTTL: cfg.AuthTokenTTL, CORS: cfg.CORS, HostWhitelist: cfg.HostWhitelist, - InitialCorruptCheck: cfg.ExperimentalInitialCorruptCheck, CorruptCheckTime: cfg.ExperimentalCorruptCheckTime, CompactHashCheckEnabled: cfg.ExperimentalCompactHashCheckEnabled, CompactHashCheckTime: cfg.ExperimentalCompactHashCheckTime, @@ -259,7 +259,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { // newly started member ("memberInitialized==false") // does not need corruption check - if memberInitialized && srvcfg.InitialCorruptCheck { + if memberInitialized && srvcfg.ServerFeatureGate.Enabled(features.InitialCorruptCheck) { if err = e.Server.CorruptionChecker().InitialCheck(); err != nil { // set "EtcdServer" to nil, so that it does not block on "EtcdServer.Close()" // (nothing to close since rafthttp transports have not been started) diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go index 23c531b720c..52b8d3f92e3 100644 --- a/server/etcdmain/help.go +++ b/server/etcdmain/help.go @@ -273,7 +273,7 @@ Experimental distributed tracing: Number of samples to collect per million spans for distributed tracing. Disabled by default. Experimental feature: - --experimental-initial-corrupt-check 'false' + --experimental-initial-corrupt-check 'false'. It's deprecated, and will be decommissioned in v3.7. Use '--feature-gates=InitialCorruptCheck=true' instead. Enable to check data corruption before serving any client/peer traffic. --experimental-corrupt-check-time '0s' Duration of time between cluster corruption check passes. diff --git a/server/features/etcd_features.go b/server/features/etcd_features.go index d6804f2d048..abb1aa171f8 100644 --- a/server/features/etcd_features.go +++ b/server/features/etcd_features.go @@ -45,18 +45,25 @@ const ( // alpha: v3.6 // main PR: https://github.com/etcd-io/etcd/pull/18279 StopGRPCServiceOnDefrag featuregate.Feature = "StopGRPCServiceOnDefrag" + // InitialCorruptCheck enable to check data corruption before serving any client/peer traffic. + // owner: @serathius + // alpha: v3.6 + // main PR: https://github.com/etcd-io/etcd/pull/10524 + InitialCorruptCheck featuregate.Feature = "InitialCorruptCheck" ) var ( DefaultEtcdServerFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ DistributedTracing: {Default: false, PreRelease: featuregate.Alpha}, StopGRPCServiceOnDefrag: {Default: false, PreRelease: featuregate.Alpha}, + InitialCorruptCheck: {Default: false, PreRelease: featuregate.Alpha}, } // ExperimentalFlagToFeatureMap is the map from the cmd line flags of experimental features // to their corresponding feature gates. // Deprecated: only add existing experimental features here. DO NOT use for new features. ExperimentalFlagToFeatureMap = map[string]featuregate.Feature{ "experimental-stop-grpc-service-on-defrag": StopGRPCServiceOnDefrag, + "experimental-initial-corrupt-check": InitialCorruptCheck, } )