Merge pull request #115668 from jiahuif-forks/feature/validating-admission-policy/type-system

Type System for ValidatingAdmissionPolicy
This commit is contained in:
Kubernetes Prow Robot 2023-03-13 23:27:09 -07:00 committed by GitHub
commit 152876a3eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 3893 additions and 122 deletions

View File

@ -366,6 +366,24 @@
],
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning": {
"description": "ExpressionWarning is a warning information that targets a specific expression.",
"properties": {
"fieldRef": {
"description": "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"",
"type": "string"
},
"warning": {
"description": "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.",
"type": "string"
}
},
"required": [
"fieldRef",
"warning"
],
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.MatchResources": {
"description": "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)",
"properties": {
@ -482,6 +500,20 @@
"type": "object",
"x-kubernetes-map-type": "atomic"
},
"io.k8s.api.admissionregistration.v1alpha1.TypeChecking": {
"description": "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy",
"properties": {
"expressionWarnings": {
"description": "The type checking warnings for each expression.",
"items": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning"
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy": {
"description": "ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.",
"properties": {
@ -500,6 +532,10 @@
"spec": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicySpec",
"description": "Specification of the desired behavior of the ValidatingAdmissionPolicy."
},
"status": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus",
"description": "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only."
}
},
"type": "object",
@ -664,6 +700,32 @@
},
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus": {
"description": "ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.",
"properties": {
"conditions": {
"description": "The conditions represent the latest available observations of a policy's current state.",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"observedGeneration": {
"description": "The generation observed by the controller.",
"format": "int64",
"type": "integer"
},
"typeChecking": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.TypeChecking",
"description": "The results of type checking for each expression. Presence of this field indicates the completion of the type checking."
}
},
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.Validation": {
"description": "Validation specifies the CEL expression which is used to apply the validation.",
"properties": {
@ -39694,6 +39756,214 @@
}
}
},
"/apis/admissionregistration.k8s.io/v1alpha1/validatingadmissionpolicies/{name}/status": {
"get": {
"consumes": [
"*/*"
],
"description": "read status of the specified ValidatingAdmissionPolicy",
"operationId": "readAdmissionregistrationV1alpha1ValidatingAdmissionPolicyStatus",
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"admissionregistration_v1alpha1"
],
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "admissionregistration.k8s.io",
"kind": "ValidatingAdmissionPolicy",
"version": "v1alpha1"
}
},
"parameters": [
{
"description": "name of the ValidatingAdmissionPolicy",
"in": "path",
"name": "name",
"required": true,
"type": "string",
"uniqueItems": true
},
{
"description": "If 'true', then the output is pretty printed.",
"in": "query",
"name": "pretty",
"type": "string",
"uniqueItems": true
}
],
"patch": {
"consumes": [
"application/json-patch+json",
"application/merge-patch+json",
"application/strategic-merge-patch+json",
"application/apply-patch+yaml"
],
"description": "partially update status of the specified ValidatingAdmissionPolicy",
"operationId": "patchAdmissionregistrationV1alpha1ValidatingAdmissionPolicyStatus",
"parameters": [
{
"in": "body",
"name": "body",
"required": true,
"schema": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"type": "string",
"uniqueItems": true
},
{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
"in": "query",
"name": "fieldManager",
"type": "string",
"uniqueItems": true
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"type": "string",
"uniqueItems": true
},
{
"description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
"in": "query",
"name": "force",
"type": "boolean",
"uniqueItems": true
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"admissionregistration_v1alpha1"
],
"x-kubernetes-action": "patch",
"x-kubernetes-group-version-kind": {
"group": "admissionregistration.k8s.io",
"kind": "ValidatingAdmissionPolicy",
"version": "v1alpha1"
}
},
"put": {
"consumes": [
"*/*"
],
"description": "replace status of the specified ValidatingAdmissionPolicy",
"operationId": "replaceAdmissionregistrationV1alpha1ValidatingAdmissionPolicyStatus",
"parameters": [
{
"in": "body",
"name": "body",
"required": true,
"schema": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"type": "string",
"uniqueItems": true
},
{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"in": "query",
"name": "fieldManager",
"type": "string",
"uniqueItems": true
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"type": "string",
"uniqueItems": true
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"admissionregistration_v1alpha1"
],
"x-kubernetes-action": "put",
"x-kubernetes-group-version-kind": {
"group": "admissionregistration.k8s.io",
"kind": "ValidatingAdmissionPolicy",
"version": "v1alpha1"
}
}
},
"/apis/admissionregistration.k8s.io/v1alpha1/validatingadmissionpolicybindings": {
"delete": {
"consumes": [

View File

@ -21,6 +21,26 @@
],
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning": {
"description": "ExpressionWarning is a warning information that targets a specific expression.",
"properties": {
"fieldRef": {
"default": "",
"description": "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"",
"type": "string"
},
"warning": {
"default": "",
"description": "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.",
"type": "string"
}
},
"required": [
"fieldRef",
"warning"
],
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.MatchResources": {
"description": "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)",
"properties": {
@ -160,6 +180,25 @@
"type": "object",
"x-kubernetes-map-type": "atomic"
},
"io.k8s.api.admissionregistration.v1alpha1.TypeChecking": {
"description": "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy",
"properties": {
"expressionWarnings": {
"description": "The type checking warnings for each expression.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy": {
"description": "ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.",
"properties": {
@ -188,6 +227,15 @@
],
"default": {},
"description": "Specification of the desired behavior of the ValidatingAdmissionPolicy."
},
"status": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus"
}
],
"default": {},
"description": "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only."
}
},
"type": "object",
@ -409,6 +457,41 @@
},
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus": {
"description": "ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.",
"properties": {
"conditions": {
"description": "The conditions represent the latest available observations of a policy's current state.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"observedGeneration": {
"description": "The generation observed by the controller.",
"format": "int64",
"type": "integer"
},
"typeChecking": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.TypeChecking"
}
],
"description": "The results of type checking for each expression. Presence of this field indicates the completion of the type checking."
}
},
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.Validation": {
"description": "Validation specifies the CEL expression which is used to apply the validation.",
"properties": {
@ -546,6 +629,53 @@
}
]
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Condition": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
}
],
"default": {},
"description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable."
},
"message": {
"default": "",
"description": "message is a human readable message indicating details about the transition. This may be an empty string.",
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.",
"format": "int64",
"type": "integer"
},
"reason": {
"default": "",
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.",
"type": "string"
},
"status": {
"default": "",
"description": "status of the condition, one of True, False, Unknown.",
"type": "string"
},
"type": {
"default": "",
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"type": "string"
}
},
"required": [
"type",
"status",
"lastTransitionTime",
"reason",
"message"
],
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": {
"description": "DeleteOptions may be provided when deleting an API object.",
"properties": {
@ -2463,6 +2593,283 @@
}
}
},
"/apis/admissionregistration.k8s.io/v1alpha1/validatingadmissionpolicies/{name}/status": {
"get": {
"description": "read status of the specified ValidatingAdmissionPolicy",
"operationId": "readAdmissionregistrationV1alpha1ValidatingAdmissionPolicyStatus",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
}
},
"description": "OK"
},
"401": {
"description": "Unauthorized"
}
},
"tags": [
"admissionregistration_v1alpha1"
],
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "admissionregistration.k8s.io",
"kind": "ValidatingAdmissionPolicy",
"version": "v1alpha1"
}
},
"parameters": [
{
"description": "name of the ValidatingAdmissionPolicy",
"in": "path",
"name": "name",
"required": true,
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "If 'true', then the output is pretty printed.",
"in": "query",
"name": "pretty",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"patch": {
"description": "partially update status of the specified ValidatingAdmissionPolicy",
"operationId": "patchAdmissionregistrationV1alpha1ValidatingAdmissionPolicyStatus",
"parameters": [
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
"in": "query",
"name": "fieldManager",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
"in": "query",
"name": "force",
"schema": {
"type": "boolean",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"application/apply-patch+yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/merge-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/strategic-merge-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
}
},
"description": "OK"
},
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
}
},
"description": "Created"
},
"401": {
"description": "Unauthorized"
}
},
"tags": [
"admissionregistration_v1alpha1"
],
"x-kubernetes-action": "patch",
"x-kubernetes-group-version-kind": {
"group": "admissionregistration.k8s.io",
"kind": "ValidatingAdmissionPolicy",
"version": "v1alpha1"
}
},
"put": {
"description": "replace status of the specified ValidatingAdmissionPolicy",
"operationId": "replaceAdmissionregistrationV1alpha1ValidatingAdmissionPolicyStatus",
"parameters": [
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"in": "query",
"name": "fieldManager",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
}
},
"description": "OK"
},
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy"
}
}
},
"description": "Created"
},
"401": {
"description": "Unauthorized"
}
},
"tags": [
"admissionregistration_v1alpha1"
],
"x-kubernetes-action": "put",
"x-kubernetes-group-version-kind": {
"group": "admissionregistration.k8s.io",
"kind": "ValidatingAdmissionPolicy",
"version": "v1alpha1"
}
}
},
"/apis/admissionregistration.k8s.io/v1alpha1/validatingadmissionpolicybindings": {
"delete": {
"description": "delete collection of ValidatingAdmissionPolicyBinding",

View File

@ -40,6 +40,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
"k8s.io/apiserver/pkg/endpoints/discovery/aggregated"
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
@ -56,6 +57,7 @@ import (
"k8s.io/apiserver/pkg/util/webhook"
clientgoinformers "k8s.io/client-go/informers"
clientgoclientset "k8s.io/client-go/kubernetes"
k8sscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/keyutil"
cliflag "k8s.io/component-base/cli/flag"
@ -452,7 +454,8 @@ func buildGenericConfig(
CloudConfigFile: s.CloudProvider.CloudConfigFile,
}
serviceResolver = buildServiceResolver(s.EnableAggregatorRouting, genericConfig.LoopbackClientConfig.Host, versionedInformers)
pluginInitializers, admissionPostStartHook, err = admissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver, genericConfig.TracerProvider)
schemaResolver := resolver.NewDefinitionsSchemaResolver(k8sscheme.Scheme, genericConfig.OpenAPIConfig.GetDefinitions)
pluginInitializers, admissionPostStartHook, err = admissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver, genericConfig.TracerProvider, schemaResolver)
if err != nil {
lastErr = fmt.Errorf("failed to create admission plugin initializer: %v", err)
return

View File

@ -123,6 +123,52 @@ type ValidatingAdmissionPolicy struct {
metav1.ObjectMeta
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
Spec ValidatingAdmissionPolicySpec
// The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy
// behaves in the expected way.
// Populated by the system.
// Read-only.
// +optional
Status ValidatingAdmissionPolicyStatus
}
// ValidatingAdmissionPolicyStatus represents the status of an admission validation policy.
type ValidatingAdmissionPolicyStatus struct {
// The generation observed by the controller.
// +optional
ObservedGeneration int64
// The results of type checking for each expression.
// Presence of this field indicates the completion of the type checking.
// +optional
TypeChecking *TypeChecking
// The conditions represent the latest available observations of a policy's current state.
// +optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition
}
// ValidatingAdmissionPolicyConditionType is the condition type of admission validation policy.
type ValidatingAdmissionPolicyConditionType string
// TypeChecking contains results of type checking the expressions in the
// ValidatingAdmissionPolicy
type TypeChecking struct {
// The type checking warnings for each expression.
// +optional
// +listType=atomic
ExpressionWarnings []ExpressionWarning
}
// ExpressionWarning is a warning information that targets a specific expression.
type ExpressionWarning struct {
// The path to the field that refers the expression.
// For example, the reference to the expression of the first item of
// validations is "spec.validations[0].expression"
FieldRef string
// The content of type checking information in a human-readable form.
// Each line of the warning contains the type that the expression is checked
// against, followed by the type check error from the compiler.
Warning string
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@ -49,6 +49,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ExpressionWarning)(nil), (*admissionregistration.ExpressionWarning)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(a.(*v1alpha1.ExpressionWarning), b.(*admissionregistration.ExpressionWarning), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.ExpressionWarning)(nil), (*v1alpha1.ExpressionWarning)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(a.(*admissionregistration.ExpressionWarning), b.(*v1alpha1.ExpressionWarning), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.MatchResources)(nil), (*admissionregistration.MatchResources)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(a.(*v1alpha1.MatchResources), b.(*admissionregistration.MatchResources), scope)
}); err != nil {
@ -89,6 +99,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.TypeChecking)(nil), (*admissionregistration.TypeChecking)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(a.(*v1alpha1.TypeChecking), b.(*admissionregistration.TypeChecking), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.TypeChecking)(nil), (*v1alpha1.TypeChecking)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(a.(*admissionregistration.TypeChecking), b.(*v1alpha1.TypeChecking), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ValidatingAdmissionPolicy)(nil), (*admissionregistration.ValidatingAdmissionPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ValidatingAdmissionPolicy_To_admissionregistration_ValidatingAdmissionPolicy(a.(*v1alpha1.ValidatingAdmissionPolicy), b.(*admissionregistration.ValidatingAdmissionPolicy), scope)
}); err != nil {
@ -149,6 +169,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ValidatingAdmissionPolicyStatus)(nil), (*admissionregistration.ValidatingAdmissionPolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(a.(*v1alpha1.ValidatingAdmissionPolicyStatus), b.(*admissionregistration.ValidatingAdmissionPolicyStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.ValidatingAdmissionPolicyStatus)(nil), (*v1alpha1.ValidatingAdmissionPolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(a.(*admissionregistration.ValidatingAdmissionPolicyStatus), b.(*v1alpha1.ValidatingAdmissionPolicyStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.Validation)(nil), (*admissionregistration.Validation)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_Validation_To_admissionregistration_Validation(a.(*v1alpha1.Validation), b.(*admissionregistration.Validation), scope)
}); err != nil {
@ -184,6 +214,28 @@ func Convert_admissionregistration_AuditAnnotation_To_v1alpha1_AuditAnnotation(i
return autoConvert_admissionregistration_AuditAnnotation_To_v1alpha1_AuditAnnotation(in, out, s)
}
func autoConvert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(in *v1alpha1.ExpressionWarning, out *admissionregistration.ExpressionWarning, s conversion.Scope) error {
out.FieldRef = in.FieldRef
out.Warning = in.Warning
return nil
}
// Convert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning is an autogenerated conversion function.
func Convert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(in *v1alpha1.ExpressionWarning, out *admissionregistration.ExpressionWarning, s conversion.Scope) error {
return autoConvert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(in, out, s)
}
func autoConvert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in *admissionregistration.ExpressionWarning, out *v1alpha1.ExpressionWarning, s conversion.Scope) error {
out.FieldRef = in.FieldRef
out.Warning = in.Warning
return nil
}
// Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning is an autogenerated conversion function.
func Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in *admissionregistration.ExpressionWarning, out *v1alpha1.ExpressionWarning, s conversion.Scope) error {
return autoConvert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in, out, s)
}
func autoConvert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(in *v1alpha1.MatchResources, out *admissionregistration.MatchResources, s conversion.Scope) error {
out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector))
out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector))
@ -322,11 +374,34 @@ func Convert_admissionregistration_ParamRef_To_v1alpha1_ParamRef(in *admissionre
return autoConvert_admissionregistration_ParamRef_To_v1alpha1_ParamRef(in, out, s)
}
func autoConvert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(in *v1alpha1.TypeChecking, out *admissionregistration.TypeChecking, s conversion.Scope) error {
out.ExpressionWarnings = *(*[]admissionregistration.ExpressionWarning)(unsafe.Pointer(&in.ExpressionWarnings))
return nil
}
// Convert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking is an autogenerated conversion function.
func Convert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(in *v1alpha1.TypeChecking, out *admissionregistration.TypeChecking, s conversion.Scope) error {
return autoConvert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(in, out, s)
}
func autoConvert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(in *admissionregistration.TypeChecking, out *v1alpha1.TypeChecking, s conversion.Scope) error {
out.ExpressionWarnings = *(*[]v1alpha1.ExpressionWarning)(unsafe.Pointer(&in.ExpressionWarnings))
return nil
}
// Convert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking is an autogenerated conversion function.
func Convert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(in *admissionregistration.TypeChecking, out *v1alpha1.TypeChecking, s conversion.Scope) error {
return autoConvert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(in, out, s)
}
func autoConvert_v1alpha1_ValidatingAdmissionPolicy_To_admissionregistration_ValidatingAdmissionPolicy(in *v1alpha1.ValidatingAdmissionPolicy, out *admissionregistration.ValidatingAdmissionPolicy, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1alpha1_ValidatingAdmissionPolicySpec_To_admissionregistration_ValidatingAdmissionPolicySpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
@ -340,6 +415,9 @@ func autoConvert_admissionregistration_ValidatingAdmissionPolicy_To_v1alpha1_Val
if err := Convert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1_ValidatingAdmissionPolicySpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
@ -544,6 +622,30 @@ func Convert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1_Val
return autoConvert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1_ValidatingAdmissionPolicySpec(in, out, s)
}
func autoConvert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(in *v1alpha1.ValidatingAdmissionPolicyStatus, out *admissionregistration.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
out.ObservedGeneration = in.ObservedGeneration
out.TypeChecking = (*admissionregistration.TypeChecking)(unsafe.Pointer(in.TypeChecking))
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
// Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus is an autogenerated conversion function.
func Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(in *v1alpha1.ValidatingAdmissionPolicyStatus, out *admissionregistration.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
return autoConvert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(in, out, s)
}
func autoConvert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(in *admissionregistration.ValidatingAdmissionPolicyStatus, out *v1alpha1.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
out.ObservedGeneration = in.ObservedGeneration
out.TypeChecking = (*v1alpha1.TypeChecking)(unsafe.Pointer(in.TypeChecking))
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
// Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus is an autogenerated conversion function.
func Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(in *admissionregistration.ValidatingAdmissionPolicyStatus, out *v1alpha1.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
return autoConvert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(in, out, s)
}
func autoConvert_v1alpha1_Validation_To_admissionregistration_Validation(in *v1alpha1.Validation, out *admissionregistration.Validation, s conversion.Scope) error {
out.Expression = in.Expression
out.Message = in.Message

View File

@ -33,6 +33,7 @@ import (
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/util/jsonpath"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
admissionregistrationv1 "k8s.io/kubernetes/pkg/apis/admissionregistration/v1"
@ -916,7 +917,56 @@ func ValidateValidatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.V
return validateValidatingAdmissionPolicy(newC)
}
// ValidateValidatingAdmissionPolicyStatusUpdate validates update of status of validating admission policy
func ValidateValidatingAdmissionPolicyStatusUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
return validateValidatingAdmissionPolicyStatus(&newC.Status, field.NewPath("status"))
}
// ValidateValidatingAdmissionPolicyBindingUpdate validates update of validating admission policy
func ValidateValidatingAdmissionPolicyBindingUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicyBinding) field.ErrorList {
return validateValidatingAdmissionPolicyBinding(newC)
}
func validateValidatingAdmissionPolicyStatus(status *admissionregistration.ValidatingAdmissionPolicyStatus, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
allErrors = append(allErrors, validateTypeChecking(status.TypeChecking, fldPath.Child("typeChecking"))...)
allErrors = append(allErrors, metav1validation.ValidateConditions(status.Conditions, fldPath.Child("conditions"))...)
return allErrors
}
func validateTypeChecking(typeChecking *admissionregistration.TypeChecking, fldPath *field.Path) field.ErrorList {
if typeChecking == nil {
return nil
}
return validateExpressionWarnings(typeChecking.ExpressionWarnings, fldPath.Child("expressionWarnings"))
}
func validateExpressionWarnings(expressionWarnings []admissionregistration.ExpressionWarning, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
for i, warning := range expressionWarnings {
allErrors = append(allErrors, validateExpressionWarning(&warning, fldPath.Index(i))...)
}
return allErrors
}
func validateExpressionWarning(expressionWarning *admissionregistration.ExpressionWarning, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if expressionWarning.Warning == "" {
allErrors = append(allErrors, field.Required(fldPath.Child("warning"), ""))
}
allErrors = append(allErrors, validateFieldRef(expressionWarning.FieldRef, fldPath.Child("fieldRef"))...)
return allErrors
}
func validateFieldRef(fieldRef string, fldPath *field.Path) field.ErrorList {
fieldRef = strings.TrimSpace(fieldRef)
if fieldRef == "" {
return field.ErrorList{field.Required(fldPath, "")}
}
jsonPath := jsonpath.New("spec")
if err := jsonPath.Parse(fmt.Sprintf("{%s}", fieldRef)); err != nil {
return field.ErrorList{field.Invalid(fldPath, fieldRef, fmt.Sprintf("invalid JSONPath: %v", err))}
}
// no further checks, for an easier upgrade/rollback
return nil
}

View File

@ -21,6 +21,7 @@ import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
)
@ -3453,3 +3454,83 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
}
}
func TestValidateValidatingAdmissionPolicyStatus(t *testing.T) {
for _, tc := range []struct {
name string
status *admissionregistration.ValidatingAdmissionPolicyStatus
expectedError string
}{
{
name: "empty",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{},
},
{
name: "type checking",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
FieldRef: "spec.validations[0].expression",
Warning: "message",
},
},
},
},
},
{
name: "type checking bad json path",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
FieldRef: "spec[foo]",
Warning: "message",
},
},
},
},
expectedError: "invalid JSONPath: invalid array index foo",
},
{
name: "type checking missing warning",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
FieldRef: "spec.validations[0].expression",
},
},
},
},
expectedError: "Required value",
},
{
name: "type checking missing fieldRef",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
Warning: "message",
},
},
},
},
expectedError: "Required value",
},
} {
t.Run(tc.name, func(t *testing.T) {
errs := validateValidatingAdmissionPolicyStatus(tc.status, field.NewPath("status"))
err := errs.ToAggregate()
if err != nil {
if e, a := tc.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
t.Errorf("expected to contain %s, got %s", e, a)
}
} else {
if tc.expectedError != "" {
t.Errorf("unexpected no error, expected to contain %s", tc.expectedError)
}
}
})
}
}

View File

@ -42,6 +42,22 @@ func (in *AuditAnnotation) DeepCopy() *AuditAnnotation {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExpressionWarning) DeepCopyInto(out *ExpressionWarning) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExpressionWarning.
func (in *ExpressionWarning) DeepCopy() *ExpressionWarning {
if in == nil {
return nil
}
out := new(ExpressionWarning)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchResources) DeepCopyInto(out *MatchResources) {
*out = *in
@ -350,12 +366,34 @@ func (in *ServiceReference) DeepCopy() *ServiceReference {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TypeChecking) DeepCopyInto(out *TypeChecking) {
*out = *in
if in.ExpressionWarnings != nil {
in, out := &in.ExpressionWarnings, &out.ExpressionWarnings
*out = make([]ExpressionWarning, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypeChecking.
func (in *TypeChecking) DeepCopy() *TypeChecking {
if in == nil {
return nil
}
out := new(TypeChecking)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingAdmissionPolicy) DeepCopyInto(out *ValidatingAdmissionPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
@ -544,6 +582,34 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopy() *ValidatingAdmissionPolicySp
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingAdmissionPolicyStatus) DeepCopyInto(out *ValidatingAdmissionPolicyStatus) {
*out = *in
if in.TypeChecking != nil {
in, out := &in.TypeChecking, &out.TypeChecking
*out = new(TypeChecking)
(*in).DeepCopyInto(*out)
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidatingAdmissionPolicyStatus.
func (in *ValidatingAdmissionPolicyStatus) DeepCopy() *ValidatingAdmissionPolicyStatus {
if in == nil {
return nil
}
out := new(ValidatingAdmissionPolicyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingWebhook) DeepCopyInto(out *ValidatingWebhook) {
*out = *in

View File

@ -46,16 +46,19 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/admissionregistration/v1.ValidatingWebhookConfigurationList": schema_k8sio_api_admissionregistration_v1_ValidatingWebhookConfigurationList(ref),
"k8s.io/api/admissionregistration/v1.WebhookClientConfig": schema_k8sio_api_admissionregistration_v1_WebhookClientConfig(ref),
"k8s.io/api/admissionregistration/v1alpha1.AuditAnnotation": schema_k8sio_api_admissionregistration_v1alpha1_AuditAnnotation(ref),
"k8s.io/api/admissionregistration/v1alpha1.ExpressionWarning": schema_k8sio_api_admissionregistration_v1alpha1_ExpressionWarning(ref),
"k8s.io/api/admissionregistration/v1alpha1.MatchResources": schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(ref),
"k8s.io/api/admissionregistration/v1alpha1.NamedRuleWithOperations": schema_k8sio_api_admissionregistration_v1alpha1_NamedRuleWithOperations(ref),
"k8s.io/api/admissionregistration/v1alpha1.ParamKind": schema_k8sio_api_admissionregistration_v1alpha1_ParamKind(ref),
"k8s.io/api/admissionregistration/v1alpha1.ParamRef": schema_k8sio_api_admissionregistration_v1alpha1_ParamRef(ref),
"k8s.io/api/admissionregistration/v1alpha1.TypeChecking": schema_k8sio_api_admissionregistration_v1alpha1_TypeChecking(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicy": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicy(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyBinding": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBinding(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyBindingList": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBindingList(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyBindingSpec": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBindingSpec(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyList": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyList(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySpec(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyStatus(ref),
"k8s.io/api/admissionregistration/v1alpha1.Validation": schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref),
"k8s.io/api/admissionregistration/v1beta1.MutatingWebhook": schema_k8sio_api_admissionregistration_v1beta1_MutatingWebhook(ref),
"k8s.io/api/admissionregistration/v1beta1.MutatingWebhookConfiguration": schema_k8sio_api_admissionregistration_v1beta1_MutatingWebhookConfiguration(ref),
@ -1889,6 +1892,36 @@ func schema_k8sio_api_admissionregistration_v1alpha1_AuditAnnotation(ref common.
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_ExpressionWarning(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ExpressionWarning is a warning information that targets a specific expression.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"fieldRef": {
SchemaProps: spec.SchemaProps{
Description: "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"warning": {
SchemaProps: spec.SchemaProps{
Description: "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"fieldRef", "warning"},
},
},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@ -2156,6 +2189,40 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ParamRef(ref common.Referen
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_TypeChecking(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"expressionWarnings": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
SchemaProps: spec.SchemaProps{
Description: "The type checking warnings for each expression.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ExpressionWarning"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.ExpressionWarning"},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@ -2191,11 +2258,18 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicy(r
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only.",
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec", "k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
@ -2469,6 +2543,56 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySp
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"observedGeneration": {
SchemaProps: spec.SchemaProps{
Description: "The generation observed by the controller.",
Type: []string{"integer"},
Format: "int64",
},
},
"typeChecking": {
SchemaProps: spec.SchemaProps{
Description: "The results of type checking for each expression. Presence of this field indicates the completion of the type checking.",
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.TypeChecking"),
},
},
"conditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "The conditions represent the latest available observations of a policy's current state.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.TypeChecking", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{

View File

@ -28,6 +28,7 @@ import (
utilwait "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
webhookinit "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
genericapiserver "k8s.io/apiserver/pkg/server"
egressselector "k8s.io/apiserver/pkg/server/egressselector"
"k8s.io/apiserver/pkg/util/webhook"
@ -47,7 +48,7 @@ type Config struct {
}
// New sets up the plugins and admission start hooks needed for admission
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider) ([]admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider, schemaResolver resolver.SchemaResolver) ([]admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
webhookAuthResolverWrapper := webhook.NewDefaultAuthenticationInfoResolverWrapper(proxyTransport, egressSelector, c.LoopbackClientConfig, tp)
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthResolverWrapper, serviceResolver)
@ -63,13 +64,13 @@ func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselec
if err != nil {
return nil, nil, err
}
discoveryClient := cacheddiscovery.NewMemCacheClient(clientset.Discovery())
discoveryRESTMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
kubePluginInitializer := NewPluginInitializer(
cloudConfig,
discoveryRESTMapper,
quotainstall.NewQuotaConfigurationForAdmission(),
schemaResolver,
)
admissionPostStartHook := func(context genericapiserver.PostStartHookContext) error {

View File

@ -20,6 +20,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
quota "k8s.io/apiserver/pkg/quota/v1"
)
@ -35,6 +36,7 @@ type PluginInitializer struct {
cloudConfig []byte
restMapper meta.RESTMapper
quotaConfiguration quota.Configuration
schemaResolver resolver.SchemaResolver
}
var _ admission.PluginInitializer = &PluginInitializer{}
@ -46,11 +48,13 @@ func NewPluginInitializer(
cloudConfig []byte,
restMapper meta.RESTMapper,
quotaConfiguration quota.Configuration,
schemaResolver resolver.SchemaResolver,
) *PluginInitializer {
return &PluginInitializer{
cloudConfig: cloudConfig,
restMapper: restMapper,
quotaConfiguration: quotaConfiguration,
schemaResolver: schemaResolver,
}
}
@ -68,4 +72,8 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
if wants, ok := plugin.(initializer.WantsQuotaConfiguration); ok {
wants.SetQuotaConfiguration(i.quotaConfiguration)
}
if wants, ok := plugin.(initializer.WantsSchemaResolver); ok {
wants.SetSchemaResolver(i.schemaResolver)
}
}

View File

@ -49,7 +49,7 @@ func (p *WantsCloudConfigAdmissionPlugin) SetCloudConfig(cloudConfig []byte) {
func TestCloudConfigAdmissionPlugin(t *testing.T) {
cloudConfig := []byte("cloud-configuration")
initializer := NewPluginInitializer(cloudConfig, nil, nil)
initializer := NewPluginInitializer(cloudConfig, nil, nil, nil)
wantsCloudConfigAdmission := &WantsCloudConfigAdmissionPlugin{}
initializer.Initialize(wantsCloudConfigAdmission)
@ -94,7 +94,7 @@ func (p *WantsRESTMapperAdmissionPlugin) SetRESTMapper(mapper meta.RESTMapper) {
func TestRESTMapperAdmissionPlugin(t *testing.T) {
mapper := doNothingRESTMapper{}
initializer := NewPluginInitializer(nil, mapper, nil)
initializer := NewPluginInitializer(nil, mapper, nil, nil)
wantsRESTMapperAdmission := &WantsRESTMapperAdmissionPlugin{}
initializer.Initialize(wantsRESTMapperAdmission)
@ -121,7 +121,7 @@ func (p *WantsQuotaConfigurationAdmissionPlugin) SetQuotaConfiguration(config qu
func TestQuotaConfigurationAdmissionPlugin(t *testing.T) {
config := doNothingQuotaConfiguration{}
initializer := NewPluginInitializer(nil, nil, config)
initializer := NewPluginInitializer(nil, nil, config, nil)
wantsQuotaConfigurationAdmission := &WantsQuotaConfigurationAdmissionPlugin{}
initializer.Initialize(wantsQuotaConfigurationAdmission)

View File

@ -95,12 +95,13 @@ func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstora
// validatingadmissionpolicies
if resource := "validatingadmissionpolicies"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1alpha1.SchemeGroupVersion.WithResource(resource)) {
policyStorage, err := validatingadmissionpolicystorage.NewREST(restOptionsGetter, p.Authorizer, r)
policyStorage, policyStatusStorage, err := validatingadmissionpolicystorage.NewREST(restOptionsGetter, p.Authorizer, r)
if err != nil {
return storage, err
}
policyGetter = policyStorage
storage[resource] = policyStorage
storage[resource+"/status"] = policyStatusStorage
}
// validatingadmissionpolicybindings

View File

@ -17,6 +17,9 @@ limitations under the License.
package storage
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
@ -28,6 +31,7 @@ import (
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/admissionregistration/resolver"
"k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicy"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
)
// REST implements a RESTStorage for validatingAdmissionPolicy against etcd
@ -35,10 +39,16 @@ type REST struct {
*genericregistry.Store
}
// StatusREST implements a RESTStorage for ValidatingAdmissionPolicyStatus
type StatusREST struct {
// DO NOT embed Store, manually select function to export.
store *genericregistry.Store
}
var groupResource = admissionregistration.Resource("validatingadmissionpolicies")
// NewREST returns a RESTStorage object that will work against validatingAdmissionPolicy.
func NewREST(optsGetter generic.RESTOptionsGetter, authorizer authorizer.Authorizer, resourceResolver resolver.ResourceResolver) (*REST, error) {
// NewREST returns two RESTStorage objects that will work against validatingAdmissionPolicy and its status.
func NewREST(optsGetter generic.RESTOptionsGetter, authorizer authorizer.Authorizer, resourceResolver resolver.ResourceResolver) (*REST, *StatusREST, error) {
r := &REST{}
strategy := validatingadmissionpolicy.NewStrategy(authorizer, resourceResolver)
store := &genericregistry.Store{
@ -53,15 +63,21 @@ func NewREST(optsGetter generic.RESTOptionsGetter, authorizer authorizer.Authori
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
ResetFieldsStrategy: 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 nil, nil, err
}
r.Store = store
return r, nil
statusStrategy := validatingadmissionpolicy.NewStatusStrategy(strategy)
statusStore := *store
statusStore.UpdateStrategy = statusStrategy
statusStore.ResetFieldsStrategy = statusStrategy
sr := &StatusREST{store: &statusStore}
return r, sr, nil
}
// Implement CategoriesProvider
@ -71,3 +87,34 @@ var _ rest.CategoriesProvider = &REST{}
func (r *REST) Categories() []string {
return []string{"api-extensions"}
}
// New generates a new ValidatingAdmissionPolicy object
func (r *StatusREST) New() runtime.Object {
return &admissionregistration.ValidatingAdmissionPolicy{}
}
// Destroy disposes the store object. For the StatusREST, this is a no-op.
func (r *StatusREST) Destroy() {
// Given that underlying store is shared with REST,
// we don't destroy it here explicitly.
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// GetResetFields returns the fields that got reset by the REST endpoint
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// ConvertToTable delegates to the store, implements TableConverter
func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return r.store.ConvertToTable(ctx, object, tableOptions)
}
// Update alters the status subset of an object. Delegates to the store
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options)
}

View File

@ -211,7 +211,7 @@ func newStorage(t *testing.T, authorizer authorizer.Authorizer, resourceResolver
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "validatingadmissionpolicies"}
storage, err := NewREST(restOptions, authorizer, resourceResolver)
storage, _, err := NewREST(restOptions, authorizer, resourceResolver)
if err != nil {
t.Fatalf("unexpected error from REST storage: %v", err)
}

View File

@ -19,7 +19,10 @@ package validatingadmissionpolicy
import (
"context"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/authorization/authorizer"
@ -56,6 +59,7 @@ func (v *validatingAdmissionPolicyStrategy) NamespaceScoped() bool {
// PrepareForCreate clears the status of an validatingAdmissionPolicy before creation.
func (v *validatingAdmissionPolicyStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.ValidatingAdmissionPolicy)
ic.Status = admissionregistration.ValidatingAdmissionPolicyStatus{}
ic.Generation = 1
}
@ -64,6 +68,9 @@ func (v *validatingAdmissionPolicyStrategy) PrepareForUpdate(ctx context.Context
newIC := obj.(*admissionregistration.ValidatingAdmissionPolicy)
oldIC := old.(*admissionregistration.ValidatingAdmissionPolicy)
// Prevent any update on the Status object
newIC.Status = oldIC.Status
// 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.
@ -120,3 +127,53 @@ func (v *validatingAdmissionPolicyStrategy) WarningsOnUpdate(ctx context.Context
func (v *validatingAdmissionPolicyStrategy) AllowUnconditionalUpdate() bool {
return false
}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (v *validatingAdmissionPolicyStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"admissionregistration.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
type validatingAdmissionPolicyStatusStrategy struct {
*validatingAdmissionPolicyStrategy
}
// ValidateUpdate is the default update validation of an update to the status object.
func (s *validatingAdmissionPolicyStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateValidatingAdmissionPolicyStatusUpdate(obj.(*admissionregistration.ValidatingAdmissionPolicy), old.(*admissionregistration.ValidatingAdmissionPolicy))
}
// PrepareForUpdate differs from the main strategy where setting the spec is not
// allowed, but setting status is OK.
func (s *validatingAdmissionPolicyStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.ValidatingAdmissionPolicy)
oldIC := old.(*admissionregistration.ValidatingAdmissionPolicy)
// Prevent any update on the Spec object from Status Strategy
newIC.Spec = oldIC.Spec
metav1.ResetObjectMetaForStatus(&newIC.ObjectMeta, &oldIC.ObjectMeta)
// No change in the generation.
}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (s *validatingAdmissionPolicyStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"admissionregistration.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata"),
),
}
}
// NewStatusStrategy creates a strategy for operating the status object.
func NewStatusStrategy(policyStrategy *validatingAdmissionPolicyStrategy) *validatingAdmissionPolicyStatusStrategy {
return &validatingAdmissionPolicyStatusStrategy{validatingAdmissionPolicyStrategy: policyStrategy}
}

View File

@ -139,7 +139,7 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err)
}
restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)
pluginInitializer := kubeadmission.NewPluginInitializer(nil, restMapper, nil)
pluginInitializer := kubeadmission.NewPluginInitializer(nil, restMapper, nil, nil)
initializersChain := admission.PluginInitializers{}
initializersChain = append(initializersChain, genericPluginInitializer)
initializersChain = append(initializersChain, pluginInitializer)

View File

@ -111,7 +111,7 @@ func createHandlerWithConfig(kubeClient kubernetes.Interface, informerFactory in
initializers := admission.PluginInitializers{
genericadmissioninitializer.New(kubeClient, nil, informerFactory, nil, nil, stopCh),
kubeapiserveradmission.NewPluginInitializer(nil, nil, quotaConfiguration),
kubeapiserveradmission.NewPluginInitializer(nil, nil, quotaConfiguration, nil),
}
initializers.Initialize(handler)

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,19 @@ message AuditAnnotation {
optional string valueExpression = 2;
}
// ExpressionWarning is a warning information that targets a specific expression.
message ExpressionWarning {
// The path to the field that refers the expression.
// For example, the reference to the expression of the first item of
// validations is "spec.validations[0].expression"
optional string fieldRef = 2;
// The content of type checking information in a human-readable form.
// Each line of the warning contains the type that the expression is checked
// against, followed by the type check error from the compiler.
optional string warning = 3;
}
// 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)
@ -198,6 +211,15 @@ message ParamRef {
optional string namespace = 2;
}
// TypeChecking contains results of type checking the expressions in the
// ValidatingAdmissionPolicy
message TypeChecking {
// The type checking warnings for each expression.
// +optional
// +listType=atomic
repeated ExpressionWarning expressionWarnings = 1;
}
// ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.
message ValidatingAdmissionPolicy {
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
@ -206,6 +228,13 @@ message ValidatingAdmissionPolicy {
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
optional ValidatingAdmissionPolicySpec spec = 2;
// The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy
// behaves in the expected way.
// Populated by the system.
// Read-only.
// +optional
optional ValidatingAdmissionPolicyStatus status = 3;
}
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
@ -353,6 +382,24 @@ message ValidatingAdmissionPolicySpec {
repeated AuditAnnotation auditAnnotations = 5;
}
// ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.
message ValidatingAdmissionPolicyStatus {
// The generation observed by the controller.
// +optional
optional int64 observedGeneration = 1;
// The results of type checking for each expression.
// Presence of this field indicates the completion of the type checking.
// +optional
optional TypeChecking typeChecking = 2;
// The conditions represent the latest available observations of a policy's current state.
// +optional
// +listType=map
// +listMapKey=type
repeated k8s.io.apimachinery.pkg.apis.meta.v1.Condition conditions = 3;
}
// Validation specifies the CEL expression which is used to apply the validation.
message Validation {
// Expression represents the expression which will be evaluated by CEL.

View File

@ -74,6 +74,49 @@ type ValidatingAdmissionPolicy struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
Spec ValidatingAdmissionPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy
// behaves in the expected way.
// Populated by the system.
// Read-only.
// +optional
Status ValidatingAdmissionPolicyStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.
type ValidatingAdmissionPolicyStatus struct {
// The generation observed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"`
// The results of type checking for each expression.
// Presence of this field indicates the completion of the type checking.
// +optional
TypeChecking *TypeChecking `json:"typeChecking,omitempty" protobuf:"bytes,2,opt,name=typeChecking"`
// The conditions represent the latest available observations of a policy's current state.
// +optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" protobuf:"bytes,3,rep,name=conditions"`
}
// TypeChecking contains results of type checking the expressions in the
// ValidatingAdmissionPolicy
type TypeChecking struct {
// The type checking warnings for each expression.
// +optional
// +listType=atomic
ExpressionWarnings []ExpressionWarning `json:"expressionWarnings,omitempty" protobuf:"bytes,1,rep,name=expressionWarnings"`
}
// ExpressionWarning is a warning information that targets a specific expression.
type ExpressionWarning struct {
// The path to the field that refers the expression.
// For example, the reference to the expression of the first item of
// validations is "spec.validations[0].expression"
FieldRef string `json:"fieldRef" protobuf:"bytes,2,opt,name=fieldRef"`
// The content of type checking information in a human-readable form.
// Each line of the warning contains the type that the expression is checked
// against, followed by the type check error from the compiler.
Warning string `json:"warning" protobuf:"bytes,3,opt,name=warning"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@ -37,6 +37,16 @@ func (AuditAnnotation) SwaggerDoc() map[string]string {
return map_AuditAnnotation
}
var map_ExpressionWarning = map[string]string{
"": "ExpressionWarning is a warning information that targets a specific expression.",
"fieldRef": "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"",
"warning": "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.",
}
func (ExpressionWarning) SwaggerDoc() map[string]string {
return map_ExpressionWarning
}
var map_MatchResources = map[string]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)",
"namespaceSelector": "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.\n\nFor 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\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf 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\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.",
@ -79,10 +89,20 @@ func (ParamRef) SwaggerDoc() map[string]string {
return map_ParamRef
}
var map_TypeChecking = map[string]string{
"": "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy",
"expressionWarnings": "The type checking warnings for each expression.",
}
func (TypeChecking) SwaggerDoc() map[string]string {
return map_TypeChecking
}
var map_ValidatingAdmissionPolicy = map[string]string{
"": "ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.",
"metadata": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.",
"spec": "Specification of the desired behavior of the ValidatingAdmissionPolicy.",
"status": "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only.",
}
func (ValidatingAdmissionPolicy) SwaggerDoc() map[string]string {
@ -144,6 +164,17 @@ func (ValidatingAdmissionPolicySpec) SwaggerDoc() map[string]string {
return map_ValidatingAdmissionPolicySpec
}
var map_ValidatingAdmissionPolicyStatus = map[string]string{
"": "ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.",
"observedGeneration": "The generation observed by the controller.",
"typeChecking": "The results of type checking for each expression. Presence of this field indicates the completion of the type checking.",
"conditions": "The conditions represent the latest available observations of a policy's current state.",
}
func (ValidatingAdmissionPolicyStatus) SwaggerDoc() map[string]string {
return map_ValidatingAdmissionPolicyStatus
}
var map_Validation = map[string]string{
"": "Validation specifies the CEL expression which is used to apply the validation.",
"expression": "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 API request/response, organized into CEL variables as well as some other useful variables:\n\n- '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 API 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. - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly 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:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality 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:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",

View File

@ -42,6 +42,22 @@ func (in *AuditAnnotation) DeepCopy() *AuditAnnotation {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExpressionWarning) DeepCopyInto(out *ExpressionWarning) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExpressionWarning.
func (in *ExpressionWarning) DeepCopy() *ExpressionWarning {
if in == nil {
return nil
}
out := new(ExpressionWarning)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchResources) DeepCopyInto(out *MatchResources) {
*out = *in
@ -141,12 +157,34 @@ func (in *ParamRef) DeepCopy() *ParamRef {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TypeChecking) DeepCopyInto(out *TypeChecking) {
*out = *in
if in.ExpressionWarnings != nil {
in, out := &in.ExpressionWarnings, &out.ExpressionWarnings
*out = make([]ExpressionWarning, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypeChecking.
func (in *TypeChecking) DeepCopy() *TypeChecking {
if in == nil {
return nil
}
out := new(TypeChecking)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingAdmissionPolicy) DeepCopyInto(out *ValidatingAdmissionPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
@ -335,6 +373,34 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopy() *ValidatingAdmissionPolicySp
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingAdmissionPolicyStatus) DeepCopyInto(out *ValidatingAdmissionPolicyStatus) {
*out = *in
if in.TypeChecking != nil {
in, out := &in.TypeChecking, &out.TypeChecking
*out = new(TypeChecking)
(*in).DeepCopyInto(*out)
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidatingAdmissionPolicyStatus.
func (in *ValidatingAdmissionPolicyStatus) DeepCopy() *ValidatingAdmissionPolicyStatus {
if in == nil {
return nil
}
out := new(ValidatingAdmissionPolicyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Validation) DeepCopyInto(out *Validation) {
*out = *in

View File

@ -134,5 +134,26 @@
"valueExpression": "valueExpressionValue"
}
]
},
"status": {
"observedGeneration": 1,
"typeChecking": {
"expressionWarnings": [
{
"fieldRef": "fieldRefValue",
"warning": "warningValue"
}
]
},
"conditions": [
{
"type": "typeValue",
"status": "statusValue",
"observedGeneration": 3,
"lastTransitionTime": "2004-01-01T01:01:01Z",
"reason": "reasonValue",
"message": "messageValue"
}
]
}
}

View File

@ -87,3 +87,16 @@ spec:
message: messageValue
messageExpression: messageExpressionValue
reason: reasonValue
status:
conditions:
- lastTransitionTime: "2004-01-01T01:01:01Z"
message: messageValue
observedGeneration: 3
reason: reasonValue
status: statusValue
type: typeValue
observedGeneration: 1
typeChecking:
expressionWarnings:
- fieldRef: fieldRefValue
warning: warningValue

View File

@ -0,0 +1,132 @@
{
"kind": "ValidatingAdmissionPolicy",
"apiVersion": "admissionregistration.k8s.io/v1alpha1",
"metadata": {
"name": "nameValue",
"generateName": "generateNameValue",
"namespace": "namespaceValue",
"selfLink": "selfLinkValue",
"uid": "uidValue",
"resourceVersion": "resourceVersionValue",
"generation": 7,
"creationTimestamp": "2008-01-01T01:01:01Z",
"deletionTimestamp": "2009-01-01T01:01:01Z",
"deletionGracePeriodSeconds": 10,
"labels": {
"labelsKey": "labelsValue"
},
"annotations": {
"annotationsKey": "annotationsValue"
},
"ownerReferences": [
{
"apiVersion": "apiVersionValue",
"kind": "kindValue",
"name": "nameValue",
"uid": "uidValue",
"controller": true,
"blockOwnerDeletion": true
}
],
"finalizers": [
"finalizersValue"
],
"managedFields": [
{
"manager": "managerValue",
"operation": "operationValue",
"apiVersion": "apiVersionValue",
"time": "2004-01-01T01:01:01Z",
"fieldsType": "fieldsTypeValue",
"fieldsV1": {},
"subresource": "subresourceValue"
}
]
},
"spec": {
"paramKind": {
"apiVersion": "apiVersionValue",
"kind": "kindValue"
},
"matchConstraints": {
"namespaceSelector": {
"matchLabels": {
"matchLabelsKey": "matchLabelsValue"
},
"matchExpressions": [
{
"key": "keyValue",
"operator": "operatorValue",
"values": [
"valuesValue"
]
}
]
},
"objectSelector": {
"matchLabels": {
"matchLabelsKey": "matchLabelsValue"
},
"matchExpressions": [
{
"key": "keyValue",
"operator": "operatorValue",
"values": [
"valuesValue"
]
}
]
},
"resourceRules": [
{
"resourceNames": [
"resourceNamesValue"
],
"operations": [
"operationsValue"
],
"apiGroups": [
"apiGroupsValue"
],
"apiVersions": [
"apiVersionsValue"
],
"resources": [
"resourcesValue"
],
"scope": "scopeValue"
}
],
"excludeResourceRules": [
{
"resourceNames": [
"resourceNamesValue"
],
"operations": [
"operationsValue"
],
"apiGroups": [
"apiGroupsValue"
],
"apiVersions": [
"apiVersionsValue"
],
"resources": [
"resourcesValue"
],
"scope": "scopeValue"
}
],
"matchPolicy": "matchPolicyValue"
},
"validations": [
{
"expression": "expressionValue",
"message": "messageValue",
"reason": "reasonValue"
}
],
"failurePolicy": "failurePolicyValue"
},
"status": {}
}

View File

@ -0,0 +1,86 @@
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
annotations:
annotationsKey: annotationsValue
creationTimestamp: "2008-01-01T01:01:01Z"
deletionGracePeriodSeconds: 10
deletionTimestamp: "2009-01-01T01:01:01Z"
finalizers:
- finalizersValue
generateName: generateNameValue
generation: 7
labels:
labelsKey: labelsValue
managedFields:
- apiVersion: apiVersionValue
fieldsType: fieldsTypeValue
fieldsV1: {}
manager: managerValue
operation: operationValue
subresource: subresourceValue
time: "2004-01-01T01:01:01Z"
name: nameValue
namespace: namespaceValue
ownerReferences:
- apiVersion: apiVersionValue
blockOwnerDeletion: true
controller: true
kind: kindValue
name: nameValue
uid: uidValue
resourceVersion: resourceVersionValue
selfLink: selfLinkValue
uid: uidValue
spec:
failurePolicy: failurePolicyValue
matchConstraints:
excludeResourceRules:
- apiGroups:
- apiGroupsValue
apiVersions:
- apiVersionsValue
operations:
- operationsValue
resourceNames:
- resourceNamesValue
resources:
- resourcesValue
scope: scopeValue
matchPolicy: matchPolicyValue
namespaceSelector:
matchExpressions:
- key: keyValue
operator: operatorValue
values:
- valuesValue
matchLabels:
matchLabelsKey: matchLabelsValue
objectSelector:
matchExpressions:
- key: keyValue
operator: operatorValue
values:
- valuesValue
matchLabels:
matchLabelsKey: matchLabelsValue
resourceRules:
- apiGroups:
- apiGroupsValue
apiVersions:
- apiVersionsValue
operations:
- operationsValue
resourceNames:
- resourceNamesValue
resources:
- resourcesValue
scope: scopeValue
paramKind:
apiVersion: apiVersionValue
kind: kindValue
validations:
- expression: expressionValue
message: messageValue
reason: reasonValue
status: {}

View File

@ -20,6 +20,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
quota "k8s.io/apiserver/pkg/quota/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
@ -81,3 +82,10 @@ type WantsRESTMapper interface {
SetRESTMapper(meta.RESTMapper)
admission.InitializationValidator
}
// WantsSchemaResolver defines a function which sets the SchemaResolver for
// an admission plugin that needs it.
type WantsSchemaResolver interface {
SetSchemaResolver(resolver resolver.SchemaResolver)
admission.InitializationValidator
}

View File

@ -81,7 +81,7 @@ func buildRequiredVarsEnv() (*cel.Env, error) {
var propDecls []cel.EnvOption
reg := apiservercel.NewRegistry(baseEnv)
requestType := buildRequestType()
requestType := BuildRequestType()
rt, err := apiservercel.NewRuleTypes(requestType.TypeName(), requestType, reg)
if err != nil {
return nil, err
@ -134,11 +134,11 @@ func buildWithOptionalVarsEnvs(requiredVarsEnv *cel.Env) (envs, error) {
return envs, nil
}
// buildRequestType generates a DeclType for AdmissionRequest. This may be replaced with a utility that
// 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 {
func BuildRequestType() *apiservercel.DeclType {
field := func(name string, declType *apiservercel.DeclType, required bool) *apiservercel.DeclField {
return apiservercel.NewDeclField(name, declType, required, nil, nil)
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
"k8s.io/apiserver/pkg/features"
"k8s.io/client-go/dynamic"
"k8s.io/component-base/featuregate"
@ -73,6 +74,7 @@ type celAdmissionPlugin struct {
dynamicClient dynamic.Interface
stopCh <-chan struct{}
authorizer authorizer.Authorizer
schemaResolver resolver.SchemaResolver
}
var _ initializer.WantsExternalKubeInformerFactory = &celAdmissionPlugin{}
@ -81,7 +83,7 @@ var _ initializer.WantsRESTMapper = &celAdmissionPlugin{}
var _ initializer.WantsDynamicClient = &celAdmissionPlugin{}
var _ initializer.WantsDrainedNotification = &celAdmissionPlugin{}
var _ initializer.WantsAuthorizer = &celAdmissionPlugin{}
var _ initializer.WantsSchemaResolver = &celAdmissionPlugin{}
var _ admission.InitializationValidator = &celAdmissionPlugin{}
var _ admission.ValidationInterface = &celAdmissionPlugin{}
@ -115,6 +117,10 @@ func (c *celAdmissionPlugin) SetAuthorizer(authorizer authorizer.Authorizer) {
c.authorizer = authorizer
}
func (c *celAdmissionPlugin) SetSchemaResolver(resolver resolver.SchemaResolver) {
c.schemaResolver = resolver
}
func (c *celAdmissionPlugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
if featureGates.Enabled(features.ValidatingAdmissionPolicy) {
c.enabled = true
@ -148,7 +154,7 @@ func (c *celAdmissionPlugin) ValidateInitialization() error {
if c.authorizer == nil {
return errors.New("missing authorizer")
}
c.evaluator = NewAdmissionController(c.informerFactory, c.client, c.restMapper, c.dynamicClient, c.authorizer)
c.evaluator = NewAdmissionController(c.informerFactory, c.client, c.restMapper, c.schemaResolver /* (optional) */, c.dynamicClient, c.authorizer)
if err := c.evaluator.ValidateInitialization(); err != nil {
return err
}

View File

@ -43,6 +43,7 @@ import (
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
"k8s.io/apiserver/pkg/warning"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
@ -124,15 +125,21 @@ func NewAdmissionController(
informerFactory informers.SharedInformerFactory,
client kubernetes.Interface,
restMapper meta.RESTMapper,
schemaResolver resolver.SchemaResolver,
dynamicClient dynamic.Interface,
authz authorizer.Authorizer,
) CELPolicyEvaluator {
var typeChecker *TypeChecker
if schemaResolver != nil {
typeChecker = &TypeChecker{schemaResolver: schemaResolver, restMapper: restMapper}
}
return &celAdmissionController{
definitions: atomic.Value{},
policyController: newPolicyController(
restMapper,
client,
dynamicClient,
typeChecker,
cel.NewFilterCompiler(),
NewMatcher(matching.NewMatcher(informerFactory.Core().V1().Namespaces().Lister(), client)),
generic.NewInformer[*v1alpha1.ValidatingAdmissionPolicy](

View File

@ -27,8 +27,10 @@ import (
corev1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
celmetrics "k8s.io/apiserver/pkg/admission/cel"
"k8s.io/apiserver/pkg/admission/plugin/cel"
@ -59,6 +61,11 @@ type policyController struct {
newValidator
// The TypeCheck checks the policy's expressions for type errors.
// Type of params is defined in policy.Spec.ParamsKind
// Types of object are calculated from policy.Spec.MatchingConstraints
typeChecker *TypeChecker
// Lock which protects:
// - cachedPolicies
// - paramCRDControllers
@ -98,6 +105,7 @@ func newPolicyController(
restMapper meta.RESTMapper,
client kubernetes.Interface,
dynamicClient dynamic.Interface,
typeChecker *TypeChecker,
filterCompiler cel.FilterCompiler,
matcher Matcher,
policiesInformer generic.Informer[*v1alpha1.ValidatingAdmissionPolicy],
@ -107,6 +115,7 @@ func newPolicyController(
res := &policyController{}
*res = policyController{
filterCompiler: filterCompiler,
typeChecker: typeChecker,
definitionInfo: make(map[namespacedName]*definitionInfo),
bindingInfos: make(map[namespacedName]*bindingInfo),
paramsCRDControllers: make(map[v1alpha1.ParamKind]*paramInfo),
@ -168,7 +177,17 @@ func (c *policyController) HasSynced() bool {
func (c *policyController) reconcilePolicyDefinition(namespace, name string, definition *v1alpha1.ValidatingAdmissionPolicy) error {
c.mutex.Lock()
defer c.mutex.Unlock()
err := c.reconcilePolicyDefinitionSpec(namespace, name, definition)
if err != nil {
return err
}
if c.typeChecker != nil {
err = c.reconcilePolicyStatus(namespace, name, definition)
}
return err
}
func (c *policyController) reconcilePolicyDefinitionSpec(namespace, name string, definition *v1alpha1.ValidatingAdmissionPolicy) error {
c.cachedPolicies = nil // invalidate cachedPolicies
// Namespace for policydefinition is empty.
@ -423,6 +442,30 @@ func (c *policyController) reconcilePolicyBinding(namespace, name string, bindin
return nil
}
func (c *policyController) reconcilePolicyStatus(namespace, name string, definition *v1alpha1.ValidatingAdmissionPolicy) error {
if definition != nil && definition.Status.ObservedGeneration < definition.Generation {
st := c.calculatePolicyStatus(definition)
newDefinition := definition.DeepCopy()
newDefinition.Status = *st
_, err := c.client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().UpdateStatus(c.context, newDefinition, metav1.UpdateOptions{})
if err != nil {
// ignore error when the controller is not able to
// mutate the definition, and to avoid infinite requeue.
utilruntime.HandleError(err)
}
}
return nil
}
func (c *policyController) calculatePolicyStatus(definition *v1alpha1.ValidatingAdmissionPolicy) *v1alpha1.ValidatingAdmissionPolicyStatus {
expressionWarnings := c.typeChecker.Check(definition)
// modifying a deepcopy of the original status, preserving unrelated existing data
status := definition.Status.DeepCopy()
status.ObservedGeneration = definition.Generation
status.TypeChecking = &v1alpha1.TypeChecking{ExpressionWarnings: expressionWarnings}
return status
}
func (c *policyController) reconcileParams(namespace, name string, params runtime.Object) error {
// Do nothing.
// When we add informational type checking we will need to compile in the

View File

@ -0,0 +1,435 @@
/*
Copyright 2023 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 (
"errors"
"fmt"
"sort"
"strings"
"sync"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types/ref"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
apiservercel "k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/common"
"k8s.io/apiserver/pkg/cel/library"
"k8s.io/apiserver/pkg/cel/openapi"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
"k8s.io/klog/v2"
)
const maxTypesToCheck = 10
type TypeChecker struct {
schemaResolver resolver.SchemaResolver
restMapper meta.RESTMapper
}
type typeOverwrite struct {
object *apiservercel.DeclType
params *apiservercel.DeclType
}
// typeCheckingResult holds the issues found during type checking, any returned
// error, and the gvk that the type checking is performed against.
type typeCheckingResult struct {
gvk schema.GroupVersionKind
issues *cel.Issues
err error
}
// Check preforms the type check against the given policy, and format the result
// as []ExpressionWarning that is ready to be set in policy.Status
// The result is nil if type checking returns no warning.
// The policy object is NOT mutated. The caller should update Status accordingly
func (c *TypeChecker) Check(policy *v1alpha1.ValidatingAdmissionPolicy) []v1alpha1.ExpressionWarning {
exps := make([]string, 0, len(policy.Spec.Validations))
// check main validation expressions, located in spec.validations[*]
fieldRef := field.NewPath("spec", "validations")
for _, v := range policy.Spec.Validations {
exps = append(exps, v.Expression)
}
msgs := c.CheckExpressions(exps, policy.Spec.ParamKind != nil, policy)
var results []v1alpha1.ExpressionWarning // intentionally not setting capacity
for i, msg := range msgs {
if msg != "" {
results = append(results, v1alpha1.ExpressionWarning{
FieldRef: fieldRef.Index(i).Child("expression").String(),
Warning: msg,
})
}
}
return results
}
// CheckExpressions checks a set of compiled CEL programs against the GVKs defined in
// policy.Spec.MatchConstraints
// The result is a human-readable form that describe which expressions
// violate what types at what place. The indexes of the return []string
// matches these of the input expressions.
// TODO: It is much more useful to have machine-readable output and let the
// client format it. That requires an update to the KEP, probably in coming
// releases.
func (c *TypeChecker) CheckExpressions(expressions []string, hasParams bool, policy *v1alpha1.ValidatingAdmissionPolicy) []string {
var allWarnings []string
allGvks := c.typesToCheck(policy)
gvks := make([]schema.GroupVersionKind, 0, len(allGvks))
schemas := make([]common.Schema, 0, len(allGvks))
for _, gvk := range allGvks {
s, err := c.schemaResolver.ResolveSchema(gvk)
if err != nil {
// type checking errors MUST NOT alter the behavior of the policy
// even if an error occurs.
if !errors.Is(err, resolver.ErrSchemaNotFound) {
// Anything except ErrSchemaNotFound is an internal error
klog.ErrorS(err, "internal error: schema resolution failure", "gvk", gvk)
}
// skip if an unrecoverable error occurs.
continue
}
gvks = append(gvks, gvk)
schemas = append(schemas, &openapi.Schema{Schema: s})
}
paramsType := c.paramsType(policy)
paramsDeclType, err := c.declType(paramsType)
if err != nil {
if !errors.Is(err, resolver.ErrSchemaNotFound) {
klog.V(2).ErrorS(err, "cannot resolve schema for params", "gvk", paramsType)
}
paramsDeclType = nil
}
for _, exp := range expressions {
var results []typeCheckingResult
for i, gvk := range gvks {
s := schemas[i]
issues, err := c.checkExpression(exp, hasParams, typeOverwrite{
object: common.SchemaDeclType(s, true),
params: paramsDeclType,
})
// save even if no issues are found, for the sake of formatting.
results = append(results, typeCheckingResult{
gvk: gvk,
issues: issues,
err: err,
})
}
allWarnings = append(allWarnings, c.formatWarning(results))
}
return allWarnings
}
// formatWarning converts the resulting issues and possible error during
// type checking into a human-readable string
func (c *TypeChecker) formatWarning(results []typeCheckingResult) string {
var sb strings.Builder
for _, result := range results {
if result.issues == nil && result.err == nil {
continue
}
if result.err != nil {
sb.WriteString(fmt.Sprintf("%v: type checking error: %v\n", result.gvk, result.err))
} else {
sb.WriteString(fmt.Sprintf("%v: %s\n", result.gvk, result.issues))
}
}
return strings.TrimSuffix(sb.String(), "\n")
}
func (c *TypeChecker) declType(gvk schema.GroupVersionKind) (*apiservercel.DeclType, error) {
if gvk.Empty() {
return nil, nil
}
s, err := c.schemaResolver.ResolveSchema(gvk)
if err != nil {
return nil, err
}
return common.SchemaDeclType(&openapi.Schema{Schema: s}, true), nil
}
func (c *TypeChecker) paramsType(policy *v1alpha1.ValidatingAdmissionPolicy) schema.GroupVersionKind {
if policy.Spec.ParamKind == nil {
return schema.GroupVersionKind{}
}
gv, err := schema.ParseGroupVersion(policy.Spec.ParamKind.APIVersion)
if err != nil {
return schema.GroupVersionKind{}
}
return gv.WithKind(policy.Spec.ParamKind.Kind)
}
func (c *TypeChecker) checkExpression(expression string, hasParams bool, types typeOverwrite) (*cel.Issues, error) {
env, err := buildEnv(hasParams, types)
if err != nil {
return nil, err
}
// We cannot reuse an AST that is parsed by another env, so reparse it here.
// Compile = Parse + Check, we especially want the results of Check.
//
// Paradoxically, we discard the type-checked result and let the admission
// controller use the dynamic typed program.
// This is a compromise that is defined in the KEP. We can revisit this
// decision and expect a change with limited size.
_, issues := env.Compile(expression)
return issues, nil
}
// typesToCheck extracts a list of GVKs that needs type checking from the policy
// the result is sorted in the order of Group, Version, and Kind
func (c *TypeChecker) typesToCheck(p *v1alpha1.ValidatingAdmissionPolicy) []schema.GroupVersionKind {
gvks := sets.New[schema.GroupVersionKind]()
if p.Spec.MatchConstraints == nil || len(p.Spec.MatchConstraints.ResourceRules) == 0 {
return nil
}
for _, rule := range p.Spec.MatchConstraints.ResourceRules {
groups := extractGroups(&rule.Rule)
if len(groups) == 0 {
continue
}
versions := extractVersions(&rule.Rule)
if len(versions) == 0 {
continue
}
resources := extractResources(&rule.Rule)
if len(resources) == 0 {
continue
}
// sort GVRs so that the loop below provides
// consistent results.
sort.Strings(groups)
sort.Strings(versions)
sort.Strings(resources)
count := 0
for _, group := range groups {
for _, version := range versions {
for _, resource := range resources {
gvr := schema.GroupVersionResource{
Group: group,
Version: version,
Resource: resource,
}
resolved, err := c.restMapper.KindsFor(gvr)
if err != nil {
continue
}
for _, r := range resolved {
if !r.Empty() {
gvks.Insert(r)
count++
// early return if maximum number of types are already
// collected
if count == maxTypesToCheck {
if gvks.Len() == 0 {
return nil
}
return sortGVKList(gvks.UnsortedList())
}
}
}
}
}
}
}
if gvks.Len() == 0 {
return nil
}
return sortGVKList(gvks.UnsortedList())
}
func extractGroups(rule *v1alpha1.Rule) []string {
groups := make([]string, 0, len(rule.APIGroups))
for _, group := range rule.APIGroups {
// give up if wildcard
if strings.ContainsAny(group, "*") {
return nil
}
groups = append(groups, group)
}
return groups
}
func extractVersions(rule *v1alpha1.Rule) []string {
versions := make([]string, 0, len(rule.APIVersions))
for _, version := range rule.APIVersions {
if strings.ContainsAny(version, "*") {
return nil
}
versions = append(versions, version)
}
return versions
}
func extractResources(rule *v1alpha1.Rule) []string {
resources := make([]string, 0, len(rule.Resources))
for _, resource := range rule.Resources {
// skip wildcard and subresources
if strings.ContainsAny(resource, "*/") {
continue
}
resources = append(resources, resource)
}
return resources
}
// sortGVKList sorts the list by Group, Version, and Kind
// returns the list itself.
func sortGVKList(list []schema.GroupVersionKind) []schema.GroupVersionKind {
sort.Slice(list, func(i, j int) bool {
if g := strings.Compare(list[i].Group, list[j].Group); g != 0 {
return g < 0
}
if v := strings.Compare(list[i].Version, list[j].Version); v != 0 {
return v < 0
}
return strings.Compare(list[i].Kind, list[j].Kind) < 0
})
return list
}
func buildEnv(hasParams bool, types typeOverwrite) (*cel.Env, error) {
baseEnv, err := getBaseEnv()
if err != nil {
return nil, err
}
reg := apiservercel.NewRegistry(baseEnv)
requestType := plugincel.BuildRequestType()
var varOpts []cel.EnvOption
var rts []*apiservercel.RuleTypes
// request, hand-crafted type
rt, opts, err := createRuleTypesAndOptions(reg, requestType, plugincel.RequestVarName)
if err != nil {
return nil, err
}
rts = append(rts, rt)
varOpts = append(varOpts, opts...)
// object and oldObject, same type, type(s) resolved from constraints
rt, opts, err = createRuleTypesAndOptions(reg, types.object, plugincel.ObjectVarName, plugincel.OldObjectVarName)
if err != nil {
return nil, err
}
rts = append(rts, rt)
varOpts = append(varOpts, opts...)
// params, defined by ParamKind
if hasParams {
rt, opts, err := createRuleTypesAndOptions(reg, types.params, plugincel.ParamsVarName)
if err != nil {
return nil, err
}
rts = append(rts, rt)
varOpts = append(varOpts, opts...)
}
opts, err = ruleTypesOpts(rts, baseEnv.TypeProvider())
if err != nil {
return nil, err
}
opts = append(opts, varOpts...) // add variables after ruleTypes.
env, err := baseEnv.Extend(opts...)
if err != nil {
return nil, err
}
return env, nil
}
// createRuleTypeAndOptions creates the cel RuleTypes and a slice of EnvOption
// that can be used for creating a CEL env containing variables of declType.
// declType can be nil, in which case the variables will be of DynType.
func createRuleTypesAndOptions(registry *apiservercel.Registry, declType *apiservercel.DeclType, variables ...string) (*apiservercel.RuleTypes, []cel.EnvOption, error) {
opts := make([]cel.EnvOption, 0, len(variables))
// untyped, use DynType
if declType == nil {
for _, v := range variables {
opts = append(opts, cel.Variable(v, cel.DynType))
}
return nil, opts, nil
}
// create a RuleType for the given type
rt, err := apiservercel.NewRuleTypes(declType.TypeName(), declType, registry)
if err != nil {
return nil, nil, err
}
if rt == nil {
return nil, nil, nil
}
for _, v := range variables {
opts = append(opts, cel.Variable(v, declType.CelType()))
}
return rt, opts, nil
}
func ruleTypesOpts(ruleTypes []*apiservercel.RuleTypes, underlyingTypeProvider ref.TypeProvider) ([]cel.EnvOption, error) {
var providers []ref.TypeProvider // may be unused, too small to matter
var adapters []ref.TypeAdapter
for _, rt := range ruleTypes {
if rt != nil {
withTP, err := rt.WithTypeProvider(underlyingTypeProvider)
if err != nil {
return nil, err
}
providers = append(providers, withTP)
adapters = append(adapters, withTP)
}
}
var tp ref.TypeProvider
var ta ref.TypeAdapter
switch len(providers) {
case 0:
return nil, nil
case 1:
tp = providers[0]
ta = adapters[0]
default:
tp = &apiservercel.CompositedTypeProvider{Providers: providers}
ta = &apiservercel.CompositedTypeAdapter{Adapters: adapters}
}
return []cel.EnvOption{cel.CustomTypeProvider(tp), cel.CustomTypeAdapter(ta)}, nil
}
func getBaseEnv() (*cel.Env, error) {
typeCheckingBaseEnvInit.Do(func() {
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...)
typeCheckingBaseEnv, typeCheckingBaseEnvError = cel.NewEnv(opts...)
})
return typeCheckingBaseEnv, typeCheckingBaseEnvError
}
var typeCheckingBaseEnv *cel.Env
var typeCheckingBaseEnvError error
var typeCheckingBaseEnvInit sync.Once

View File

@ -0,0 +1,409 @@
/*
Copyright 2023 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 (
"fmt"
"reflect"
"strings"
"testing"
"k8s.io/api/admissionregistration/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
"k8s.io/kube-openapi/pkg/validation/spec"
)
func TestExtractTypeNames(t *testing.T) {
for _, tc := range []struct {
name string
policy *v1alpha1.ValidatingAdmissionPolicy
expected []schema.GroupVersionKind // must be sorted
}{
{
name: "empty",
policy: &v1alpha1.ValidatingAdmissionPolicy{},
expected: nil,
},
{
name: "specific",
policy: &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
},
}},
}},
expected: []schema.GroupVersionKind{{
Group: "apps",
Version: "v1",
Kind: "Deployment",
}},
},
{
name: "multiple",
policy: &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
}, {
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{""},
APIVersions: []string{"v1"},
Resources: []string{"pods"},
},
},
},
}},
}},
expected: []schema.GroupVersionKind{
{
Version: "v1",
Kind: "Pod",
}, {
Group: "apps",
Version: "v1",
Kind: "Deployment",
}},
},
{
name: "all resources",
policy: &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"*"},
},
},
},
}},
}},
expected: nil,
},
{
name: "sub resources",
policy: &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"pods/*"},
},
},
},
}},
}},
expected: nil,
},
{
name: "mixtures",
policy: &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
},
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"*"},
Resources: []string{"deployments"},
},
},
},
}},
}},
expected: []schema.GroupVersionKind{{
Group: "apps",
Version: "v1",
Kind: "Deployment",
}},
},
} {
t.Run(tc.name, func(t *testing.T) {
typeChecker := buildTypeChecker(nil)
got := typeChecker.typesToCheck(tc.policy)
if !reflect.DeepEqual(tc.expected, got) {
t.Errorf("expected %v but got %v", tc.expected, got)
}
})
}
}
func TestTypeCheck(t *testing.T) {
deploymentPolicy := &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
Validations: []v1alpha1.Validation{
{
Expression: "object.foo == 'bar'",
},
},
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
},
}},
}}
multiExpressionPolicy := &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
Validations: []v1alpha1.Validation{
{
Expression: "object.foo == 'bar'",
},
{
Expression: "object.bar == 'foo'",
},
},
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
},
}},
}}
paramsRefPolicy := &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
ParamKind: &v1alpha1.ParamKind{
APIVersion: "v1",
Kind: "DoesNotMatter",
},
Validations: []v1alpha1.Validation{
{
Expression: "object.foo == params.bar",
},
},
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
},
}},
}}
for _, tc := range []struct {
name string
schemaToReturn *spec.Schema
policy *v1alpha1.ValidatingAdmissionPolicy
assertions []assertionFunc
}{
{
name: "empty",
policy: &v1alpha1.ValidatingAdmissionPolicy{},
assertions: []assertionFunc{toBeEmpty},
},
{
name: "unresolved schema",
policy: deploymentPolicy,
schemaToReturn: nil,
assertions: []assertionFunc{toBeEmpty},
},
{
name: "passed check",
policy: deploymentPolicy,
schemaToReturn: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"foo": *spec.StringProperty(),
},
},
},
assertions: []assertionFunc{toBeEmpty},
},
{
name: "undefined field",
policy: deploymentPolicy,
schemaToReturn: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"bar": *spec.StringProperty(),
},
},
},
assertions: []assertionFunc{
toHaveFieldRef("spec.validations[0].expression"),
toContain(`undefined field 'foo'`),
},
},
{
name: "field type mismatch",
policy: deploymentPolicy,
schemaToReturn: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"foo": *spec.Int64Property(),
},
},
},
assertions: []assertionFunc{
toHaveFieldRef("spec.validations[0].expression"),
toContain(`found no matching overload`),
},
},
{
name: "params",
policy: paramsRefPolicy,
schemaToReturn: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"foo": *spec.StringProperty(),
},
},
},
assertions: []assertionFunc{
toHaveFieldRef("spec.validations[0].expression"),
toContain(`undefined field 'bar'`),
},
},
{
name: "multiple expressions",
policy: multiExpressionPolicy,
schemaToReturn: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"foo": *spec.StringProperty(),
},
},
},
assertions: []assertionFunc{
toHaveFieldRef("spec.validations[1].expression"), // expressions[0] is okay, [1] is wrong
toHaveLengthOf(1),
},
},
} {
t.Run(tc.name, func(t *testing.T) {
typeChecker := buildTypeChecker(tc.schemaToReturn)
warnings := typeChecker.Check(tc.policy)
for _, a := range tc.assertions {
a(warnings, t)
}
})
}
}
func buildTypeChecker(schemaToReturn *spec.Schema) *TypeChecker {
restMapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{
{
Group: "",
Version: "v1",
},
})
restMapper.Add(must3(scheme.ObjectKinds(&corev1.Pod{}))[0], meta.RESTScopeRoot)
restMapper.Add(must3(scheme.ObjectKinds(&appsv1.Deployment{}))[0], meta.RESTScopeRoot)
return &TypeChecker{
schemaResolver: &fakeSchemaResolver{schemaToReturn: schemaToReturn},
restMapper: restMapper,
}
}
type fakeSchemaResolver struct {
schemaToReturn *spec.Schema
}
func (r *fakeSchemaResolver) ResolveSchema(gvk schema.GroupVersionKind) (*spec.Schema, error) {
if r.schemaToReturn == nil {
return nil, fmt.Errorf("cannot resolve for %v: %w", gvk, resolver.ErrSchemaNotFound)
}
return r.schemaToReturn, nil
}
func toBeEmpty(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
if len(warnings) != 0 {
t.Fatalf("expected empty but got %v", warnings)
}
}
func toContain(substring string) func(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
if len(warnings) == 0 {
t.Errorf("expected containing %q but got empty", substring)
}
for i, w := range warnings {
if !strings.Contains(w.Warning, substring) {
t.Errorf("warning %d does not contain %q, got %v", i, substring, w)
}
}
}
}
func toHaveLengthOf(expected int) func(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
got := len(warnings)
if expected != got {
t.Errorf("expect warnings to have length of %d, but got %d", expected, got)
}
}
}
func toHaveFieldRef(paths ...string) func(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []v1alpha1.ExpressionWarning, t *testing.T) {
if len(paths) != len(warnings) {
t.Errorf("expect warnings to have length of %d, but got %d", len(paths), len(warnings))
}
for i := range paths {
if paths[i] != warnings[i].FieldRef {
t.Errorf("wrong fieldRef at %d, expected %q but got %q", i, paths[i], warnings[i].FieldRef)
}
}
}
}
type assertionFunc func(warnings []v1alpha1.ExpressionWarning, t *testing.T)

View File

@ -0,0 +1,48 @@
/*
Copyright 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.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1alpha1
// ExpressionWarningApplyConfiguration represents an declarative configuration of the ExpressionWarning type for use
// with apply.
type ExpressionWarningApplyConfiguration struct {
FieldRef *string `json:"fieldRef,omitempty"`
Warning *string `json:"warning,omitempty"`
}
// ExpressionWarningApplyConfiguration constructs an declarative configuration of the ExpressionWarning type for use with
// apply.
func ExpressionWarning() *ExpressionWarningApplyConfiguration {
return &ExpressionWarningApplyConfiguration{}
}
// WithFieldRef sets the FieldRef field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the FieldRef field is set to the value of the last call.
func (b *ExpressionWarningApplyConfiguration) WithFieldRef(value string) *ExpressionWarningApplyConfiguration {
b.FieldRef = &value
return b
}
// WithWarning sets the Warning field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Warning field is set to the value of the last call.
func (b *ExpressionWarningApplyConfiguration) WithWarning(value string) *ExpressionWarningApplyConfiguration {
b.Warning = &value
return b
}

View File

@ -0,0 +1,44 @@
/*
Copyright 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.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1alpha1
// TypeCheckingApplyConfiguration represents an declarative configuration of the TypeChecking type for use
// with apply.
type TypeCheckingApplyConfiguration struct {
ExpressionWarnings []ExpressionWarningApplyConfiguration `json:"expressionWarnings,omitempty"`
}
// TypeCheckingApplyConfiguration constructs an declarative configuration of the TypeChecking type for use with
// apply.
func TypeChecking() *TypeCheckingApplyConfiguration {
return &TypeCheckingApplyConfiguration{}
}
// WithExpressionWarnings adds the given value to the ExpressionWarnings field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the ExpressionWarnings field.
func (b *TypeCheckingApplyConfiguration) WithExpressionWarnings(values ...*ExpressionWarningApplyConfiguration) *TypeCheckingApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithExpressionWarnings")
}
b.ExpressionWarnings = append(b.ExpressionWarnings, *values[i])
}
return b
}

View File

@ -33,6 +33,7 @@ type ValidatingAdmissionPolicyApplyConfiguration struct {
v1.TypeMetaApplyConfiguration `json:",inline"`
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
Spec *ValidatingAdmissionPolicySpecApplyConfiguration `json:"spec,omitempty"`
Status *ValidatingAdmissionPolicyStatusApplyConfiguration `json:"status,omitempty"`
}
// ValidatingAdmissionPolicy constructs an declarative configuration of the ValidatingAdmissionPolicy type for use with
@ -245,3 +246,11 @@ func (b *ValidatingAdmissionPolicyApplyConfiguration) WithSpec(value *Validating
b.Spec = value
return b
}
// WithStatus sets the Status field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Status field is set to the value of the last call.
func (b *ValidatingAdmissionPolicyApplyConfiguration) WithStatus(value *ValidatingAdmissionPolicyStatusApplyConfiguration) *ValidatingAdmissionPolicyApplyConfiguration {
b.Status = value
return b
}

View File

@ -0,0 +1,66 @@
/*
Copyright 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.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
)
// ValidatingAdmissionPolicyStatusApplyConfiguration represents an declarative configuration of the ValidatingAdmissionPolicyStatus type for use
// with apply.
type ValidatingAdmissionPolicyStatusApplyConfiguration struct {
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
TypeChecking *TypeCheckingApplyConfiguration `json:"typeChecking,omitempty"`
Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"`
}
// ValidatingAdmissionPolicyStatusApplyConfiguration constructs an declarative configuration of the ValidatingAdmissionPolicyStatus type for use with
// apply.
func ValidatingAdmissionPolicyStatus() *ValidatingAdmissionPolicyStatusApplyConfiguration {
return &ValidatingAdmissionPolicyStatusApplyConfiguration{}
}
// WithObservedGeneration sets the ObservedGeneration field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the ObservedGeneration field is set to the value of the last call.
func (b *ValidatingAdmissionPolicyStatusApplyConfiguration) WithObservedGeneration(value int64) *ValidatingAdmissionPolicyStatusApplyConfiguration {
b.ObservedGeneration = &value
return b
}
// WithTypeChecking sets the TypeChecking field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the TypeChecking field is set to the value of the last call.
func (b *ValidatingAdmissionPolicyStatusApplyConfiguration) WithTypeChecking(value *TypeCheckingApplyConfiguration) *ValidatingAdmissionPolicyStatusApplyConfiguration {
b.TypeChecking = value
return b
}
// WithConditions adds the given value to the Conditions field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Conditions field.
func (b *ValidatingAdmissionPolicyStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *ValidatingAdmissionPolicyStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithConditions")
}
b.Conditions = append(b.Conditions, *values[i])
}
return b
}

View File

@ -236,6 +236,17 @@ var schemaYAML = typed.YAMLObject(`types:
type:
scalar: string
default: ""
- name: io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning
map:
fields:
- name: fieldRef
type:
scalar: string
default: ""
- name: warning
type:
scalar: string
default: ""
- name: io.k8s.api.admissionregistration.v1alpha1.MatchResources
map:
fields:
@ -318,6 +329,15 @@ var schemaYAML = typed.YAMLObject(`types:
type:
scalar: string
elementRelationship: atomic
- name: io.k8s.api.admissionregistration.v1alpha1.TypeChecking
map:
fields:
- name: expressionWarnings
type:
list:
elementType:
namedType: io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning
elementRelationship: atomic
- name: io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy
map:
fields:
@ -335,6 +355,10 @@ var schemaYAML = typed.YAMLObject(`types:
type:
namedType: io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicySpec
default: {}
- name: status
type:
namedType: io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus
default: {}
- name: io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBinding
map:
fields:
@ -394,6 +418,23 @@ var schemaYAML = typed.YAMLObject(`types:
elementType:
namedType: io.k8s.api.admissionregistration.v1alpha1.Validation
elementRelationship: atomic
- name: io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus
map:
fields:
- name: conditions
type:
list:
elementType:
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Condition
elementRelationship: associative
keys:
- type
- name: observedGeneration
type:
scalar: numeric
- name: typeChecking
type:
namedType: io.k8s.api.admissionregistration.v1alpha1.TypeChecking
- name: io.k8s.api.admissionregistration.v1alpha1.Validation
map:
fields:

View File

@ -141,6 +141,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
// Group=admissionregistration.k8s.io, Version=v1alpha1
case v1alpha1.SchemeGroupVersion.WithKind("AuditAnnotation"):
return &admissionregistrationv1alpha1.AuditAnnotationApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ExpressionWarning"):
return &admissionregistrationv1alpha1.ExpressionWarningApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("MatchResources"):
return &admissionregistrationv1alpha1.MatchResourcesApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("NamedRuleWithOperations"):
@ -149,6 +151,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &admissionregistrationv1alpha1.ParamKindApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ParamRef"):
return &admissionregistrationv1alpha1.ParamRefApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("TypeChecking"):
return &admissionregistrationv1alpha1.TypeCheckingApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicy"):
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicyApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicyBinding"):
@ -157,6 +161,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicyBindingSpecApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicySpec"):
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicySpecApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicyStatus"):
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicyStatusApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("Validation"):
return &admissionregistrationv1alpha1.ValidationApplyConfiguration{}

View File

@ -98,6 +98,17 @@ func (c *FakeValidatingAdmissionPolicies) Update(ctx context.Context, validating
return obj.(*v1alpha1.ValidatingAdmissionPolicy), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeValidatingAdmissionPolicies) UpdateStatus(ctx context.Context, validatingAdmissionPolicy *v1alpha1.ValidatingAdmissionPolicy, opts v1.UpdateOptions) (*v1alpha1.ValidatingAdmissionPolicy, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(validatingadmissionpoliciesResource, "status", validatingAdmissionPolicy), &v1alpha1.ValidatingAdmissionPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ValidatingAdmissionPolicy), err
}
// Delete takes name of the validatingAdmissionPolicy and deletes it. Returns an error if one occurs.
func (c *FakeValidatingAdmissionPolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
@ -143,3 +154,25 @@ func (c *FakeValidatingAdmissionPolicies) Apply(ctx context.Context, validatingA
}
return obj.(*v1alpha1.ValidatingAdmissionPolicy), err
}
// ApplyStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
func (c *FakeValidatingAdmissionPolicies) ApplyStatus(ctx context.Context, validatingAdmissionPolicy *admissionregistrationv1alpha1.ValidatingAdmissionPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.ValidatingAdmissionPolicy, err error) {
if validatingAdmissionPolicy == nil {
return nil, fmt.Errorf("validatingAdmissionPolicy provided to Apply must not be nil")
}
data, err := json.Marshal(validatingAdmissionPolicy)
if err != nil {
return nil, err
}
name := validatingAdmissionPolicy.Name
if name == nil {
return nil, fmt.Errorf("validatingAdmissionPolicy.Name must be provided to Apply")
}
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceAction(validatingadmissionpoliciesResource, *name, types.ApplyPatchType, data, "status"), &v1alpha1.ValidatingAdmissionPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ValidatingAdmissionPolicy), err
}

View File

@ -43,6 +43,7 @@ type ValidatingAdmissionPoliciesGetter interface {
type ValidatingAdmissionPolicyInterface interface {
Create(ctx context.Context, validatingAdmissionPolicy *v1alpha1.ValidatingAdmissionPolicy, opts v1.CreateOptions) (*v1alpha1.ValidatingAdmissionPolicy, error)
Update(ctx context.Context, validatingAdmissionPolicy *v1alpha1.ValidatingAdmissionPolicy, opts v1.UpdateOptions) (*v1alpha1.ValidatingAdmissionPolicy, error)
UpdateStatus(ctx context.Context, validatingAdmissionPolicy *v1alpha1.ValidatingAdmissionPolicy, opts v1.UpdateOptions) (*v1alpha1.ValidatingAdmissionPolicy, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ValidatingAdmissionPolicy, error)
@ -50,6 +51,7 @@ type ValidatingAdmissionPolicyInterface interface {
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ValidatingAdmissionPolicy, err error)
Apply(ctx context.Context, validatingAdmissionPolicy *admissionregistrationv1alpha1.ValidatingAdmissionPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.ValidatingAdmissionPolicy, err error)
ApplyStatus(ctx context.Context, validatingAdmissionPolicy *admissionregistrationv1alpha1.ValidatingAdmissionPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.ValidatingAdmissionPolicy, err error)
ValidatingAdmissionPolicyExpansion
}
@ -132,6 +134,21 @@ func (c *validatingAdmissionPolicies) Update(ctx context.Context, validatingAdmi
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *validatingAdmissionPolicies) UpdateStatus(ctx context.Context, validatingAdmissionPolicy *v1alpha1.ValidatingAdmissionPolicy, opts v1.UpdateOptions) (result *v1alpha1.ValidatingAdmissionPolicy, err error) {
result = &v1alpha1.ValidatingAdmissionPolicy{}
err = c.client.Put().
Resource("validatingadmissionpolicies").
Name(validatingAdmissionPolicy.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(validatingAdmissionPolicy).
Do(ctx).
Into(result)
return
}
// Delete takes name of the validatingAdmissionPolicy and deletes it. Returns an error if one occurs.
func (c *validatingAdmissionPolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
@ -195,3 +212,32 @@ func (c *validatingAdmissionPolicies) Apply(ctx context.Context, validatingAdmis
Into(result)
return
}
// ApplyStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
func (c *validatingAdmissionPolicies) ApplyStatus(ctx context.Context, validatingAdmissionPolicy *admissionregistrationv1alpha1.ValidatingAdmissionPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.ValidatingAdmissionPolicy, err error) {
if validatingAdmissionPolicy == nil {
return nil, fmt.Errorf("validatingAdmissionPolicy provided to Apply must not be nil")
}
patchOpts := opts.ToPatchOptions()
data, err := json.Marshal(validatingAdmissionPolicy)
if err != nil {
return nil, err
}
name := validatingAdmissionPolicy.Name
if name == nil {
return nil, fmt.Errorf("validatingAdmissionPolicy.Name must be provided to Apply")
}
result = &v1alpha1.ValidatingAdmissionPolicy{}
err = c.client.Patch(types.ApplyPatchType).
Resource("validatingadmissionpolicies").
Name(*name).
SubResource("status").
VersionedParams(&patchOpts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View File

@ -24,6 +24,7 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
@ -56,6 +57,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect

View File

@ -48,6 +48,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -250,6 +252,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View File

@ -20,6 +20,7 @@ require (
require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
@ -52,6 +53,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

View File

@ -45,6 +45,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -245,6 +247,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View File

@ -28,6 +28,7 @@ require (
require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
@ -56,6 +57,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

View File

@ -46,6 +46,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -247,6 +249,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View File

@ -23,6 +23,7 @@ require (
require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
@ -54,6 +55,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

View File

@ -46,6 +46,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -247,6 +249,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View File

@ -20,6 +20,7 @@ require (
require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
@ -51,6 +52,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

View File

@ -46,6 +46,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -247,6 +249,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View File

@ -143,6 +143,7 @@ var (
gvr("admissionregistration.k8s.io", "v1", "mutatingwebhookconfigurations"): true,
gvr("admissionregistration.k8s.io", "v1", "validatingwebhookconfigurations"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies/status"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicybindings"): true,
}

View File

@ -67,6 +67,8 @@ var resetFieldsStatusData = map[schema.GroupVersionResource]string{
gvr("resource.k8s.io", "v1alpha1", "podschedulings"): `{"status": {"resourceClaims": [{"name": "my-claim", "unsuitableNodes": ["node2"]}]}}`, // Not really a conflict with status_test.go: Apply just stores both nodes. Conflict testing therefore gets disabled for podschedulings.
gvr("resource.k8s.io", "v1alpha1", "resourceclaims"): `{"status": {"driverName": "other.example.com"}}`,
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`,
// standard for []metav1.Condition
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
}
// resetFieldsStatusDefault conflicts with statusDefault
@ -151,6 +153,7 @@ var resetFieldsSpecData = map[schema.GroupVersionResource]string{
gvr("resource.k8s.io", "v1alpha1", "resourceclaims"): `{"spec": {"resourceClassName": "class2name"}}`, // ResourceClassName is immutable, but that doesn't matter for the test.
gvr("resource.k8s.io", "v1alpha1", "resourceclaimtemplates"): `{"spec": {"spec": {"resourceClassName": "class2name"}}}`,
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{}`,
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`,
}
// TestResetFields makes sure that fieldManager does not own fields reset by the storage strategy.

View File

@ -57,6 +57,8 @@ var statusData = map[schema.GroupVersionResource]string{
gvr("resource.k8s.io", "v1alpha1", "podschedulings"): `{"status": {"resourceClaims": [{"name": "my-claim", "unsuitableNodes": ["node1"]}]}}`,
gvr("resource.k8s.io", "v1alpha1", "resourceclaims"): `{"status": {"driverName": "example.com"}}`,
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`,
// standard for []metav1.Condition
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
}
const statusDefault = `{"status": {"conditions": [{"type": "MyStatus", "status":"True"}]}}`

View File

@ -751,10 +751,13 @@ func Test_PolicyExemption(t *testing.T) {
}
// validate that operations to ValidatingAdmissionPolicy are exempt from an existing policy that catches all resources
policyCopy := policy.DeepCopy()
policy, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Get(context.TODO(), policy.Name, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
ignoreFailurePolicy := admissionregistrationv1alpha1.Ignore
policyCopy.Spec.FailurePolicy = &ignoreFailurePolicy
_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Update(context.TODO(), policyCopy, metav1.UpdateOptions{})
policy.Spec.FailurePolicy = &ignoreFailurePolicy
_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{})
if err != nil {
t.Error(err)
}
@ -866,9 +869,12 @@ func Test_ValidatingAdmissionPolicy_UpdateParamKind(t *testing.T) {
APIVersion: "v1",
Kind: "Secret",
}
policyCopy := policy.DeepCopy()
policyCopy.Spec.ParamKind = paramKind
_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Update(context.TODO(), policyCopy, metav1.UpdateOptions{})
policy, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Get(context.TODO(), policy.Name, metav1.GetOptions{})
if err != nil {
t.Error(err)
}
policy.Spec.ParamKind = paramKind
_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{})
if err != nil {
t.Error(err)
}
@ -2611,6 +2617,26 @@ func withPolicyExistsLabels(labels []string, policy *admissionregistrationv1alph
return policy
}
func withGVRMatch(groups []string, versions []string, resources []string, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
policy.Spec.MatchConstraints = &admissionregistrationv1alpha1.MatchResources{
ResourceRules: []admissionregistrationv1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistrationv1alpha1.RuleWithOperations{
Operations: []admissionregistrationv1.OperationType{
"*",
},
Rule: admissionregistrationv1.Rule{
APIGroups: groups,
APIVersions: versions,
Resources: resources,
},
},
},
},
}
return policy
}
func withValidations(validations []admissionregistrationv1alpha1.Validation, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
policy.Spec.Validations = validations
return policy
@ -2885,3 +2911,114 @@ rules:
resources: ["configmaps"]
`
)
func TestValidatingAdmissionPolicyTypeChecking(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
server, err := apiservertesting.StartTestServer(t, nil, []string{
"--enable-admission-plugins", "ValidatingAdmissionPolicy",
}, framework.SharedEtcd())
if err != nil {
t.Fatal(err)
}
defer server.TearDownFn()
config := server.ClientConfig
client, err := clientset.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
name string
policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy
assertFieldRef func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) // warning.fieldRef
assertWarnings func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) // warning.warning
}{
{
name: "deployment with correct expression",
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1alpha1.Validation{
{
Expression: "object.spec.replicas > 1",
},
}, makePolicy("replicated-deployment"))),
assertFieldRef: toHasLengthOf(0),
assertWarnings: toHasLengthOf(0),
},
{
name: "deployment with type confusion",
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1alpha1.Validation{
{
Expression: "object.spec.replicas < 100", // this one passes
},
{
Expression: "object.spec.replicas > '1'", // '1' should be int
},
}, makePolicy("confused-deployment"))),
assertFieldRef: toBe("spec.validations[1].expression"),
assertWarnings: toHasSubstring(`found no matching overload for '_>_' applied to '(int, string)'`),
},
} {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
policy, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(ctx, tc.policy, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
defer client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Delete(context.Background(), policy.Name, metav1.DeleteOptions{})
err = wait.PollImmediateWithContext(ctx, time.Second, time.Minute, func(ctx context.Context) (done bool, err error) {
name := policy.Name
// wait until the typeChecking is set, which means the type checking
// is complete.
updated, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Get(ctx, name, metav1.GetOptions{})
if err != nil {
return false, err
}
if updated.Status.TypeChecking != nil {
policy = updated
return true, nil
}
return false, nil
})
if err != nil {
t.Fatal(err)
}
tc.assertFieldRef(policy.Status.TypeChecking.ExpressionWarnings, t)
tc.assertWarnings(policy.Status.TypeChecking.ExpressionWarnings, t)
})
}
}
func toBe(expected ...string) func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
if len(expected) != len(warnings) {
t.Fatalf("mismatched length, expect %d, got %d", len(expected), len(warnings))
}
for i := range expected {
if expected[i] != warnings[i].FieldRef {
t.Errorf("expected %q but got %q", expected[i], warnings[i].FieldRef)
}
}
}
}
func toHasSubstring(substrings ...string) func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
if len(substrings) != len(warnings) {
t.Fatalf("mismatched length, expect %d, got %d", len(substrings), len(warnings))
}
for i := range substrings {
if !strings.Contains(warnings[i].Warning, substrings[i]) {
t.Errorf("missing expected substring %q in %v", substrings[i], warnings[i])
}
}
}
}
func toHasLengthOf(n int) func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
return func(warnings []admissionregistrationv1alpha1.ExpressionWarning, t *testing.T) {
if n != len(warnings) {
t.Fatalf("mismatched length, expect %d, got %d", n, len(warnings))
}
}
}