-
Notifications
You must be signed in to change notification settings - Fork 893
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce feature gates for the scheduler component
Signed-off-by: iawia002 <[email protected]>
- Loading branch information
Showing
7 changed files
with
223 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package features | ||
|
||
import ( | ||
"fmt" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/spf13/pflag" | ||
"k8s.io/apimachinery/pkg/util/sets" | ||
"k8s.io/klog/v2" | ||
) | ||
|
||
var ( | ||
allFeatures = sets.NewString(SchedulerFailover) | ||
defaultFeatures = map[string]bool{ | ||
SchedulerFailover: false, | ||
} | ||
// DefaultFeatureGate is a shared global FeatureGate. | ||
DefaultFeatureGate = NewDefaultFeatureGate() | ||
) | ||
|
||
const ( | ||
// SchedulerFailover indicates if scheduler should reschedule on cluster failure. | ||
SchedulerFailover string = "SchedulerFailover" | ||
) | ||
|
||
// FeatureGate indicates whether a given feature is enabled or not. | ||
type FeatureGate interface { | ||
// AddFlag adds a flag for setting global feature gates to the specified FlagSet. | ||
AddFlag(flagset *pflag.FlagSet) | ||
// Enabled returns true if the key is enabled. | ||
Enabled(key string) bool | ||
// Set parses and stores flag gates for known features | ||
// from a string like feature1=true,feature2=false,... | ||
Set(value string) error | ||
// SetFromMap stores flag gates for enabled features from a map[string]bool | ||
SetFromMap(m map[string]bool) | ||
// String returns a string representation of feature gate. | ||
String() string | ||
} | ||
|
||
var _ pflag.Value = &featureGate{} | ||
|
||
type featureGate struct { | ||
lock sync.Mutex | ||
enabledFeatures map[string]bool | ||
} | ||
|
||
func (f *featureGate) AddFlag(flagset *pflag.FlagSet) { | ||
flagset.Var(f, "features", fmt.Sprintf("A set of key={true,false} pairs to enable/disable features, available features: %s", strings.Join(allFeatures.List(), ","))) | ||
} | ||
|
||
func (f *featureGate) Enabled(key string) bool { | ||
if b, ok := f.enabledFeatures[key]; ok { | ||
return b | ||
} | ||
return false | ||
} | ||
|
||
// String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,...". | ||
func (f *featureGate) String() string { | ||
pairs := make([]string, 0, len(f.enabledFeatures)) | ||
for k, v := range f.enabledFeatures { | ||
pairs = append(pairs, fmt.Sprintf("%s=%t", k, v)) | ||
} | ||
sort.Strings(pairs) | ||
return strings.Join(pairs, ",") | ||
} | ||
|
||
func (f *featureGate) Set(value string) error { | ||
m := make(map[string]bool) | ||
for _, s := range strings.Split(value, ",") { | ||
if len(s) == 0 { | ||
continue | ||
} | ||
arr := strings.SplitN(s, "=", 2) | ||
k := strings.TrimSpace(arr[0]) | ||
if len(arr) != 2 { | ||
return fmt.Errorf("missing bool value for %s", k) | ||
} | ||
v := strings.TrimSpace(arr[1]) | ||
boolValue, err := strconv.ParseBool(v) | ||
if err != nil { | ||
return fmt.Errorf("invalid value of %s=%s, err: %v", k, v, err) | ||
} | ||
m[k] = boolValue | ||
} | ||
f.SetFromMap(m) | ||
return nil | ||
} | ||
|
||
func (f *featureGate) Type() string { | ||
return "mapStringBool" | ||
} | ||
|
||
func (f *featureGate) SetFromMap(m map[string]bool) { | ||
f.lock.Lock() | ||
defer f.lock.Unlock() | ||
|
||
for k, v := range m { | ||
f.enabledFeatures[k] = v | ||
} | ||
|
||
klog.V(1).Infof("feature gates: %v", f.enabledFeatures) | ||
} | ||
|
||
// NewFeatureGate returns a new FeatureGate. | ||
func NewFeatureGate() FeatureGate { | ||
return &featureGate{ | ||
enabledFeatures: make(map[string]bool), | ||
} | ||
} | ||
|
||
// NewDefaultFeatureGate returns the shared global FeatureGate. | ||
func NewDefaultFeatureGate() FeatureGate { | ||
f := NewFeatureGate() | ||
f.SetFromMap(defaultFeatures) | ||
return f | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package features | ||
|
||
import "testing" | ||
|
||
func TestSet(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
setStr string | ||
wantStr string | ||
}{ | ||
{ | ||
name: "set multiple features", | ||
setStr: "a=true,b=false", | ||
wantStr: "a=true,b=false", | ||
}, | ||
{ | ||
name: "set multiple features", | ||
setStr: "a=True,b=False", | ||
wantStr: "a=true,b=false", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
gates := NewFeatureGate() | ||
_ = gates.Set(tt.setStr) | ||
got := gates.String() | ||
if got != tt.wantStr { | ||
t.Errorf("want: %s, got %s", tt.wantStr, got) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestSetFromMap(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
setMap map[string]bool | ||
wantStr string | ||
}{ | ||
{ | ||
name: "set multiple features", | ||
setMap: map[string]bool{ | ||
"a": true, | ||
"b": false, | ||
}, | ||
wantStr: "a=true,b=false", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
gates := NewFeatureGate() | ||
gates.SetFromMap(tt.setMap) | ||
got := gates.String() | ||
if got != tt.wantStr { | ||
t.Errorf("want: %s, got %s", tt.wantStr, got) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEnabled(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
setMap map[string]bool | ||
wantEnabled map[string]bool | ||
}{ | ||
{ | ||
name: "set multiple features", | ||
setMap: map[string]bool{ | ||
"a": true, | ||
"b": false, | ||
}, | ||
wantEnabled: map[string]bool{ | ||
"a": true, | ||
"b": false, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
gates := NewFeatureGate() | ||
gates.SetFromMap(tt.setMap) | ||
for k, want := range tt.wantEnabled { | ||
got := gates.Enabled(k) | ||
if got != want { | ||
t.Errorf("[feature: %s] want %v, got %v", k, want, got) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters