@@ -42,6 +42,7 @@ import (
42
42
"go.etcd.io/etcd/client/pkg/v3/transport"
43
43
"go.etcd.io/etcd/client/pkg/v3/types"
44
44
clientv3 "go.etcd.io/etcd/client/v3"
45
+ "go.etcd.io/etcd/pkg/v3/featuregate"
45
46
"go.etcd.io/etcd/pkg/v3/flags"
46
47
"go.etcd.io/etcd/pkg/v3/netutil"
47
48
"go.etcd.io/etcd/server/v3/config"
@@ -50,6 +51,7 @@ import (
50
51
"go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
51
52
"go.etcd.io/etcd/server/v3/etcdserver/api/v3compactor"
52
53
"go.etcd.io/etcd/server/v3/etcdserver/api/v3discovery"
54
+ "go.etcd.io/etcd/server/v3/features"
53
55
)
54
56
55
57
const (
@@ -455,6 +457,9 @@ type Config struct {
455
457
456
458
// V2Deprecation describes phase of API & Storage V2 support
457
459
V2Deprecation config.V2DeprecationEnum `json:"v2-deprecation"`
460
+
461
+ // ServerFeatureGate is a server level feature gate
462
+ ServerFeatureGate featuregate.FeatureGate
458
463
}
459
464
460
465
// configYAML holds the config suitable for yaml parsing
@@ -476,6 +481,8 @@ type configJSON struct {
476
481
477
482
ClientSecurityJSON securityConfig `json:"client-transport-security"`
478
483
PeerSecurityJSON securityConfig `json:"peer-transport-security"`
484
+
485
+ FeatureGatesJSON string `json:"feature-gates"`
479
486
}
480
487
481
488
type securityConfig struct {
@@ -576,6 +583,7 @@ func NewConfig() *Config {
576
583
},
577
584
578
585
AutoCompactionMode : DefaultAutoCompactionMode ,
586
+ ServerFeatureGate : features .NewDefaultServerFeatureGate (DefaultName , nil ),
579
587
}
580
588
cfg .InitialCluster = cfg .InitialClusterFromName (cfg .Name )
581
589
return cfg
@@ -762,6 +770,9 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
762
770
// unsafe
763
771
fs .BoolVar (& cfg .UnsafeNoFsync , "unsafe-no-fsync" , false , "Disables fsync, unsafe, will cause data loss." )
764
772
fs .BoolVar (& cfg .ForceNewCluster , "force-new-cluster" , false , "Force to create a new one member cluster." )
773
+
774
+ // featuregate
775
+ cfg .ServerFeatureGate .(featuregate.MutableFeatureGate ).AddFlag (fs )
765
776
}
766
777
767
778
func ConfigFromFile (path string ) (* Config , error ) {
@@ -785,6 +796,26 @@ func (cfg *configYAML) configFromFile(path string) error {
785
796
return err
786
797
}
787
798
799
+ if cfg .configJSON .FeatureGatesJSON != "" {
800
+ err = cfg .Config .ServerFeatureGate .(featuregate.MutableFeatureGate ).Set (cfg .configJSON .FeatureGatesJSON )
801
+ if err != nil {
802
+ return err
803
+ }
804
+ }
805
+ var cfgMap map [string ]interface {}
806
+ err = yaml .Unmarshal (b , & cfgMap )
807
+ if err != nil {
808
+ return err
809
+ }
810
+ isExperimentalFlagSet := func (expFlag string ) bool {
811
+ _ , ok := cfgMap [expFlag ]
812
+ return ok
813
+ }
814
+ err = cfg .SetFeatureGatesFromExperimentalFlags (isExperimentalFlagSet , cfg .configJSON .FeatureGatesJSON )
815
+ if err != nil {
816
+ return err
817
+ }
818
+
788
819
if cfg .configJSON .ListenPeerURLs != "" {
789
820
u , err := types .NewURLs (strings .Split (cfg .configJSON .ListenPeerURLs , "," ))
790
821
if err != nil {
@@ -877,6 +908,25 @@ func (cfg *configYAML) configFromFile(path string) error {
877
908
return cfg .Validate ()
878
909
}
879
910
911
+ // SetFeatureGatesFromExperimentalFlags sets the feature gate values if their corresponding experimental flags are
912
+ // explicitly set.
913
+ func (cfg * Config ) SetFeatureGatesFromExperimentalFlags (isExperimentalFlagSet func (string ) bool , featureGatesVal string ) error {
914
+ // verify that the feature gate and its experimental flag are not both set at the same time.
915
+ for expFlagName , featureName := range features .ExperimentalFlagToFeatureMap {
916
+ if isExperimentalFlagSet (expFlagName ) && strings .Contains (featureGatesVal , string (featureName )) {
917
+ return fmt .Errorf ("cannot specify both flags: --%s=(true|false) and --%s=%s=(true|false) at the same time, please just use --%s=%s=(true|false)" ,
918
+ expFlagName , featuregate .FlagName , featureName , featuregate .FlagName , featureName )
919
+ }
920
+ }
921
+
922
+ m := make (map [string ]bool )
923
+ defaultEc := NewConfig ()
924
+ if cfg .ExperimentalStopGRPCServiceOnDefrag != defaultEc .ExperimentalStopGRPCServiceOnDefrag {
925
+ m [string (features .StopGRPCServiceOnDefrag )] = cfg .ExperimentalStopGRPCServiceOnDefrag
926
+ }
927
+ return cfg .ServerFeatureGate .(featuregate.MutableFeatureGate ).SetFromMap (m )
928
+ }
929
+
880
930
func updateCipherSuites (tls * transport.TLSInfo , ss []string ) error {
881
931
if len (tls .CipherSuites ) > 0 && len (ss ) > 0 {
882
932
return fmt .Errorf ("TLSInfo.CipherSuites is already specified (given %v)" , ss )
@@ -907,6 +957,7 @@ func (cfg *Config) Validate() error {
907
957
if err := cfg .setupLogging (); err != nil {
908
958
return err
909
959
}
960
+ cfg .ServerFeatureGate .(featuregate.MutableFeatureGate ).SetLogger (cfg .logger )
910
961
if err := checkBindURLs (cfg .ListenPeerUrls ); err != nil {
911
962
return err
912
963
}
0 commit comments