Adding new api version of admissionregistration.k8s.io v1alpha1 for CEL in Admission Control
This commit is contained in:
parent
f8de127789
commit
0486e06261
@ -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
|
||||
|
@ -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},
|
||||
|
@ -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",
|
||||
|
@ -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 \
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -51,6 +51,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&ValidatingWebhookConfigurationList{},
|
||||
&MutatingWebhookConfiguration{},
|
||||
&MutatingWebhookConfigurationList{},
|
||||
&ValidatingAdmissionPolicy{},
|
||||
&ValidatingAdmissionPolicyList{},
|
||||
&ValidatingAdmissionPolicyBinding{},
|
||||
&ValidatingAdmissionPolicyBindingList{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
@ -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
|
||||
|
43
pkg/apis/admissionregistration/v1/conversion.go
Normal file
43
pkg/apis/admissionregistration/v1/conversion.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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{
|
||||
|
@ -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},
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
@ -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"}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
@ -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"
|
@ -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"}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -50,6 +50,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&ValidatingWebhookConfigurationList{},
|
||||
&MutatingWebhookConfiguration{},
|
||||
&MutatingWebhookConfigurationList{},
|
||||
&ValidatingAdmissionPolicy{},
|
||||
&ValidatingAdmissionPolicyList{},
|
||||
&ValidatingAdmissionPolicyBinding{},
|
||||
&ValidatingAdmissionPolicyBindingList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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}`,
|
||||
|
Loading…
Reference in New Issue
Block a user