k8s.io/apiserver@v0.31.1/pkg/server/resourceconfig/helpers.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package resourceconfig 18 19 import ( 20 "fmt" 21 "regexp" 22 "strconv" 23 "strings" 24 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 serverstore "k8s.io/apiserver/pkg/server/storage" 28 cliflag "k8s.io/component-base/cli/flag" 29 ) 30 31 // GroupVersionRegistry provides access to registered group versions. 32 type GroupVersionRegistry interface { 33 // IsGroupRegistered returns true if given group is registered. 34 IsGroupRegistered(group string) bool 35 // IsVersionRegistered returns true if given version is registered. 36 IsVersionRegistered(v schema.GroupVersion) bool 37 // PrioritizedVersionsAllGroups returns all registered group versions. 38 PrioritizedVersionsAllGroups() []schema.GroupVersion 39 } 40 41 // MergeResourceEncodingConfigs merges the given defaultResourceConfig with specific GroupVersionResource overrides. 42 func MergeResourceEncodingConfigs( 43 defaultResourceEncoding *serverstore.DefaultResourceEncodingConfig, 44 resourceEncodingOverrides []schema.GroupVersionResource, 45 ) *serverstore.DefaultResourceEncodingConfig { 46 resourceEncodingConfig := defaultResourceEncoding 47 for _, gvr := range resourceEncodingOverrides { 48 resourceEncodingConfig.SetResourceEncoding(gvr.GroupResource(), gvr.GroupVersion(), 49 schema.GroupVersion{Group: gvr.Group, Version: runtime.APIVersionInternal}) 50 } 51 return resourceEncodingConfig 52 } 53 54 // Recognized values for the --runtime-config parameter to enable/disable groups of APIs 55 const ( 56 APIAll = "api/all" 57 APIGA = "api/ga" 58 APIBeta = "api/beta" 59 APIAlpha = "api/alpha" 60 ) 61 62 var ( 63 gaPattern = regexp.MustCompile(`^v\d+$`) 64 betaPattern = regexp.MustCompile(`^v\d+beta\d+$`) 65 alphaPattern = regexp.MustCompile(`^v\d+alpha\d+$`) 66 67 groupVersionMatchers = map[string]func(gv schema.GroupVersion) bool{ 68 // allows users to address all api versions 69 APIAll: func(gv schema.GroupVersion) bool { return true }, 70 // allows users to address all api versions in the form v[0-9]+ 71 APIGA: func(gv schema.GroupVersion) bool { return gaPattern.MatchString(gv.Version) }, 72 // allows users to address all beta api versions 73 APIBeta: func(gv schema.GroupVersion) bool { return betaPattern.MatchString(gv.Version) }, 74 // allows users to address all alpha api versions 75 APIAlpha: func(gv schema.GroupVersion) bool { return alphaPattern.MatchString(gv.Version) }, 76 } 77 78 groupVersionMatchersOrder = []string{APIAll, APIGA, APIBeta, APIAlpha} 79 ) 80 81 // MergeAPIResourceConfigs merges the given defaultAPIResourceConfig with the given resourceConfigOverrides. 82 // Exclude the groups not registered in registry, and check if version is 83 // not registered in group, then it will fail. 84 func MergeAPIResourceConfigs( 85 defaultAPIResourceConfig *serverstore.ResourceConfig, 86 resourceConfigOverrides cliflag.ConfigurationMap, 87 registry GroupVersionRegistry, 88 ) (*serverstore.ResourceConfig, error) { 89 resourceConfig := defaultAPIResourceConfig 90 overrides := resourceConfigOverrides 91 92 for _, flag := range groupVersionMatchersOrder { 93 if value, ok := overrides[flag]; ok { 94 if value == "false" { 95 resourceConfig.DisableMatchingVersions(groupVersionMatchers[flag]) 96 } else if value == "true" { 97 resourceConfig.EnableMatchingVersions(groupVersionMatchers[flag]) 98 } else { 99 return nil, fmt.Errorf("invalid value %v=%v", flag, value) 100 } 101 } 102 } 103 104 type versionEnablementPreference struct { 105 key string 106 enabled bool 107 groupVersion schema.GroupVersion 108 } 109 type resourceEnablementPreference struct { 110 key string 111 enabled bool 112 groupVersionResource schema.GroupVersionResource 113 } 114 versionPreferences := []versionEnablementPreference{} 115 resourcePreferences := []resourceEnablementPreference{} 116 117 // "<resourceSpecifier>={true|false} allows users to enable/disable API. 118 // This takes preference over api/all, if specified. 119 // Iterate through all group/version overrides specified in runtimeConfig. 120 for key := range overrides { 121 // Have already handled them above. Can skip them here. 122 if _, ok := groupVersionMatchers[key]; ok { 123 continue 124 } 125 126 tokens := strings.Split(key, "/") 127 if len(tokens) < 2 || len(tokens) > 3 { 128 continue 129 } 130 groupVersionString := tokens[0] + "/" + tokens[1] 131 groupVersion, err := schema.ParseGroupVersion(groupVersionString) 132 if err != nil { 133 return nil, fmt.Errorf("invalid key %s", key) 134 } 135 136 // Exclude group not registered into the registry. 137 if !registry.IsGroupRegistered(groupVersion.Group) { 138 continue 139 } 140 141 // Verify that the groupVersion is registered into registry. 142 if !registry.IsVersionRegistered(groupVersion) { 143 return nil, fmt.Errorf("group version %s that has not been registered", groupVersion.String()) 144 } 145 enabled, err := getRuntimeConfigValue(overrides, key, false) 146 if err != nil { 147 return nil, err 148 } 149 150 switch len(tokens) { 151 case 2: 152 versionPreferences = append(versionPreferences, versionEnablementPreference{ 153 key: key, 154 enabled: enabled, 155 groupVersion: groupVersion, 156 }) 157 case 3: 158 if strings.ToLower(tokens[2]) != tokens[2] { 159 return nil, fmt.Errorf("invalid key %v: group/version/resource and resource is always lowercase plural, not %q", key, tokens[2]) 160 } 161 resourcePreferences = append(resourcePreferences, resourceEnablementPreference{ 162 key: key, 163 enabled: enabled, 164 groupVersionResource: groupVersion.WithResource(tokens[2]), 165 }) 166 } 167 } 168 169 // apply version preferences first, so that we can remove the hardcoded resource preferences that are being overridden 170 for _, versionPreference := range versionPreferences { 171 if versionPreference.enabled { 172 // enable the groupVersion for "group/version=true" 173 resourceConfig.EnableVersions(versionPreference.groupVersion) 174 175 } else { 176 // disable the groupVersion only for "group/version=false" 177 resourceConfig.DisableVersions(versionPreference.groupVersion) 178 } 179 } 180 181 // apply resource preferences last, so they have the highest priority 182 for _, resourcePreference := range resourcePreferences { 183 if resourcePreference.enabled { 184 // enable the resource for "group/version/resource=true" 185 resourceConfig.EnableResources(resourcePreference.groupVersionResource) 186 } else { 187 resourceConfig.DisableResources(resourcePreference.groupVersionResource) 188 } 189 } 190 191 return resourceConfig, nil 192 } 193 194 func getRuntimeConfigValue(overrides cliflag.ConfigurationMap, apiKey string, defaultValue bool) (bool, error) { 195 flagValue, ok := overrides[apiKey] 196 if ok { 197 if flagValue == "" { 198 return true, nil 199 } 200 boolValue, err := strconv.ParseBool(flagValue) 201 if err != nil { 202 return false, fmt.Errorf("invalid value of %s: %s, err: %v", apiKey, flagValue, err) 203 } 204 return boolValue, nil 205 } 206 return defaultValue, nil 207 } 208 209 // ParseGroups takes in resourceConfig and returns parsed groups. 210 func ParseGroups(resourceConfig cliflag.ConfigurationMap) ([]string, error) { 211 groups := []string{} 212 for key := range resourceConfig { 213 if _, ok := groupVersionMatchers[key]; ok { 214 continue 215 } 216 tokens := strings.Split(key, "/") 217 if len(tokens) != 2 && len(tokens) != 3 { 218 return groups, fmt.Errorf("runtime-config invalid key %s", key) 219 } 220 groupVersionString := tokens[0] + "/" + tokens[1] 221 groupVersion, err := schema.ParseGroupVersion(groupVersionString) 222 if err != nil { 223 return nil, fmt.Errorf("runtime-config invalid key %s", key) 224 } 225 groups = append(groups, groupVersion.Group) 226 } 227 228 return groups, nil 229 }