From 7c508741b31ef08f3d0d45d1629fe24aa935f656 Mon Sep 17 00:00:00 2001 From: Piotr Tabor Date: Tue, 4 May 2021 11:16:10 +0200 Subject: [PATCH] Adding --v2-deprecation flag. --- server/embed/config.go | 13 ++++++++ server/embed/v2_deprecation.go | 46 +++++++++++++++++++++++++++++ server/embed/v2_deprecation_test.go | 41 +++++++++++++++++++++++++ server/etcdmain/config.go | 29 ++++++++++++++---- server/etcdmain/help.go | 7 +++++ 5 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 server/embed/v2_deprecation.go create mode 100644 server/embed/v2_deprecation_test.go diff --git a/server/embed/config.go b/server/embed/config.go index f50ff1663..bf24b0483 100644 --- a/server/embed/config.go +++ b/server/embed/config.go @@ -400,6 +400,9 @@ type Config struct { // ExperimentalTxnModeWriteWithSharedBuffer enables write transaction to use a shared buffer in its readonly check operations. ExperimentalTxnModeWriteWithSharedBuffer bool `json:"experimental-txn-mode-write-with-shared-buffer"` + + // V2Deprecation describes phase of API & Storage V2 support + V2Deprecation V2DeprecationEnum `json:"v2-deprecation"` } // configYAML holds the config suitable for yaml parsing @@ -494,6 +497,8 @@ func NewConfig() *Config { ExperimentalDowngradeCheckTime: DefaultDowngradeCheckTime, ExperimentalMemoryMlock: false, ExperimentalTxnModeWriteWithSharedBuffer: true, + + V2Deprecation: V2_DEPR_DEFAULT, } cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name) return cfg @@ -795,6 +800,14 @@ func (cfg Config) InitialClusterFromName(name string) (ret string) { func (cfg Config) IsNewCluster() bool { return cfg.ClusterState == ClusterStateFlagNew } func (cfg Config) ElectionTicks() int { return int(cfg.ElectionMs / cfg.TickMs) } +func (cfg Config) V2DeprecationEffective() V2DeprecationEnum { + if cfg.V2Deprecation == "" { + return V2_DEPR_DEFAULT + } else { + return cfg.V2Deprecation + } +} + func (cfg Config) defaultPeerHost() bool { return len(cfg.APUrls) == 1 && cfg.APUrls[0].String() == DefaultInitialAdvertisePeerURLs } diff --git a/server/embed/v2_deprecation.go b/server/embed/v2_deprecation.go new file mode 100644 index 000000000..2fb9ba374 --- /dev/null +++ b/server/embed/v2_deprecation.go @@ -0,0 +1,46 @@ +// Copyright 2021 The etcd Authors +// +// 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 embed + +type V2DeprecationEnum string + +const ( +// Default in v3.5. Issues a warning if v2store have meaningful content. +V2_DEPR_0_NOT_YET = V2DeprecationEnum("not-yet") +// Default in v3.6. Meaningful v2 state is not allowed. +// The V2 files are maintained for v3.5 rollback. +V2_DEPR_1_WRITE_ONLY = V2DeprecationEnum("write-only") +// V2store is WIPED if found !!! +V2_DEPR_1_WRITE_ONLY_DROP = V2DeprecationEnum("write-only-drop-data") +// V2store is neither written nor read. Usage of this configuration is blocking +// ability to rollback to etcd v3.5. +V2_DEPR_2_GONE = V2DeprecationEnum("gone") + +V2_DEPR_DEFAULT = V2_DEPR_0_NOT_YET +) + +func (e V2DeprecationEnum) IsAtLeast(v2d V2DeprecationEnum) bool { + return e.level() >= v2d.level() +} + +func (e V2DeprecationEnum) level() int { + switch e { + case V2_DEPR_0_NOT_YET: return 0 + case V2_DEPR_1_WRITE_ONLY: return 1 + case V2_DEPR_1_WRITE_ONLY_DROP: return 2 + case V2_DEPR_2_GONE: return 3 + } + panic("Unknown V2DeprecationEnum: " + e) +} \ No newline at end of file diff --git a/server/embed/v2_deprecation_test.go b/server/embed/v2_deprecation_test.go new file mode 100644 index 000000000..f9533bd5a --- /dev/null +++ b/server/embed/v2_deprecation_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 The etcd Authors +// +// 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 embed + +import "testing" + +func TestV2DeprecationEnum_IsAtLeast(t *testing.T) { + tests := []struct { + e V2DeprecationEnum + v2d V2DeprecationEnum + want bool + }{ + {V2_DEPR_0_NOT_YET, V2_DEPR_0_NOT_YET, true}, + {V2_DEPR_0_NOT_YET, V2_DEPR_1_WRITE_ONLY_DROP, false}, + {V2_DEPR_0_NOT_YET, V2_DEPR_2_GONE, false}, + {V2_DEPR_2_GONE, V2_DEPR_1_WRITE_ONLY_DROP, true}, + {V2_DEPR_2_GONE, V2_DEPR_0_NOT_YET, true}, + {V2_DEPR_2_GONE, V2_DEPR_2_GONE, true}, + {V2_DEPR_1_WRITE_ONLY, V2_DEPR_1_WRITE_ONLY_DROP, false}, + {V2_DEPR_1_WRITE_ONLY_DROP, V2_DEPR_1_WRITE_ONLY, true}, + } + for _, tt := range tests { + t.Run(string(tt.e) + " >= " + string(tt.v2d), func(t *testing.T) { + if got := tt.e.IsAtLeast(tt.v2d); got != tt.want { + t.Errorf("IsAtLeast() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/server/etcdmain/config.go b/server/etcdmain/config.go index fefb230f2..0072109d6 100644 --- a/server/etcdmain/config.go +++ b/server/etcdmain/config.go @@ -86,10 +86,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 - proxy *flags.SelectiveStringValue + flagSet *flag.FlagSet + clusterState *flags.SelectiveStringValue + fallback *flags.SelectiveStringValue + proxy *flags.SelectiveStringValue + v2deprecation *flags.SelectiveStringsValue } func newConfig() *config { @@ -119,6 +120,11 @@ func newConfig() *config { proxyFlagReadonly, proxyFlagOn, ), + v2deprecation: flags.NewSelectiveStringsValue( + string(embed.V2_DEPR_0_NOT_YET), + string(embed.V2_DEPR_1_WRITE_ONLY), + string(embed.V2_DEPR_1_WRITE_ONLY_DROP), + string(embed.V2_DEPR_2_GONE)), } fs := cfg.cf.flagSet @@ -190,9 +196,13 @@ func newConfig() *config { fs.Var(cfg.cf.clusterState, "initial-cluster-state", "Initial cluster state ('new' or 'existing').") fs.BoolVar(&cfg.ec.StrictReconfigCheck, "strict-reconfig-check", cfg.ec.StrictReconfigCheck, "Reject reconfiguration requests that would cause quorum loss.") - fs.BoolVar(&cfg.ec.EnableV2, "enable-v2", cfg.ec.EnableV2, "Accept etcd V2 client requests. Deprecated in v3.5. Will be decommission in v3.6.") + fs.BoolVar(&cfg.ec.PreVote, "pre-vote", cfg.ec.PreVote, "Enable to run an additional Raft election phase.") + fs.BoolVar(&cfg.ec.EnableV2, "enable-v2", cfg.ec.EnableV2, "Accept etcd V2 client requests. Deprecated in v3.5. Will be decommission in v3.6.") + fs.StringVar(&cfg.ec.ExperimentalEnableV2V3, "experimental-enable-v2v3", cfg.ec.ExperimentalEnableV2V3, "v3 prefix for serving emulated v2 state. Deprecated in 3.5. Will be decomissioned in 3.6.") + fs.Var(cfg.cf.v2deprecation, "v2-deprecation", fmt.Sprintf("v2store deprecation stage: %q. ", cfg.cf.proxy.Valids())) + // proxy fs.Var(cfg.cf.proxy, "proxy", fmt.Sprintf("Valid values include %q", cfg.cf.proxy.Valids())) fs.UintVar(&cfg.cp.ProxyFailureWaitMs, "proxy-failure-wait", cfg.cp.ProxyFailureWaitMs, "Time (in milliseconds) an endpoint will be held in a failed state.") @@ -268,7 +278,7 @@ func newConfig() *config { // experimental fs.BoolVar(&cfg.ec.ExperimentalInitialCorruptCheck, "experimental-initial-corrupt-check", cfg.ec.ExperimentalInitialCorruptCheck, "Enable to check data corruption before serving any client/peer traffic.") fs.DurationVar(&cfg.ec.ExperimentalCorruptCheckTime, "experimental-corrupt-check-time", cfg.ec.ExperimentalCorruptCheckTime, "Duration of time between cluster corruption check passes.") - fs.StringVar(&cfg.ec.ExperimentalEnableV2V3, "experimental-enable-v2v3", cfg.ec.ExperimentalEnableV2V3, "v3 prefix for serving emulated v2 state. Deprecated in 3.5. Will be decomissioned in 3.6.") + fs.BoolVar(&cfg.ec.ExperimentalEnableLeaseCheckpoint, "experimental-enable-lease-checkpoint", false, "Enable to persist lease remaining TTL to prevent indefinite auto-renewal of long lived leases.") fs.IntVar(&cfg.ec.ExperimentalCompactionBatchLimit, "experimental-compaction-batch-limit", cfg.ec.ExperimentalCompactionBatchLimit, "Sets the maximum revisions deleted in each compaction batch.") fs.DurationVar(&cfg.ec.ExperimentalWatchProgressNotifyInterval, "experimental-watch-progress-notify-interval", cfg.ec.ExperimentalWatchProgressNotifyInterval, "Duration of periodic watch progress notifications.") @@ -331,6 +341,11 @@ func (cfg *config) parse(arguments []string) error { } else { err = cfg.configFromCmdLine() } + + if cfg.ec.V2Deprecation == "" { + cfg.ec.V2Deprecation = embed.V2_DEPR_DEFAULT + } + // now logger is set up return err } @@ -385,6 +400,8 @@ func (cfg *config) configFromCmdLine() error { cfg.cp.Fallback = cfg.cf.fallback.String() cfg.cp.Proxy = cfg.cf.proxy.String() + cfg.ec.V2Deprecation = embed.V2DeprecationEnum(cfg.cf.v2deprecation.String()) + // disable default advertise-client-urls if lcurls is set missingAC := flags.IsSet(cfg.cf.flagSet, "listen-client-urls") && !flags.IsSet(cfg.cf.flagSet, "advertise-client-urls") if !cfg.mayBeProxy() && missingAC { diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go index b63282e89..ca8ac039d 100644 --- a/server/etcdmain/help.go +++ b/server/etcdmain/help.go @@ -124,6 +124,13 @@ Clustering: 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. --enable-v2 '` + strconv.FormatBool(embed.DefaultEnableV2) + `' Accept etcd V2 client requests. Deprecated and to be decommissioned in v3.6. + --v2-deprecation '` + string(embed.V2_DEPR_DEFAULT) + `' + Phase of v2store deprecation. Allows to optin for higher compatibility mode. + 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) Security: --cert-file ''