2019-01-12 04:58:27 +00:00
/ *
Copyright 2017 The Kubernetes 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 resourceconfig
import (
"fmt"
2019-12-12 01:27:03 +00:00
"regexp"
2019-01-12 04:58:27 +00:00
"strconv"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
serverstore "k8s.io/apiserver/pkg/server/storage"
2019-04-07 17:07:55 +00:00
cliflag "k8s.io/component-base/cli/flag"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-01-12 04:58:27 +00:00
)
// GroupVersionRegistry provides access to registered group versions.
type GroupVersionRegistry interface {
// IsGroupRegistered returns true if given group is registered.
IsGroupRegistered ( group string ) bool
// IsVersionRegistered returns true if given version is registered.
IsVersionRegistered ( v schema . GroupVersion ) bool
// PrioritizedVersionsAllGroups returns all registered group versions.
PrioritizedVersionsAllGroups ( ) [ ] schema . GroupVersion
}
// MergeResourceEncodingConfigs merges the given defaultResourceConfig with specific GroupVersionResource overrides.
func MergeResourceEncodingConfigs (
defaultResourceEncoding * serverstore . DefaultResourceEncodingConfig ,
resourceEncodingOverrides [ ] schema . GroupVersionResource ,
) * serverstore . DefaultResourceEncodingConfig {
resourceEncodingConfig := defaultResourceEncoding
for _ , gvr := range resourceEncodingOverrides {
resourceEncodingConfig . SetResourceEncoding ( gvr . GroupResource ( ) , gvr . GroupVersion ( ) ,
schema . GroupVersion { Group : gvr . Group , Version : runtime . APIVersionInternal } )
}
return resourceEncodingConfig
}
2019-12-12 01:27:03 +00:00
// 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 }
)
2019-01-12 04:58:27 +00:00
// 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.
func MergeAPIResourceConfigs (
defaultAPIResourceConfig * serverstore . ResourceConfig ,
2019-04-07 17:07:55 +00:00
resourceConfigOverrides cliflag . ConfigurationMap ,
2019-01-12 04:58:27 +00:00
registry GroupVersionRegistry ,
) ( * serverstore . ResourceConfig , error ) {
resourceConfig := defaultAPIResourceConfig
overrides := resourceConfigOverrides
2019-12-12 01:27:03 +00:00
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 )
}
2019-01-12 04:58:27 +00:00
}
}
// "<resourceSpecifier>={true|false} allows users to enable/disable API.
// This takes preference over api/all, if specified.
// Iterate through all group/version overrides specified in runtimeConfig.
for key := range overrides {
// Have already handled them above. Can skip them here.
2019-12-12 01:27:03 +00:00
if _ , ok := matchers [ key ] ; ok {
2019-01-12 04:58:27 +00:00
continue
}
tokens := strings . Split ( key , "/" )
2019-04-07 17:07:55 +00:00
if len ( tokens ) < 2 {
2019-01-12 04:58:27 +00:00
continue
}
groupVersionString := tokens [ 0 ] + "/" + tokens [ 1 ]
groupVersion , err := schema . ParseGroupVersion ( groupVersionString )
if err != nil {
return nil , fmt . Errorf ( "invalid key %s" , key )
}
2019-04-07 17:07:55 +00:00
// individual resource enablement/disablement is only supported in the extensions/v1beta1 API group for legacy reasons.
// all other API groups are expected to contain coherent sets of resources that are enabled/disabled together.
if len ( tokens ) > 2 && ( groupVersion != schema . GroupVersion { Group : "extensions" , Version : "v1beta1" } ) {
klog . Warningf ( "ignoring invalid key %s, individual resource enablement/disablement is not supported in %s, and will prevent starting in future releases" , key , groupVersion . String ( ) )
continue
}
2019-01-12 04:58:27 +00:00
// Exclude group not registered into the registry.
if ! registry . IsGroupRegistered ( groupVersion . Group ) {
continue
}
// Verify that the groupVersion is registered into registry.
if ! registry . IsVersionRegistered ( groupVersion ) {
return nil , fmt . Errorf ( "group version %s that has not been registered" , groupVersion . String ( ) )
}
enabled , err := getRuntimeConfigValue ( overrides , key , false )
if err != nil {
return nil , err
}
if enabled {
2019-04-07 17:07:55 +00:00
// enable the groupVersion for "group/version=true" and "group/version/resource=true"
2019-01-12 04:58:27 +00:00
resourceConfig . EnableVersions ( groupVersion )
2019-04-07 17:07:55 +00:00
} else if len ( tokens ) == 2 {
// disable the groupVersion only for "group/version=false", not "group/version/resource=false"
2019-01-12 04:58:27 +00:00
resourceConfig . DisableVersions ( groupVersion )
}
2019-04-07 17:07:55 +00:00
if len ( tokens ) < 3 {
continue
}
groupVersionResource := groupVersion . WithResource ( tokens [ 2 ] )
if enabled {
resourceConfig . EnableResources ( groupVersionResource )
} else {
resourceConfig . DisableResources ( groupVersionResource )
}
2019-01-12 04:58:27 +00:00
}
return resourceConfig , nil
}
2019-04-07 17:07:55 +00:00
func getRuntimeConfigValue ( overrides cliflag . ConfigurationMap , apiKey string , defaultValue bool ) ( bool , error ) {
2019-01-12 04:58:27 +00:00
flagValue , ok := overrides [ apiKey ]
if ok {
if flagValue == "" {
return true , nil
}
boolValue , err := strconv . ParseBool ( flagValue )
if err != nil {
return false , fmt . Errorf ( "invalid value of %s: %s, err: %v" , apiKey , flagValue , err )
}
return boolValue , nil
}
return defaultValue , nil
}
// ParseGroups takes in resourceConfig and returns parsed groups.
2019-04-07 17:07:55 +00:00
func ParseGroups ( resourceConfig cliflag . ConfigurationMap ) ( [ ] string , error ) {
2019-01-12 04:58:27 +00:00
groups := [ ] string { }
for key := range resourceConfig {
2019-12-12 01:27:03 +00:00
if _ , ok := matchers [ key ] ; ok {
2019-01-12 04:58:27 +00:00
continue
}
tokens := strings . Split ( key , "/" )
if len ( tokens ) != 2 && len ( tokens ) != 3 {
return groups , fmt . Errorf ( "runtime-config invalid key %s" , key )
}
groupVersionString := tokens [ 0 ] + "/" + tokens [ 1 ]
groupVersion , err := schema . ParseGroupVersion ( groupVersionString )
if err != nil {
return nil , fmt . Errorf ( "runtime-config invalid key %s" , key )
}
groups = append ( groups , groupVersion . Group )
}
return groups , nil
}