1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-20 23:02:36 +00:00

chore: add policy api unit tests (#12315)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2025-03-06 17:31:14 +01:00 committed by GitHub
parent da1fbd9475
commit 705ced765d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 766 additions and 577 deletions

View file

@ -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

View file

@ -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)
})
}
}

View file

@ -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)
})
}
}

View file

@ -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"`
}

View file

@ -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)
})
}
}

View file

@ -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"
)

View file

@ -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)
})
}
}

View file

@ -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"
)

View file

@ -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)
})
}
}

View file

@ -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 {