k8s.io/apiserver@v0.31.1/pkg/admission/plugin/webhook/accessors.go (about) 1 /* 2 Copyright 2019 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 webhook 18 19 import ( 20 "sync" 21 22 v1 "k8s.io/api/admissionregistration/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/labels" 25 "k8s.io/apiserver/pkg/admission/plugin/cel" 26 "k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions" 27 "k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace" 28 "k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object" 29 "k8s.io/apiserver/pkg/cel/environment" 30 "k8s.io/apiserver/pkg/features" 31 utilfeature "k8s.io/apiserver/pkg/util/feature" 32 webhookutil "k8s.io/apiserver/pkg/util/webhook" 33 "k8s.io/client-go/rest" 34 ) 35 36 // WebhookAccessor provides a common interface to both mutating and validating webhook types. 37 type WebhookAccessor interface { 38 // This accessor provides the methods needed to support matching against webhook 39 // predicates 40 namespace.NamespaceSelectorProvider 41 object.ObjectSelectorProvider 42 43 // GetUID gets a string that uniquely identifies the webhook. 44 GetUID() string 45 46 // GetConfigurationName gets the name of the webhook configuration that owns this webhook. 47 GetConfigurationName() string 48 49 // GetRESTClient gets the webhook client 50 GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) 51 52 // GetCompiledMatcher gets the compiled matcher object 53 GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher 54 55 // GetName gets the webhook Name field. Note that the name is scoped to the webhook 56 // configuration and does not provide a globally unique identity, if a unique identity is 57 // needed, use GetUID. 58 GetName() string 59 // GetClientConfig gets the webhook ClientConfig field. 60 GetClientConfig() v1.WebhookClientConfig 61 // GetRules gets the webhook Rules field. 62 GetRules() []v1.RuleWithOperations 63 // GetFailurePolicy gets the webhook FailurePolicy field. 64 GetFailurePolicy() *v1.FailurePolicyType 65 // GetMatchPolicy gets the webhook MatchPolicy field. 66 GetMatchPolicy() *v1.MatchPolicyType 67 // GetNamespaceSelector gets the webhook NamespaceSelector field. 68 GetNamespaceSelector() *metav1.LabelSelector 69 // GetObjectSelector gets the webhook ObjectSelector field. 70 GetObjectSelector() *metav1.LabelSelector 71 // GetSideEffects gets the webhook SideEffects field. 72 GetSideEffects() *v1.SideEffectClass 73 // GetTimeoutSeconds gets the webhook TimeoutSeconds field. 74 GetTimeoutSeconds() *int32 75 // GetAdmissionReviewVersions gets the webhook AdmissionReviewVersions field. 76 GetAdmissionReviewVersions() []string 77 78 // GetMatchConditions gets the webhook match conditions field. 79 GetMatchConditions() []v1.MatchCondition 80 81 // GetMutatingWebhook if the accessor contains a MutatingWebhook, returns it and true, else returns false. 82 GetMutatingWebhook() (*v1.MutatingWebhook, bool) 83 // GetValidatingWebhook if the accessor contains a ValidatingWebhook, returns it and true, else returns false. 84 GetValidatingWebhook() (*v1.ValidatingWebhook, bool) 85 86 // GetType returns the type of the accessor (validate or admit) 87 GetType() string 88 } 89 90 // NewMutatingWebhookAccessor creates an accessor for a MutatingWebhook. 91 func NewMutatingWebhookAccessor(uid, configurationName string, h *v1.MutatingWebhook) WebhookAccessor { 92 return &mutatingWebhookAccessor{uid: uid, configurationName: configurationName, MutatingWebhook: h} 93 } 94 95 type mutatingWebhookAccessor struct { 96 *v1.MutatingWebhook 97 uid string 98 configurationName string 99 100 initObjectSelector sync.Once 101 objectSelector labels.Selector 102 objectSelectorErr error 103 104 initNamespaceSelector sync.Once 105 namespaceSelector labels.Selector 106 namespaceSelectorErr error 107 108 initClient sync.Once 109 client *rest.RESTClient 110 clientErr error 111 112 compileMatcher sync.Once 113 compiledMatcher matchconditions.Matcher 114 } 115 116 func (m *mutatingWebhookAccessor) GetUID() string { 117 return m.uid 118 } 119 120 func (m *mutatingWebhookAccessor) GetConfigurationName() string { 121 return m.configurationName 122 } 123 124 func (m *mutatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) { 125 m.initClient.Do(func() { 126 m.client, m.clientErr = clientManager.HookClient(hookClientConfigForWebhook(m)) 127 }) 128 return m.client, m.clientErr 129 } 130 131 func (m *mutatingWebhookAccessor) GetType() string { 132 return "admit" 133 } 134 135 func (m *mutatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher { 136 m.compileMatcher.Do(func() { 137 expressions := make([]cel.ExpressionAccessor, len(m.MutatingWebhook.MatchConditions)) 138 for i, matchCondition := range m.MutatingWebhook.MatchConditions { 139 expressions[i] = &matchconditions.MatchCondition{ 140 Name: matchCondition.Name, 141 Expression: matchCondition.Expression, 142 } 143 } 144 strictCost := false 145 if utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks) { 146 strictCost = true 147 } 148 m.compiledMatcher = matchconditions.NewMatcher(compiler.Compile( 149 expressions, 150 cel.OptionalVariableDeclarations{ 151 HasParams: false, 152 HasAuthorizer: true, 153 StrictCost: strictCost, 154 }, 155 environment.StoredExpressions, 156 ), m.FailurePolicy, "webhook", "admit", m.Name) 157 }) 158 return m.compiledMatcher 159 } 160 161 func (m *mutatingWebhookAccessor) GetParsedNamespaceSelector() (labels.Selector, error) { 162 m.initNamespaceSelector.Do(func() { 163 m.namespaceSelector, m.namespaceSelectorErr = metav1.LabelSelectorAsSelector(m.NamespaceSelector) 164 }) 165 return m.namespaceSelector, m.namespaceSelectorErr 166 } 167 168 func (m *mutatingWebhookAccessor) GetParsedObjectSelector() (labels.Selector, error) { 169 m.initObjectSelector.Do(func() { 170 m.objectSelector, m.objectSelectorErr = metav1.LabelSelectorAsSelector(m.ObjectSelector) 171 }) 172 return m.objectSelector, m.objectSelectorErr 173 } 174 175 func (m *mutatingWebhookAccessor) GetName() string { 176 return m.Name 177 } 178 179 func (m *mutatingWebhookAccessor) GetClientConfig() v1.WebhookClientConfig { 180 return m.ClientConfig 181 } 182 183 func (m *mutatingWebhookAccessor) GetRules() []v1.RuleWithOperations { 184 return m.Rules 185 } 186 187 func (m *mutatingWebhookAccessor) GetFailurePolicy() *v1.FailurePolicyType { 188 return m.FailurePolicy 189 } 190 191 func (m *mutatingWebhookAccessor) GetMatchPolicy() *v1.MatchPolicyType { 192 return m.MatchPolicy 193 } 194 195 func (m *mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector { 196 return m.NamespaceSelector 197 } 198 199 func (m *mutatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector { 200 return m.ObjectSelector 201 } 202 203 func (m *mutatingWebhookAccessor) GetSideEffects() *v1.SideEffectClass { 204 return m.SideEffects 205 } 206 207 func (m *mutatingWebhookAccessor) GetTimeoutSeconds() *int32 { 208 return m.TimeoutSeconds 209 } 210 211 func (m *mutatingWebhookAccessor) GetAdmissionReviewVersions() []string { 212 return m.AdmissionReviewVersions 213 } 214 215 func (m *mutatingWebhookAccessor) GetMatchConditions() []v1.MatchCondition { 216 return m.MatchConditions 217 } 218 219 func (m *mutatingWebhookAccessor) GetMutatingWebhook() (*v1.MutatingWebhook, bool) { 220 return m.MutatingWebhook, true 221 } 222 223 func (m *mutatingWebhookAccessor) GetValidatingWebhook() (*v1.ValidatingWebhook, bool) { 224 return nil, false 225 } 226 227 // NewValidatingWebhookAccessor creates an accessor for a ValidatingWebhook. 228 func NewValidatingWebhookAccessor(uid, configurationName string, h *v1.ValidatingWebhook) WebhookAccessor { 229 return &validatingWebhookAccessor{uid: uid, configurationName: configurationName, ValidatingWebhook: h} 230 } 231 232 type validatingWebhookAccessor struct { 233 *v1.ValidatingWebhook 234 uid string 235 configurationName string 236 237 initObjectSelector sync.Once 238 objectSelector labels.Selector 239 objectSelectorErr error 240 241 initNamespaceSelector sync.Once 242 namespaceSelector labels.Selector 243 namespaceSelectorErr error 244 245 initClient sync.Once 246 client *rest.RESTClient 247 clientErr error 248 249 compileMatcher sync.Once 250 compiledMatcher matchconditions.Matcher 251 } 252 253 func (v *validatingWebhookAccessor) GetUID() string { 254 return v.uid 255 } 256 257 func (v *validatingWebhookAccessor) GetConfigurationName() string { 258 return v.configurationName 259 } 260 261 func (v *validatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) { 262 v.initClient.Do(func() { 263 v.client, v.clientErr = clientManager.HookClient(hookClientConfigForWebhook(v)) 264 }) 265 return v.client, v.clientErr 266 } 267 268 func (v *validatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher { 269 v.compileMatcher.Do(func() { 270 expressions := make([]cel.ExpressionAccessor, len(v.ValidatingWebhook.MatchConditions)) 271 for i, matchCondition := range v.ValidatingWebhook.MatchConditions { 272 expressions[i] = &matchconditions.MatchCondition{ 273 Name: matchCondition.Name, 274 Expression: matchCondition.Expression, 275 } 276 } 277 strictCost := false 278 if utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks) { 279 strictCost = true 280 } 281 v.compiledMatcher = matchconditions.NewMatcher(compiler.Compile( 282 expressions, 283 cel.OptionalVariableDeclarations{ 284 HasParams: false, 285 HasAuthorizer: true, 286 StrictCost: strictCost, 287 }, 288 environment.StoredExpressions, 289 ), v.FailurePolicy, "webhook", "validating", v.Name) 290 }) 291 return v.compiledMatcher 292 } 293 294 func (v *validatingWebhookAccessor) GetParsedNamespaceSelector() (labels.Selector, error) { 295 v.initNamespaceSelector.Do(func() { 296 v.namespaceSelector, v.namespaceSelectorErr = metav1.LabelSelectorAsSelector(v.NamespaceSelector) 297 }) 298 return v.namespaceSelector, v.namespaceSelectorErr 299 } 300 301 func (v *validatingWebhookAccessor) GetParsedObjectSelector() (labels.Selector, error) { 302 v.initObjectSelector.Do(func() { 303 v.objectSelector, v.objectSelectorErr = metav1.LabelSelectorAsSelector(v.ObjectSelector) 304 }) 305 return v.objectSelector, v.objectSelectorErr 306 } 307 308 func (m *validatingWebhookAccessor) GetType() string { 309 return "validate" 310 } 311 312 func (v *validatingWebhookAccessor) GetName() string { 313 return v.Name 314 } 315 316 func (v *validatingWebhookAccessor) GetClientConfig() v1.WebhookClientConfig { 317 return v.ClientConfig 318 } 319 320 func (v *validatingWebhookAccessor) GetRules() []v1.RuleWithOperations { 321 return v.Rules 322 } 323 324 func (v *validatingWebhookAccessor) GetFailurePolicy() *v1.FailurePolicyType { 325 return v.FailurePolicy 326 } 327 328 func (v *validatingWebhookAccessor) GetMatchPolicy() *v1.MatchPolicyType { 329 return v.MatchPolicy 330 } 331 332 func (v *validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector { 333 return v.NamespaceSelector 334 } 335 336 func (v *validatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector { 337 return v.ObjectSelector 338 } 339 340 func (v *validatingWebhookAccessor) GetSideEffects() *v1.SideEffectClass { 341 return v.SideEffects 342 } 343 344 func (v *validatingWebhookAccessor) GetTimeoutSeconds() *int32 { 345 return v.TimeoutSeconds 346 } 347 348 func (v *validatingWebhookAccessor) GetAdmissionReviewVersions() []string { 349 return v.AdmissionReviewVersions 350 } 351 352 func (v *validatingWebhookAccessor) GetMatchConditions() []v1.MatchCondition { 353 return v.MatchConditions 354 } 355 356 func (v *validatingWebhookAccessor) GetMutatingWebhook() (*v1.MutatingWebhook, bool) { 357 return nil, false 358 } 359 360 func (v *validatingWebhookAccessor) GetValidatingWebhook() (*v1.ValidatingWebhook, bool) { 361 return v.ValidatingWebhook, true 362 } 363 364 // hookClientConfigForWebhook construct a webhookutil.ClientConfig using a WebhookAccessor to access 365 // v1beta1.MutatingWebhook and v1beta1.ValidatingWebhook API objects. webhookutil.ClientConfig is used 366 // to create a HookClient and the purpose of the config struct is to share that with other packages 367 // that need to create a HookClient. 368 func hookClientConfigForWebhook(w WebhookAccessor) webhookutil.ClientConfig { 369 ret := webhookutil.ClientConfig{Name: w.GetName(), CABundle: w.GetClientConfig().CABundle} 370 if w.GetClientConfig().URL != nil { 371 ret.URL = *w.GetClientConfig().URL 372 } 373 if w.GetClientConfig().Service != nil { 374 ret.Service = &webhookutil.ClientConfigService{ 375 Name: w.GetClientConfig().Service.Name, 376 Namespace: w.GetClientConfig().Service.Namespace, 377 } 378 if w.GetClientConfig().Service.Port != nil { 379 ret.Service.Port = *w.GetClientConfig().Service.Port 380 } else { 381 ret.Service.Port = 443 382 } 383 if w.GetClientConfig().Service.Path != nil { 384 ret.Service.Path = *w.GetClientConfig().Service.Path 385 } 386 } 387 return ret 388 }