@@ -346,7 +346,115 @@ Since the apiserver can convert between all of the versions by which a resource
346346is made available, this situation can be improved by having the apiserver
347347convert resources to the group/versions a webhook registered for.
348348
349- The API representation and behavior for this feature is still under design and will be updated/approved here prior to implementation.
349+ Because admission can be used for out-of-tree defaulting and field enforcement,
350+ admission plugins may intentionally target specific versions of resources.
351+ A ` matchPolicy ` field will be added to the webhook configuration object,
352+ allowing a configuration to specify whether the apiserver should only route requests
353+ which exactly match the specified rules to the webhook, or whether it should route
354+ requests for equivalent resources via different API groups or versions as well.
355+ For safety, this field defaults to ` Exact ` in ` v1beta1 ` . In ` v1 ` , we can default it to ` Equivalent ` .
356+
357+ ``` golang
358+ // Webhook describes an admission webhook and the resources and operations it applies to.
359+ type Webhook struct {
360+ ...
361+ // matchPolicy defines how the "rules" field is applied when a request is made
362+ // to a different API group or version of a resource listed in "rules".
363+ // Allowed values are "Exact" or "Equivalent".
364+ // - Exact: match requests only if they exactly match a given rule. For example, if an object can be modified
365+ // via API versions v1 and v2, and "rules" only includes "v1", do not send a request to "v2" to the webhook.
366+ // - Equivalent: match requests if they modify a resource listed in rules via another API group or version.
367+ // For example, if an object can be modified via API versions v1 and v2, and "rules" only includes "v1",
368+ // a request to "v2" should be converted to "v1" and sent to the webhook.
369+ // Defaults to "Exact"
370+ // +optional
371+ MatchPolicy *MatchPolicyType ` json:"matchPolicy,omitempty"`
372+ ` ` `
373+
374+ The apiserver will do the following:
375+
376+ 1. For each resource, compute the set of other resources that access or affect the same data, and the kind of the expected object. For example:
377+ * ` apps,v1,deployments` (` apiVersion: apps/v1, kind: Deployment ` ) is also available via:
378+ * ` apps,v1beta2,deployments` (` apiVersion: apps/v1beta2, kind: Deployment ` )
379+ * ` apps,v1beta1,deployments` (` apiVersion: apps/v1beta1, kind: Deployment ` )
380+ * ` extensions,v1beta1,deployments` (` apiVersion: extensions/v1beta1, kind: Deployment ` )
381+ * ` apps,v1,deployments/scale` (` apiVersion: autoscaling/v1, kind: Scale ` ) is also available via:
382+ * ` apps,v1beta2,deployments/scale` (` apiVersion: apps/v1beta2, kind: Scale ` )
383+ * ` apps,v1beta1,deployments/scale` (` apiVersion: apps/v1beta1, kind: Scale ` )
384+ * ` extensions,v1beta1,deployments/scale` (` apiVersion: extensions/v1beta1, kind: Scale ` )
385+ 2. When evaluating whether to dispatch an incoming request to a webhook with
386+ ` matchPolicy: Equivalent ` , check the request's resource *and* all equivalent
387+ resources against the ones the webhook had registered for. If needed, convert
388+ the incoming object to one the webhook indicated it understood.
389+
390+ The ` AdmissionRequest` sent to a webhook includes the fully-qualified
391+ kind (group/version/kind) and resource (group/version/resource):
392+
393+ ` ` ` golang
394+ type AdmissionRequest struct {
395+ ...
396+ // Kind is the type of object being manipulated. For example: Pod
397+ Kind metav1.GroupVersionKind ` json:"kind" protobuf:"bytes,2,opt,name=kind"`
398+ // Resource is the name of the resource being requested. This is not the kind. For example: pods
399+ Resource metav1.GroupVersionResource ` json:"resource" protobuf:"bytes,3,opt,name=resource"`
400+ // SubResource is the name of the subresource being requested. This is a different resource, scoped to the parent
401+ // resource, but it may have a different kind. For instance, /pods has the resource "pods" and the kind "Pod", while
402+ // /pods/foo/status has the resource "pods", the sub resource "status", and the kind "Pod" (because status operates on
403+ // pods). The binding resource for a pod though may be /pods/foo/binding, which has resource "pods", subresource
404+ // "binding", and kind "Binding".
405+ // +optional
406+ SubResource string ` json:"subResource,omitempty" protobuf:"bytes,4,opt,name=subResource"`
407+ ` ` `
408+
409+ Prior to this conversion feature, the resource and kind of the request made to the
410+ API server, and the resource and kind sent in the AdmissionRequest were identical.
411+
412+ When a conversion occurs and the object we send to the webhook is a different kind
413+ than was sent to the API server, or the resource the webhook registered for is different
414+ than the request made to the API server, we have three options for communicating that to the webhook:
415+ 1. Do not expose that fact to the webhook:
416+ * Set AdmissionRequest ` kind` to the converted kind
417+ * Set AdmissionRequest ` resource` to the registered-for resource
418+ 2. Expose that fact to the webhook using the existing fields:
419+ * Set AdmissionRequest ` kind` to the API request's kind (not matching the object in the AdmissionRequest)
420+ * Set AdmissionRequest ` resource` to the API request's resource (not matching the registered-for resource)
421+ 3. Expose that fact to the webhook using new AdmissionRequest fields:
422+ * Set AdmissionRequest ` kind` to the converted kind
423+ * Set AdmissionRequest ` requestKind` to the API request's kind
424+ * Set AdmissionRequest ` resource` to the registered-for resource
425+ * Set AdmissionRequest ` requestResource` to the API request's resource
426+
427+ Option 1 loses information the webhook could use (for example, to enforce different validation or defaulting rules for newer APIs).
428+
429+ Option 2 risks breaking webhook logic by sending it resources it did not register for, and kinds it did not expect.
430+
431+ Option 3 is preferred, and is the safest option that preserves information for use by the webhook.
432+
433+ To support this, three fields will be added to AdmissionRequest, and populated with the original request's kind, resource, and subResource:
434+
435+ ` ` ` golang
436+ type AdmissionRequest struct {
437+ ...
438+ // RequestKind is the type of object being manipulated by the the original API request. For example: Pod
439+ // If this differs from the value in "kind", an equivalent match and conversion was performed.
440+ // See documentation for the "matchPolicy" field in the webhook configuration type.
441+ // +optional
442+ RequestKind *metav1.GroupVersionKind ` json:"requestKind,omitempty"`
443+ // RequestResource is the name of the resource being requested by the the original API request. This is not the kind. For example: ""/v1/pods
444+ // If this differs from the value in "resource", an equivalent match and conversion was performed.
445+ // See documentation for the "matchPolicy" field in the webhook configuration type.
446+ // +optional
447+ RequestResource *metav1.GroupVersionResource ` json:"requestResource,omitempty"`
448+ // RequestSubResource is the name of the subresource being requested by the the original API request. This is a different resource, scoped to the parent
449+ // resource, but it may have a different kind. For instance, /pods has the resource "pods" and the kind "Pod", while
450+ // /pods/foo/status has the resource "pods", the sub resource "status", and the kind "Pod" (because status operates on
451+ // pods). The binding resource for a pod though may be /pods/foo/binding, which has resource "pods", subresource
452+ // "binding", and kind "Binding".
453+ // If this differs from the value in "subResource", an equivalent match and conversion was performed.
454+ // See documentation for the "matchPolicy" field in the webhook configuration type.
455+ // +optional
456+ RequestSubResource string ` json:"requestSubResource,omitempty"`
457+ ` ` `
350458
351459## V1 API
352460
@@ -410,6 +518,17 @@ type Rule struct {
410518 Scope ScopeType ` json:"scope,omitempty" protobuf:"bytes,3,opt,name=scope"`
411519}
412520
521+ type ConversionPolicyType string
522+
523+ const (
524+ // ConversionIgnore means that requests that do not match a webhook's rules but could be
525+ // converted to a resource the webhook registered for, should be ignored.
526+ ConversionIgnore ConversionPolicyType = " Ignore"
527+ // ConversionConvert means that requests that do not match a webhook's rules but could be
528+ // converted to a resource the webhook registered for, should be converted and sent to the webhook.
529+ ConversionConvert ConversionPolicyType = " Convert"
530+ )
531+
413532type FailurePolicyType string
414533
415534const (
@@ -516,6 +635,18 @@ type Webhook struct {
516635 // on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.
517636 Rules []RuleWithOperations ` json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"`
518637
638+ // matchPolicy defines how the "rules" field is applied when a request is made
639+ // to a different API group or version of a resource listed in "rules".
640+ // Allowed values are "Exact" or "Equivalent".
641+ // - Exact: match requests only if they exactly match a given rule. For example, if an object can be modified
642+ // via API version v1 and v2, and "rules" only includes "v1", do not send a request to "v2" to the webhook.
643+ // - Equivalent: match requests if they modify a resource listed in rules via another API group or version.
644+ // For example, if an object can be modified via API version v1 and v2, and "rules" only includes "v1",
645+ // a request to "v2" should be converted to "v1" and sent to the webhook.
646+ // Defaults to "Equivalent"
647+ // +optional
648+ MatchPolicy *MatchPolicyType ` json:"matchPolicy,omitempty"`
649+
519650 // FailurePolicy defines how unrecognized errors from the admission endpoint are handled -
520651 // allowed values are Ignore or Fail. Defaults to Ignore.
521652 // +optional
0 commit comments