diff --git a/server/config/v2_deprecation.go b/server/config/v2_deprecation.go index c66fd0c05d6..c50401cc937 100644 --- a/server/config/v2_deprecation.go +++ b/server/config/v2_deprecation.go @@ -17,47 +17,50 @@ package config type V2DeprecationEnum string const ( - // No longer supported in v3.6 + // V2Depr0NotYet means v2store isn't deprecated yet. + // Default in v3.5, and no longer supported in v3.6. V2Depr0NotYet = V2DeprecationEnum("not-yet") - // No longer supported in v3.6 - // - // Deprecated: Please use V2Depr0NotYet. + + // Deprecated: to be decommissioned in 3.7. Please use V2Depr0NotYet. + // TODO: remove in 3.7 //revive:disable-next-line:var-naming V2_DEPR_0_NOT_YET = V2Depr0NotYet - // Default in v3.6. Meaningful v2 state is not allowed. - // The V2 files are maintained for v3.5 rollback. - V2Depr1WriteOnly = V2DeprecationEnum("write-only") + // V2Depr1WriteOnly means only writing v2store is allowed. // Default in v3.6. Meaningful v2 state is not allowed. // The V2 files are maintained for v3.5 rollback. - // - // Deprecated: Please use V2Depr1WriteOnly. + V2Depr1WriteOnly = V2DeprecationEnum("write-only") + + // Deprecated: to be decommissioned in 3.7. Please use V2Depr1WriteOnly. + // TODO: remove in 3.7 //revive:disable-next-line:var-naming V2_DEPR_1_WRITE_ONLY = V2Depr1WriteOnly - // V2store is WIPED if found !!! + // V2Depr1WriteOnlyDrop means v2store is WIPED if found !!! + // Will be default in 3.7. V2Depr1WriteOnlyDrop = V2DeprecationEnum("write-only-drop-data") - // V2store is WIPED if found !!! - // - // Deprecated: Pleae use V2Depr1WriteOnlyDrop. + + // Deprecated: to be decommissioned in 3.7. Pleae use V2Depr1WriteOnlyDrop. + // TODO: remove in 3.7 //revive:disable-next-line:var-naming V2_DEPR_1_WRITE_ONLY_DROP = V2Depr1WriteOnlyDrop - // V2store is neither written nor read. Usage of this configuration is blocking + // V2Depr2Gone means v2store is completely gone. The v2store is + // neither written nor read. Anything related to v2store will be + // cleaned up in v3.8. Usage of this configuration is blocking // ability to rollback to etcd v3.5. V2Depr2Gone = V2DeprecationEnum("gone") - // V2store is neither written nor read. Usage of this configuration is blocking - // ability to rollback to etcd v3.5. - // - // Deprecated: Please use V2Depr2Gone + + // Deprecated: to be decommissioned in 3.7. Please use V2Depr2Gone. + // TODO: remove in 3.7 //revive:disable-next-line:var-naming V2_DEPR_2_GONE = V2Depr2Gone - // Default deprecation level. + // V2DeprDefault is the default deprecation level. V2DeprDefault = V2Depr1WriteOnly - // Default deprecation level. - // - // Deprecated: Please use V2DeprDefault. + + // Deprecated: to be decommissioned in 3.7. Please use V2DeprDefault. + // TODO: remove in 3.7 //revive:disable-next-line:var-naming V2_DEPR_DEFAULT = V2DeprDefault ) diff --git a/server/embed/config.go b/server/embed/config.go index df806ede2ec..7ffcdf066cc 100644 --- a/server/embed/config.go +++ b/server/embed/config.go @@ -459,7 +459,9 @@ type Config struct { // ExperimentalStopGRPCServiceOnDefrag enables etcd gRPC service to stop serving client requests on defragmentation. ExperimentalStopGRPCServiceOnDefrag bool `json:"experimental-stop-grpc-service-on-defrag"` - // V2Deprecation describes phase of API & Storage V2 support + // V2Deprecation describes phase of API & Storage V2 support. + // Deprecated and scheduled for removal in v3.8. + // Do not set this field for embedded use cases, as it has no effect. However, setting it will not cause any harm. V2Deprecation config.V2DeprecationEnum `json:"v2-deprecation"` // ServerFeatureGate is a server level feature gate diff --git a/server/embed/etcd.go b/server/embed/etcd.go index 2f3d47d0a42..81d9962adde 100644 --- a/server/embed/etcd.go +++ b/server/embed/etcd.go @@ -376,6 +376,8 @@ func print(lg *zap.Logger, ec Config, sc config.ServerConfig, memberInitialized zap.String("downgrade-check-interval", sc.DowngradeCheckTime.String()), zap.Int("max-learners", sc.ExperimentalMaxLearners), + + zap.String("v2-deprecation", string(ec.V2Deprecation)), ) } diff --git a/server/etcdmain/config.go b/server/etcdmain/config.go index bf1625e7954..b1cfc03c688 100644 --- a/server/etcdmain/config.go +++ b/server/etcdmain/config.go @@ -60,6 +60,7 @@ var ( deprecatedFlags = map[string]string{ // TODO: remove in 3.7. "snapshot-count": "--snapshot-count is deprecated in 3.6 and will be decommissioned in 3.7.", + "v2-deprecation": "--v2-deprecation is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input.", } ) @@ -74,9 +75,11 @@ type config struct { // configFlags has the set of flags used for command line parsing a Config type configFlags struct { - flagSet *flag.FlagSet - clusterState *flags.SelectiveStringValue - fallback *flags.SelectiveStringValue + flagSet *flag.FlagSet + clusterState *flags.SelectiveStringValue + fallback *flags.SelectiveStringValue + // Deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input. + // TODO: remove in v3.8. v2deprecation *flags.SelectiveStringsValue } @@ -108,7 +111,7 @@ func newConfig() *config { fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file. Note that if a configuration file is provided, other command line flags and environment variables will be ignored.") fs.Var(cfg.cf.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %q", cfg.cf.fallback.Valids())) fs.Var(cfg.cf.clusterState, "initial-cluster-state", "Initial cluster state ('new' when bootstrapping a new cluster or 'existing' when adding new members to an existing cluster). After successful initialization (bootstrapping or adding), flag is ignored on restarts.") - fs.Var(cfg.cf.v2deprecation, "v2-deprecation", fmt.Sprintf("v2store deprecation stage: %q. ", cfg.cf.v2deprecation.Valids())) + fs.Var(cfg.cf.v2deprecation, "v2-deprecation", fmt.Sprintf("v2store deprecation stage: %q. Deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input.", cfg.cf.v2deprecation.Valids())) fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.") // ignored @@ -161,9 +164,8 @@ func (cfg *config) parse(arguments []string) error { err = cfg.configFromCmdLine() } - if cfg.ec.V2Deprecation == "" { - cfg.ec.V2Deprecation = cconfig.V2DeprDefault - } + // `V2Deprecation` (--v2-deprecation) is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input. + cfg.ec.V2Deprecation = cconfig.V2DeprDefault cfg.ec.WarningUnaryRequestDuration, perr = cfg.parseWarningUnaryRequestDuration() if perr != nil { diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go index 81c1b131e4e..8871dca78cf 100644 --- a/server/etcdmain/help.go +++ b/server/etcdmain/help.go @@ -169,12 +169,12 @@ Clustering: --auto-compaction-mode 'periodic' Interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention. --v2-deprecation '` + string(cconfig.V2DeprDefault) + `' - Phase of v2store deprecation. Allows to opt-in for higher compatibility mode. + Phase of v2store deprecation. Deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input. Supported values: 'not-yet' // Issues a warning if v2store have meaningful content (default in v3.5) - 'write-only' // Custom v2 state is not allowed (planned default in v3.6) - 'write-only-drop-data' // Custom v2 state will get DELETED ! - 'gone' // v2store is not maintained any longer. (planned default in v3.7) + 'write-only' // Custom v2 state is not allowed (default in v3.6) + 'write-only-drop-data' // Custom v2 state will get DELETED ! (planned default in v3.7) + 'gone' // v2store is not maintained any longer. (planned to cleanup anything related to v2store in v3.8) Security: --cert-file '' diff --git a/tests/e2e/etcd_config_test.go b/tests/e2e/etcd_config_test.go index 7e4c347314a..370422e1d0b 100644 --- a/tests/e2e/etcd_config_test.go +++ b/tests/e2e/etcd_config_test.go @@ -666,17 +666,67 @@ func TestEtcdTLSVersion(t *testing.T) { func TestEtcdDeprecatedFlags(t *testing.T) { e2e.SkipInShortMode(t) - proc, err := e2e.SpawnCmd( - []string{ - e2e.BinPath.Etcd, - "--name", "e1", - "--snapshot-count=100", - }, nil, - ) - require.NoError(t, err) - require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{"--snapshot-count is deprecated in 3.6 and will be decommissioned in 3.7"})) - require.NoError(t, proc.Stop()) + commonArgs := []string{ + e2e.BinPath.Etcd, + "--name", "e1", + } - proc.Wait() // ensure the port has been released - proc.Close() + testCases := []struct { + name string + args []string + expectedMsg string + }{ + { + name: "snapshot-count", + args: append(commonArgs, "--snapshot-count=100"), + expectedMsg: "--snapshot-count is deprecated in 3.6 and will be decommissioned in 3.7", + }, + { + name: "v2-deprecation", + args: append(commonArgs, "--v2-deprecation", "write-only-drop-data"), + expectedMsg: "--v2-deprecation is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + proc, err := e2e.SpawnCmd( + tc.args, nil, + ) + require.NoError(t, err) + require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{tc.expectedMsg})) + require.NoError(t, proc.Stop()) + + proc.Wait() // ensure the port has been released + proc.Close() + }) + } +} + +// TestV2DeprecationEnforceDefaultValue verifies that etcd enforces the default V2Deprecation level +// and ignores users input. +func TestV2DeprecationEnforceDefaultValue(t *testing.T) { + e2e.SkipInShortMode(t) + + commonArgs := []string{ + e2e.BinPath.Etcd, + "--name", "e1", + } + + validV2DeprecationLevels := []string{"write-only", "write-only-drop-data", "gone"} + expectedDeprecationLevelMsg := `"v2-deprecation":"write-only"` + + for _, optionLevel := range validV2DeprecationLevels { + t.Run(optionLevel, func(t *testing.T) { + proc, err := e2e.SpawnCmd( + append(commonArgs, "--v2-deprecation", optionLevel), nil, + ) + require.NoError(t, err) + require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{expectedDeprecationLevelMsg})) + require.NoError(t, proc.Stop()) + + proc.Wait() // ensure the port has been released + proc.Close() + }) + } }