From 705ced765d45f395367f331cce2d0edb685ef563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Thu, 6 Mar 2025 17:31:14 +0100 Subject: [PATCH] chore: add policy api unit tests (#12315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- ...c_types.go => imageverification_policy.go} | 64 +++ ...mageverification_policy_spec_types_test.go | 169 ------- .../v1alpha1/imageverification_policy_test.go | 422 ++++++++++++++++++ .../imageverification_policy_types.go | 69 --- .../imageverification_policy_types_test.go | 44 -- .../v1alpha1/validating_policy.go | 167 +++++++ .../v1alpha1/validating_policy_test.go | 114 ++++- .../v1alpha1/validating_spec_types.go | 172 ------- .../v1alpha1/validating_spec_types_test.go | 120 ----- hack/api-group-resources/main.go | 2 - 10 files changed, 766 insertions(+), 577 deletions(-) rename api/policies.kyverno.io/v1alpha1/{imageverification_policy_spec_types.go => imageverification_policy.go} (87%) delete mode 100644 api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types_test.go create mode 100644 api/policies.kyverno.io/v1alpha1/imageverification_policy_test.go delete mode 100644 api/policies.kyverno.io/v1alpha1/imageverification_policy_types.go delete mode 100644 api/policies.kyverno.io/v1alpha1/imageverification_policy_types_test.go delete mode 100644 api/policies.kyverno.io/v1alpha1/validating_spec_types.go delete mode 100644 api/policies.kyverno.io/v1alpha1/validating_spec_types_test.go diff --git a/api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types.go b/api/policies.kyverno.io/v1alpha1/imageverification_policy.go similarity index 87% rename from api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types.go rename to api/policies.kyverno.io/v1alpha1/imageverification_policy.go index 663fd482f1..0d083c9971 100644 --- a/api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types.go +++ b/api/policies.kyverno.io/v1alpha1/imageverification_policy.go @@ -3,8 +3,72 @@ package v1alpha1 import ( admissionregistrationv1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// +genclient +// +genclient:nonNamespaced +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=imageverificationpolicies,scope="Cluster",shortName=ivpol,categories=kyverno +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type ImageVerificationPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec ImageVerificationPolicySpec `json:"spec"` + // Status contains policy runtime data. + // +optional + Status PolicyStatus `json:"status,omitempty"` +} + +func (s *ImageVerificationPolicy) GetMatchConstraints() admissionregistrationv1.MatchResources { + if s.Spec.MatchConstraints == nil { + return admissionregistrationv1.MatchResources{} + } + return *s.Spec.MatchConstraints +} + +func (s *ImageVerificationPolicy) GetMatchConditions() []admissionregistrationv1.MatchCondition { + return s.Spec.MatchConditions +} + +func (s *ImageVerificationPolicy) GetWebhookConfiguration() *WebhookConfiguration { + return s.Spec.WebhookConfiguration +} + +func (s *ImageVerificationPolicy) GetFailurePolicy() admissionregistrationv1.FailurePolicyType { + if s.Spec.FailurePolicy == nil { + return admissionregistrationv1.Fail + } + return *s.Spec.FailurePolicy +} + +func (s *ImageVerificationPolicy) GetVariables() []admissionregistrationv1.Variable { + return s.Spec.Variables +} + +func (s *ImageVerificationPolicy) GetSpec() *ImageVerificationPolicySpec { + return &s.Spec +} + +func (s *ImageVerificationPolicy) GetStatus() *PolicyStatus { + return &s.Status +} + +func (s *ImageVerificationPolicy) GetKind() string { + return "ImageVerificationPolicy" +} + +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ImageVerificationPolicyList is a list of ImageVerificationPolicy instances +type ImageVerificationPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []ImageVerificationPolicy `json:"items"` +} + // CredentialsProvidersType provides the list of credential providers required. // +kubebuilder:validation:Enum=default;amazon;azure;google;github type CredentialsProvidersType string diff --git a/api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types_test.go b/api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types_test.go deleted file mode 100644 index 44f077ff37..0000000000 --- a/api/policies.kyverno.io/v1alpha1/imageverification_policy_spec_types_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package v1alpha1 - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAttestor_GetKey(t *testing.T) { - tests := []struct { - name string - attestor Attestor - want string - }{{ - name: "foo", - attestor: Attestor{ - Name: "foo", - }, - want: "foo", - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.attestor.GetKey() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestAttestor_IsCosign(t *testing.T) { - tests := []struct { - name string - attestor Attestor - want bool - }{{ - name: "no", - attestor: Attestor{}, - want: false, - }, { - name: "yes", - attestor: Attestor{ - Cosign: &Cosign{}, - }, - want: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.attestor.IsCosign() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestAttestor_IsNotary(t *testing.T) { - tests := []struct { - name string - attestor Attestor - want bool - }{{ - name: "no", - attestor: Attestor{}, - want: false, - }, { - name: "yes", - attestor: Attestor{ - Notary: &Notary{}, - }, - want: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.attestor.IsNotary() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestAttestation_GetKey(t *testing.T) { - tests := []struct { - name string - attestation Attestation - want string - }{{ - name: "foo", - attestation: Attestation{ - Name: "foo", - }, - want: "foo", - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.attestation.GetKey() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestAttestation_IsInToto(t *testing.T) { - tests := []struct { - name string - attestation Attestation - want bool - }{{ - name: "no", - attestation: Attestation{}, - want: false, - }, { - name: "yes", - attestation: Attestation{ - InToto: &InToto{}, - }, - want: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.attestation.IsInToto() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestAttestation_IsReferrer(t *testing.T) { - tests := []struct { - name string - attestation Attestation - want bool - }{{ - name: "no", - attestation: Attestation{}, - want: false, - }, { - name: "yes", - attestation: Attestation{ - Referrer: &Referrer{}, - }, - want: true, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.attestation.IsReferrer() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestImageVerificationPolicySpec_EvaluationMode(t *testing.T) { - tests := []struct { - name string - policy *ImageVerificationPolicySpec - want EvaluationMode - }{{ - name: "nil", - policy: &ImageVerificationPolicySpec{}, - want: EvaluationModeKubernetes, - }, { - name: "json", - policy: &ImageVerificationPolicySpec{ - EvaluationConfiguration: &EvaluationConfiguration{ - Mode: EvaluationModeJSON, - }, - }, - want: EvaluationModeJSON, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.policy.EvaluationMode() - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/api/policies.kyverno.io/v1alpha1/imageverification_policy_test.go b/api/policies.kyverno.io/v1alpha1/imageverification_policy_test.go new file mode 100644 index 0000000000..678fda223e --- /dev/null +++ b/api/policies.kyverno.io/v1alpha1/imageverification_policy_test.go @@ -0,0 +1,422 @@ +package v1alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +func TestImageVerificationPolicy_GetFailurePolicy(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want admissionregistrationv1.FailurePolicyType + }{{ + name: "nil", + policy: &ImageVerificationPolicy{}, + want: admissionregistrationv1.Fail, + }, { + name: "fail", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + FailurePolicy: ptr.To(admissionregistrationv1.Fail), + }, + }, + want: admissionregistrationv1.Fail, + }, { + name: "ignore", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + FailurePolicy: ptr.To(admissionregistrationv1.Ignore), + }, + }, + want: admissionregistrationv1.Ignore, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetFailurePolicy() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestAttestor_GetKey(t *testing.T) { + tests := []struct { + name string + attestor Attestor + want string + }{{ + name: "foo", + attestor: Attestor{ + Name: "foo", + }, + want: "foo", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.attestor.GetKey() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestAttestor_IsCosign(t *testing.T) { + tests := []struct { + name string + attestor Attestor + want bool + }{{ + name: "no", + attestor: Attestor{}, + want: false, + }, { + name: "yes", + attestor: Attestor{ + Cosign: &Cosign{}, + }, + want: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.attestor.IsCosign() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestAttestor_IsNotary(t *testing.T) { + tests := []struct { + name string + attestor Attestor + want bool + }{{ + name: "no", + attestor: Attestor{}, + want: false, + }, { + name: "yes", + attestor: Attestor{ + Notary: &Notary{}, + }, + want: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.attestor.IsNotary() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestAttestation_GetKey(t *testing.T) { + tests := []struct { + name string + attestation Attestation + want string + }{{ + name: "foo", + attestation: Attestation{ + Name: "foo", + }, + want: "foo", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.attestation.GetKey() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestAttestation_IsInToto(t *testing.T) { + tests := []struct { + name string + attestation Attestation + want bool + }{{ + name: "no", + attestation: Attestation{}, + want: false, + }, { + name: "yes", + attestation: Attestation{ + InToto: &InToto{}, + }, + want: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.attestation.IsInToto() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestAttestation_IsReferrer(t *testing.T) { + tests := []struct { + name string + attestation Attestation + want bool + }{{ + name: "no", + attestation: Attestation{}, + want: false, + }, { + name: "yes", + attestation: Attestation{ + Referrer: &Referrer{}, + }, + want: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.attestation.IsReferrer() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicySpec_EvaluationMode(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicySpec + want EvaluationMode + }{{ + name: "nil", + policy: &ImageVerificationPolicySpec{}, + want: EvaluationModeKubernetes, + }, { + name: "json", + policy: &ImageVerificationPolicySpec{ + EvaluationConfiguration: &EvaluationConfiguration{ + Mode: EvaluationModeJSON, + }, + }, + want: EvaluationModeJSON, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.EvaluationMode() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetMatchConstraints(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want admissionregistrationv1.MatchResources + }{{ + name: "nil", + policy: &ImageVerificationPolicy{}, + want: admissionregistrationv1.MatchResources{}, + }, { + name: "not nil", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + MatchConstraints: &admissionregistrationv1.MatchResources{}, + }, + }, + want: admissionregistrationv1.MatchResources{}, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetMatchConstraints() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetMatchConditions(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want []admissionregistrationv1.MatchCondition + }{{ + name: "nil", + policy: &ImageVerificationPolicy{}, + want: nil, + }, { + name: "empty", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + MatchConditions: []admissionregistrationv1.MatchCondition{}, + }, + }, + want: []admissionregistrationv1.MatchCondition{}, + }, { + name: "not empty", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + MatchConditions: []admissionregistrationv1.MatchCondition{{ + Name: "dummy", + Expression: "expression", + }}, + }, + }, + want: []admissionregistrationv1.MatchCondition{{ + Name: "dummy", + Expression: "expression", + }}, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetMatchConditions() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetWebhookConfiguration(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want *WebhookConfiguration + }{{ + name: "nil", + policy: &ImageVerificationPolicy{}, + want: nil, + }, { + name: "fail", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + WebhookConfiguration: &WebhookConfiguration{}, + }, + }, + want: &WebhookConfiguration{}, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetWebhookConfiguration() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetVariables(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want []admissionregistrationv1.Variable + }{{ + name: "nil", + policy: &ImageVerificationPolicy{}, + want: nil, + }, { + name: "empty", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + Variables: []admissionregistrationv1.Variable{}, + }, + }, + want: []admissionregistrationv1.Variable{}, + }, { + name: "not empty", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + Variables: []admissionregistrationv1.Variable{{ + Name: "dummy", + Expression: "expression", + }}, + }, + }, + want: []admissionregistrationv1.Variable{{ + Name: "dummy", + Expression: "expression", + }}, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetVariables() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetSpec(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want *ImageVerificationPolicySpec + }{{ + name: "empty", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + Variables: []admissionregistrationv1.Variable{}, + }, + }, + want: &ImageVerificationPolicySpec{ + Variables: []admissionregistrationv1.Variable{}, + }, + }, { + name: "not empty", + policy: &ImageVerificationPolicy{ + Spec: ImageVerificationPolicySpec{ + Variables: []admissionregistrationv1.Variable{{ + Name: "dummy", + Expression: "expression", + }}, + }, + }, + want: &ImageVerificationPolicySpec{ + Variables: []admissionregistrationv1.Variable{{ + Name: "dummy", + Expression: "expression", + }}, + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetSpec() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetStatus(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want *PolicyStatus + }{{ + policy: &ImageVerificationPolicy{}, + want: &PolicyStatus{}, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetStatus() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestImageVerificationPolicy_GetKind(t *testing.T) { + tests := []struct { + name string + policy *ImageVerificationPolicy + want string + }{{ + name: "not set", + policy: &ImageVerificationPolicy{}, + want: "ImageVerificationPolicy", + }, { + name: "set", + policy: &ImageVerificationPolicy{ + TypeMeta: v1.TypeMeta{ + Kind: "Foo", + }, + }, + want: "ImageVerificationPolicy", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.GetKind() + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/api/policies.kyverno.io/v1alpha1/imageverification_policy_types.go b/api/policies.kyverno.io/v1alpha1/imageverification_policy_types.go deleted file mode 100644 index 9614dc31d0..0000000000 --- a/api/policies.kyverno.io/v1alpha1/imageverification_policy_types.go +++ /dev/null @@ -1,69 +0,0 @@ -package v1alpha1 - -import ( - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +genclient:nonNamespaced -// +kubebuilder:object:root=true -// +kubebuilder:resource:path=imageverificationpolicies,scope="Cluster",shortName=ivpol,categories=kyverno -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type ImageVerificationPolicy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec ImageVerificationPolicySpec `json:"spec"` - // Status contains policy runtime data. - // +optional - Status PolicyStatus `json:"status,omitempty"` -} - -func (s *ImageVerificationPolicy) GetMatchConstraints() admissionregistrationv1.MatchResources { - if s.Spec.MatchConstraints == nil { - return admissionregistrationv1.MatchResources{} - } - return *s.Spec.MatchConstraints -} - -func (s *ImageVerificationPolicy) GetMatchConditions() []admissionregistrationv1.MatchCondition { - return s.Spec.MatchConditions -} - -func (s *ImageVerificationPolicy) GetWebhookConfiguration() *WebhookConfiguration { - return s.Spec.WebhookConfiguration -} - -func (s *ImageVerificationPolicy) GetFailurePolicy() admissionregistrationv1.FailurePolicyType { - if s.Spec.FailurePolicy == nil { - return admissionregistrationv1.Fail - } - return *s.Spec.FailurePolicy -} - -func (s *ImageVerificationPolicy) GetVariables() []admissionregistrationv1.Variable { - return s.Spec.Variables -} - -func (s *ImageVerificationPolicy) GetSpec() *ImageVerificationPolicySpec { - return &s.Spec -} - -func (s *ImageVerificationPolicy) GetStatus() *PolicyStatus { - return &s.Status -} - -func (s *ImageVerificationPolicy) GetKind() string { - return "ImageVerificationPolicy" -} - -// +kubebuilder:object:root=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ImageVerificationPolicyList is a list of ImageVerificationPolicy instances -type ImageVerificationPolicyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - Items []ImageVerificationPolicy `json:"items"` -} diff --git a/api/policies.kyverno.io/v1alpha1/imageverification_policy_types_test.go b/api/policies.kyverno.io/v1alpha1/imageverification_policy_types_test.go deleted file mode 100644 index 35e2abe848..0000000000 --- a/api/policies.kyverno.io/v1alpha1/imageverification_policy_types_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package v1alpha1 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - "k8s.io/utils/ptr" -) - -func TestImageVerificationPolicy_GetFailurePolicy(t *testing.T) { - tests := []struct { - name string - policy *ImageVerificationPolicy - want admissionregistrationv1.FailurePolicyType - }{{ - name: "nil", - policy: &ImageVerificationPolicy{}, - want: admissionregistrationv1.Fail, - }, { - name: "fail", - policy: &ImageVerificationPolicy{ - Spec: ImageVerificationPolicySpec{ - FailurePolicy: ptr.To(admissionregistrationv1.Fail), - }, - }, - want: admissionregistrationv1.Fail, - }, { - name: "ignore", - policy: &ImageVerificationPolicy{ - Spec: ImageVerificationPolicySpec{ - FailurePolicy: ptr.To(admissionregistrationv1.Ignore), - }, - }, - want: admissionregistrationv1.Ignore, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.policy.GetFailurePolicy() - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/api/policies.kyverno.io/v1alpha1/validating_policy.go b/api/policies.kyverno.io/v1alpha1/validating_policy.go index 8ced45b4dd..34eaf3c424 100644 --- a/api/policies.kyverno.io/v1alpha1/validating_policy.go +++ b/api/policies.kyverno.io/v1alpha1/validating_policy.go @@ -70,3 +70,170 @@ type ValidatingPolicyList struct { metav1.ListMeta `json:"metadata"` Items []ValidatingPolicy `json:"items"` } + +// ValidatingPolicySpec is the specification of the desired behavior of the ValidatingPolicy. +type ValidatingPolicySpec struct { + // MatchConstraints specifies what resources this policy is designed to validate. + // The AdmissionPolicy cares about a request if it matches _all_ Constraints. + // However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API + // ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding. + // Required. + MatchConstraints *admissionregistrationv1.MatchResources `json:"matchConstraints,omitempty"` + + // Validations contain CEL expressions which is used to apply the validation. + // Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is + // required. + // +listType=atomic + // +optional + Validations []admissionregistrationv1.Validation `json:"validations,omitempty"` + + // failurePolicy defines how to handle failures for the admission policy. Failures can + // occur from CEL expression parse errors, type check errors, runtime errors and invalid + // or mis-configured policy definitions or bindings. + // + // A policy is invalid if spec.paramKind refers to a non-existent Kind. + // A binding is invalid if spec.paramRef.name refers to a non-existent resource. + // + // failurePolicy does not define how validations that evaluate to false are handled. + // + // When failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions + // define how failures are enforced. + // + // Allowed values are Ignore or Fail. Defaults to Fail. + // +optional + FailurePolicy *admissionregistrationv1.FailurePolicyType `json:"failurePolicy,omitempty"` + + // auditAnnotations contains CEL expressions which are used to produce audit + // annotations for the audit event of the API request. + // validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is + // required. + // +listType=atomic + // +optional + AuditAnnotations []admissionregistrationv1.AuditAnnotation `json:"auditAnnotations,omitempty"` + + // MatchConditions is a list of conditions that must be met for a request to be validated. + // Match conditions filter requests that have already been matched by the rules, + // namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. + // There are a maximum of 64 match conditions allowed. + // + // If a parameter object is provided, it can be accessed via the `params` handle in the same + // manner as validation expressions. + // + // The exact matching logic is (in order): + // 1. If ANY matchCondition evaluates to FALSE, the policy is skipped. + // 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated. + // 3. If any matchCondition evaluates to an error (but none are FALSE): + // - If failurePolicy=Fail, reject the request + // - If failurePolicy=Ignore, the policy is skipped + // + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + // +optional + MatchConditions []admissionregistrationv1.MatchCondition `json:"matchConditions,omitempty" patchStrategy:"merge" patchMergeKey:"name"` + + // Variables contain definitions of variables that can be used in composition of other expressions. + // Each variable is defined as a named CEL expression. + // The variables defined here will be available under `variables` in other expressions of the policy + // except MatchConditions because MatchConditions are evaluated before the rest of the policy. + // + // The expression of a variable can refer to other variables defined earlier in the list but not those after. + // Thus, Variables must be sorted by the order of first appearance and acyclic. + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + // +optional + Variables []admissionregistrationv1.Variable `json:"variables,omitempty" patchStrategy:"merge" patchMergeKey:"name"` + + // Generate specifies whether to generate a Kubernetes ValidatingAdmissionPolicy. + // Optional. Defaults to "false" if not specified. + // +optional + // +kubebuilder:default=false + Generate *bool `json:"generate,omitempty"` + + // ValidationAction specifies the action to be taken when the matched resource violates the policy. + // Required. + // +listType=set + ValidationAction []admissionregistrationv1.ValidationAction `json:"validationActions,omitempty"` + + // WebhookConfiguration defines the configuration for the webhook. + // +optional + WebhookConfiguration *WebhookConfiguration `json:"webhookConfiguration,omitempty"` + + // EvaluationConfiguration defines the configuration for the policy evaluation. + // +optional + EvaluationConfiguration *EvaluationConfiguration `json:"evaluation,omitempty"` +} + +// AdmissionEnabled checks if admission is set to true +func (s ValidatingPolicySpec) AdmissionEnabled() bool { + if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Admission == nil || s.EvaluationConfiguration.Admission.Enabled == nil { + return true + } + return *s.EvaluationConfiguration.Admission.Enabled +} + +// BackgroundEnabled checks if background is set to true +func (s ValidatingPolicySpec) BackgroundEnabled() bool { + if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Background == nil || s.EvaluationConfiguration.Background.Enabled == nil { + return true + } + return *s.EvaluationConfiguration.Background.Enabled +} + +// EvaluationMode returns the evaluation mode of the policy. +func (s ValidatingPolicySpec) EvaluationMode() EvaluationMode { + if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Mode == "" { + return EvaluationModeKubernetes + } + return s.EvaluationConfiguration.Mode +} + +type WebhookConfiguration struct { + // TimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. + // After the configured time expires, the admission request may fail, or may simply ignore the policy results, + // based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds. + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` +} + +type EvaluationConfiguration struct { + // Mode is the mode of policy evaluation. + // Allowed values are "Kubernetes" or "JSON". + // Optional. Default value is "Kubernetes". + // +optional + Mode EvaluationMode `json:"mode,omitempty"` + + // Admission controls policy evaluation during admission. + // +optional + Admission *AdmissionConfiguration `json:"admission,omitempty"` + + // Background controls policy evaluation during background scan. + // +optional + Background *BackgroundConfiguration `json:"background,omitempty"` +} + +type AdmissionConfiguration struct { + // Enabled controls if rules are applied during admission. + // Optional. Default value is "true". + // +optional + // +kubebuilder:default=true + Enabled *bool `json:"enabled,omitempty"` +} + +type BackgroundConfiguration struct { + // Enabled controls if rules are applied to existing resources during a background scan. + // Optional. Default value is "true". The value must be set to "false" if the policy rule + // uses variables that are only available in the admission review request (e.g. user name). + // +optional + // +kubebuilder:default=true + Enabled *bool `json:"enabled,omitempty"` +} + +type EvaluationMode string + +const ( + EvaluationModeKubernetes EvaluationMode = "Kubernetes" + EvaluationModeJSON EvaluationMode = "JSON" +) diff --git a/api/policies.kyverno.io/v1alpha1/validating_policy_test.go b/api/policies.kyverno.io/v1alpha1/validating_policy_test.go index 7e9509b3d1..d54441c2ab 100644 --- a/api/policies.kyverno.io/v1alpha1/validating_policy_test.go +++ b/api/policies.kyverno.io/v1alpha1/validating_policy_test.go @@ -243,7 +243,7 @@ func TestValidatingPolicy_GetKind(t *testing.T) { policy: &ValidatingPolicy{}, want: "ValidatingPolicy", }, { - name: "not set", + name: "set", policy: &ValidatingPolicy{ TypeMeta: v1.TypeMeta{ Kind: "Foo", @@ -258,3 +258,115 @@ func TestValidatingPolicy_GetKind(t *testing.T) { }) } } + +func TestValidatingPolicySpec_AdmissionEnabled(t *testing.T) { + tests := []struct { + name string + policy *ValidatingPolicy + want bool + }{{ + name: "nil", + policy: &ValidatingPolicy{}, + want: true, + }, { + name: "true", + policy: &ValidatingPolicy{ + Spec: ValidatingPolicySpec{ + EvaluationConfiguration: &EvaluationConfiguration{ + Admission: &AdmissionConfiguration{ + Enabled: ptr.To(true), + }, + }, + }, + }, + want: true, + }, { + name: "false", + policy: &ValidatingPolicy{ + Spec: ValidatingPolicySpec{ + EvaluationConfiguration: &EvaluationConfiguration{ + Admission: &AdmissionConfiguration{ + Enabled: ptr.To(false), + }, + }, + }, + }, + want: false, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.Spec.AdmissionEnabled() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestValidatingPolicySpec_BackgroundEnabled(t *testing.T) { + tests := []struct { + name string + policy *ValidatingPolicy + want bool + }{{ + name: "nil", + policy: &ValidatingPolicy{}, + want: true, + }, { + name: "true", + policy: &ValidatingPolicy{ + Spec: ValidatingPolicySpec{ + EvaluationConfiguration: &EvaluationConfiguration{ + Background: &BackgroundConfiguration{ + Enabled: ptr.To(true), + }, + }, + }, + }, + want: true, + }, { + name: "false", + policy: &ValidatingPolicy{ + Spec: ValidatingPolicySpec{ + EvaluationConfiguration: &EvaluationConfiguration{ + Background: &BackgroundConfiguration{ + Enabled: ptr.To(false), + }, + }, + }, + }, + want: false, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.Spec.BackgroundEnabled() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestValidatingPolicySpec_EvaluationMode(t *testing.T) { + tests := []struct { + name string + policy *ValidatingPolicy + want EvaluationMode + }{{ + name: "nil", + policy: &ValidatingPolicy{}, + want: EvaluationModeKubernetes, + }, { + name: "json", + policy: &ValidatingPolicy{ + Spec: ValidatingPolicySpec{ + EvaluationConfiguration: &EvaluationConfiguration{ + Mode: EvaluationModeJSON, + }, + }, + }, + want: EvaluationModeJSON, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.policy.Spec.EvaluationMode() + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/api/policies.kyverno.io/v1alpha1/validating_spec_types.go b/api/policies.kyverno.io/v1alpha1/validating_spec_types.go deleted file mode 100644 index 5d0e30612c..0000000000 --- a/api/policies.kyverno.io/v1alpha1/validating_spec_types.go +++ /dev/null @@ -1,172 +0,0 @@ -package v1alpha1 - -import ( - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" -) - -// ValidatingPolicySpec is the specification of the desired behavior of the ValidatingPolicy. -type ValidatingPolicySpec struct { - // MatchConstraints specifies what resources this policy is designed to validate. - // The AdmissionPolicy cares about a request if it matches _all_ Constraints. - // However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API - // ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding. - // Required. - MatchConstraints *admissionregistrationv1.MatchResources `json:"matchConstraints,omitempty"` - - // Validations contain CEL expressions which is used to apply the validation. - // Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is - // required. - // +listType=atomic - // +optional - Validations []admissionregistrationv1.Validation `json:"validations,omitempty"` - - // failurePolicy defines how to handle failures for the admission policy. Failures can - // occur from CEL expression parse errors, type check errors, runtime errors and invalid - // or mis-configured policy definitions or bindings. - // - // A policy is invalid if spec.paramKind refers to a non-existent Kind. - // A binding is invalid if spec.paramRef.name refers to a non-existent resource. - // - // failurePolicy does not define how validations that evaluate to false are handled. - // - // When failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions - // define how failures are enforced. - // - // Allowed values are Ignore or Fail. Defaults to Fail. - // +optional - FailurePolicy *admissionregistrationv1.FailurePolicyType `json:"failurePolicy,omitempty"` - - // auditAnnotations contains CEL expressions which are used to produce audit - // annotations for the audit event of the API request. - // validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is - // required. - // +listType=atomic - // +optional - AuditAnnotations []admissionregistrationv1.AuditAnnotation `json:"auditAnnotations,omitempty"` - - // MatchConditions is a list of conditions that must be met for a request to be validated. - // Match conditions filter requests that have already been matched by the rules, - // namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. - // There are a maximum of 64 match conditions allowed. - // - // If a parameter object is provided, it can be accessed via the `params` handle in the same - // manner as validation expressions. - // - // The exact matching logic is (in order): - // 1. If ANY matchCondition evaluates to FALSE, the policy is skipped. - // 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated. - // 3. If any matchCondition evaluates to an error (but none are FALSE): - // - If failurePolicy=Fail, reject the request - // - If failurePolicy=Ignore, the policy is skipped - // - // +patchMergeKey=name - // +patchStrategy=merge - // +listType=map - // +listMapKey=name - // +optional - MatchConditions []admissionregistrationv1.MatchCondition `json:"matchConditions,omitempty" patchStrategy:"merge" patchMergeKey:"name"` - - // Variables contain definitions of variables that can be used in composition of other expressions. - // Each variable is defined as a named CEL expression. - // The variables defined here will be available under `variables` in other expressions of the policy - // except MatchConditions because MatchConditions are evaluated before the rest of the policy. - // - // The expression of a variable can refer to other variables defined earlier in the list but not those after. - // Thus, Variables must be sorted by the order of first appearance and acyclic. - // +patchMergeKey=name - // +patchStrategy=merge - // +listType=map - // +listMapKey=name - // +optional - Variables []admissionregistrationv1.Variable `json:"variables,omitempty" patchStrategy:"merge" patchMergeKey:"name"` - - // Generate specifies whether to generate a Kubernetes ValidatingAdmissionPolicy. - // Optional. Defaults to "false" if not specified. - // +optional - // +kubebuilder:default=false - Generate *bool `json:"generate,omitempty"` - - // ValidationAction specifies the action to be taken when the matched resource violates the policy. - // Required. - // +listType=set - ValidationAction []admissionregistrationv1.ValidationAction `json:"validationActions,omitempty"` - - // WebhookConfiguration defines the configuration for the webhook. - // +optional - WebhookConfiguration *WebhookConfiguration `json:"webhookConfiguration,omitempty"` - - // EvaluationConfiguration defines the configuration for the policy evaluation. - // +optional - EvaluationConfiguration *EvaluationConfiguration `json:"evaluation,omitempty"` -} - -// AdmissionEnabled checks if admission is set to true -func (s ValidatingPolicySpec) AdmissionEnabled() bool { - if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Admission == nil || s.EvaluationConfiguration.Admission.Enabled == nil { - return true - } - return *s.EvaluationConfiguration.Admission.Enabled -} - -// BackgroundEnabled checks if background is set to true -func (s ValidatingPolicySpec) BackgroundEnabled() bool { - if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Background == nil || s.EvaluationConfiguration.Background.Enabled == nil { - return true - } - return *s.EvaluationConfiguration.Background.Enabled -} - -// EvaluationMode returns the evaluation mode of the policy. -func (s ValidatingPolicySpec) EvaluationMode() EvaluationMode { - if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Mode == "" { - return EvaluationModeKubernetes - } - return s.EvaluationConfiguration.Mode -} - -type WebhookConfiguration struct { - // TimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. - // After the configured time expires, the admission request may fail, or may simply ignore the policy results, - // based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds. - TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` -} - -type EvaluationConfiguration struct { - // Mode is the mode of policy evaluation. - // Allowed values are "Kubernetes" or "JSON". - // Optional. Default value is "Kubernetes". - // +optional - Mode EvaluationMode `json:"mode,omitempty"` - - // Admission controls policy evaluation during admission. - // +optional - Admission *AdmissionConfiguration `json:"admission,omitempty"` - - // Background controls policy evaluation during background scan. - // +optional - Background *BackgroundConfiguration `json:"background,omitempty"` -} - -type AdmissionConfiguration struct { - // Enabled controls if rules are applied during admission. - // Optional. Default value is "true". - // +optional - // +kubebuilder:default=true - Enabled *bool `json:"enabled,omitempty"` -} - -type BackgroundConfiguration struct { - // Enabled controls if rules are applied to existing resources during a background scan. - // Optional. Default value is "true". The value must be set to "false" if the policy rule - // uses variables that are only available in the admission review request (e.g. user name). - // +optional - // +kubebuilder:default=true - Enabled *bool `json:"enabled,omitempty"` -} - -type EvaluationMode string - -const ( - EvaluationModeKubernetes EvaluationMode = "Kubernetes" - EvaluationModeJSON EvaluationMode = "JSON" -) diff --git a/api/policies.kyverno.io/v1alpha1/validating_spec_types_test.go b/api/policies.kyverno.io/v1alpha1/validating_spec_types_test.go deleted file mode 100644 index eb7269038e..0000000000 --- a/api/policies.kyverno.io/v1alpha1/validating_spec_types_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package v1alpha1 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "k8s.io/utils/ptr" -) - -func TestValidatingPolicySpec_AdmissionEnabled(t *testing.T) { - tests := []struct { - name string - policy *ValidatingPolicy - want bool - }{{ - name: "nil", - policy: &ValidatingPolicy{}, - want: true, - }, { - name: "true", - policy: &ValidatingPolicy{ - Spec: ValidatingPolicySpec{ - EvaluationConfiguration: &EvaluationConfiguration{ - Admission: &AdmissionConfiguration{ - Enabled: ptr.To(true), - }, - }, - }, - }, - want: true, - }, { - name: "false", - policy: &ValidatingPolicy{ - Spec: ValidatingPolicySpec{ - EvaluationConfiguration: &EvaluationConfiguration{ - Admission: &AdmissionConfiguration{ - Enabled: ptr.To(false), - }, - }, - }, - }, - want: false, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.policy.Spec.AdmissionEnabled() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestValidatingPolicySpec_BackgroundEnabled(t *testing.T) { - tests := []struct { - name string - policy *ValidatingPolicy - want bool - }{{ - name: "nil", - policy: &ValidatingPolicy{}, - want: true, - }, { - name: "true", - policy: &ValidatingPolicy{ - Spec: ValidatingPolicySpec{ - EvaluationConfiguration: &EvaluationConfiguration{ - Background: &BackgroundConfiguration{ - Enabled: ptr.To(true), - }, - }, - }, - }, - want: true, - }, { - name: "false", - policy: &ValidatingPolicy{ - Spec: ValidatingPolicySpec{ - EvaluationConfiguration: &EvaluationConfiguration{ - Background: &BackgroundConfiguration{ - Enabled: ptr.To(false), - }, - }, - }, - }, - want: false, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.policy.Spec.BackgroundEnabled() - assert.Equal(t, tt.want, got) - }) - } -} - -func TestValidatingPolicySpec_EvaluationMode(t *testing.T) { - tests := []struct { - name string - policy *ValidatingPolicy - want EvaluationMode - }{{ - name: "nil", - policy: &ValidatingPolicy{}, - want: EvaluationModeKubernetes, - }, { - name: "json", - policy: &ValidatingPolicy{ - Spec: ValidatingPolicySpec{ - EvaluationConfiguration: &EvaluationConfiguration{ - Mode: EvaluationModeJSON, - }, - }, - }, - want: EvaluationModeJSON, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.policy.Spec.EvaluationMode() - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/hack/api-group-resources/main.go b/hack/api-group-resources/main.go index df99adcc51..425c786bea 100644 --- a/hack/api-group-resources/main.go +++ b/hack/api-group-resources/main.go @@ -21,13 +21,11 @@ func main() { kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") } flag.Parse() - // use the current context in kubeconfig config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err.Error()) } - client := kubernetes.NewForConfigOrDie(config) groupResources, err := restmapper.GetAPIGroupResources(client.Discovery()) if err != nil {