Adding new api version of admissionregistration.k8s.io v1alpha1 for CEL in Admission Control

This commit is contained in:
Cici Huang 2022-10-04 04:46:55 +00:00
parent f8de127789
commit 0486e06261
42 changed files with 3864 additions and 746 deletions

View File

@ -1,10 +1,6 @@
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,MutatingWebhook,AdmissionReviewVersions
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,MutatingWebhook,Rules
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,MutatingWebhookConfiguration,Webhooks
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,Rule,APIGroups
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,Rule,APIVersions
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,Rule,Resources
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,RuleWithOperations,Operations
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,ValidatingWebhook,AdmissionReviewVersions
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,ValidatingWebhook,Rules
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,ValidatingWebhookConfiguration,Webhooks
@ -12,10 +8,6 @@ API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1,Webhoo
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,MutatingWebhook,AdmissionReviewVersions
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,MutatingWebhook,Rules
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,MutatingWebhookConfiguration,Webhooks
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,Rule,APIGroups
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,Rule,APIVersions
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,Rule,Resources
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,RuleWithOperations,Operations
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,ValidatingWebhook,AdmissionReviewVersions
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,ValidatingWebhook,Rules
API rule violation: list_type_missing,k8s.io/api/admissionregistration/v1beta1,ValidatingWebhookConfiguration,Webhooks

View File

@ -275,6 +275,7 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{
{Group: "storage.k8s.io", Version: "v1alpha1"}: {group: 16800, version: 1},
{Group: "apiextensions.k8s.io", Version: "v1"}: {group: 16700, version: 15},
{Group: "admissionregistration.k8s.io", Version: "v1"}: {group: 16700, version: 15},
{Group: "admissionregistration.k8s.io", Version: "v1alpha1"}: {group: 16700, version: 9},
{Group: "scheduling.k8s.io", Version: "v1"}: {group: 16600, version: 15},
{Group: "coordination.k8s.io", Version: "v1"}: {group: 16500, version: 15},
{Group: "node.k8s.io", Version: "v1"}: {group: 16300, version: 15},

View File

@ -1,5 +1,6 @@
{
"k8s.io/api/admissionregistration/v1": "admissionregistrationv1",
"k8s.io/api/admissionregistration/v1alpha1": "admissionregistrationv1alpha1",
"k8s.io/api/admissionregistration/v1beta1": "admissionregistrationv1beta1",
"k8s.io/api/admission/v1beta1": "admissionv1beta1",
"k8s.io/api/admission/v1": "admissionv1",

View File

@ -65,6 +65,7 @@ export KUBE_OUTPUT_HOSTBIN
KUBE_AVAILABLE_GROUP_VERSIONS="${KUBE_AVAILABLE_GROUP_VERSIONS:-\
v1 \
admissionregistration.k8s.io/v1 \
admissionregistration.k8s.io/v1alpha1 \
admissionregistration.k8s.io/v1beta1 \
admission.k8s.io/v1 \
admission.k8s.io/v1beta1 \

View File

@ -46,132 +46,136 @@ func (o orderedGroupVersionKinds) Less(i, j int) bool {
func TestDefaulting(t *testing.T) {
// these are the known types with defaulters - you must add to this list if you add a top level defaulter
typesWithDefaulting := map[schema.GroupVersionKind]struct{}{
{Group: "", Version: "v1", Kind: "ConfigMap"}: {},
{Group: "", Version: "v1", Kind: "ConfigMapList"}: {},
{Group: "", Version: "v1", Kind: "Endpoints"}: {},
{Group: "", Version: "v1", Kind: "EndpointsList"}: {},
{Group: "", Version: "v1", Kind: "EphemeralContainers"}: {},
{Group: "", Version: "v1", Kind: "Namespace"}: {},
{Group: "", Version: "v1", Kind: "NamespaceList"}: {},
{Group: "", Version: "v1", Kind: "Node"}: {},
{Group: "", Version: "v1", Kind: "NodeList"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolume"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeList"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeClaimList"}: {},
{Group: "", Version: "v1", Kind: "Pod"}: {},
{Group: "", Version: "v1", Kind: "PodList"}: {},
{Group: "", Version: "v1", Kind: "PodTemplate"}: {},
{Group: "", Version: "v1", Kind: "PodTemplateList"}: {},
{Group: "", Version: "v1", Kind: "ReplicationController"}: {},
{Group: "", Version: "v1", Kind: "ReplicationControllerList"}: {},
{Group: "", Version: "v1", Kind: "Secret"}: {},
{Group: "", Version: "v1", Kind: "SecretList"}: {},
{Group: "", Version: "v1", Kind: "Service"}: {},
{Group: "", Version: "v1", Kind: "ServiceList"}: {},
{Group: "apps", Version: "v1beta1", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1beta1", Kind: "StatefulSetList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1beta2", Kind: "StatefulSetList"}: {},
{Group: "apps", Version: "v1", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1", Kind: "StatefulSetList"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "batch", Version: "v1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v1", Kind: "Job"}: {},
{Group: "batch", Version: "v1", Kind: "JobList"}: {},
{Group: "batch", Version: "v1beta1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v1beta1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v1beta1", Kind: "JobTemplate"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {},
{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}: {},
{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}: {},
{Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSlice"}: {},
{Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSliceList"}: {},
{Group: "discovery.k8s.io", Version: "v1beta1", Kind: "EndpointSlice"}: {},
{Group: "discovery.k8s.io", Version: "v1beta1", Kind: "EndpointSliceList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "DaemonSet"}: {},
{Group: "apps", Version: "v1beta2", Kind: "DaemonSetList"}: {},
{Group: "apps", Version: "v1", Kind: "DaemonSet"}: {},
{Group: "apps", Version: "v1", Kind: "DaemonSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {},
{Group: "apps", Version: "v1beta1", Kind: "Deployment"}: {},
{Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "Deployment"}: {},
{Group: "apps", Version: "v1beta2", Kind: "DeploymentList"}: {},
{Group: "apps", Version: "v1", Kind: "Deployment"}: {},
{Group: "apps", Version: "v1", Kind: "DeploymentList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Ingress"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "IngressList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicy"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicyList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "ReplicaSet"}: {},
{Group: "apps", Version: "v1beta2", Kind: "ReplicaSetList"}: {},
{Group: "apps", Version: "v1", Kind: "ReplicaSet"}: {},
{Group: "apps", Version: "v1", Kind: "ReplicaSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicyList"}: {},
{Group: "policy", Version: "v1beta1", Kind: "PodSecurityPolicy"}: {},
{Group: "policy", Version: "v1beta1", Kind: "PodSecurityPolicyList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBindingList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfigurationList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfigurationList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfigurationList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfigurationList"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}: {},
{Group: "networking.k8s.io", Version: "v1beta1", Kind: "Ingress"}: {},
{Group: "networking.k8s.io", Version: "v1beta1", Kind: "IngressList"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClass"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClassList"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClassList"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "CSIDriver"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "CSIDriverList"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClassList"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachmentList"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriver"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriverList"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "VolumeAttachment"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "VolumeAttachmentList"}: {},
{Group: "authentication.k8s.io", Version: "v1", Kind: "TokenRequest"}: {},
{Group: "scheduling.k8s.io", Version: "v1alpha1", Kind: "PriorityClass"}: {},
{Group: "scheduling.k8s.io", Version: "v1beta1", Kind: "PriorityClass"}: {},
{Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClass"}: {},
{Group: "scheduling.k8s.io", Version: "v1alpha1", Kind: "PriorityClassList"}: {},
{Group: "scheduling.k8s.io", Version: "v1beta1", Kind: "PriorityClassList"}: {},
{Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClassList"}: {},
{Group: "", Version: "v1", Kind: "ConfigMap"}: {},
{Group: "", Version: "v1", Kind: "ConfigMapList"}: {},
{Group: "", Version: "v1", Kind: "Endpoints"}: {},
{Group: "", Version: "v1", Kind: "EndpointsList"}: {},
{Group: "", Version: "v1", Kind: "EphemeralContainers"}: {},
{Group: "", Version: "v1", Kind: "Namespace"}: {},
{Group: "", Version: "v1", Kind: "NamespaceList"}: {},
{Group: "", Version: "v1", Kind: "Node"}: {},
{Group: "", Version: "v1", Kind: "NodeList"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolume"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeList"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeClaimList"}: {},
{Group: "", Version: "v1", Kind: "Pod"}: {},
{Group: "", Version: "v1", Kind: "PodList"}: {},
{Group: "", Version: "v1", Kind: "PodTemplate"}: {},
{Group: "", Version: "v1", Kind: "PodTemplateList"}: {},
{Group: "", Version: "v1", Kind: "ReplicationController"}: {},
{Group: "", Version: "v1", Kind: "ReplicationControllerList"}: {},
{Group: "", Version: "v1", Kind: "Secret"}: {},
{Group: "", Version: "v1", Kind: "SecretList"}: {},
{Group: "", Version: "v1", Kind: "Service"}: {},
{Group: "", Version: "v1", Kind: "ServiceList"}: {},
{Group: "apps", Version: "v1beta1", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1beta1", Kind: "StatefulSetList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1beta2", Kind: "StatefulSetList"}: {},
{Group: "apps", Version: "v1", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1", Kind: "StatefulSetList"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "batch", Version: "v1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v1", Kind: "Job"}: {},
{Group: "batch", Version: "v1", Kind: "JobList"}: {},
{Group: "batch", Version: "v1beta1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v1beta1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v1beta1", Kind: "JobTemplate"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {},
{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}: {},
{Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}: {},
{Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSlice"}: {},
{Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSliceList"}: {},
{Group: "discovery.k8s.io", Version: "v1beta1", Kind: "EndpointSlice"}: {},
{Group: "discovery.k8s.io", Version: "v1beta1", Kind: "EndpointSliceList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "DaemonSet"}: {},
{Group: "apps", Version: "v1beta2", Kind: "DaemonSetList"}: {},
{Group: "apps", Version: "v1", Kind: "DaemonSet"}: {},
{Group: "apps", Version: "v1", Kind: "DaemonSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {},
{Group: "apps", Version: "v1beta1", Kind: "Deployment"}: {},
{Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "Deployment"}: {},
{Group: "apps", Version: "v1beta2", Kind: "DeploymentList"}: {},
{Group: "apps", Version: "v1", Kind: "Deployment"}: {},
{Group: "apps", Version: "v1", Kind: "DeploymentList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Ingress"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "IngressList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicy"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicyList"}: {},
{Group: "apps", Version: "v1beta2", Kind: "ReplicaSet"}: {},
{Group: "apps", Version: "v1beta2", Kind: "ReplicaSetList"}: {},
{Group: "apps", Version: "v1", Kind: "ReplicaSet"}: {},
{Group: "apps", Version: "v1", Kind: "ReplicaSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicyList"}: {},
{Group: "policy", Version: "v1beta1", Kind: "PodSecurityPolicy"}: {},
{Group: "policy", Version: "v1beta1", Kind: "PodSecurityPolicyList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBindingList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicy"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyBinding"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyBindingList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfigurationList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfigurationList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfigurationList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfigurationList"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}: {},
{Group: "networking.k8s.io", Version: "v1beta1", Kind: "Ingress"}: {},
{Group: "networking.k8s.io", Version: "v1beta1", Kind: "IngressList"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClass"}: {},
{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClassList"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClassList"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "CSIDriver"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "CSIDriverList"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClassList"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachmentList"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriver"}: {},
{Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriverList"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "VolumeAttachment"}: {},
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "VolumeAttachmentList"}: {},
{Group: "authentication.k8s.io", Version: "v1", Kind: "TokenRequest"}: {},
{Group: "scheduling.k8s.io", Version: "v1alpha1", Kind: "PriorityClass"}: {},
{Group: "scheduling.k8s.io", Version: "v1beta1", Kind: "PriorityClass"}: {},
{Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClass"}: {},
{Group: "scheduling.k8s.io", Version: "v1alpha1", Kind: "PriorityClassList"}: {},
{Group: "scheduling.k8s.io", Version: "v1beta1", Kind: "PriorityClassList"}: {},
{Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClassList"}: {},
}
f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))

View File

@ -35,12 +35,18 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
},
func(obj *admissionregistration.ValidatingWebhook, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
p := admissionregistration.FailurePolicyType("Fail")
obj.FailurePolicy = &p
m := admissionregistration.MatchPolicyType("Exact")
obj.MatchPolicy = &m
s := admissionregistration.SideEffectClassUnknown
obj.SideEffects = &s
if obj.FailurePolicy == nil {
p := admissionregistration.FailurePolicyType("Fail")
obj.FailurePolicy = &p
}
if obj.MatchPolicy == nil {
m := admissionregistration.MatchPolicyType("Exact")
obj.MatchPolicy = &m
}
if obj.SideEffects == nil {
s := admissionregistration.SideEffectClassUnknown
obj.SideEffects = &s
}
if obj.TimeoutSeconds == nil {
i := int32(30)
obj.TimeoutSeconds = &i
@ -49,19 +55,41 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
},
func(obj *admissionregistration.MutatingWebhook, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
p := admissionregistration.FailurePolicyType("Fail")
obj.FailurePolicy = &p
m := admissionregistration.MatchPolicyType("Exact")
obj.MatchPolicy = &m
s := admissionregistration.SideEffectClassUnknown
obj.SideEffects = &s
n := admissionregistration.NeverReinvocationPolicy
obj.ReinvocationPolicy = &n
if obj.FailurePolicy == nil {
p := admissionregistration.FailurePolicyType("Fail")
obj.FailurePolicy = &p
}
if obj.MatchPolicy == nil {
m := admissionregistration.MatchPolicyType("Exact")
obj.MatchPolicy = &m
}
if obj.SideEffects == nil {
s := admissionregistration.SideEffectClassUnknown
obj.SideEffects = &s
}
if obj.ReinvocationPolicy == nil {
r := admissionregistration.NeverReinvocationPolicy
obj.ReinvocationPolicy = &r
}
if obj.TimeoutSeconds == nil {
i := int32(30)
obj.TimeoutSeconds = &i
}
obj.AdmissionReviewVersions = []string{"v1beta1"}
},
func(obj *admissionregistration.ValidatingAdmissionPolicySpec, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
if obj.FailurePolicy == nil {
p := admissionregistration.FailurePolicyType("Fail")
obj.FailurePolicy = &p
}
},
func(obj *admissionregistration.MatchResources, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
if obj.MatchPolicy == nil {
m := admissionregistration.MatchPolicyType("Exact")
obj.MatchPolicy = &m
}
},
}
}

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
v1 "k8s.io/kubernetes/pkg/apis/admissionregistration/v1"
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1"
)
@ -33,6 +34,7 @@ func init() {
func Install(scheme *runtime.Scheme) {
utilruntime.Must(admissionregistration.AddToScheme(scheme))
utilruntime.Must(v1beta1.AddToScheme(scheme))
utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion, v1alpha1.SchemeGroupVersion))
}

View File

@ -51,6 +51,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ValidatingWebhookConfigurationList{},
&MutatingWebhookConfiguration{},
&MutatingWebhookConfigurationList{},
&ValidatingAdmissionPolicy{},
&ValidatingAdmissionPolicyList{},
&ValidatingAdmissionPolicyBinding{},
&ValidatingAdmissionPolicyBindingList{},
)
return nil
}

View File

@ -115,6 +115,285 @@ const (
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.
type ValidatingAdmissionPolicy struct {
metav1.TypeMeta
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
Spec ValidatingAdmissionPolicySpec
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ValidatingAdmissionPolicyList is a list of ValidatingAdmissionPolicy.
type ValidatingAdmissionPolicyList struct {
metav1.TypeMeta
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta
// List of ValidatingAdmissionPolicy.
Items []ValidatingAdmissionPolicy
}
// ValidatingAdmissionPolicySpec is the specification of the desired behavior of the AdmissionPolicy.
type ValidatingAdmissionPolicySpec struct {
// ParamKind specifies the kind of resources used to parameterize this policy.
// If absent, there are no parameters for this policy and the param CEL variable will not be provided to validation expressions.
// If ParamKind refers to a non-existent kind, this policy definition is mis-configured and the FailurePolicy is applied.
// If paramKind is specified but paramRef is unset in ValidatingAdmissionPolicyBinding, the params variable will be null.
// +optional
ParamKind *ParamKind
// MatchConstraints specifies what resources this policy is designed to validate.
// The AdmissionPolicy cares about a request if it matches _all_ Constraint.
// However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API
// ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding.
// Required.
MatchConstraints *MatchResources
// Validations contain CEL expressions which is used to apply the validation.
// A minimum of one validation is required for a policy definition.
// Required.
Validations []Validation
// FailurePolicy defines how to handle failures for the admission policy.
// Failures can occur from invalid or mis-configured policy definitions or bindings.
// A policy is invalid if spec.paramKind refers to a non-existent Kind.
// A binding is invalid if spec.paramRef.name refers to a non-existent resource.
// Allowed values are Ignore or Fail. Defaults to Fail.
// +optional
FailurePolicy *FailurePolicyType
}
// ParamKind is a tuple of Group Kind and Version.
type ParamKind struct {
// APIVersion is the API group version the resources belong to.
// In format of "group/version".
// Required.
APIVersion string
// Kind is the API kind the resources belong to.
// Required.
Kind string
}
// Validation specifies the CEL expression which is used to apply the validation.
type Validation struct {
// Expression represents the expression which will be evaluated by CEL.
// ref: https://github.com/google/cel-spec
// CEL expressions have access to the contents of the Admission request/response, organized into CEL variables as well as some other useful variables:
//
//'object' - The object from the incoming request. The value is null for DELETE requests.
//'oldObject' - The existing object. The value is null for CREATE requests.
//'request' - Attributes of the admission request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
//'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
//
// The `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the
// object. No other metadata properties are accessible.
//
// Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.
// Accessible property names are escaped according to the following rules when accessed in the expression:
// - '__' escapes to '__underscores__'
// - '.' escapes to '__dot__'
// - '-' escapes to '__dash__'
// - '/' escapes to '__slash__'
// - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:
// "true", "false", "null", "in", "as", "break", "const", "continue", "else", "for", "function", "if",
// "import", "let", "loop", "package", "namespace", "return".
// Examples:
// - Expression accessing a property named "namespace": {"Expression": "object.__namespace__ > 0"}
// - Expression accessing a property named "x-prop": {"Expression": "object.x__dash__prop > 0"}
// - Expression accessing a property named "redact__d": {"Expression": "object.redact__underscores__d > 0"}
//
// Equality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1].
// Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:
// - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and
// non-intersecting elements in `Y` are appended, retaining their partial order.
// - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values
// are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with
// non-intersecting keys are appended, retaining their partial order.
// Required.
Expression string
// Message represents the message displayed when validation fails. The message is required if the Expression contains
// line breaks. The message must not contain line breaks.
// If unset, the message is "failed rule: {Rule}".
// e.g. "must be a URL with the host matching spec.host"
// If ExpressMessage is specified, Message will be ignored
// If the Expression contains line breaks. Eith Message or ExpressMessage is required.
// The message must not contain line breaks.
// If unset, the message is "failed Expression: {Expression}".
// +optional
Message string
// Reason represents a machine-readable description of why this validation failed.
// If this is the first validation in the list to fail, this reason, as well as the
// corresponding HTTP response code, are used in the
// HTTP response to the client.
// The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge".
// If not set, StatusReasonInvalid is used in the response to the client.
// +optional
Reason *metav1.StatusReason
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
type ValidatingAdmissionPolicyBinding struct {
metav1.TypeMeta
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta
// Specification of the desired behavior of the ValidatingAdmissionPolicyBinding.
Spec ValidatingAdmissionPolicyBindingSpec
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ValidatingAdmissionPolicyBindingList is a list of PolicyBinding.
type ValidatingAdmissionPolicyBindingList struct {
metav1.TypeMeta
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta
// List of PolicyBinding.
Items []ValidatingAdmissionPolicyBinding
}
// ValidatingAdmissionPolicyBindingSpec is the specification of the ValidatingAdmissionPolicyBinding.
type ValidatingAdmissionPolicyBindingSpec struct {
// PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to.
// If the referenced resource does not exist, this binding is considered invalid and will be ignored
// Required.
PolicyName string
// ParamRef specifies the parameter resource used to configure the admission control policy.
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
// +optional
ParamRef *ParamRef
// MatchResources declares what resources match this binding and will be validated by it.
// Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this.
// If this is unset, all resources matched by the policy are validated by this binding
// When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated.
// Note that this is differs from ValidatingAdmissionPolicy matchConstraints, where resourceRules are required.
// +optional
MatchResources *MatchResources
}
// ParamRef references a parameter resource
type ParamRef struct {
// Name of the resource being referenced.
Name string
// Namespace of the referenced resource.
// Should be empty for the cluster-scoped resources
// +optional
Namespace string
}
// MatchResources decides whether to run the admission control policy on an object based
// on whether it meets the match criteria.
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
type MatchResources struct {
// NamespaceSelector decides whether to run the admission control policy on an object based
// on whether the namespace for that object matches the selector. If the
// object itself is a namespace, the matching is performed on
// object.metadata.labels. If the object is another cluster scoped resource,
// it never skips the policy.
//
// For example, to run the webhook on any objects whose namespace is not
// associated with "runlevel" of "0" or "1"; you will set the selector as
// follows:
// "namespaceSelector": {
// "matchExpressions": [
// {
// "key": "runlevel",
// "operator": "NotIn",
// "values": [
// "0",
// "1"
// ]
// }
// ]
// }
//
// If instead you want to only run the policy on any objects whose
// namespace is associated with the "environment" of "prod" or "staging";
// you will set the selector as follows:
// "namespaceSelector": {
// "matchExpressions": [
// {
// "key": "environment",
// "operator": "In",
// "values": [
// "prod",
// "staging"
// ]
// }
// ]
// }
//
// See
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
// for more examples of label selectors.
//
// Default to the empty LabelSelector, which matches everything.
// +optional
NamespaceSelector *metav1.LabelSelector
// ObjectSelector decides whether to run the validation based on if the
// object has matching labels. objectSelector is evaluated against both
// the oldObject and newObject that would be sent to the cel validation, and
// is considered to match if either object matches the selector. A null
// object (oldObject in the case of create, or newObject in the case of
// delete) or an object that cannot have labels (like a
// DeploymentRollback or a PodProxyOptions object) is not considered to
// match.
// Use the object selector only if the webhook is opt-in, because end
// users may skip the admission webhook by setting the labels.
// Default to the empty LabelSelector, which matches everything.
// +optional
ObjectSelector *metav1.LabelSelector
// ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches.
// The policy cares about an operation if it matches _any_ Rule.
// +optional
ResourceRules []NamedRuleWithOperations
// ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about.
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
// +optional
ExcludeResourceRules []NamedRuleWithOperations
// matchPolicy defines how the "MatchResources" list is used to match incoming requests.
// Allowed values are "Exact" or "Equivalent".
//
// - Exact: match a request only if it exactly matches a specified rule.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy.
//
// - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy.
//
// Defaults to "Equivalent"
// +optional
MatchPolicy *MatchPolicyType
}
// NamedRuleWithOperations is a tuple of Operations and Resources with ResourceNames.
type NamedRuleWithOperations struct {
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.
// +optional
ResourceNames []string
// RuleWithOperations is a tuple of Operations and Resources.
RuleWithOperations RuleWithOperations
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ValidatingWebhookConfiguration describes the configuration of an admission webhook that accepts or rejects and object without changing it.
type ValidatingWebhookConfiguration struct {
metav1.TypeMeta

View File

@ -0,0 +1,43 @@
/*
Copyright 2022 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 v1
import (
v1 "k8s.io/api/admissionregistration/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
admissionregistration "k8s.io/kubernetes/pkg/apis/admissionregistration"
)
// Convert_admissionregistration_Rule_To_v1_Rule is an autogenerated conversion function.
func Convert_admissionregistration_Rule_To_v1_Rule(in *admissionregistration.Rule, out *v1.Rule, s conversion.Scope) error {
return autoConvert_admissionregistration_Rule_To_v1_Rule(in, out, s)
}
// Convert_v1_Rule_To_admissionregistration_Rule is an autogenerated conversion function.
func Convert_v1_Rule_To_admissionregistration_Rule(in *v1.Rule, out *admissionregistration.Rule, s conversion.Scope) error {
return autoConvert_v1_Rule_To_admissionregistration_Rule(in, out, s)
}
// Convert_admissionregistration_RuleWithOperations_To_v1_RuleWithOperations is an autogenerated conversion function.
func Convert_admissionregistration_RuleWithOperations_To_v1_RuleWithOperations(in *admissionregistration.RuleWithOperations, out *v1.RuleWithOperations, s conversion.Scope) error {
return autoConvert_admissionregistration_RuleWithOperations_To_v1_RuleWithOperations(in, out, s)
}
// Convert_v1_RuleWithOperations_To_admissionregistration_RuleWithOperations is an autogenerated conversion function.
func Convert_v1_RuleWithOperations_To_admissionregistration_RuleWithOperations(in *v1.RuleWithOperations, out *admissionregistration.RuleWithOperations, s conversion.Scope) error {
return autoConvert_v1_RuleWithOperations_To_admissionregistration_RuleWithOperations(in, out, s)
}

View File

@ -20,19 +20,22 @@ import (
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
utilpointer "k8s.io/utils/pointer"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}
// SetDefaults_ValidatingWebhook sets defaults for webhook validating
func SetDefaults_ValidatingWebhook(obj *admissionregistrationv1alpha1.ValidatingWebhook) {
// SetDefaults_ValidatingAdmissionPolicySpec sets defaults for ValidatingAdmissionPolicySpec
func SetDefaults_ValidatingAdmissionPolicySpec(obj *admissionregistrationv1alpha1.ValidatingAdmissionPolicySpec) {
if obj.FailurePolicy == nil {
policy := admissionregistrationv1alpha1.Fail
obj.FailurePolicy = &policy
}
}
// SetDefaults_MatchResources sets defaults for MatchResources
func SetDefaults_MatchResources(obj *admissionregistrationv1alpha1.MatchResources) {
if obj.MatchPolicy == nil {
policy := admissionregistrationv1alpha1.Equivalent
obj.MatchPolicy = &policy
@ -45,51 +48,4 @@ func SetDefaults_ValidatingWebhook(obj *admissionregistrationv1alpha1.Validating
selector := metav1.LabelSelector{}
obj.ObjectSelector = &selector
}
if obj.TimeoutSeconds == nil {
obj.TimeoutSeconds = new(int32)
*obj.TimeoutSeconds = 10
}
}
// SetDefaults_MutatingWebhook sets defaults for webhook mutating
func SetDefaults_MutatingWebhook(obj *admissionregistrationv1alpha1.MutatingWebhook) {
if obj.FailurePolicy == nil {
policy := admissionregistrationv1alpha1.Fail
obj.FailurePolicy = &policy
}
if obj.MatchPolicy == nil {
policy := admissionregistrationv1alpha1.Equivalent
obj.MatchPolicy = &policy
}
if obj.NamespaceSelector == nil {
selector := metav1.LabelSelector{}
obj.NamespaceSelector = &selector
}
if obj.ObjectSelector == nil {
selector := metav1.LabelSelector{}
obj.ObjectSelector = &selector
}
if obj.TimeoutSeconds == nil {
obj.TimeoutSeconds = new(int32)
*obj.TimeoutSeconds = 10
}
if obj.ReinvocationPolicy == nil {
never := admissionregistrationv1alpha1.NeverReinvocationPolicy
obj.ReinvocationPolicy = &never
}
}
// SetDefaults_Rule sets defaults for webhook rule
func SetDefaults_Rule(obj *admissionregistrationv1alpha1.Rule) {
if obj.Scope == nil {
s := admissionregistrationv1alpha1.AllScopes
obj.Scope = &s
}
}
// SetDefaults_ServiceReference sets defaults for Webhook's ServiceReference
func SetDefaults_ServiceReference(obj *admissionregistrationv1alpha1.ServiceReference) {
if obj.Port == nil {
obj.Port = utilpointer.Int32Ptr(443)
}
}

View File

@ -27,14 +27,11 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api/legacyscheme"
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
utilpointer "k8s.io/utils/pointer"
)
func TestDefaultAdmissionWebhook(t *testing.T) {
func TestDefaultAdmissionPolicy(t *testing.T) {
fail := v1alpha1.Fail
equivalent := v1alpha1.Equivalent
never := v1alpha1.NeverReinvocationPolicy
ten := int32(10)
allScopes := v1alpha1.AllScopes
tests := []struct {
@ -43,80 +40,67 @@ func TestDefaultAdmissionWebhook(t *testing.T) {
expected runtime.Object
}{
{
name: "ValidatingWebhookConfiguration",
original: &v1alpha1.ValidatingWebhookConfiguration{
Webhooks: []v1alpha1.ValidatingWebhook{{}},
name: "ValidatingAdmissionPolicy",
original: &v1alpha1.ValidatingAdmissionPolicy{
Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{},
},
},
expected: &v1alpha1.ValidatingWebhookConfiguration{
Webhooks: []v1alpha1.ValidatingWebhook{{
FailurePolicy: &fail,
MatchPolicy: &equivalent,
TimeoutSeconds: &ten,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
}},
expected: &v1alpha1.ValidatingAdmissionPolicy{
Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: &equivalent,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
FailurePolicy: &fail,
},
},
},
{
name: "MutatingWebhookConfiguration",
original: &v1alpha1.MutatingWebhookConfiguration{
Webhooks: []v1alpha1.MutatingWebhook{{}},
name: "ValidatingAdmissionPolicyBinding",
original: &v1alpha1.ValidatingAdmissionPolicyBinding{
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
MatchResources: &v1alpha1.MatchResources{},
},
},
expected: &v1alpha1.MutatingWebhookConfiguration{
Webhooks: []v1alpha1.MutatingWebhook{{
FailurePolicy: &fail,
MatchPolicy: &equivalent,
ReinvocationPolicy: &never,
TimeoutSeconds: &ten,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
}},
expected: &v1alpha1.ValidatingAdmissionPolicyBinding{
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
MatchResources: &v1alpha1.MatchResources{
MatchPolicy: &equivalent,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
},
},
},
{
name: "scope=*",
original: &v1alpha1.MutatingWebhookConfiguration{
Webhooks: []v1alpha1.MutatingWebhook{{
Rules: []v1alpha1.RuleWithOperations{{}},
}},
},
expected: &v1alpha1.MutatingWebhookConfiguration{
Webhooks: []v1alpha1.MutatingWebhook{{
Rules: []v1alpha1.RuleWithOperations{{Rule: v1alpha1.Rule{
Scope: &allScopes, // defaulted
}}},
FailurePolicy: &fail,
MatchPolicy: &equivalent,
ReinvocationPolicy: &never,
TimeoutSeconds: &ten,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
}},
},
},
{
name: "port=443",
original: &v1alpha1.MutatingWebhookConfiguration{
Webhooks: []v1alpha1.MutatingWebhook{{
ClientConfig: v1alpha1.WebhookClientConfig{
Service: &v1alpha1.ServiceReference{},
original: &v1alpha1.ValidatingAdmissionPolicy{
Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
ResourceRules: []v1alpha1.NamedRuleWithOperations{{}},
},
}},
},
},
expected: &v1alpha1.MutatingWebhookConfiguration{
Webhooks: []v1alpha1.MutatingWebhook{{
ClientConfig: v1alpha1.WebhookClientConfig{
Service: &v1alpha1.ServiceReference{
Port: utilpointer.Int32Ptr(443), // defaulted
expected: &v1alpha1.ValidatingAdmissionPolicy{
Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: &equivalent,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
Scope: &allScopes, // defaulted
},
},
},
},
},
FailurePolicy: &fail,
MatchPolicy: &equivalent,
ReinvocationPolicy: &never,
TimeoutSeconds: &ten,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
}},
FailurePolicy: &fail,
},
},
},
}

View File

@ -21,7 +21,4 @@ limitations under the License.
// +groupName=admissionregistration.k8s.io
// Package v1alpha1 is the v1alpha1 version of the API.
// AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration
// ValidatingWebhookConfiguration, and MutatingWebhookConfiguration are for the
// new dynamic admission controller configuration.
package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"

View File

@ -25,7 +25,7 @@ import (
const GroupName = "admissionregistration.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {

View File

@ -97,14 +97,6 @@ func SetDefaults_MutatingWebhook(obj *admissionregistrationv1beta1.MutatingWebho
}
}
// SetDefaults_Rule sets defaults for webhook rule
func SetDefaults_Rule(obj *admissionregistrationv1beta1.Rule) {
if obj.Scope == nil {
s := admissionregistrationv1beta1.AllScopes
obj.Scope = &s
}
}
// SetDefaults_ServiceReference sets defaults for Webhook's ServiceReference
func SetDefaults_ServiceReference(obj *admissionregistrationv1beta1.ServiceReference) {
if obj.Port == nil {

View File

@ -18,13 +18,18 @@ package validation
import (
"fmt"
"regexp"
"strings"
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/api/validation/path"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
admissionregistrationv1 "k8s.io/kubernetes/pkg/apis/admissionregistration/v1"
@ -390,6 +395,12 @@ var supportedReinvocationPolicies = sets.NewString(
string(admissionregistration.IfNeededReinvocationPolicy),
)
var supportedValidationPolicyReason = sets.NewString(
string(metav1.StatusReasonForbidden),
string(metav1.StatusReasonInvalid),
string(metav1.StatusReasonRequestEntityTooLarge),
)
func hasWildcardOperation(operations []admissionregistration.OperationType) bool {
for _, o := range operations {
if o == admissionregistration.OperationAll {
@ -514,3 +525,235 @@ func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistratio
requireUniqueWebhookNames: mutatingHasUniqueWebhookNames(oldC.Webhooks),
})
}
// ValidateValidatingAdmissionPolicy validates a ValidatingAdmissionPolicy before creation.
func ValidateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
return validateValidatingAdmissionPolicy(p)
}
func validateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
allErrors := genericvalidation.ValidateObjectMeta(&p.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
allErrors = append(allErrors, validateValidatingAdmissionPolicySpec(&p.Spec, field.NewPath("spec"))...)
return allErrors
}
func validateValidatingAdmissionPolicySpec(spec *admissionregistration.ValidatingAdmissionPolicySpec, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if spec.FailurePolicy == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("failurePolicy"), ""))
} else if !supportedFailurePolicies.Has(string(*spec.FailurePolicy)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *spec.FailurePolicy, supportedFailurePolicies.List()))
}
if spec.ParamKind != nil {
allErrors = append(allErrors, validateParamKind(*spec.ParamKind, fldPath.Child("paramKind"))...)
}
if spec.MatchConstraints == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("matchConstraints"), ""))
} else {
allErrors = append(allErrors, validateMatchResources(spec.MatchConstraints, fldPath.Child("matchConstraints"))...)
// at least one resourceRule must be defined to provide type information
if len(spec.MatchConstraints.ResourceRules) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("matchConstraints", "resourceRules"), ""))
}
}
if len(spec.Validations) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("validations"), ""))
} else {
for i, validation := range spec.Validations {
allErrors = append(allErrors, validateValidation(&validation, spec.ParamKind, fldPath.Child("validations").Index(i))...)
}
}
return allErrors
}
func validateParamKind(gvk admissionregistration.ParamKind, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if len(gvk.APIVersion) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("apiVersion"), ""))
} else if gv, err := parseGroupVersion(gvk.APIVersion); err != nil {
allErrors = append(allErrors, field.Invalid(fldPath.Child("apiVersion"), gvk.APIVersion, err.Error()))
} else {
//this matches the APIService group field validation
if len(gv.Group) > 0 {
if errs := utilvalidation.IsDNS1123Subdomain(gv.Group); len(errs) > 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("apiVersion"), gv.Group, strings.Join(errs, ",")))
}
}
//this matches the APIService version field validation
if len(gv.Version) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("apiVersion"), gvk.APIVersion, "version must be specified"))
} else {
if errs := utilvalidation.IsDNS1035Label(gv.Version); len(errs) > 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("apiVersion"), gv.Version, strings.Join(errs, ",")))
}
}
}
if len(gvk.Kind) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("kind"), ""))
} else if errs := utilvalidation.IsDNS1035Label(strings.ToLower(gvk.Kind)); len(errs) > 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("kind"), gvk.Kind, "may have mixed case, but should otherwise match: "+strings.Join(errs, ",")))
}
return allErrors
}
type groupVersion struct {
Group string
Version string
}
// parseGroupVersion turns "group/version" string into a groupVersion struct. It reports error
// if it cannot parse the string.
func parseGroupVersion(gv string) (groupVersion, error) {
if (len(gv) == 0) || (gv == "/") {
return groupVersion{}, nil
}
switch strings.Count(gv, "/") {
case 0:
return groupVersion{"", gv}, nil
case 1:
i := strings.Index(gv, "/")
return groupVersion{gv[:i], gv[i+1:]}, nil
default:
return groupVersion{}, fmt.Errorf("unexpected GroupVersion string: %v", gv)
}
}
func validateMatchResources(mc *admissionregistration.MatchResources, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if mc == nil {
return allErrors
}
if mc.MatchPolicy == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("matchPolicy"), ""))
} else if !supportedMatchPolicies.Has(string(*mc.MatchPolicy)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("matchPolicy"), *mc.MatchPolicy, supportedMatchPolicies.List()))
}
if mc.NamespaceSelector == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("namespaceSelector"), ""))
} else {
allErrors = append(allErrors, metav1validation.ValidateLabelSelector(mc.NamespaceSelector, fldPath.Child("namespaceSelector"))...)
}
if mc.ObjectSelector == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("labelSelector"), ""))
} else {
allErrors = append(allErrors, metav1validation.ValidateLabelSelector(mc.ObjectSelector, fldPath.Child("labelSelector"))...)
}
for i, namedRuleWithOperations := range mc.ResourceRules {
allErrors = append(allErrors, validateNamedRuleWithOperations(&namedRuleWithOperations, fldPath.Child("resourceRules").Index(i))...)
}
for i, namedRuleWithOperations := range mc.ExcludeResourceRules {
allErrors = append(allErrors, validateNamedRuleWithOperations(&namedRuleWithOperations, fldPath.Child("excludeResourceRules").Index(i))...)
}
return allErrors
}
func validateNamedRuleWithOperations(n *admissionregistration.NamedRuleWithOperations, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
resourceNames := sets.NewString()
for i, rName := range n.ResourceNames {
for _, msg := range path.ValidatePathSegmentName(rName, false) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("resourceNames").Index(i), rName, msg))
}
if resourceNames.Has(rName) {
allErrors = append(allErrors, field.Duplicate(fldPath.Child("resourceNames").Index(i), rName))
} else {
resourceNames.Insert(rName)
}
}
allErrors = append(allErrors, validateRuleWithOperations(&n.RuleWithOperations, fldPath)...)
return allErrors
}
func validateValidation(v *admissionregistration.Validation, paramKind *admissionregistration.ParamKind, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
trimmedExpression := strings.TrimSpace(v.Expression)
trimmedMsg := strings.TrimSpace(v.Message)
if len(trimmedExpression) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("expression"), "expression is not specified"))
} else {
result := plugincel.CompileValidatingPolicyExpression(trimmedExpression, paramKind != nil)
if result.Error != nil {
switch result.Error.Type {
case cel.ErrorTypeRequired:
allErrors = append(allErrors, field.Required(fldPath.Child("expression"), result.Error.Detail))
case cel.ErrorTypeInvalid:
allErrors = append(allErrors, field.Invalid(fldPath.Child("expression"), v.Expression, result.Error.Detail))
case cel.ErrorTypeInternal:
allErrors = append(allErrors, field.InternalError(fldPath.Child("expression"), result.Error))
default:
allErrors = append(allErrors, field.InternalError(fldPath.Child("expression"), fmt.Errorf("unsupported error type: %w", result.Error)))
}
}
}
if len(v.Message) > 0 && len(trimmedMsg) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("message"), v.Message, "message must be non-empty if specified"))
} else if hasNewlines(trimmedMsg) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("message"), v.Message, "message must not contain line breaks"))
} else if hasNewlines(trimmedMsg) && trimmedMsg == "" {
allErrors = append(allErrors, field.Required(fldPath.Child("message"), "message must be specified if expression contains line breaks"))
}
if v.Reason != nil && !supportedValidationPolicyReason.Has(string(*v.Reason)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("reason"), *v.Reason, supportedValidationPolicyReason.List()))
}
return allErrors
}
var newlineMatcher = regexp.MustCompile(`[\n\r]+`) // valid newline chars in CEL grammar
func hasNewlines(s string) bool {
return newlineMatcher.MatchString(s)
}
// ValidateValidatingAdmissionPolicyBinding validates a ValidatingAdmissionPolicyBinding before create.
func ValidateValidatingAdmissionPolicyBinding(pb *admissionregistration.ValidatingAdmissionPolicyBinding) field.ErrorList {
return validateValidatingAdmissionPolicyBinding(pb)
}
func validateValidatingAdmissionPolicyBinding(pb *admissionregistration.ValidatingAdmissionPolicyBinding) field.ErrorList {
allErrors := genericvalidation.ValidateObjectMeta(&pb.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
allErrors = append(allErrors, validateValidatingAdmissionPolicyBindingSpec(&pb.Spec, field.NewPath("spec"))...)
return allErrors
}
func validateValidatingAdmissionPolicyBindingSpec(spec *admissionregistration.ValidatingAdmissionPolicyBindingSpec, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if len(spec.PolicyName) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("policyName"), ""))
} else {
for _, msg := range genericvalidation.NameIsDNSSubdomain(spec.PolicyName, false) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("policyName"), spec.PolicyName, msg))
}
}
allErrors = append(allErrors, validateParamRef(spec.ParamRef, fldPath.Child("paramRef"))...)
allErrors = append(allErrors, validateMatchResources(spec.MatchResources, fldPath.Child("matchResouces"))...)
return allErrors
}
func validateParamRef(pr *admissionregistration.ParamRef, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if pr == nil {
return allErrors
}
for _, msg := range path.ValidatePathSegmentName(pr.Name, false) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), pr.Name, msg))
}
return allErrors
}
// ValidateValidatingAdmissionPolicyUpdate validates update of validating admission policy
func ValidateValidatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
return validateValidatingAdmissionPolicy(newC)
}
// ValidateValidatingAdmissionPolicyBindingUpdate validates update of validating admission policy
func ValidateValidatingAdmissionPolicyBindingUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicyBinding) field.ErrorList {
return validateValidatingAdmissionPolicyBinding(newC)
}

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ import (
"time"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
apiserverinternalv1alpha1 "k8s.io/api/apiserverinternal/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
authenticationv1 "k8s.io/api/authentication/v1"
@ -668,6 +669,7 @@ var (
// alphaAPIGroupVersionsDisabledByDefault holds the alpha APIs we have. They are always disabled by default.
alphaAPIGroupVersionsDisabledByDefault = []schema.GroupVersion{
admissionregistrationv1alpha1.SchemeGroupVersion,
apiserverinternalv1alpha1.SchemeGroupVersion,
authenticationv1alpha1.SchemeGroupVersion,
networkingapiv1alpha1.SchemeGroupVersion,

View File

@ -26,6 +26,7 @@ import (
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiserver/pkg/storage/storagebackend"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/events"
@ -70,6 +71,8 @@ func NewStorageFactoryConfig() *StorageFactoryConfig {
// TODO (https://github.com/kubernetes/kubernetes/issues/108451): remove the override in 1.25.
// apisstorage.Resource("csistoragecapacities").WithVersion("v1beta1"),
networking.Resource("clustercidrs").WithVersion("v1alpha1"),
admissionregistration.Resource("validatingadmissionpolicies").WithVersion("v1alpha1"),
admissionregistration.Resource("validatingadmissionpolicybindings").WithVersion("v1alpha1"),
}
return &StorageFactoryConfig{

View File

@ -553,6 +553,24 @@ func AddHandlers(h printers.PrintHandler) {
h.TableHandler(validatingWebhookColumnDefinitions, printValidatingWebhook)
h.TableHandler(validatingWebhookColumnDefinitions, printValidatingWebhookList)
validatingAdmissionPolicy := []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "Validations", Type: "integer", Description: "Validations indicates the number of validation rules defined in this configuration"},
{Name: "ParamKind", Type: "string", Description: "ParamKind specifies the kind of resources used to parameterize this policy"},
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
}
h.TableHandler(validatingAdmissionPolicy, printValidatingAdmissionPolicy)
h.TableHandler(validatingAdmissionPolicy, printValidatingAdmissionPolicyList)
validatingAdmissionPolicyBinding := []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "PolicyName", Type: "string", Description: "PolicyName indicates the policy definition which the policy binding binded to"},
{Name: "ParamRef", Type: "string", Description: "ParamRef indicates the param resource which sets the configration param"},
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
}
h.TableHandler(validatingAdmissionPolicyBinding, printValidatingAdmissionPolicyBinding)
h.TableHandler(validatingAdmissionPolicyBinding, printValidatingAdmissionPolicyBindingList)
flowSchemaColumnDefinitions := []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "PriorityLevel", Type: "string", Description: flowcontrolv1beta3.PriorityLevelConfigurationReference{}.SwaggerDoc()["name"]},
@ -1533,6 +1551,59 @@ func printValidatingWebhookList(list *admissionregistration.ValidatingWebhookCon
return rows, nil
}
func printValidatingAdmissionPolicy(obj *admissionregistration.ValidatingAdmissionPolicy, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},
}
paramKind := "<unset>"
if obj.Spec.ParamKind != nil {
paramKind = obj.Spec.ParamKind.APIVersion + "/" + obj.Spec.ParamKind.Kind
}
row.Cells = append(row.Cells, obj.Name, int64(len(obj.Spec.Validations)), paramKind, translateTimestampSince(obj.CreationTimestamp))
return []metav1.TableRow{row}, nil
}
func printValidatingAdmissionPolicyList(list *admissionregistration.ValidatingAdmissionPolicyList, options printers.GenerateOptions) ([]metav1.TableRow, error) {
rows := make([]metav1.TableRow, 0, len(list.Items))
for i := range list.Items {
r, err := printValidatingAdmissionPolicy(&list.Items[i], options)
if err != nil {
return nil, err
}
rows = append(rows, r...)
}
return rows, nil
}
func printValidatingAdmissionPolicyBinding(obj *admissionregistration.ValidatingAdmissionPolicyBinding, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},
}
paramName := "<unset>"
if obj.Spec.ParamRef != nil {
if obj.Spec.ParamRef.Namespace != "" {
paramName = obj.Spec.ParamRef.Namespace + "/" + obj.Spec.ParamRef.Name
} else {
paramName = obj.Spec.ParamRef.Name
}
}
row.Cells = append(row.Cells, obj.Name, obj.Spec.PolicyName, paramName, translateTimestampSince(obj.CreationTimestamp))
return []metav1.TableRow{row}, nil
}
func printValidatingAdmissionPolicyBindingList(list *admissionregistration.ValidatingAdmissionPolicyBindingList, options printers.GenerateOptions) ([]metav1.TableRow, error) {
rows := make([]metav1.TableRow, 0, len(list.Items))
for i := range list.Items {
r, err := printValidatingAdmissionPolicyBinding(&list.Items[i], options)
if err != nil {
return nil, err
}
rows = append(rows, r...)
}
return rows, nil
}
func printNamespace(obj *api.Namespace, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},

View File

@ -5983,6 +5983,18 @@ func TestTableRowDeepCopyShouldNotPanic(t *testing.T) {
return printValidatingWebhook(&admissionregistration.ValidatingWebhookConfiguration{}, printers.GenerateOptions{})
},
},
{
name: "ValidatingAdmissionPolicy",
printer: func() ([]metav1.TableRow, error) {
return printValidatingAdmissionPolicy(&admissionregistration.ValidatingAdmissionPolicy{}, printers.GenerateOptions{})
},
},
{
name: "ValidatingAdmissionPolicyBinding",
printer: func() ([]metav1.TableRow, error) {
return printValidatingAdmissionPolicyBinding(&admissionregistration.ValidatingAdmissionPolicyBinding{}, printers.GenerateOptions{})
},
},
{
name: "Namespace",
printer: func() ([]metav1.TableRow, error) {

View File

@ -18,6 +18,7 @@ package rest
import (
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
@ -25,6 +26,8 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
mutatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage"
validatingadmissionpolicystorage "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicy/storage"
policybindingstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicybinding/storage"
validatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage"
)
@ -40,6 +43,12 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
} else if len(storageMap) > 0 {
apiGroupInfo.VersionedResourcesStorageMap[admissionregistrationv1.SchemeGroupVersion.Version] = storageMap
}
if storageMap, err := p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, err
} else if len(storageMap) > 0 {
apiGroupInfo.VersionedResourcesStorageMap[admissionregistrationv1alpha1.SchemeGroupVersion.Version] = storageMap
}
return apiGroupInfo, nil
}
@ -67,6 +76,30 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API
return storage, nil
}
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// validatingadmissionpolicies
if resource := "validatingadmissionpolicies"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1alpha1.SchemeGroupVersion.WithResource(resource)) {
policyStorage, err := validatingadmissionpolicystorage.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage[resource] = policyStorage
}
// validatingadmissionpolicybindings
if resource := "validatingadmissionpolicybindings"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1alpha1.SchemeGroupVersion.WithResource(resource)) {
policyBindingStorage, err := policybindingstorage.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage[resource] = policyBindingStorage
}
return storage, nil
}
func (p RESTStorageProvider) GroupName() string {
return admissionregistration.GroupName
}

View File

@ -0,0 +1,17 @@
/*
Copyright 2022 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 validatingadmissionpolicy // import "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicy"

View File

@ -0,0 +1,65 @@
/*
Copyright 2022 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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicy"
)
// REST implements a RESTStorage for validatingAdmissionPolicy against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against validatingAdmissionPolicy.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &admissionregistration.ValidatingAdmissionPolicy{} },
NewListFunc: func() runtime.Object { return &admissionregistration.ValidatingAdmissionPolicyList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*admissionregistration.ValidatingAdmissionPolicy).Name, nil
},
DefaultQualifiedResource: admissionregistration.Resource("validatingadmissionpolicies"),
CreateStrategy: validatingadmissionpolicy.Strategy,
UpdateStrategy: validatingadmissionpolicy.Strategy,
DeleteStrategy: validatingadmissionpolicy.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
}
return &REST{store}, nil
}
// Implement CategoriesProvider
var _ rest.CategoriesProvider = &REST{}
// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
func (r *REST) Categories() []string {
return []string{"api-extensions"}
}

View File

@ -0,0 +1,221 @@
/*
Copyright 2022 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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/registry/registrytest"
// Ensure that admissionregistration package is initialized.
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
)
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
configuration := validValidatingAdmissionPolicy()
test.TestCreate(
// valid
configuration,
// invalid
newValidatingAdmissionPolicy(""),
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestUpdate(
// valid
validValidatingAdmissionPolicy(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*admissionregistration.ValidatingAdmissionPolicy)
object.Labels = map[string]string{"c": "d"}
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*admissionregistration.ValidatingAdmissionPolicy)
object.Name = ""
return object
},
)
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestGet(validValidatingAdmissionPolicy())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestList(validValidatingAdmissionPolicy())
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestDelete(validValidatingAdmissionPolicy())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestWatch(
validValidatingAdmissionPolicy(),
[]labels.Set{},
[]labels.Set{
{"hoo": "bar"},
},
[]fields.Set{
{"metadata.name": "foo"},
},
[]fields.Set{
{"metadata.name": "nomatch"},
},
)
}
func validValidatingAdmissionPolicy() *admissionregistration.ValidatingAdmissionPolicy {
return &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Fail")
return &r
}(),
ParamKind: &admissionregistration.ParamKind{
APIVersion: "rules.example.com/v1",
Kind: "ReplicaLimit",
},
Validations: []admissionregistration.Validation{
{
Expression: "object.spec.replicas <= params.maxReplicas",
},
},
MatchConstraints: &admissionregistration.MatchResources{
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"CREATE"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
},
},
}
}
func newValidatingAdmissionPolicy(name string) *admissionregistration.ValidatingAdmissionPolicy {
ignore := admissionregistration.Ignore
return &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{"foo": "bar"},
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
ParamKind: &admissionregistration.ParamKind{
APIVersion: "rules.example.com/v1",
Kind: "ReplicaLimit",
},
Validations: []admissionregistration.Validation{
{
Expression: "object.spec.replicas <= params.maxReplicas",
},
},
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"CREATE"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
},
FailurePolicy: &ignore,
},
}
}
func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorageForResource(t, admissionregistration.Resource("validatingadmissionpolicies"))
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "validatingadmissionpolicies"}
storage, err := NewREST(restOptions)
if err != nil {
t.Fatalf("unexpected error from REST storage: %v", err)
}
return storage, server
}
func TestCategories(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
expected := []string{"api-extensions"}
registrytest.AssertCategories(t, storage, expected)
}

View File

@ -0,0 +1,96 @@
/*
Copyright 2022 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 validatingadmissionpolicy
import (
"context"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/apis/admissionregistration/validation"
)
// validatingAdmissionPolicyStrategy implements verification logic for ValidatingAdmissionPolicy.
type validatingAdmissionPolicyStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating validatingAdmissionPolicy objects.
var Strategy = validatingAdmissionPolicyStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns false because ValidatingAdmissionPolicy is cluster-scoped resource.
func (validatingAdmissionPolicyStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears the status of an validatingAdmissionPolicy before creation.
func (validatingAdmissionPolicyStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.ValidatingAdmissionPolicy)
ic.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (validatingAdmissionPolicyStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.ValidatingAdmissionPolicy)
oldIC := old.(*admissionregistration.ValidatingAdmissionPolicy)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
if !apiequality.Semantic.DeepEqual(oldIC.Spec, newIC.Spec) {
newIC.Generation = oldIC.Generation + 1
}
}
// Validate validates a new validatingAdmissionPolicy.
func (validatingAdmissionPolicyStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
return validation.ValidateValidatingAdmissionPolicy(obj.(*admissionregistration.ValidatingAdmissionPolicy))
}
// WarningsOnCreate returns warnings for the creation of the given object.
func (validatingAdmissionPolicyStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation.
func (validatingAdmissionPolicyStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is true for validatingAdmissionPolicy; this means you may create one with a PUT request.
func (validatingAdmissionPolicyStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (validatingAdmissionPolicyStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateValidatingAdmissionPolicyUpdate(obj.(*admissionregistration.ValidatingAdmissionPolicy), old.(*admissionregistration.ValidatingAdmissionPolicy))
}
// WarningsOnUpdate returns warnings for the given update.
func (validatingAdmissionPolicyStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for validatingAdmissionPolicy objects. Status update should
// only be allowed if version match.
func (validatingAdmissionPolicyStrategy) AllowUnconditionalUpdate() bool {
return false
}

View File

@ -0,0 +1,94 @@
/*
Copyright 2022 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 validatingadmissionpolicy
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
)
func TestValidatingAdmissionPolicyStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if Strategy.NamespaceScoped() {
t.Error("ValidatingAdmissionPolicy strategy must be cluster scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("ValidatingAdmissionPolicy should not allow create on update")
}
configuration := validValidatingAdmissionPolicy()
Strategy.PrepareForCreate(ctx, configuration)
errs := Strategy.Validate(ctx, configuration)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
invalidConfiguration := &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{Name: ""},
}
Strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration)
errs = Strategy.ValidateUpdate(ctx, invalidConfiguration, configuration)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
}
func validValidatingAdmissionPolicy() *admissionregistration.ValidatingAdmissionPolicy {
ignore := admissionregistration.Ignore
return &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
ParamKind: &admissionregistration.ParamKind{
Kind: "ReplicaLimit",
APIVersion: "rules.example.com/v1",
},
Validations: []admissionregistration.Validation{
{
Expression: "object.spec.replicas <= params.maxReplicas",
},
},
MatchConstraints: &admissionregistration.MatchResources{
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"CREATE"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
},
FailurePolicy: &ignore,
},
}
}

View File

@ -0,0 +1,17 @@
/*
Copyright 2022 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 validatingadmissionpolicybinding // import "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicybinding"

View File

@ -0,0 +1,65 @@
/*
Copyright 2022 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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicybinding"
)
// REST implements a RESTStorage for policyBinding against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against policyBinding.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &admissionregistration.ValidatingAdmissionPolicyBinding{} },
NewListFunc: func() runtime.Object { return &admissionregistration.ValidatingAdmissionPolicyBindingList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*admissionregistration.ValidatingAdmissionPolicyBinding).Name, nil
},
DefaultQualifiedResource: admissionregistration.Resource("validatingadmissionpolicybindings"),
CreateStrategy: validatingadmissionpolicybinding.Strategy,
UpdateStrategy: validatingadmissionpolicybinding.Strategy,
DeleteStrategy: validatingadmissionpolicybinding.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
}
return &REST{store}, nil
}
// Implement CategoriesProvider
var _ rest.CategoriesProvider = &REST{}
// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
func (r *REST) Categories() []string {
return []string{"api-extensions"}
}

View File

@ -0,0 +1,192 @@
/*
Copyright 2022 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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/registry/registrytest"
// Ensure that admissionregistration package is initialized.
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
)
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
configuration := validPolicyBinding()
test.TestCreate(
// valid
configuration,
// invalid
newPolicyBinding(""),
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestUpdate(
// valid
validPolicyBinding(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
object.Labels = map[string]string{"c": "d"}
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
object.Name = ""
return object
},
)
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestGet(validPolicyBinding())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestList(validPolicyBinding())
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestDelete(validPolicyBinding())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestWatch(
validPolicyBinding(),
[]labels.Set{},
[]labels.Set{
{"hoo": "bar"},
},
[]fields.Set{
{"metadata.name": "foo"},
},
[]fields.Set{
{"metadata.name": "nomatch"},
},
)
}
func validPolicyBinding() *admissionregistration.ValidatingAdmissionPolicyBinding {
return &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "param-test",
},
MatchResources: &admissionregistration.MatchResources{
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"CREATE"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
},
},
}
}
func newPolicyBinding(name string) *admissionregistration.ValidatingAdmissionPolicyBinding {
return &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{"foo": "bar"},
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "param-test",
},
MatchResources: &admissionregistration.MatchResources{},
},
}
}
func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorageForResource(t, admissionregistration.Resource("validatingadmissionpolicybindings"))
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "validatingadmissionpolicybindings"}
storage, err := NewREST(restOptions)
if err != nil {
t.Fatalf("unexpected error from REST storage: %v", err)
}
return storage, server
}
func TestCategories(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
expected := []string{"api-extensions"}
registrytest.AssertCategories(t, storage, expected)
}

View File

@ -0,0 +1,96 @@
/*
Copyright 2022 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 validatingadmissionpolicybinding
import (
"context"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/apis/admissionregistration/validation"
)
// ValidatingAdmissionPolicyBindingStrategy implements verification logic for ValidatingAdmissionPolicyBinding.
type ValidatingAdmissionPolicyBindingStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ValidatingAdmissionPolicyBinding objects.
var Strategy = ValidatingAdmissionPolicyBindingStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns false because ValidatingAdmissionPolicyBinding is cluster-scoped resource.
func (ValidatingAdmissionPolicyBindingStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears the status of an ValidatingAdmissionPolicyBinding before creation.
func (ValidatingAdmissionPolicyBindingStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
ic.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (ValidatingAdmissionPolicyBindingStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
oldIC := old.(*admissionregistration.ValidatingAdmissionPolicyBinding)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
if !apiequality.Semantic.DeepEqual(oldIC.Spec, newIC.Spec) {
newIC.Generation = oldIC.Generation + 1
}
}
// Validate validates a new ValidatingAdmissionPolicyBinding.
func (ValidatingAdmissionPolicyBindingStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
return validation.ValidateValidatingAdmissionPolicyBinding(obj.(*admissionregistration.ValidatingAdmissionPolicyBinding))
}
// WarningsOnCreate returns warnings for the creation of the given object.
func (ValidatingAdmissionPolicyBindingStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation.
func (ValidatingAdmissionPolicyBindingStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is true for ValidatingAdmissionPolicyBinding; this means you may create one with a PUT request.
func (ValidatingAdmissionPolicyBindingStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (ValidatingAdmissionPolicyBindingStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateValidatingAdmissionPolicyBindingUpdate(obj.(*admissionregistration.ValidatingAdmissionPolicyBinding), old.(*admissionregistration.ValidatingAdmissionPolicyBinding))
}
// WarningsOnUpdate returns warnings for the given update.
func (ValidatingAdmissionPolicyBindingStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for ValidatingAdmissionPolicyBinding objects. Status update should
// only be allowed if version match.
func (ValidatingAdmissionPolicyBindingStrategy) AllowUnconditionalUpdate() bool {
return false
}

View File

@ -0,0 +1,63 @@
/*
Copyright 2022 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 validatingadmissionpolicybinding
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
)
func TestPolicyBindingStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if Strategy.NamespaceScoped() {
t.Error("PolicyBinding strategy must be cluster scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("PolicyBinding should not allow create on update")
}
configuration := validPolicyBinding()
Strategy.PrepareForCreate(ctx, configuration)
errs := Strategy.Validate(ctx, configuration)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
invalidConfiguration := &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: ""},
}
Strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration)
errs = Strategy.ValidateUpdate(ctx, invalidConfiguration, configuration)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
}
func validPolicyBinding() *admissionregistration.ValidatingAdmissionPolicyBinding {
return &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "replica-limit-test.example.com",
},
},
}
}

View File

@ -26,11 +26,13 @@ type Rule struct {
// APIGroups is the API groups the resources belong to. '*' is all groups.
// If '*' is present, the length of the slice must be one.
// Required.
// +listType=atomic
APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"`
// APIVersions is the API versions the resources belong to. '*' is all versions.
// If '*' is present, the length of the slice must be one.
// Required.
// +listType=atomic
APIVersions []string `json:"apiVersions,omitempty" protobuf:"bytes,2,rep,name=apiVersions"`
// Resources is a list of resources this rule applies to.
@ -48,6 +50,7 @@ type Rule struct {
//
// Depending on the enclosing object, subresources might not be allowed.
// Required.
// +listType=atomic
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
// scope specifies the scope of this rule.
@ -474,6 +477,7 @@ type RuleWithOperations struct {
// for all of those operations and any future admission operations that are added.
// If '*' is present, the length of the slice must be one.
// Required.
// +listType=atomic
Operations []OperationType `json:"operations,omitempty" protobuf:"bytes,1,rep,name=operations,casttype=OperationType"`
// Rule is embedded, it describes other criteria of the rule, like
// APIGroups, APIVersions, Resources, etc.

View File

@ -20,7 +20,4 @@ limitations under the License.
// +groupName=admissionregistration.k8s.io
// Package v1alpha1 is the v1alpha1 version of the API.
// AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration
// MutatingWebhookConfiguration and ValidatingWebhookConfiguration are for the
// new dynamic admission controller configuration.
package v1alpha1 // import "k8s.io/api/admissionregistration/v1alpha1"

View File

@ -50,6 +50,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ValidatingWebhookConfigurationList{},
&MutatingWebhookConfiguration{},
&MutatingWebhookConfigurationList{},
&ValidatingAdmissionPolicy{},
&ValidatingAdmissionPolicyList{},
&ValidatingAdmissionPolicyBinding{},
&ValidatingAdmissionPolicyBindingList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@ -17,64 +17,26 @@ limitations under the License.
package v1alpha1
import (
v1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Rule is a tuple of APIGroups, APIVersion, and Resources.It is recommended
// to make sure that all the tuple expansions are valid.
type Rule struct {
// APIGroups is the API groups the resources belong to. '*' is all groups.
// If '*' is present, the length of the slice must be one.
// Required.
APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"`
// APIVersions is the API versions the resources belong to. '*' is all versions.
// If '*' is present, the length of the slice must be one.
// Required.
APIVersions []string `json:"apiVersions,omitempty" protobuf:"bytes,2,rep,name=apiVersions"`
// Resources is a list of resources this rule applies to.
//
// For example:
// 'pods' means pods.
// 'pods/log' means the log subresource of pods.
// '*' means all resources, but not subresources.
// 'pods/*' means all subresources of pods.
// '*/scale' means all scale subresources.
// '*/*' means all resources and their subresources.
//
// If wildcard is present, the validation rule will ensure resources do not
// overlap with each other.
//
// Depending on the enclosing object, subresources might not be allowed.
// Required.
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
// scope specifies the scope of this rule.
// Valid values are "Cluster", "Namespaced", and "*"
// "Cluster" means that only cluster-scoped resources will match this rule.
// Namespace API objects are cluster-scoped.
// "Namespaced" means that only namespaced resources will match this rule.
// "*" means that there are no scope restrictions.
// Subresources match the scope of their parent resource.
// Default is "*".
//
// +optional
Scope *ScopeType `json:"scope,omitempty" protobuf:"bytes,4,rep,name=scope"`
}
type Rule = v1.Rule
// ScopeType specifies a scope for a Rule.
// +enum
type ScopeType string
type ScopeType = v1.ScopeType
const (
// ClusterScope means that scope is limited to cluster-scoped objects.
// Namespace objects are cluster-scoped.
ClusterScope ScopeType = "Cluster"
ClusterScope ScopeType = v1.ClusterScope
// NamespacedScope means that scope is limited to namespaced objects.
NamespacedScope ScopeType = "Namespaced"
NamespacedScope ScopeType = v1.NamespacedScope
// AllScopes means that all scopes are included.
AllScopes ScopeType = "*"
AllScopes ScopeType = v1.AllScopes
)
// FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled.
@ -99,132 +61,208 @@ const (
Equivalent MatchPolicyType = "Equivalent"
)
// SideEffectClass specifies the types of side effects a webhook may have.
// +enum
type SideEffectClass string
const (
// SideEffectClassUnknown means that no information is known about the side effects of calling the webhook.
// If a request with the dry-run attribute would trigger a call to this webhook, the request will instead fail.
SideEffectClassUnknown SideEffectClass = "Unknown"
// SideEffectClassNone means that calling the webhook will have no side effects.
SideEffectClassNone SideEffectClass = "None"
// SideEffectClassSome means that calling the webhook will possibly have side effects.
// If a request with the dry-run attribute would trigger a call to this webhook, the request will instead fail.
SideEffectClassSome SideEffectClass = "Some"
// SideEffectClassNoneOnDryRun means that calling the webhook will possibly have side effects, but if the
// request being reviewed has the dry-run attribute, the side effects will be suppressed.
SideEffectClassNoneOnDryRun SideEffectClass = "NoneOnDryRun"
)
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.26
// ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.
type ValidatingWebhookConfiguration struct {
// ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.
type ValidatingAdmissionPolicy struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Webhooks is a list of webhooks and the affected resources and operations.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
Webhooks []ValidatingWebhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"`
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
Spec ValidatingAdmissionPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.26
// ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration.
type ValidatingWebhookConfigurationList struct {
// ValidatingAdmissionPolicyList is a list of ValidatingAdmissionPolicy.
type ValidatingAdmissionPolicyList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// List of ValidatingWebhookConfiguration.
Items []ValidatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"`
// List of ValidatingAdmissionPolicy.
Items []ValidatingAdmissionPolicy `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
}
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object.
type MutatingWebhookConfiguration struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// ValidatingAdmissionPolicySpec is the specification of the desired behavior of the AdmissionPolicy.
type ValidatingAdmissionPolicySpec struct {
// ParamKind specifies the kind of resources used to parameterize this policy.
// If absent, there are no parameters for this policy and the param CEL variable will not be provided to validation expressions.
// If ParamKind refers to a non-existent kind, this policy definition is mis-configured and the FailurePolicy is applied.
// If paramKind is specified but paramRef is unset in ValidatingAdmissionPolicyBinding, the params variable will be null.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Webhooks is a list of webhooks and the affected resources and operations.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
Webhooks []MutatingWebhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"`
}
ParamKind *ParamKind `json:"paramKind,omitempty" protobuf:"bytes,1,rep,name=paramKind"`
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration.
type MutatingWebhookConfigurationList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// List of MutatingWebhookConfiguration.
Items []MutatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// ValidatingWebhook describes an admission webhook and the resources and operations it applies to.
type ValidatingWebhook struct {
// The name of the admission webhook.
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
// of the organization.
// MatchConstraints specifies what resources this policy is designed to validate.
// The AdmissionPolicy cares about a request if it matches _all_ Constraints.
// However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API
// ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding.
// Required.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
MatchConstraints *MatchResources `json:"matchConstraints,omitempty" protobuf:"bytes,2,rep,name=matchConstraints"`
// ClientConfig defines how to communicate with the hook.
// Required
ClientConfig WebhookClientConfig `json:"clientConfig" protobuf:"bytes,2,opt,name=clientConfig"`
// Validations contain CEL expressions which is used to apply the validation.
// A minimum of one validation is required for a policy definition.
// +listType=atomic
// Required.
Validations []Validation `json:"validations" protobuf:"bytes,3,rep,name=validations"`
// Rules describes what operations on what resources/subresources the webhook cares about.
// The webhook cares about an operation if it matches _any_ Rule.
// However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks
// from putting the cluster in a state which cannot be recovered from without completely
// disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called
// on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.
Rules []RuleWithOperations `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"`
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled -
// allowed values are Ignore or Fail. Defaults to Fail.
// FailurePolicy defines how to handle failures for the admission policy.
// Failures can occur from invalid or mis-configured policy definitions or bindings.
// A policy is invalid if spec.paramKind refers to a non-existent Kind.
// A binding is invalid if spec.paramRef.name refers to a non-existent resource.
// Allowed values are Ignore or Fail. Defaults to Fail.
// +optional
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,4,opt,name=failurePolicy,casttype=FailurePolicyType"`
}
// matchPolicy defines how the "rules" list is used to match incoming requests.
// Allowed values are "Exact" or "Equivalent".
// ParamKind is a tuple of Group Kind and Version.
// +structType=atomic
type ParamKind struct {
// APIVersion is the API group version the resources belong to.
// In format of "group/version".
// Required.
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,1,rep,name=apiVersion"`
// Kind is the API kind the resources belong to.
// Required.
Kind string `json:"kind,omitempty" protobuf:"bytes,2,rep,name=kind"`
}
// Validation specifies the CEL expression which is used to apply the validation.
type Validation struct {
// Expression represents the expression which will be evaluated by CEL.
// ref: https://github.com/google/cel-spec
// CEL expressions have access to the contents of the Admission request/response, organized into CEL variables as well as some other useful variables:
//
// - Exact: match a request only if it exactly matches a specified rule.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.
//'object' - The object from the incoming request. The value is null for DELETE requests.
//'oldObject' - The existing object. The value is null for CREATE requests.
//'request' - Attributes of the admission request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
//'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
//
// - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.
// The `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the
// object. No other metadata properties are accessible.
//
// Defaults to "Equivalent"
// Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.
// Accessible property names are escaped according to the following rules when accessed in the expression:
// - '__' escapes to '__underscores__'
// - '.' escapes to '__dot__'
// - '-' escapes to '__dash__'
// - '/' escapes to '__slash__'
// - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:
// "true", "false", "null", "in", "as", "break", "const", "continue", "else", "for", "function", "if",
// "import", "let", "loop", "package", "namespace", "return".
// Examples:
// - Expression accessing a property named "namespace": {"Expression": "object.__namespace__ > 0"}
// - Expression accessing a property named "x-prop": {"Expression": "object.x__dash__prop > 0"}
// - Expression accessing a property named "redact__d": {"Expression": "object.redact__underscores__d > 0"}
//
// Equality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1].
// Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:
// - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and
// non-intersecting elements in `Y` are appended, retaining their partial order.
// - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values
// are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with
// non-intersecting keys are appended, retaining their partial order.
// Required.
Expression string `json:"expression" protobuf:"bytes,1,opt,name=Expression"`
// Message represents the message displayed when validation fails. The message is required if the Expression contains
// line breaks. The message must not contain line breaks.
// If unset, the message is "failed rule: {Rule}".
// e.g. "must be a URL with the host matching spec.host"
// If the Expression contains line breaks. Message is required.
// The message must not contain line breaks.
// If unset, the message is "failed Expression: {Expression}".
// +optional
MatchPolicy *MatchPolicyType `json:"matchPolicy,omitempty" protobuf:"bytes,9,opt,name=matchPolicy,casttype=MatchPolicyType"`
Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"`
// Reason represents a machine-readable description of why this validation failed.
// If this is the first validation in the list to fail, this reason, as well as the
// corresponding HTTP response code, are used in the
// HTTP response to the client.
// The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge".
// If not set, StatusReasonInvalid is used in the response to the client.
// +optional
Reason *metav1.StatusReason `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
}
// NamespaceSelector decides whether to run the webhook on an object based
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.26
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
type ValidatingAdmissionPolicyBinding struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the ValidatingAdmissionPolicyBinding.
Spec ValidatingAdmissionPolicyBindingSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.26
// ValidatingAdmissionPolicyBindingList is a list of ValidatingAdmissionPolicyBinding.
type ValidatingAdmissionPolicyBindingList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// List of PolicyBinding.
Items []ValidatingAdmissionPolicyBinding `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
}
// ValidatingAdmissionPolicyBindingSpec is the specification of the ValidatingAdmissionPolicyBinding.
type ValidatingAdmissionPolicyBindingSpec struct {
// PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to.
// If the referenced resource does not exist, this binding is considered invalid and will be ignored
// Required.
PolicyName string `json:"policyName,omitempty" protobuf:"bytes,1,rep,name=policyName"`
// ParamRef specifies the parameter resource used to configure the admission control policy.
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
// +optional
ParamRef *ParamRef `json:"paramRef,omitempty" protobuf:"bytes,2,rep,name=paramRef"`
// MatchResources declares what resources match this binding and will be validated by it.
// Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this.
// If this is unset, all resources matched by the policy are validated by this binding
// When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated.
// Note that this is differs from ValidatingAdmissionPolicy matchConstraints, where resourceRules are required.
// +optional
MatchResources *MatchResources `json:"matchResources,omitempty" protobuf:"bytes,3,rep,name=matchResources"`
}
// ParamRef references a parameter resource
// +structType=atomic
type ParamRef struct {
// Name of the resource being referenced.
Name string `json:"name,omitempty" protobuf:"bytes,1,rep,name=name"`
// Namespace of the referenced resource.
// Should be empty for the cluster-scoped resources
// +optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,rep,name=namespace"`
}
// MatchResources decides whether to run the admission control policy on an object based
// on whether it meets the match criteria.
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
// +structType=atomic
type MatchResources struct {
// NamespaceSelector decides whether to run the admission control policy on an object based
// on whether the namespace for that object matches the selector. If the
// object itself is a namespace, the matching is performed on
// object.metadata.labels. If the object is another cluster scoped resource,
// it never skips the webhook.
// it never skips the policy.
//
// For example, to run the webhook on any objects whose namespace is not
// associated with "runlevel" of "0" or "1"; you will set the selector as
@ -242,136 +280,7 @@ type ValidatingWebhook struct {
// ]
// }
//
// If instead you want to only run the webhook on any objects whose
// namespace is associated with the "environment" of "prod" or "staging";
// you will set the selector as follows:
// "namespaceSelector": {
// "matchExpressions": [
// {
// "key": "environment",
// "operator": "In",
// "values": [
// "prod",
// "staging"
// ]
// }
// ]
// }
//
// See
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels
// for more examples of label selectors.
//
// Default to the empty LabelSelector, which matches everything.
// +optional
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"`
// ObjectSelector decides whether to run the webhook based on if the
// object has matching labels. objectSelector is evaluated against both
// the oldObject and newObject that would be sent to the webhook, and
// is considered to match if either object matches the selector. A null
// object (oldObject in the case of create, or newObject in the case of
// delete) or an object that cannot have labels (like a
// DeploymentRollback or a PodProxyOptions object) is not considered to
// match.
// Use the object selector only if the webhook is opt-in, because end
// users may skip the admission webhook by setting the labels.
// Default to the empty LabelSelector, which matches everything.
// +optional
ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,10,opt,name=objectSelector"`
// SideEffects states whether this webhook has side effects.
// Acceptable values are: None, NoneOnDryRun (webhooks created via v1beta1 may also specify Some or Unknown).
// Webhooks with side effects MUST implement a reconciliation system, since a request may be
// rejected by a future step in the admission chain and the side effects therefore need to be undone.
// Requests with the dryRun attribute will be auto-rejected if they match a webhook with
// sideEffects == Unknown or Some.
SideEffects *SideEffectClass `json:"sideEffects" protobuf:"bytes,6,opt,name=sideEffects,casttype=SideEffectClass"`
// TimeoutSeconds specifies the timeout for this webhook. After the timeout passes,
// the webhook call will be ignored or the API call will fail based on the
// failure policy.
// The timeout value must be between 1 and 30 seconds.
// Default to 10 seconds.
// +optional
TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty" protobuf:"varint,7,opt,name=timeoutSeconds"`
// AdmissionReviewVersions is an ordered list of preferred `AdmissionReview`
// versions the Webhook expects. API server will try to use first version in
// the list which it supports. If none of the versions specified in this list
// supported by API server, validation will fail for this object.
// If a persisted webhook configuration specifies allowed versions and does not
// include any versions known to the API Server, calls to the webhook will fail
// and be subject to the failure policy.
AdmissionReviewVersions []string `json:"admissionReviewVersions" protobuf:"bytes,8,rep,name=admissionReviewVersions"`
}
// MutatingWebhook describes an admission webhook and the resources and operations it applies to.
type MutatingWebhook struct {
// The name of the admission webhook.
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
// of the organization.
// Required.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// ClientConfig defines how to communicate with the hook.
// Required
ClientConfig WebhookClientConfig `json:"clientConfig" protobuf:"bytes,2,opt,name=clientConfig"`
// Rules describes what operations on what resources/subresources the webhook cares about.
// The webhook cares about an operation if it matches _any_ Rule.
// However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks
// from putting the cluster in a state which cannot be recovered from without completely
// disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called
// on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.
Rules []RuleWithOperations `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"`
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled -
// allowed values are Ignore or Fail. Defaults to Fail.
// +optional
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,4,opt,name=failurePolicy,casttype=FailurePolicyType"`
// matchPolicy defines how the "rules" list is used to match incoming requests.
// Allowed values are "Exact" or "Equivalent".
//
// - Exact: match a request only if it exactly matches a specified rule.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.
//
// - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.
//
// Defaults to "Equivalent"
// +optional
MatchPolicy *MatchPolicyType `json:"matchPolicy,omitempty" protobuf:"bytes,9,opt,name=matchPolicy,casttype=MatchPolicyType"`
// NamespaceSelector decides whether to run the webhook on an object based
// on whether the namespace for that object matches the selector. If the
// object itself is a namespace, the matching is performed on
// object.metadata.labels. If the object is another cluster scoped resource,
// it never skips the webhook.
//
// For example, to run the webhook on any objects whose namespace is not
// associated with "runlevel" of "0" or "1"; you will set the selector as
// follows:
// "namespaceSelector": {
// "matchExpressions": [
// {
// "key": "runlevel",
// "operator": "NotIn",
// "values": [
// "0",
// "1"
// ]
// }
// ]
// }
//
// If instead you want to only run the webhook on any objects whose
// If instead you want to only run the policy on any objects whose
// namespace is associated with the "environment" of "prod" or "staging";
// you will set the selector as follows:
// "namespaceSelector": {
@ -393,11 +302,10 @@ type MutatingWebhook struct {
//
// Default to the empty LabelSelector, which matches everything.
// +optional
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"`
// ObjectSelector decides whether to run the webhook based on if the
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,1,opt,name=namespaceSelector"`
// ObjectSelector decides whether to run the validation based on if the
// object has matching labels. objectSelector is evaluated against both
// the oldObject and newObject that would be sent to the webhook, and
// the oldObject and newObject that would be sent to the cel validation, and
// is considered to match if either object matches the selector. A null
// object (oldObject in the case of create, or newObject in the case of
// delete) or an object that cannot have labels (like a
@ -407,155 +315,59 @@ type MutatingWebhook struct {
// users may skip the admission webhook by setting the labels.
// Default to the empty LabelSelector, which matches everything.
// +optional
ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,11,opt,name=objectSelector"`
// SideEffects states whether this webhook has side effects.
// Acceptable values are: None, NoneOnDryRun (webhooks created via v1beta1 may also specify Some or Unknown).
// Webhooks with side effects MUST implement a reconciliation system, since a request may be
// rejected by a future step in the admission chain and the side effects therefore need to be undone.
// Requests with the dryRun attribute will be auto-rejected if they match a webhook with
// sideEffects == Unknown or Some.
SideEffects *SideEffectClass `json:"sideEffects" protobuf:"bytes,6,opt,name=sideEffects,casttype=SideEffectClass"`
// TimeoutSeconds specifies the timeout for this webhook. After the timeout passes,
// the webhook call will be ignored or the API call will fail based on the
// failure policy.
// The timeout value must be between 1 and 30 seconds.
// Default to 10 seconds.
ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,2,opt,name=objectSelector"`
// ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches.
// The policy cares about an operation if it matches _any_ Rule.
// +listType=atomic
// +optional
TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty" protobuf:"varint,7,opt,name=timeoutSeconds"`
// AdmissionReviewVersions is an ordered list of preferred `AdmissionReview`
// versions the Webhook expects. API server will try to use first version in
// the list which it supports. If none of the versions specified in this list
// supported by API server, validation will fail for this object.
// If a persisted webhook configuration specifies allowed versions and does not
// include any versions known to the API Server, calls to the webhook will fail
// and be subject to the failure policy.
AdmissionReviewVersions []string `json:"admissionReviewVersions" protobuf:"bytes,8,rep,name=admissionReviewVersions"`
// reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation.
// Allowed values are "Never" and "IfNeeded".
//
// Never: the webhook will not be called more than once in a single admission evaluation.
//
// IfNeeded: the webhook will be called at least one additional time as part of the admission evaluation
// if the object being admitted is modified by other admission plugins after the initial webhook call.
// Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted.
// Note:
// * the number of additional invocations is not guaranteed to be exactly one.
// * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again.
// * webhooks that use this option may be reordered to minimize the number of additional invocations.
// * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead.
//
// Defaults to "Never".
ResourceRules []NamedRuleWithOperations `json:"resourceRules,omitempty" protobuf:"bytes,3,rep,name=resourceRules"`
// ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about.
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
// +listType=atomic
// +optional
ReinvocationPolicy *ReinvocationPolicyType `json:"reinvocationPolicy,omitempty" protobuf:"bytes,10,opt,name=reinvocationPolicy,casttype=ReinvocationPolicyType"`
ExcludeResourceRules []NamedRuleWithOperations `json:"excludeResourceRules,omitempty" protobuf:"bytes,4,rep,name=excludeResourceRules"`
// matchPolicy defines how the "MatchResources" list is used to match incoming requests.
// Allowed values are "Exact" or "Equivalent".
//
// - Exact: match a request only if it exactly matches a specified rule.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy.
//
// - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version.
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
// and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
// a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy.
//
// Defaults to "Equivalent"
// +optional
MatchPolicy *MatchPolicyType `json:"matchPolicy,omitempty" protobuf:"bytes,7,opt,name=matchPolicy,casttype=MatchPolicyType"`
}
// ReinvocationPolicyType specifies what type of policy the admission hook uses.
// +enum
type ReinvocationPolicyType string
const (
// NeverReinvocationPolicy indicates that the webhook must not be called more than once in a
// single admission evaluation.
NeverReinvocationPolicy ReinvocationPolicyType = "Never"
// IfNeededReinvocationPolicy indicates that the webhook may be called at least one
// additional time as part of the admission evaluation if the object being admitted is
// modified by other admission plugins after the initial webhook call.
IfNeededReinvocationPolicy ReinvocationPolicyType = "IfNeeded"
)
// NamedRuleWithOperations is a tuple of Operations and Resources with ResourceNames.
// +structType=atomic
type NamedRuleWithOperations struct {
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.
// +listType=atomic
// +optional
ResourceNames []string `json:"resourceNames,omitempty" protobuf:"bytes,1,rep,name=resourceNames"`
// RuleWithOperations is a tuple of Operations and Resources.
RuleWithOperations `json:",inline" protobuf:"bytes,2,opt,name=ruleWithOperations"`
}
// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make
// sure that all the tuple expansions are valid.
type RuleWithOperations struct {
// Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or *
// for all of those operations and any future admission operations that are added.
// If '*' is present, the length of the slice must be one.
// Required.
Operations []OperationType `json:"operations,omitempty" protobuf:"bytes,1,rep,name=operations,casttype=OperationType"`
// Rule is embedded, it describes other criteria of the rule, like
// APIGroups, APIVersions, Resources, etc.
Rule `json:",inline" protobuf:"bytes,2,opt,name=rule"`
}
type RuleWithOperations = v1.RuleWithOperations
// OperationType specifies an operation for a request.
// +enum
type OperationType string
type OperationType = v1.OperationType
// The constants should be kept in sync with those defined in k8s.io/kubernetes/pkg/admission/interface.go.
const (
OperationAll OperationType = "*"
Create OperationType = "CREATE"
Update OperationType = "UPDATE"
Delete OperationType = "DELETE"
Connect OperationType = "CONNECT"
OperationAll OperationType = v1.OperationAll
Create OperationType = v1.Create
Update OperationType = v1.Update
Delete OperationType = v1.Delete
Connect OperationType = v1.Connect
)
// WebhookClientConfig contains the information to make a TLS
// connection with the webhook
type WebhookClientConfig struct {
// `url` gives the location of the webhook, in standard URL form
// (`scheme://host:port/path`). Exactly one of `url` or `service`
// must be specified.
//
// The `host` should not refer to a service running in the cluster; use
// the `service` field instead. The host might be resolved via external
// DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
// in-cluster DNS as that would be a layering violation). `host` may
// also be an IP address.
//
// Please note that using `localhost` or `127.0.0.1` as a `host` is
// risky unless you take great care to run this webhook on all hosts
// which run an apiserver which might need to make calls to this
// webhook. Such installs are likely to be non-portable, i.e., not easy
// to turn up in a new cluster.
//
// The scheme must be "https"; the URL must begin with "https://".
//
// A path is optional, and if present may be any string permissible in
// a URL. You may use the path to pass an arbitrary string to the
// webhook, for example, a cluster identifier.
//
// Attempting to use a user or basic auth e.g. "user:password@" is not
// allowed. Fragments ("#...") and query parameters ("?...") are not
// allowed, either.
//
// +optional
URL *string `json:"url,omitempty" protobuf:"bytes,3,opt,name=url"`
// `service` is a reference to the service for this webhook. Either
// `service` or `url` must be specified.
//
// If the webhook is running within the cluster, then you should use `service`.
//
// +optional
Service *ServiceReference `json:"service,omitempty" protobuf:"bytes,1,opt,name=service"`
// `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
// If unspecified, system trust roots on the apiserver are used.
// +optional
CABundle []byte `json:"caBundle,omitempty" protobuf:"bytes,2,opt,name=caBundle"`
}
// ServiceReference holds a reference to Service.legacy.k8s.io
type ServiceReference struct {
// `namespace` is the namespace of the service.
// Required
Namespace string `json:"namespace" protobuf:"bytes,1,opt,name=namespace"`
// `name` is the name of the service.
// Required
Name string `json:"name" protobuf:"bytes,2,opt,name=name"`
// `path` is an optional URL path which will be sent in any request to
// this service.
// +optional
Path *string `json:"path,omitempty" protobuf:"bytes,3,opt,name=path"`
// If specified, the port on the service that hosting webhook.
// Default to 443 for backward compatibility.
// `port` should be a valid port number (1-65535, inclusive).
// +optional
Port *int32 `json:"port,omitempty" protobuf:"varint,4,opt,name=port"`
}

View File

@ -17,63 +17,25 @@ limitations under the License.
package v1beta1
import (
v1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Rule is a tuple of APIGroups, APIVersion, and Resources.It is recommended
// to make sure that all the tuple expansions are valid.
type Rule struct {
// APIGroups is the API groups the resources belong to. '*' is all groups.
// If '*' is present, the length of the slice must be one.
// Required.
APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"`
// APIVersions is the API versions the resources belong to. '*' is all versions.
// If '*' is present, the length of the slice must be one.
// Required.
APIVersions []string `json:"apiVersions,omitempty" protobuf:"bytes,2,rep,name=apiVersions"`
// Resources is a list of resources this rule applies to.
//
// For example:
// 'pods' means pods.
// 'pods/log' means the log subresource of pods.
// '*' means all resources, but not subresources.
// 'pods/*' means all subresources of pods.
// '*/scale' means all scale subresources.
// '*/*' means all resources and their subresources.
//
// If wildcard is present, the validation rule will ensure resources do not
// overlap with each other.
//
// Depending on the enclosing object, subresources might not be allowed.
// Required.
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
// scope specifies the scope of this rule.
// Valid values are "Cluster", "Namespaced", and "*"
// "Cluster" means that only cluster-scoped resources will match this rule.
// Namespace API objects are cluster-scoped.
// "Namespaced" means that only namespaced resources will match this rule.
// "*" means that there are no scope restrictions.
// Subresources match the scope of their parent resource.
// Default is "*".
//
// +optional
Scope *ScopeType `json:"scope,omitempty" protobuf:"bytes,4,rep,name=scope"`
}
type Rule = v1.Rule
// ScopeType specifies a scope for a Rule.
type ScopeType string
type ScopeType = v1.ScopeType
const (
// ClusterScope means that scope is limited to cluster-scoped objects.
// Namespace objects are cluster-scoped.
ClusterScope ScopeType = "Cluster"
ClusterScope ScopeType = v1.ClusterScope
// NamespacedScope means that scope is limited to namespaced objects.
NamespacedScope ScopeType = "Namespaced"
NamespacedScope ScopeType = v1.NamespacedScope
// AllScopes means that all scopes are included.
AllScopes ScopeType = "*"
AllScopes ScopeType = v1.AllScopes
)
// FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled.
@ -488,27 +450,19 @@ const (
// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make
// sure that all the tuple expansions are valid.
type RuleWithOperations struct {
// Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or *
// for all of those operations and any future admission operations that are added.
// If '*' is present, the length of the slice must be one.
// Required.
Operations []OperationType `json:"operations,omitempty" protobuf:"bytes,1,rep,name=operations,casttype=OperationType"`
// Rule is embedded, it describes other criteria of the rule, like
// APIGroups, APIVersions, Resources, etc.
Rule `json:",inline" protobuf:"bytes,2,opt,name=rule"`
}
type RuleWithOperations = v1.RuleWithOperations
// OperationType specifies an operation for a request.
type OperationType string
// +enum
type OperationType = v1.OperationType
// The constants should be kept in sync with those defined in k8s.io/kubernetes/pkg/admission/interface.go.
const (
OperationAll OperationType = "*"
Create OperationType = "CREATE"
Update OperationType = "UPDATE"
Delete OperationType = "DELETE"
Connect OperationType = "CONNECT"
OperationAll OperationType = v1.OperationAll
Create OperationType = v1.Create
Update OperationType = v1.Update
Delete OperationType = v1.Delete
Connect OperationType = v1.Connect
)
// WebhookClientConfig contains the information to make a TLS

View File

@ -23,6 +23,7 @@ import (
admissionv1 "k8s.io/api/admission/v1"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
admissionregv1 "k8s.io/api/admissionregistration/v1"
admissionregv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
admissionregv1beta1 "k8s.io/api/admissionregistration/v1beta1"
apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
apiserverinternalv1alpha1 "k8s.io/api/apiserverinternal/v1alpha1"
@ -83,6 +84,7 @@ import (
var groups = []runtime.SchemeBuilder{
admissionv1beta1.SchemeBuilder,
admissionv1.SchemeBuilder,
admissionregv1alpha1.SchemeBuilder,
admissionregv1beta1.SchemeBuilder,
admissionregv1.SchemeBuilder,
apiserverinternalv1alpha1.SchemeBuilder,

View File

@ -75,6 +75,8 @@ var (
initEnvErr error
)
// This func is duplicated in k8s.io/apiserver/pkg/admission/plugin/cel/internal/implementation.go
// If any changes are made here, consider to make the same changes there as well.
func getBaseEnv() (*cel.Env, error) {
initEnvOnce.Do(func() {
var opts []cel.EnvOption

View File

@ -0,0 +1,220 @@
/*
Copyright 2022 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 cel
import (
"sync"
"github.com/google/cel-go/cel"
apiservercel "k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/library"
)
const (
ObjectVarName = "object"
OldObjectVarName = "oldObject"
ParamsVarName = "params"
RequestVarName = "request"
checkFrequency = 100
)
type envs struct {
noParams *cel.Env
withParams *cel.Env
}
var (
initEnvsOnce sync.Once
initEnvs *envs
initEnvsErr error
)
func getEnvs() (*envs, error) {
initEnvsOnce.Do(func() {
base, err := buildBaseEnv()
if err != nil {
initEnvsErr = err
return
}
noParams, err := buildNoParamsEnv(base)
if err != nil {
initEnvsErr = err
return
}
withParams, err := buildWithParamsEnv(noParams)
if err != nil {
initEnvsErr = err
return
}
initEnvs = &envs{noParams: noParams, withParams: withParams}
})
return initEnvs, initEnvsErr
}
// This is a similar code as in k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go
// If any changes are made here, consider to make the same changes there as well.
func buildBaseEnv() (*cel.Env, error) {
var opts []cel.EnvOption
opts = append(opts, cel.HomogeneousAggregateLiterals())
// Validate function declarations once during base env initialization,
// so they don't need to be evaluated each time a CEL rule is compiled.
// This is a relatively expensive operation.
opts = append(opts, cel.EagerlyValidateDeclarations(true), cel.DefaultUTCTimeZone(true))
opts = append(opts, library.ExtensionLibs...)
return cel.NewEnv(opts...)
}
func buildNoParamsEnv(baseEnv *cel.Env) (*cel.Env, error) {
var propDecls []cel.EnvOption
reg := apiservercel.NewRegistry(baseEnv)
requestType := buildRequestType()
rt, err := apiservercel.NewRuleTypes(requestType.TypeName(), requestType, reg)
if err != nil {
return nil, err
}
if rt == nil {
return nil, nil
}
opts, err := rt.EnvOptions(baseEnv.TypeProvider())
if err != nil {
return nil, err
}
propDecls = append(propDecls, cel.Variable(ObjectVarName, cel.DynType))
propDecls = append(propDecls, cel.Variable(OldObjectVarName, cel.DynType))
propDecls = append(propDecls, cel.Variable(RequestVarName, requestType.CelType()))
opts = append(opts, propDecls...)
env, err := baseEnv.Extend(opts...)
if err != nil {
return nil, err
}
return env, nil
}
func buildWithParamsEnv(noParams *cel.Env) (*cel.Env, error) {
return noParams.Extend(cel.Variable(ParamsVarName, cel.DynType))
}
// buildRequestType generates a DeclType for AdmissionRequest. This may be replaced with a utility that
// converts the native type definition to apiservercel.DeclType once such a utility becomes available.
// The 'uid' field is omitted since it is not needed for in-process admission review.
// The 'object' and 'oldObject' fields are omitted since they are exposed as root level CEL variables.
func buildRequestType() *apiservercel.DeclType {
field := func(name string, declType *apiservercel.DeclType, required bool) *apiservercel.DeclField {
return apiservercel.NewDeclField(name, declType, required, nil, nil)
}
fields := func(fields ...*apiservercel.DeclField) map[string]*apiservercel.DeclField {
result := make(map[string]*apiservercel.DeclField, len(fields))
for _, f := range fields {
result[f.Name] = f
}
return result
}
gvkType := apiservercel.NewObjectType("kubernetes.GroupVersionKind", fields(
field("group", apiservercel.StringType, true),
field("version", apiservercel.StringType, true),
field("kind", apiservercel.StringType, true),
))
gvrType := apiservercel.NewObjectType("kubernetes.GroupVersionResource", fields(
field("group", apiservercel.StringType, true),
field("version", apiservercel.StringType, true),
field("resource", apiservercel.StringType, true),
))
userInfoType := apiservercel.NewObjectType("kubernetes.UserInfo", fields(
field("username", apiservercel.StringType, false),
field("uid", apiservercel.StringType, false),
field("groups", apiservercel.NewListType(apiservercel.StringType, -1), false),
field("extra", apiservercel.NewMapType(apiservercel.StringType, apiservercel.NewListType(apiservercel.StringType, -1), -1), false),
))
return apiservercel.NewObjectType("kubernetes.AdmissionRequest", fields(
field("kind", gvkType, true),
field("resource", gvrType, true),
field("subResource", apiservercel.StringType, false),
field("requestKind", gvkType, true),
field("requestResource", gvrType, true),
field("requestSubResource", apiservercel.StringType, false),
field("name", apiservercel.StringType, true),
field("namespace", apiservercel.StringType, false),
field("operation", apiservercel.StringType, true),
field("userInfo", userInfoType, true),
field("dryRun", apiservercel.BoolType, false),
field("options", apiservercel.DynType, false),
))
}
// CompilationResult represents a compiled ValidatingAdmissionPolicy validation expression.
type CompilationResult struct {
Program cel.Program
Error *apiservercel.Error
}
// CompileValidatingPolicyExpression returns a compiled vaalidating policy CEL expression.
func CompileValidatingPolicyExpression(validationExpression string, hasParams bool) CompilationResult {
var env *cel.Env
envs, err := getEnvs()
if err != nil {
return CompilationResult{
Error: &apiservercel.Error{
Type: apiservercel.ErrorTypeInternal,
Detail: "compiler initialization failed: " + err.Error(),
},
}
}
if hasParams {
env = envs.withParams
} else {
env = envs.noParams
}
ast, issues := env.Compile(validationExpression)
if issues != nil {
return CompilationResult{
Error: &apiservercel.Error{
Type: apiservercel.ErrorTypeInvalid,
Detail: "compilation failed: " + issues.String(),
},
}
}
if ast.OutputType() != cel.BoolType {
return CompilationResult{
Error: &apiservercel.Error{
Type: apiservercel.ErrorTypeInvalid,
Detail: "cel expression must evaluate to a bool",
},
}
}
prog, err := env.Program(ast,
cel.EvalOptions(cel.OptOptimize),
cel.OptimizeRegex(library.ExtensionLibRegexOptimizations...),
cel.InterruptCheckFrequency(checkFrequency),
)
if err != nil {
return CompilationResult{
Error: &apiservercel.Error{
Type: apiservercel.ErrorTypeInvalid,
Detail: "program instantiation failed: " + err.Error(),
},
}
}
return CompilationResult{
Program: prog,
}
}

View File

@ -0,0 +1,125 @@
/*
Copyright 2022 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 cel
import (
"strings"
"testing"
)
func TestCompileValidatingPolicyExpression(t *testing.T) {
cases := []struct {
name string
expressions []string
hasParams bool
errorExpressions map[string]string
}{
{
name: "invalid syntax",
errorExpressions: map[string]string{
"1 < 'asdf'": "found no matching overload for '_<_' applied to '(int, string)",
"'asdf'.contains('x'": "Syntax error: missing ')' at",
},
},
{
name: "with params",
expressions: []string{"object.foo < params.x"},
hasParams: true,
},
{
name: "without params",
errorExpressions: map[string]string{"object.foo < params.x": "undeclared reference to 'params'"},
hasParams: false,
},
{
name: "oldObject comparison",
expressions: []string{"object.foo == oldObject.foo"},
},
{
name: "object null checks",
// since object and oldObject are CEL variable, has() cannot be used (it works only on fields),
// so we always populate it to allow for a null check in the case of CREATE, where oldObject is
// null, and DELETE, where object is null.
expressions: []string{"object == null || oldObject == null || object.foo == oldObject.foo"},
},
{
name: "invalid root var",
errorExpressions: map[string]string{"object.foo < invalid.x": "undeclared reference to 'invalid'"},
hasParams: false,
},
{
name: "function library",
// sanity check that functions of the various libraries are available
expressions: []string{
"object.spec.string.matches('[0-9]+')", // strings extension lib
"object.spec.string.findAll('[0-9]+').size() > 0", // kubernetes string lib
"object.spec.list.isSorted()", // kubernetes list lib
"url(object.spec.endpoint).getHostname() in ['ok1', 'ok2']", // kubernetes url lib
},
},
{
name: "valid request",
expressions: []string{
"request.kind.group == 'example.com' && request.kind.version == 'v1' && request.kind.kind == 'Fake'",
"request.resource.group == 'example.com' && request.resource.version == 'v1' && request.resource.resource == 'fake' && request.subResource == 'scale'",
"request.requestKind.group == 'example.com' && request.requestKind.version == 'v1' && request.requestKind.kind == 'Fake'",
"request.requestResource.group == 'example.com' && request.requestResource.version == 'v1' && request.requestResource.resource == 'fake' && request.requestSubResource == 'scale'",
"request.name == 'fake-name'",
"request.namespace == 'fake-namespace'",
"request.operation == 'CREATE'",
"request.userInfo.username == 'admin'",
"request.userInfo.uid == '014fbff9a07c'",
"request.userInfo.groups == ['system:authenticated', 'my-admin-group']",
"request.userInfo.extra == {'some-key': ['some-value1', 'some-value2']}",
"request.dryRun == false",
"request.options == {'whatever': 'you want'}",
},
},
{
name: "invalid request",
errorExpressions: map[string]string{
"request.foo1 == 'nope'": "undefined field 'foo1'",
"request.resource.foo2 == 'nope'": "undefined field 'foo2'",
"request.requestKind.foo3 == 'nope'": "undefined field 'foo3'",
"request.requestResource.foo4 == 'nope'": "undefined field 'foo4'",
"request.userInfo.foo5 == 'nope'": "undefined field 'foo5'",
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
for _, expr := range tc.expressions {
result := CompileValidatingPolicyExpression(expr, tc.hasParams)
if result.Error != nil {
t.Errorf("Unexpected error: %v", result.Error)
}
}
for expr, expectErr := range tc.errorExpressions {
result := CompileValidatingPolicyExpression(expr, tc.hasParams)
if result.Error == nil {
t.Errorf("Expected expression '%s' to contain '%v' but got no error", expr, expectErr)
continue
}
if !strings.Contains(result.Error.Error(), expectErr) {
t.Errorf("Expected validation '%s' error to contain '%v' but got: %v", expr, expectErr, result.Error)
}
continue
}
})
}
}

View File

@ -386,6 +386,17 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
},
// --
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"vap1","creationTimestamp":null},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"validations":[{"expression":"object.spec.replicas <= params.maxReplicas","message":"Too many replicas"}]}}`,
ExpectedEtcdPath: "/registry/validatingadmissionpolicies/vap1",
},
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"pb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com"}}}`,
ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1",
},
// --
// k8s.io/kubernetes/pkg/apis/scheduling/v1
gvr("scheduling.k8s.io", "v1", "priorityclasses"): {
Stub: `{"metadata":{"name":"pc3"},"Value":1000}`,