Skip to content

Commit

Permalink
Add support for --runtime-config=api/beta=false, --feature-gates=AllB…
Browse files Browse the repository at this point in the history
…eta=false

Allow disabling all beta features and APIs
  • Loading branch information
liggitt committed Nov 14, 2019
1 parent 78d2e52 commit a5760de
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 36 deletions.
2 changes: 1 addition & 1 deletion cmd/kube-apiserver/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
s.Authentication.AddFlags(fss.FlagSet("authentication"))
s.Authorization.AddFlags(fss.FlagSet("authorization"))
s.CloudProvider.AddFlags(fss.FlagSet("cloud provider"))
s.APIEnablement.AddFlags(fss.FlagSet("api enablement"))
s.APIEnablement.AddFlags(fss.FlagSet("API enablement"))
s.EgressSelector.AddFlags(fss.FlagSet("egress selector"))
s.Admission.AddFlags(fss.FlagSet("admission"))

Expand Down
17 changes: 10 additions & 7 deletions staging/src/k8s.io/apiserver/pkg/server/options/api_enablement.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@ func NewAPIEnablementOptions() *APIEnablementOptions {
// AddFlags adds flags for a specific APIServer to the specified FlagSet
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
fs.Var(&s.RuntimeConfig, "runtime-config", ""+
"A set of key=value pairs that describe runtime configuration that may be passed "+
"to apiserver. <group>/<version> (or <version> for the core group) key can be used to "+
"turn on/off specific api versions. api/all is special key to control all api versions, "+
"be careful setting it false, unless you know what you do. api/legacy is deprecated, "+
"we will remove it in the future, so stop using it.")
"A set of key=value pairs that enable or disable built-in APIs. Supported options are:\n"+
"v1=true|false for the core API group\n"+
"<group>/<version>=true|false for a specific API group and version (e.g. apps/v1=true)\n"+
"api/all=true|false controls all API versions\n"+
"api/ga=true|false controls all API versions of the form v[0-9]+\n"+
"api/beta=true|false controls all API versions of the form v[0-9]+beta[0-9]+\n"+
"api/alpha=true|false controls all API versions of the form v[0-9]+alpha[0-9]+\n"+
"api/legacy is deprecated, and will be removed in a future version")
}

// Validate validates RuntimeConfig with a list of registries.
Expand All @@ -61,9 +64,9 @@ func (s *APIEnablementOptions) Validate(registries ...GroupRegisty) []error {
}

errors := []error{}
if s.RuntimeConfig["api/all"] == "false" && len(s.RuntimeConfig) == 1 {
if s.RuntimeConfig[resourceconfig.APIAll] == "false" && len(s.RuntimeConfig) == 1 {
// Do not allow only set api/all=false, in such case apiserver startup has no meaning.
return append(errors, fmt.Errorf("invalid key with only api/all=false"))
return append(errors, fmt.Errorf("invalid key with only %v=false", resourceconfig.APIAll))
}

groups, err := resourceconfig.ParseGroups(s.RuntimeConfig)
Expand Down
49 changes: 39 additions & 10 deletions staging/src/k8s.io/apiserver/pkg/server/resourceconfig/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package resourceconfig

import (
"fmt"
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -51,6 +52,33 @@ func MergeResourceEncodingConfigs(
return resourceEncodingConfig
}

// Recognized values for the --runtime-config parameter to enable/disable groups of APIs
const (
APIAll = "api/all"
APIGA = "api/ga"
APIBeta = "api/beta"
APIAlpha = "api/alpha"
)

var (
gaPattern = regexp.MustCompile(`^v\d+$`)
betaPattern = regexp.MustCompile(`^v\d+beta\d+$`)
alphaPattern = regexp.MustCompile(`^v\d+alpha\d+$`)

matchers = map[string]func(gv schema.GroupVersion) bool{
// allows users to address all api versions
APIAll: func(gv schema.GroupVersion) bool { return true },
// allows users to address all api versions in the form v[0-9]+
APIGA: func(gv schema.GroupVersion) bool { return gaPattern.MatchString(gv.Version) },
// allows users to address all beta api versions
APIBeta: func(gv schema.GroupVersion) bool { return betaPattern.MatchString(gv.Version) },
// allows users to address all alpha api versions
APIAlpha: func(gv schema.GroupVersion) bool { return alphaPattern.MatchString(gv.Version) },
}

matcherOrder = []string{APIAll, APIGA, APIBeta, APIAlpha}
)

// MergeAPIResourceConfigs merges the given defaultAPIResourceConfig with the given resourceConfigOverrides.
// Exclude the groups not registered in registry, and check if version is
// not registered in group, then it will fail.
Expand All @@ -62,14 +90,15 @@ func MergeAPIResourceConfigs(
resourceConfig := defaultAPIResourceConfig
overrides := resourceConfigOverrides

// "api/all=false" allows users to selectively enable specific api versions.
allAPIFlagValue, ok := overrides["api/all"]
if ok {
if allAPIFlagValue == "false" {
// Disable all group versions.
resourceConfig.DisableAll()
} else if allAPIFlagValue == "true" {
resourceConfig.EnableAll()
for _, flag := range matcherOrder {
if value, ok := overrides[flag]; ok {
if value == "false" {
resourceConfig.DisableMatchingVersions(matchers[flag])
} else if value == "true" {
resourceConfig.EnableMatchingVersions(matchers[flag])
} else {
return nil, fmt.Errorf("invalid value %v=%v", flag, value)
}
}
}

Expand All @@ -78,7 +107,7 @@ func MergeAPIResourceConfigs(
// Iterate through all group/version overrides specified in runtimeConfig.
for key := range overrides {
// Have already handled them above. Can skip them here.
if key == "api/all" {
if _, ok := matchers[key]; ok {
continue
}

Expand Down Expand Up @@ -153,7 +182,7 @@ func getRuntimeConfigValue(overrides cliflag.ConfigurationMap, apiKey string, de
func ParseGroups(resourceConfig cliflag.ConfigurationMap) ([]string, error) {
groups := []string{}
for key := range resourceConfig {
if key == "api/all" {
if _, ok := matchers[key]; ok {
continue
}
tokens := strings.Split(key, "/")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,21 @@ func TestParseRuntimeConfig(t *testing.T) {
},
err: false, // no error for backwards compatibility
},
{
// disable all beta resources
runtimeConfig: map[string]string{
"api/beta": "false",
},
defaultResourceConfig: func() *serverstore.ResourceConfig {
return newFakeAPIResourceConfigSource()
},
expectedAPIConfig: func() *serverstore.ResourceConfig {
config := newFakeAPIResourceConfigSource()
config.DisableVersions(extensionsapiv1beta1.SchemeGroupVersion)
return config
},
err: false, // no error for backwards compatibility
},
}
for index, test := range testCases {
t.Log(scheme.PrioritizedVersionsAllGroups())
Expand Down
18 changes: 18 additions & 0 deletions staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ func (o *ResourceConfig) EnableAll() {
}
}

// DisableMatchingVersions disables all group/versions for which the matcher function returns true. It does not modify individual resource enablement/disablement.
func (o *ResourceConfig) DisableMatchingVersions(matcher func(gv schema.GroupVersion) bool) {
for k := range o.GroupVersionConfigs {
if matcher(k) {
o.GroupVersionConfigs[k] = false
}
}
}

// EnableMatchingVersions enables all group/versions for which the matcher function returns true. It does not modify individual resource enablement/disablement.
func (o *ResourceConfig) EnableMatchingVersions(matcher func(gv schema.GroupVersion) bool) {
for k := range o.GroupVersionConfigs {
if matcher(k) {
o.GroupVersionConfigs[k] = true
}
}
}

// DisableVersions disables the versions entirely.
func (o *ResourceConfig) DisableVersions(versions ...schema.GroupVersion) {
for _, version := range versions {
Expand Down
18 changes: 18 additions & 0 deletions staging/src/k8s.io/component-base/featuregate/feature_gate.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,25 @@ const (
// AllAlpha=false,NewFeature=true will result in newFeature=true
// AllAlpha=true,NewFeature=false will result in newFeature=false
allAlphaGate Feature = "AllAlpha"

// allBetaGate is a global toggle for beta features. Per-feature key
// values override the default set by allBetaGate. Examples:
// AllBeta=false,NewFeature=true will result in NewFeature=true
// AllBeta=true,NewFeature=false will result in NewFeature=false
allBetaGate Feature = "AllBeta"
)

var (
// The generic features.
defaultFeatures = map[Feature]FeatureSpec{
allAlphaGate: {Default: false, PreRelease: Alpha},
allBetaGate: {Default: false, PreRelease: Beta},
}

// Special handling for a few gates.
specialFeatures = map[Feature]func(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool){
allAlphaGate: setUnsetAlphaGates,
allBetaGate: setUnsetBetaGates,
}
)

Expand Down Expand Up @@ -129,6 +137,16 @@ func setUnsetAlphaGates(known map[Feature]FeatureSpec, enabled map[Feature]bool,
}
}

func setUnsetBetaGates(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool) {
for k, v := range known {
if v.PreRelease == Beta {
if _, found := enabled[k]; !found {
enabled[k] = val
}
}
}
}

// Set, String, and Type implement pflag.Value
var _ pflag.Value = &featureGate{}

Expand Down
Loading

0 comments on commit a5760de

Please sign in to comment.