diff --git a/go.mod b/go.mod index d5bd3cb7e..878ac5e1d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.etcd.io/etcd/v3 -go 1.22 +go 1.22.0 toolchain go1.22.4 @@ -95,6 +95,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index b4f42c86d..bbdd90c96 100644 --- a/go.sum +++ b/go.sum @@ -250,7 +250,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/server/config/config.go b/server/config/config.go index d8edc514f..bc767d4ca 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -30,6 +30,7 @@ import ( "go.etcd.io/etcd/client/pkg/v3/types" "go.etcd.io/etcd/pkg/v3/netutil" "go.etcd.io/etcd/server/v3/etcdserver/api/v3discovery" + "go.etcd.io/etcd/server/v3/internal/pkg/featuregate" "go.etcd.io/etcd/server/v3/storage/datadir" ) @@ -207,6 +208,9 @@ type ServerConfig struct { // ExperimentalLocalAddress is the local IP address to use when communicating with a peer. ExperimentalLocalAddress string `json:"experimental-local-address"` + + // ServerFeatureGate is a server level feature gate + ServerFeatureGate featuregate.FeatureGate } // VerifyBootstrap sanity-checks the initial config for bootstrap case diff --git a/server/etcdserver/server.go b/server/etcdserver/server.go index 2eebb421d..2e849a2eb 100644 --- a/server/etcdserver/server.go +++ b/server/etcdserver/server.go @@ -62,6 +62,7 @@ import ( "go.etcd.io/etcd/server/v3/etcdserver/errors" "go.etcd.io/etcd/server/v3/etcdserver/txn" serverversion "go.etcd.io/etcd/server/v3/etcdserver/version" + "go.etcd.io/etcd/server/v3/internal/pkg/featuregate" "go.etcd.io/etcd/server/v3/lease" "go.etcd.io/etcd/server/v3/lease/leasehttp" serverstorage "go.etcd.io/etcd/server/v3/storage" @@ -456,6 +457,11 @@ func (s *EtcdServer) Config() config.ServerConfig { return s.Cfg } +// FeatureEnabled returns true if the feature is enabled by the etcd server, false otherwise. +func (s *EtcdServer) FeatureEnabled(f featuregate.Feature) bool { + return s.Cfg.ServerFeatureGate.Enabled(f) +} + func tickToDur(ticks int, tickMs uint) string { return fmt.Sprintf("%v", time.Duration(ticks)*time.Duration(tickMs)*time.Millisecond) } diff --git a/server/internal/pkg/featuregate/doc.go b/server/internal/pkg/featuregate/doc.go new file mode 100644 index 000000000..64d74ae51 --- /dev/null +++ b/server/internal/pkg/featuregate/doc.go @@ -0,0 +1,17 @@ +// Copyright 2024 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. + +// featuregate package is copied from k8s.io/component-base@v0.30.1 to avoid any potential circular dependency between k8s and etcd. +// Note that only necessary portions are copied to support etcd feature gate functionalities.s +package featuregate diff --git a/server/internal/pkg/featuregate/feature_gate.go b/server/internal/pkg/featuregate/feature_gate.go new file mode 100644 index 000000000..a60c08c46 --- /dev/null +++ b/server/internal/pkg/featuregate/feature_gate.go @@ -0,0 +1,35 @@ +// Copyright 2024 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 featuregate + +type Feature string + +// FeatureGate indicates whether a given feature is enabled or not +type FeatureGate interface { + // Enabled returns true if the key is enabled. + Enabled(key Feature) bool + // KnownFeatures returns a slice of strings describing the FeatureGate's known features. + KnownFeatures() []string + // DeepCopy returns a deep copy of the FeatureGate object, such that gates can be + // set on the copy without mutating the original. This is useful for validating + // config against potential feature gate changes before committing those changes. + DeepCopy() MutableFeatureGate +} + +// MutableFeatureGate parses and stores flag gates for known features from +// a string like feature1=true,feature2=false,... +type MutableFeatureGate interface { + FeatureGate +}