mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
Support V2beta1 Version (#4514)
introduce new version V2beta1 which remove deprecated CRD types from version v1. Signed-off-by: Vyankatesh <vyankateshkd@gmail.com>
This commit is contained in:
parent
f0fa50b27e
commit
aa6abd99f2
32 changed files with 32131 additions and 102 deletions
2
.github/workflows/e2e-autogen-internals.yaml
vendored
2
.github/workflows/e2e-autogen-internals.yaml
vendored
|
@ -65,7 +65,7 @@ jobs:
|
|||
echo ">>> Install Kyverno"
|
||||
cat ${GITHUB_WORKSPACE}/config/install.yaml | \
|
||||
sed -e 's/imagePullPolicy:.*$/imagePullPolicy: IfNotPresent/g' | \
|
||||
kubectl apply -f -
|
||||
kubectl create -f -
|
||||
kubectl apply -f ${GITHUB_WORKSPACE}/config/github/rbac.yaml
|
||||
chmod a+x ${GITHUB_WORKSPACE}/scripts/verify-deployment.sh
|
||||
sleep 50
|
||||
|
|
2
.github/workflows/e2e.yaml
vendored
2
.github/workflows/e2e.yaml
vendored
|
@ -65,7 +65,7 @@ jobs:
|
|||
cat ${GITHUB_WORKSPACE}/config/install.yaml | \
|
||||
sed -e 's/imagePullPolicy:.*$/imagePullPolicy: IfNotPresent/g' | \
|
||||
sed -e 's/--autogenInternals:true$/--autogenInternals:false/g' | \
|
||||
kubectl apply -f -
|
||||
kubectl create -f -
|
||||
kubectl apply -f ${GITHUB_WORKSPACE}/config/github/rbac.yaml
|
||||
chmod a+x ${GITHUB_WORKSPACE}/scripts/verify-deployment.sh
|
||||
sleep 50
|
||||
|
|
1
Makefile
1
Makefile
|
@ -408,6 +408,7 @@ codegen-api-docs: $(PACKAGE_SHIM) $(GEN_CRD_API_REFERENCE_DOCS) ## Generate API
|
|||
@GOPATH=$(GOPATH_SHIM) $(GEN_CRD_API_REFERENCE_DOCS) -v 6 -api-dir ./api/kyverno/v1alpha2 -config docs/config.json -template-dir docs/template -out-file docs/crd/v1alpha2/index.html
|
||||
@GOPATH=$(GOPATH_SHIM) $(GEN_CRD_API_REFERENCE_DOCS) -v 6 -api-dir ./api/kyverno/v1beta1 -config docs/config.json -template-dir docs/template -out-file docs/crd/v1beta1/index.html
|
||||
@GOPATH=$(GOPATH_SHIM) $(GEN_CRD_API_REFERENCE_DOCS) -v 6 -api-dir ./api/kyverno/v1 -config docs/config.json -template-dir docs/template -out-file docs/crd/v1/index.html
|
||||
@GOPATH=$(GOPATH_SHIM) $(GEN_CRD_API_REFERENCE_DOCS) -v 6 -api-dir ./api/kyverno/v2beta1 -config docs/config.json -template-dir docs/template -out-file docs/crd/v2beta1/index.html
|
||||
|
||||
.PHONY: codegen-helm-docs
|
||||
codegen-helm-docs: ## Generate helm docs
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
// +kubebuilder:printcolumn:name="Validate Action",type="string",JSONPath=".spec.validationFailureAction"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy",priority=1
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.ready`
|
||||
// +kubebuilder:storageversion
|
||||
|
||||
// ClusterPolicy declares validation, mutation, and generation behaviors for matching resources.
|
||||
type ClusterPolicy struct {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// GenerateRequest is a request to process generate rule.
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
|
@ -18,6 +17,9 @@ import (
|
|||
// +kubebuilder:printcolumn:name="status",type="string",JSONPath=".status.state"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
||||
// +kubebuilder:resource:shortName=gr
|
||||
// +kubebuilder:storageversion
|
||||
|
||||
// GenerateRequest is a request to process generate rule.
|
||||
type GenerateRequest struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy",priority=1
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.ready`
|
||||
// +kubebuilder:resource:shortName=pol
|
||||
// +kubebuilder:storageversion
|
||||
|
||||
// Policy declares validation, mutation, and generation behaviors for matching resources.
|
||||
// See: https://kyverno.io/docs/writing-policies/ for more information.
|
||||
|
|
55
api/kyverno/v2beta1/clusterpolicy_test.go
Normal file
55
api/kyverno/v2beta1/clusterpolicy_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_ClusterPolicy_Name(t *testing.T) {
|
||||
subject := ClusterPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "this-is-a-way-too-long-policy-name-that-should-trigger-an-error-when-calling-the-policy-validation-method",
|
||||
Namespace: "abcd",
|
||||
},
|
||||
}
|
||||
errs := subject.Validate(nil)
|
||||
assert.Assert(t, len(errs) == 1)
|
||||
assert.Equal(t, errs[0].Field, "name")
|
||||
assert.Equal(t, errs[0].Type, field.ErrorTypeTooLong)
|
||||
assert.Equal(t, errs[0].Detail, "must have at most 63 bytes")
|
||||
assert.Equal(t, errs[0].Error(), "name: Too long: must have at most 63 bytes")
|
||||
}
|
||||
|
||||
func Test_ClusterPolicy_IsNamespaced(t *testing.T) {
|
||||
namespaced := ClusterPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "this-is-a-way-too-long-policy-name-that-should-trigger-an-error-when-calling-the-policy-validation-method",
|
||||
Namespace: "abcd",
|
||||
},
|
||||
}
|
||||
notNamespaced := ClusterPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "this-is-a-way-too-long-policy-name-that-should-trigger-an-error-when-calling-the-policy-validation-method",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, namespaced.IsNamespaced(), true)
|
||||
assert.Equal(t, notNamespaced.IsNamespaced(), false)
|
||||
}
|
||||
|
||||
func Test_ClusterPolicy_Autogen_All(t *testing.T) {
|
||||
subject := ClusterPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "policy",
|
||||
Annotations: map[string]string{
|
||||
kyvernov1.PodControllersAnnotation: "all",
|
||||
},
|
||||
},
|
||||
}
|
||||
errs := subject.Validate(nil)
|
||||
assert.Equal(t, len(errs), 1)
|
||||
assert.Equal(t, errs[0].Error(), "metadata.annotations: Forbidden: Autogen annotation does not support 'all' anymore, remove the annotation or set it to a valid value")
|
||||
}
|
117
api/kyverno/v2beta1/clusterpolicy_types.go
Normal file
117
api/kyverno/v2beta1/clusterpolicy_types.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:path=clusterpolicies,scope="Cluster",shortName=cpol
|
||||
// +kubebuilder:printcolumn:name="Background",type="string",JSONPath=".spec.background"
|
||||
// +kubebuilder:printcolumn:name="Action",type="string",JSONPath=".spec.validationFailureAction"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy",priority=1
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.ready`
|
||||
|
||||
// ClusterPolicy declares validation, mutation, and generation behaviors for matching resources.
|
||||
type ClusterPolicy struct {
|
||||
metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
|
||||
// Spec declares policy behaviors.
|
||||
Spec Spec `json:"spec" yaml:"spec"`
|
||||
|
||||
// Status contains policy runtime data.
|
||||
// +optional
|
||||
Status kyvernov1.PolicyStatus `json:"status,omitempty" yaml:"status,omitempty"`
|
||||
}
|
||||
|
||||
// HasAutoGenAnnotation checks if a policy has auto-gen annotation
|
||||
func (p *ClusterPolicy) HasAutoGenAnnotation() bool {
|
||||
annotations := p.GetAnnotations()
|
||||
val, ok := annotations[kyvernov1.PodControllersAnnotation]
|
||||
if ok && strings.ToLower(val) != "none" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasMutateOrValidateOrGenerate checks for rule types
|
||||
func (p *ClusterPolicy) HasMutateOrValidateOrGenerate() bool {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasMutate checks for mutate rule types
|
||||
func (p *ClusterPolicy) HasMutate() bool {
|
||||
return p.Spec.HasMutate()
|
||||
}
|
||||
|
||||
// HasValidate checks for validate rule types
|
||||
func (p *ClusterPolicy) HasValidate() bool {
|
||||
return p.Spec.HasValidate()
|
||||
}
|
||||
|
||||
// HasGenerate checks for generate rule types
|
||||
func (p *ClusterPolicy) HasGenerate() bool {
|
||||
return p.Spec.HasGenerate()
|
||||
}
|
||||
|
||||
// HasVerifyImages checks for image verification rule types
|
||||
func (p *ClusterPolicy) HasVerifyImages() bool {
|
||||
return p.Spec.HasVerifyImages()
|
||||
}
|
||||
|
||||
// BackgroundProcessingEnabled checks if background is set to true
|
||||
func (p *ClusterPolicy) BackgroundProcessingEnabled() bool {
|
||||
return p.Spec.BackgroundProcessingEnabled()
|
||||
}
|
||||
|
||||
// GetSpec returns the policy spec
|
||||
func (p *ClusterPolicy) GetSpec() *Spec {
|
||||
return &p.Spec
|
||||
}
|
||||
|
||||
// IsNamespaced indicates if the policy is namespace scoped
|
||||
func (p *ClusterPolicy) IsNamespaced() bool {
|
||||
return p.GetNamespace() != ""
|
||||
}
|
||||
|
||||
// IsReady indicates if the policy is ready to serve the admission request
|
||||
func (p *ClusterPolicy) IsReady() bool {
|
||||
return p.Status.IsReady()
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
// namespaced means that the policy is bound to a namespace and therefore
|
||||
// should not filter/generate cluster wide resources.
|
||||
func (p *ClusterPolicy) Validate(clusterResources sets.String) (errs field.ErrorList) {
|
||||
errs = append(errs, kyvernov1.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
|
||||
errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
|
||||
errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), clusterResources)...)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (p *ClusterPolicy) GetKind() string {
|
||||
return p.Kind
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// ClusterPolicyList is a list of ClusterPolicy instances.
|
||||
type ClusterPolicyList struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ListMeta `json:"metadata" yaml:"metadata"`
|
||||
Items []ClusterPolicy `json:"items" yaml:"items"`
|
||||
}
|
128
api/kyverno/v2beta1/common_types.go
Normal file
128
api/kyverno/v2beta1/common_types.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
)
|
||||
|
||||
// Validation defines checks to be performed on matching resources.
|
||||
type Validation struct {
|
||||
// Message specifies a custom message to be displayed on failure.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||
|
||||
// Manifest specifies conditions for manifest verification
|
||||
// +optional
|
||||
Manifests *kyvernov1.Manifests `json:"manifests,omitempty" yaml:"manifests,omitempty"`
|
||||
|
||||
// ForEach applies validate rules to a list of sub-elements by creating a context for each entry in the list and looping over it to apply the specified logic.
|
||||
// +optional
|
||||
ForEachValidation []kyvernov1.ForEachValidation `json:"foreach,omitempty" yaml:"foreach,omitempty"`
|
||||
|
||||
// Pattern specifies an overlay-style pattern used to check resources.
|
||||
// +optional
|
||||
RawPattern *apiextv1.JSON `json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
||||
|
||||
// AnyPattern specifies list of validation patterns. At least one of the patterns
|
||||
// must be satisfied for the validation rule to succeed.
|
||||
// +optional
|
||||
RawAnyPattern *apiextv1.JSON `json:"anyPattern,omitempty" yaml:"anyPattern,omitempty"`
|
||||
|
||||
// Deny defines conditions used to pass or fail a validation rule.
|
||||
// +optional
|
||||
Deny *Deny `json:"deny,omitempty" yaml:"deny,omitempty"`
|
||||
|
||||
// PodSecurity applies exemptions for Kubernetes Pod Security admission
|
||||
// by specifying exclusions for Pod Security Standards controls.
|
||||
// +optional
|
||||
PodSecurity *kyvernov1.PodSecurity `json:"podSecurity,omitempty" yaml:"podSecurity,omitempty"`
|
||||
}
|
||||
|
||||
// ConditionOperator is the operation performed on condition key and value.
|
||||
// +kubebuilder:validation:Enum=Equals;NotEquals;AnyIn;AllIn;AnyNotIn;AllNotIn;GreaterThanOrEquals;GreaterThan;LessThanOrEquals;LessThan;DurationGreaterThanOrEquals;DurationGreaterThan;DurationLessThanOrEquals;DurationLessThan
|
||||
type ConditionOperator string
|
||||
|
||||
// ConditionOperators stores all the valid ConditionOperator types as key-value pairs.
|
||||
// "Equals" evaluates if the key is equal to the value.
|
||||
// "NotEquals" evaluates if the key is not equal to the value.
|
||||
// "AnyIn" evaluates if any of the keys are contained in the set of values.
|
||||
// "AllIn" evaluates if all the keys are contained in the set of values.
|
||||
// "AnyNotIn" evaluates if any of the keys are not contained in the set of values.
|
||||
// "AllNotIn" evaluates if all the keys are not contained in the set of values.
|
||||
// "GreaterThanOrEquals" evaluates if the key (numeric) is greater than or equal to the value (numeric).
|
||||
// "GreaterThan" evaluates if the key (numeric) is greater than the value (numeric).
|
||||
// "LessThanOrEquals" evaluates if the key (numeric) is less than or equal to the value (numeric).
|
||||
// "LessThan" evaluates if the key (numeric) is less than the value (numeric).
|
||||
// "DurationGreaterThanOrEquals" evaluates if the key (duration) is greater than or equal to the value (duration)
|
||||
// "DurationGreaterThan" evaluates if the key (duration) is greater than the value (duration)
|
||||
// "DurationLessThanOrEquals" evaluates if the key (duration) is less than or equal to the value (duration)
|
||||
// "DurationLessThan" evaluates if the key (duration) is greater than the value (duration)
|
||||
var ConditionOperators = map[string]ConditionOperator{
|
||||
"Equals": ConditionOperator("Equals"),
|
||||
"NotEquals": ConditionOperator("NotEquals"),
|
||||
"AnyIn": ConditionOperator("AnyIn"),
|
||||
"AllIn": ConditionOperator("AllIn"),
|
||||
"AnyNotIn": ConditionOperator("AnyNotIn"),
|
||||
"AllNotIn": ConditionOperator("AllNotIn"),
|
||||
"GreaterThanOrEquals": ConditionOperator("GreaterThanOrEquals"),
|
||||
"GreaterThan": ConditionOperator("GreaterThan"),
|
||||
"LessThanOrEquals": ConditionOperator("LessThanOrEquals"),
|
||||
"LessThan": ConditionOperator("LessThan"),
|
||||
"DurationGreaterThanOrEquals": ConditionOperator("DurationGreaterThanOrEquals"),
|
||||
"DurationGreaterThan": ConditionOperator("DurationGreaterThan"),
|
||||
"DurationLessThanOrEquals": ConditionOperator("DurationLessThanOrEquals"),
|
||||
"DurationLessThan": ConditionOperator("DurationLessThan"),
|
||||
}
|
||||
|
||||
// Deny specifies a list of conditions used to pass or fail a validation rule.
|
||||
type Deny struct {
|
||||
// Multiple conditions can be declared under an `any` or `all` statement. A direct list
|
||||
// of conditions (without `any` or `all` statements) is also supported for backwards compatibility
|
||||
// See: https://kyverno.io/docs/writing-policies/validate/#deny-rules
|
||||
RawAnyAllConditions *AnyAllConditions `json:"conditions,omitempty" yaml:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
// Key is the context entry (using JMESPath) for conditional rule evaluation.
|
||||
RawKey *apiextv1.JSON `json:"key,omitempty" yaml:"key,omitempty"`
|
||||
|
||||
// Operator is the conditional operation to perform. Valid operators are:
|
||||
// Equals, NotEquals, In, AnyIn, AllIn, NotIn, AnyNotIn, AllNotIn, GreaterThanOrEquals,
|
||||
// GreaterThan, LessThanOrEquals, LessThan, DurationGreaterThanOrEquals, DurationGreaterThan,
|
||||
// DurationLessThanOrEquals, DurationLessThan
|
||||
Operator ConditionOperator `json:"operator,omitempty" yaml:"operator,omitempty"`
|
||||
|
||||
// Value is the conditional value, or set of values. The values can be fixed set
|
||||
// or can be variables declared using JMESPath.
|
||||
// +optional
|
||||
RawValue *apiextv1.JSON `json:"value,omitempty" yaml:"value,omitempty"`
|
||||
}
|
||||
|
||||
type AnyAllConditions struct {
|
||||
// AnyConditions enable variable-based conditional rule execution. This is useful for
|
||||
// finer control of when an rule is applied. A condition can reference object data
|
||||
// using JMESPath notation.
|
||||
// Here, at least one of the conditions need to pass
|
||||
// +optional
|
||||
AnyConditions []Condition `json:"any,omitempty" yaml:"any,omitempty"`
|
||||
|
||||
// AllConditions enable variable-based conditional rule execution. This is useful for
|
||||
// finer control of when an rule is applied. A condition can reference object data
|
||||
// using JMESPath notation.
|
||||
// Here, all of the conditions need to pass
|
||||
// +optional
|
||||
AllConditions []Condition `json:"all,omitempty" yaml:"all,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceFilters is a slice of ResourceFilter
|
||||
type ResourceFilters []ResourceFilter
|
||||
|
||||
// ResourceFilter allow users to "AND" or "OR" between resources
|
||||
type ResourceFilter struct {
|
||||
// UserInfo contains information about the user performing the operation.
|
||||
// +optional
|
||||
kyvernov1.UserInfo `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// ResourceDescription contains information about the resource being created or modified.
|
||||
ResourceDescription `json:"resources,omitempty" yaml:"resources,omitempty"`
|
||||
}
|
4
api/kyverno/v2beta1/doc.go
Executable file
4
api/kyverno/v2beta1/doc.go
Executable file
|
@ -0,0 +1,4 @@
|
|||
// +k8s:deepcopy-gen=package
|
||||
// +groupName=kyverno.io
|
||||
|
||||
package v2beta1
|
141
api/kyverno/v2beta1/image_verification_test.go
Normal file
141
api/kyverno/v2beta1/image_verification_test.go
Normal file
|
@ -0,0 +1,141 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"gotest.tools/assert"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_ImageVerification(t *testing.T) {
|
||||
path := field.NewPath("dummy")
|
||||
testCases := []struct {
|
||||
name string
|
||||
subject ImageVerification
|
||||
errors func(*ImageVerification) field.ErrorList
|
||||
}{{
|
||||
name: "no attestors",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no entries",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{
|
||||
{Entries: []kyvernov1.Attestor{}},
|
||||
},
|
||||
},
|
||||
errors: func(i *ImageVerification) field.ErrorList {
|
||||
return field.ErrorList{
|
||||
field.Invalid(path.Child("attestors").Index(0), &i.Attestors[0], "An entry is required"),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty attestor",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{
|
||||
{Entries: []kyvernov1.Attestor{{}}},
|
||||
},
|
||||
},
|
||||
errors: func(i *ImageVerification) field.ErrorList {
|
||||
return field.ErrorList{
|
||||
field.Invalid(path.Child("attestors").Index(0).Child("entries").Index(0),
|
||||
&i.Attestors[0].Entries[0], "keys, certificates, keyless, or a nested attestor is required"),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty static key attestor",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{
|
||||
{Entries: []kyvernov1.Attestor{{
|
||||
Keys: &kyvernov1.StaticKeyAttestor{},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
errors: func(i *ImageVerification) field.ErrorList {
|
||||
return field.ErrorList{
|
||||
field.Invalid(path.Child("attestors").Index(0).Child("entries").Index(0).Child("keys"),
|
||||
i.Attestors[0].Entries[0].Keys, "A key is required"),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid static key attestor",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{
|
||||
{Entries: []kyvernov1.Attestor{{
|
||||
Keys: &kyvernov1.StaticKeyAttestor{PublicKeys: "bla"},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid keyless attestor",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{
|
||||
{Entries: []kyvernov1.Attestor{{
|
||||
Keyless: &kyvernov1.KeylessAttestor{Rekor: &kyvernov1.CTLog{}, Issuer: "", Subject: ""},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
errors: func(i *ImageVerification) field.ErrorList {
|
||||
return field.ErrorList{
|
||||
field.Invalid(path.Child("attestors").Index(0).Child("entries").Index(0).Child("keyless"),
|
||||
i.Attestors[0].Entries[0].Keyless, "An URL is required"),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid keyless attestor",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyvernov1.AttestorSet{
|
||||
{Entries: []kyvernov1.Attestor{{
|
||||
Keyless: &kyvernov1.KeylessAttestor{Rekor: &kyvernov1.CTLog{URL: "https://rekor.sigstore.dev"}, Issuer: "bla", Subject: "bla"},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid keyless attestor",
|
||||
subject: ImageVerification{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestations: []kyvernov1.Attestation{
|
||||
{
|
||||
PredicateType: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
errors: func(i *ImageVerification) field.ErrorList {
|
||||
return field.ErrorList{
|
||||
field.Invalid(path, i, "An attestor is required"),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
subject := test.subject
|
||||
errs := subject.Validate(path)
|
||||
var expectedErrs field.ErrorList
|
||||
if test.errors != nil {
|
||||
expectedErrs = test.errors(&subject)
|
||||
}
|
||||
|
||||
assert.Equal(t, len(errs), len(expectedErrs), fmt.Sprintf("test `%s` error count mismatch, errors %v", test.name, errs))
|
||||
if len(errs) != 0 {
|
||||
assert.DeepEqual(t, errs, expectedErrs)
|
||||
}
|
||||
}
|
||||
}
|
72
api/kyverno/v2beta1/image_verification_types.go
Normal file
72
api/kyverno/v2beta1/image_verification_types.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// ImageVerification validates that images that match the specified pattern
|
||||
// are signed with the supplied public key. Once the image is verified it is
|
||||
// mutated to include the SHA digest retrieved during the registration.
|
||||
type ImageVerification struct {
|
||||
// ImageReferences is a list of matching image reference patterns. At least one pattern in the
|
||||
// list must match the image for the rule to apply. Each image reference consists of a registry
|
||||
// address (defaults to docker.io), repository, image, and tag (defaults to latest).
|
||||
// Wildcards ('*' and '?') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.
|
||||
// +kubebuilder:validation:Optional
|
||||
ImageReferences []string `json:"imageReferences,omitempty" yaml:"imageReferences,omitempty"`
|
||||
|
||||
// Attestors specified the required attestors (i.e. authorities)
|
||||
// +kubebuilder:validation:Optional
|
||||
Attestors []kyvernov1.AttestorSet `json:"attestors,omitempty" yaml:"attestors,omitempty"`
|
||||
|
||||
// Attestations are optional checks for signed in-toto Statements used to verify the image.
|
||||
// See https://github.com/in-toto/attestation. Kyverno fetches signed attestations from the
|
||||
// OCI registry and decodes them into a list of Statement declarations.
|
||||
Attestations []kyvernov1.Attestation `json:"attestations,omitempty" yaml:"attestations,omitempty"`
|
||||
|
||||
// Repository is an optional alternate OCI repository to use for image signatures and attestations that match this rule.
|
||||
// If specified Repository will override the default OCI image repository configured for the installation.
|
||||
// The repository can also be overridden per Attestor or Attestation.
|
||||
Repository string `json:"repository,omitempty" yaml:"repository,omitempty"`
|
||||
|
||||
// MutateDigest enables replacement of image tags with digests.
|
||||
// Defaults to true.
|
||||
// +kubebuilder:default=true
|
||||
// +kubebuilder:validation:Optional
|
||||
MutateDigest bool `json:"mutateDigest" yaml:"mutateDigest"`
|
||||
|
||||
// VerifyDigest validates that images have a digest.
|
||||
// +kubebuilder:default=true
|
||||
// +kubebuilder:validation:Optional
|
||||
VerifyDigest bool `json:"verifyDigest" yaml:"verifyDigest"`
|
||||
|
||||
// Required validates that images are verified i.e. have matched passed a signature or attestation check.
|
||||
// +kubebuilder:default=true
|
||||
// +kubebuilder:validation:Optional
|
||||
Required bool `json:"required" yaml:"required"`
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (iv *ImageVerification) Validate(path *field.Path) (errs field.ErrorList) {
|
||||
copy := iv
|
||||
|
||||
if len(copy.ImageReferences) == 0 {
|
||||
errs = append(errs, field.Invalid(path, iv, "An image reference is required"))
|
||||
}
|
||||
|
||||
hasAttestors := len(copy.Attestors) > 0
|
||||
hasAttestations := len(copy.Attestations) > 0
|
||||
|
||||
if hasAttestations && !hasAttestors {
|
||||
errs = append(errs, field.Invalid(path, iv, "An attestor is required"))
|
||||
}
|
||||
|
||||
attestorsPath := path.Child("attestors")
|
||||
for i, as := range copy.Attestors {
|
||||
attestorErrors := as.Validate(attestorsPath.Index(i))
|
||||
errs = append(errs, attestorErrors...)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
68
api/kyverno/v2beta1/match_resources_test.go
Normal file
68
api/kyverno/v2beta1/match_resources_test.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"gotest.tools/assert"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_MatchResources(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
namespaced bool
|
||||
subject MatchResources
|
||||
errors []string
|
||||
}{{
|
||||
name: "valid",
|
||||
namespaced: true,
|
||||
subject: MatchResources{
|
||||
Any: kyvernov1.ResourceFilters{{
|
||||
UserInfo: kyvernov1.UserInfo{
|
||||
Subjects: []rbacv1.Subject{{
|
||||
Kind: "ServiceAccount",
|
||||
Namespace: "ns",
|
||||
Name: "sa-1",
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "any-all",
|
||||
namespaced: true,
|
||||
subject: MatchResources{
|
||||
Any: kyvernov1.ResourceFilters{{
|
||||
UserInfo: kyvernov1.UserInfo{
|
||||
Subjects: []rbacv1.Subject{{
|
||||
Kind: "ServiceAccount",
|
||||
Namespace: "ns",
|
||||
Name: "sa-1",
|
||||
}},
|
||||
},
|
||||
}},
|
||||
All: kyvernov1.ResourceFilters{{
|
||||
UserInfo: kyvernov1.UserInfo{
|
||||
Subjects: []rbacv1.Subject{{
|
||||
Kind: "ServiceAccount",
|
||||
Namespace: "ns",
|
||||
Name: "sa-1",
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
errors: []string{
|
||||
`dummy: Invalid value: v2beta1.MatchResources{Any:v1.ResourceFilters{v1.ResourceFilter{UserInfo:v1.UserInfo{Roles:[]string(nil), ClusterRoles:[]string(nil), Subjects:[]v1.Subject{v1.Subject{Kind:"ServiceAccount", APIGroup:"", Name:"sa-1", Namespace:"ns"}}}, ResourceDescription:v1.ResourceDescription{Kinds:[]string(nil), Name:"", Names:[]string(nil), Namespaces:[]string(nil), Annotations:map[string]string(nil), Selector:(*v1.LabelSelector)(nil), NamespaceSelector:(*v1.LabelSelector)(nil)}}}, All:v1.ResourceFilters{v1.ResourceFilter{UserInfo:v1.UserInfo{Roles:[]string(nil), ClusterRoles:[]string(nil), Subjects:[]v1.Subject{v1.Subject{Kind:"ServiceAccount", APIGroup:"", Name:"sa-1", Namespace:"ns"}}}, ResourceDescription:v1.ResourceDescription{Kinds:[]string(nil), Name:"", Names:[]string(nil), Namespaces:[]string(nil), Annotations:map[string]string(nil), Selector:(*v1.LabelSelector)(nil), NamespaceSelector:(*v1.LabelSelector)(nil)}}}}: Can't specify any and all together`,
|
||||
},
|
||||
}}
|
||||
|
||||
path := field.NewPath("dummy")
|
||||
for _, testCase := range testCases {
|
||||
errs := testCase.subject.Validate(path, testCase.namespaced, nil)
|
||||
assert.Equal(t, len(errs), len(testCase.errors))
|
||||
for i, err := range errs {
|
||||
assert.Equal(t, err.Error(), testCase.errors[i])
|
||||
}
|
||||
}
|
||||
}
|
49
api/kyverno/v2beta1/match_resources_types.go
Normal file
49
api/kyverno/v2beta1/match_resources_types.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// MatchResources is used to specify resource and admission review request data for
|
||||
// which a policy rule is applicable.
|
||||
type MatchResources struct {
|
||||
// Any allows specifying resources which will be ORed
|
||||
// +optional
|
||||
Any kyvernov1.ResourceFilters `json:"any,omitempty" yaml:"any,omitempty"`
|
||||
|
||||
// All allows specifying resources which will be ANDed
|
||||
// +optional
|
||||
All kyvernov1.ResourceFilters `json:"all,omitempty" yaml:"all,omitempty"`
|
||||
}
|
||||
|
||||
// GetKinds returns all kinds
|
||||
func (m *MatchResources) GetKinds() []string {
|
||||
var kinds []string
|
||||
for _, value := range m.All {
|
||||
kinds = append(kinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
for _, value := range m.Any {
|
||||
kinds = append(kinds, value.ResourceDescription.Kinds...)
|
||||
}
|
||||
return kinds
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.String) (errs field.ErrorList) {
|
||||
if len(m.Any) > 0 && len(m.All) > 0 {
|
||||
errs = append(errs, field.Invalid(path, m, "Can't specify any and all together"))
|
||||
}
|
||||
anyPath := path.Child("any")
|
||||
for i, filter := range m.Any {
|
||||
errs = append(errs, filter.UserInfo.Validate(anyPath.Index(i))...)
|
||||
errs = append(errs, filter.ResourceDescription.Validate(anyPath.Index(i), namespaced, clusterResources)...)
|
||||
}
|
||||
allPath := path.Child("all")
|
||||
for i, filter := range m.All {
|
||||
errs = append(errs, filter.UserInfo.Validate(anyPath.Index(i))...)
|
||||
errs = append(errs, filter.ResourceDescription.Validate(allPath.Index(i), namespaced, clusterResources)...)
|
||||
}
|
||||
return errs
|
||||
}
|
55
api/kyverno/v2beta1/policy_test.go
Normal file
55
api/kyverno/v2beta1/policy_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_Policy_Name(t *testing.T) {
|
||||
subject := Policy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "this-is-a-way-too-long-policy-name-that-should-trigger-an-error-when-calling-the-policy-validation-method",
|
||||
},
|
||||
}
|
||||
errs := subject.Validate(nil)
|
||||
assert.Assert(t, len(errs) == 1)
|
||||
assert.Equal(t, errs[0].Field, "name")
|
||||
assert.Equal(t, errs[0].Type, field.ErrorTypeTooLong)
|
||||
assert.Equal(t, errs[0].Detail, "must have at most 63 bytes")
|
||||
assert.Equal(t, errs[0].Error(), "name: Too long: must have at most 63 bytes")
|
||||
}
|
||||
|
||||
func Test_Policy_IsNamespaced(t *testing.T) {
|
||||
namespaced := Policy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "this-is-a-way-too-long-policy-name-that-should-trigger-an-error-when-calling-the-policy-validation-method",
|
||||
Namespace: "abcd",
|
||||
},
|
||||
}
|
||||
notNamespaced := Policy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "this-is-a-way-too-long-policy-name-that-should-trigger-an-error-when-calling-the-policy-validation-method",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, namespaced.IsNamespaced(), true)
|
||||
assert.Equal(t, notNamespaced.IsNamespaced(), true)
|
||||
}
|
||||
|
||||
func Test_Policy_Autogen_All(t *testing.T) {
|
||||
subject := Policy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "policy",
|
||||
Namespace: "abcd",
|
||||
Annotations: map[string]string{
|
||||
kyvernov1.PodControllersAnnotation: "all",
|
||||
},
|
||||
},
|
||||
}
|
||||
errs := subject.Validate(nil)
|
||||
assert.Equal(t, len(errs), 1)
|
||||
assert.Equal(t, errs[0].Error(), "metadata.annotations: Forbidden: Autogen annotation does not support 'all' anymore, remove the annotation or set it to a valid value")
|
||||
}
|
108
api/kyverno/v2beta1/policy_types.go
Executable file
108
api/kyverno/v2beta1/policy_types.go
Executable file
|
@ -0,0 +1,108 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Background",type="string",JSONPath=".spec.background"
|
||||
// +kubebuilder:printcolumn:name="Action",type="string",JSONPath=".spec.validationFailureAction"
|
||||
// +kubebuilder:printcolumn:name="Failure Policy",type="string",JSONPath=".spec.failurePolicy",priority=1
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.ready`
|
||||
// +kubebuilder:resource:shortName=pol
|
||||
|
||||
// Policy declares validation, mutation, and generation behaviors for matching resources.
|
||||
// See: https://kyverno.io/docs/writing-policies/ for more information.
|
||||
type Policy struct {
|
||||
metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
|
||||
// Spec defines policy behaviors and contains one or more rules.
|
||||
Spec Spec `json:"spec" yaml:"spec"`
|
||||
}
|
||||
|
||||
// HasAutoGenAnnotation checks if a policy has auto-gen annotation
|
||||
func (p *Policy) HasAutoGenAnnotation() bool {
|
||||
annotations := p.GetAnnotations()
|
||||
val, ok := annotations[kyvernov1.PodControllersAnnotation]
|
||||
if ok && strings.ToLower(val) != "none" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasMutateOrValidateOrGenerate checks for rule types
|
||||
func (p *Policy) HasMutateOrValidateOrGenerate() bool {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasMutate checks for mutate rule types
|
||||
func (p *Policy) HasMutate() bool {
|
||||
return p.Spec.HasMutate()
|
||||
}
|
||||
|
||||
// HasValidate checks for validate rule types
|
||||
func (p *Policy) HasValidate() bool {
|
||||
return p.Spec.HasValidate()
|
||||
}
|
||||
|
||||
// HasGenerate checks for generate rule types
|
||||
func (p *Policy) HasGenerate() bool {
|
||||
return p.Spec.HasGenerate()
|
||||
}
|
||||
|
||||
// HasVerifyImages checks for image verification rule types
|
||||
func (p *Policy) HasVerifyImages() bool {
|
||||
return p.Spec.HasVerifyImages()
|
||||
}
|
||||
|
||||
// BackgroundProcessingEnabled checks if background is set to true
|
||||
func (p *Policy) BackgroundProcessingEnabled() bool {
|
||||
return p.Spec.BackgroundProcessingEnabled()
|
||||
}
|
||||
|
||||
// GetSpec returns the policy spec
|
||||
func (p *Policy) GetSpec() *Spec {
|
||||
return &p.Spec
|
||||
}
|
||||
|
||||
// IsNamespaced indicates if the policy is namespace scoped
|
||||
func (p *Policy) IsNamespaced() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation.
|
||||
// namespaced means that the policy is bound to a namespace and therefore
|
||||
// should not filter/generate cluster wide resources.
|
||||
func (p *Policy) Validate(clusterResources sets.String) (errs field.ErrorList) {
|
||||
errs = append(errs, kyvernov1.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
|
||||
errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
|
||||
errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), clusterResources)...)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (p *Policy) GetKind() string {
|
||||
return p.Kind
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// PolicyList is a list of Policy instances.
|
||||
type PolicyList struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ListMeta `json:"metadata" yaml:"metadata"`
|
||||
Items []Policy `json:"items" yaml:"items"`
|
||||
}
|
41
api/kyverno/v2beta1/register.go
Executable file
41
api/kyverno/v2beta1/register.go
Executable file
|
@ -0,0 +1,41 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"github.com/kyverno/kyverno/api/kyverno"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: kyverno.GroupName, Version: "v2beta1"}
|
||||
|
||||
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
|
||||
func Kind(kind string) schema.GroupKind {
|
||||
return SchemeGroupVersion.WithKind(kind).GroupKind()
|
||||
}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
// SchemeBuilder builds the scheme
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
|
||||
// AddToScheme adds all types of this clientset into the given scheme
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// Adds the list of known types to Scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&ClusterPolicy{},
|
||||
&ClusterPolicyList{},
|
||||
&Policy{},
|
||||
&PolicyList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
}
|
69
api/kyverno/v2beta1/resource_description_test.go
Normal file
69
api/kyverno/v2beta1/resource_description_test.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_ResourceDescription(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
namespaced bool
|
||||
subject ResourceDescription
|
||||
errors []string
|
||||
}{{
|
||||
name: "valid",
|
||||
namespaced: true,
|
||||
subject: ResourceDescription{},
|
||||
}, {
|
||||
name: "name-names",
|
||||
namespaced: true,
|
||||
subject: ResourceDescription{
|
||||
Names: []string{"bar", "baz"},
|
||||
},
|
||||
errors: []string{
|
||||
`dummy: Invalid value: v2beta1.ResourceDescription{Kinds:[]string(nil), Names:[]string{"bar", "baz"}, Namespaces:[]string(nil), Annotations:map[string]string(nil), Selector:(*v1.LabelSelector)(nil), NamespaceSelector:(*v1.LabelSelector)(nil)}: Both name and names can not be specified together`,
|
||||
},
|
||||
}, {
|
||||
name: "selector",
|
||||
namespaced: true,
|
||||
subject: ResourceDescription{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app.type": "prod",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "bad-selector",
|
||||
namespaced: true,
|
||||
subject: ResourceDescription{
|
||||
Kinds: []string{"Deployment"},
|
||||
Selector: &metav1.LabelSelector{},
|
||||
},
|
||||
errors: []string{
|
||||
`dummy.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string(nil), MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: The requirements are not specified in selector`,
|
||||
},
|
||||
}, {
|
||||
name: "namespaces",
|
||||
namespaced: true,
|
||||
subject: ResourceDescription{
|
||||
Namespaces: []string{"abc"},
|
||||
},
|
||||
errors: []string{
|
||||
"dummy.namespaces: Forbidden: Filtering namespaces not allowed in namespaced policies",
|
||||
},
|
||||
}}
|
||||
|
||||
path := field.NewPath("dummy")
|
||||
for _, testCase := range testCases {
|
||||
errs := testCase.subject.Validate(path, testCase.namespaced, nil)
|
||||
assert.Equal(t, len(errs), len(testCase.errors))
|
||||
for i, err := range errs {
|
||||
assert.Equal(t, err.Error(), testCase.errors[i])
|
||||
}
|
||||
}
|
||||
}
|
77
api/kyverno/v2beta1/resource_description_types.go
Normal file
77
api/kyverno/v2beta1/resource_description_types.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// ResourceDescription contains criteria used to match resources.
|
||||
type ResourceDescription struct {
|
||||
// Kinds is a list of resource kinds.
|
||||
// +optional
|
||||
Kinds []string `json:"kinds,omitempty" yaml:"kinds,omitempty"`
|
||||
|
||||
// Names are the names of the resources. Each name supports wildcard characters
|
||||
// "*" (matches zero or many characters) and "?" (at least one character).
|
||||
// +optional
|
||||
Names []string `json:"names,omitempty" yaml:"names,omitempty"`
|
||||
|
||||
// Namespaces is a list of namespaces names. Each name supports wildcard characters
|
||||
// "*" (matches zero or many characters) and "?" (at least one character).
|
||||
// +optional
|
||||
Namespaces []string `json:"namespaces,omitempty" yaml:"namespaces,omitempty"`
|
||||
|
||||
// Annotations is a map of annotations (key-value pairs of type string). Annotation keys
|
||||
// and values support the wildcard characters "*" (matches zero or many characters) and
|
||||
// "?" (matches at least one character).
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
|
||||
// Selector is a label selector. Label keys and values in `matchLabels` support the wildcard
|
||||
// characters `*` (matches zero or many characters) and `?` (matches one character).
|
||||
// Wildcards allows writing label selectors like ["storage.k8s.io/*": "*"]. Note that
|
||||
// using ["*" : "*"] matches any key and value but does not match an empty label set.
|
||||
// +optional
|
||||
Selector *metav1.LabelSelector `json:"selector,omitempty" yaml:"selector,omitempty"`
|
||||
|
||||
// NamespaceSelector is a label selector for the resource namespace. Label keys and values
|
||||
// in `matchLabels` support the wildcard characters `*` (matches zero or many characters)
|
||||
// and `?` (matches one character).Wildcards allows writing label selectors like
|
||||
// ["storage.k8s.io/*": "*"]. Note that using ["*" : "*"] matches any key and value but
|
||||
// does not match an empty label set.
|
||||
// +optional
|
||||
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" yaml:"namespaceSelector,omitempty"`
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (r *ResourceDescription) Validate(path *field.Path, namespaced bool, clusterResources sets.String) (errs field.ErrorList) {
|
||||
if len(r.Names) > 0 {
|
||||
errs = append(errs, field.Invalid(path, r, "Both name and names can not be specified together"))
|
||||
}
|
||||
if r.Selector != nil && !kubeutils.LabelSelectorContainsWildcard(r.Selector) {
|
||||
if selector, err := metav1.LabelSelectorAsSelector(r.Selector); err != nil {
|
||||
errs = append(errs, field.Invalid(path.Child("selector"), r.Selector, err.Error()))
|
||||
} else {
|
||||
requirements, _ := selector.Requirements()
|
||||
if len(requirements) == 0 {
|
||||
errs = append(errs, field.Invalid(path.Child("selector"), r.Selector, "The requirements are not specified in selector"))
|
||||
}
|
||||
}
|
||||
}
|
||||
if namespaced {
|
||||
if len(r.Namespaces) > 0 {
|
||||
errs = append(errs, field.Forbidden(path.Child("namespaces"), "Filtering namespaces not allowed in namespaced policies"))
|
||||
}
|
||||
kindsChild := path.Child("kinds")
|
||||
for i, kind := range r.Kinds {
|
||||
if clusterResources.Has(kind) {
|
||||
errs = append(errs, field.Forbidden(kindsChild.Index(i), fmt.Sprintf("Cluster wide resource '%s' not allowed in namespaced policy", kind)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
237
api/kyverno/v2beta1/rule_test.go
Normal file
237
api/kyverno/v2beta1/rule_test.go
Normal file
|
@ -0,0 +1,237 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_Validate_RuleType_EmptyRule(t *testing.T) {
|
||||
subject := Rule{
|
||||
Name: "validate-user-privilege",
|
||||
}
|
||||
path := field.NewPath("dummy")
|
||||
errs := subject.Validate(path, false, nil)
|
||||
assert.Equal(t, len(errs), 1)
|
||||
assert.Equal(t, errs[0].Field, "dummy")
|
||||
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
||||
assert.Equal(t, errs[0].Detail, "No operation defined in the rule 'validate-user-privilege'.(supported operations: mutate,validate,generate,verifyImages)")
|
||||
}
|
||||
|
||||
func Test_Validate_RuleType_MultipleRule(t *testing.T) {
|
||||
rawPolicy := []byte(`
|
||||
{
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-user-privilege",
|
||||
"match": {
|
||||
"all": [
|
||||
{
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Deployment"
|
||||
],
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app.type": "prod"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"mutate": {
|
||||
"patchStrategicMerge": {
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"(name)": "*",
|
||||
"resources": {
|
||||
"limits": {
|
||||
"+(memory)": "300Mi",
|
||||
"+(cpu)": "100"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "validate container security contexts",
|
||||
"anyPattern": [
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"securityContext": {
|
||||
"runAsNonRoot": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
var policy *ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
path := field.NewPath("dummy")
|
||||
errs := rule.Validate(path, false, nil)
|
||||
assert.Assert(t, len(errs) != 0)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Validate_RuleType_SingleRule(t *testing.T) {
|
||||
rawPolicy := []byte(`
|
||||
{
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-user-privilege",
|
||||
"match": {
|
||||
"all": [
|
||||
{
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Deployment"
|
||||
],
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app.type": "prod"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"validate": {
|
||||
"message": "validate container security contexts",
|
||||
"anyPattern": [
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"securityContext": {
|
||||
"runAsNonRoot": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
var policy *ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
path := field.NewPath("dummy")
|
||||
errs := rule.Validate(path, false, nil)
|
||||
assert.Assert(t, len(errs) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_doesMatchExcludeConflict(t *testing.T) {
|
||||
path := field.NewPath("dummy")
|
||||
testcases := []struct {
|
||||
description string
|
||||
rule []byte
|
||||
errors func(r *Rule) field.ErrorList
|
||||
}{
|
||||
{
|
||||
description: "Same match and exclude",
|
||||
rule: []byte(`{ "name": "set-image-pull-policy-2", "match": { "any": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ], "selector": { "matchLabels": { "memory": "high" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ], "subjects": [ { "name": "something", "kind": "something", "Namespace": "something", "apiGroup": "something" }, { "name": "something1", "kind": "something1", "Namespace": "something1", "apiGroup": "something1" } ], "clusterroles": [ "something", "something1" ], "roles": [ "something", "something1" ] }, "exclude": { "any": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ], "selector": { "matchLabels": { "memory": "high" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ], "subjects": [ { "name": "something", "kind": "something", "Namespace": "something", "apiGroup": "something" }, { "name": "something1", "kind": "something1", "Namespace": "something1", "apiGroup": "something1" } ], "clusterroles": [ "something", "something1" ], "roles": [ "something", "something1" ] } }`),
|
||||
errors: func(r *Rule) (errs field.ErrorList) {
|
||||
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude kind",
|
||||
rule: []byte(`{ "name": "set-image-pull-policy-2", "match": { "all": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ], "selector": { "matchLabels": { "memory": "high" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ], "subjects": [ { "name": "something", "kind": "something", "Namespace": "something", "apiGroup": "something" }, { "name": "something1", "kind": "something1", "Namespace": "something1", "apiGroup": "something1" } ], "clusterroles": [ "something", "something1" ], "roles": [ "something", "something1" ] }, "exclude": { "all": [ { "resources": { "kinds": [ "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ], "selector": { "matchLabels": { "memory": "high" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ], "subjects": [ { "name": "something", "kind": "something", "Namespace": "something", "apiGroup": "something" }, { "name": "something1", "kind": "something1", "Namespace": "something1", "apiGroup": "something1" } ], "clusterroles": [ "something", "something1" ], "roles": [ "something", "something1" ] } }`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude name",
|
||||
rule: []byte(`{ "name": "set-image-pull-policy-2", "match": { "all": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ], "selector": { "matchLabels": { "memory": "high" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ], "subjects": [ { "name": "something", "kind": "something", "Namespace": "something", "apiGroup": "something" }, { "name": "something1", "kind": "something1", "Namespace": "something1", "apiGroup": "something1" } ], "clusterroles": [ "something", "something1" ], "roles": [ "something", "something1" ] }, "exclude": { "all": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something-*", "namespaces": [ "something", "something1" ], "selector": { "matchLabels": { "memory": "high" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ], "subjects": [ { "name": "something", "kind": "something", "Namespace": "something", "apiGroup": "something" }, { "name": "something1", "kind": "something1", "Namespace": "something1", "apiGroup": "something1" } ], "clusterroles": [ "something", "something1" ], "roles": [ "something", "something1" ] } }`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude namespace",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something3","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude labels",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"higha"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude expression",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["databases"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude subjects",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something2","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude clusterroles",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something3","something1"],"roles":["something","something1"]}}`),
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude roles",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude": { "all": [ { "resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"] } ] } } } ],"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something3","something1"]}}`),
|
||||
},
|
||||
{
|
||||
description: "simple",
|
||||
rule: []byte(`{ "name": "set-image-pull-policy-2", "match": { "any": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ] } } ] }, "exclude": { "any": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "something", "namespaces": [ "something", "something1" ] } } ] } }`),
|
||||
errors: func(r *Rule) (errs field.ErrorList) {
|
||||
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "simple - fail",
|
||||
rule: []byte(`{ "name": "set-image-pull-policy-2", "match": { "any": [ { "resources": { "kinds": [ "Pod", "Namespace" ], "name": "somxething", "namespaces": [ "something", "something1" ] } } ] }, "exclude": { "any": [ { "resources": { "kinds": [ "Pod", "Namespace", "Job" ], "name": "some*", "namespaces": [ "something", "something1", "something2" ] } } ] } }`),
|
||||
},
|
||||
{
|
||||
description: "empty case",
|
||||
rule: []byte(`{ "name": "check-allow-deletes", "match": { "all": [ { "resources": { "selector": { "matchLabels": { "allow-deletes": "false" } } } } ] }, "exclude": { "clusterRoles": [ "random" ] }, "validate": { "message": "Deleting {{request.object.kind}}/{{request.object.metadata.name}} is not allowed", "deny": { "conditions": { "all": [ { "key": "{{request.operation}}", "operator": "Equal", "value": "DELETE" } ] } } } }`),
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
var rule Rule
|
||||
err := json.Unmarshal(testcase.rule, &rule)
|
||||
assert.NilError(t, err)
|
||||
errs := rule.ValidateMatchExcludeConflict(path)
|
||||
var expectedErrs field.ErrorList
|
||||
if testcase.errors != nil {
|
||||
expectedErrs = testcase.errors(&rule)
|
||||
}
|
||||
assert.Equal(t, len(errs), len(expectedErrs))
|
||||
for i := range errs {
|
||||
assert.Equal(t, errs[i].Error(), expectedErrs[i].Error())
|
||||
}
|
||||
}
|
||||
}
|
184
api/kyverno/v2beta1/rule_types.go
Normal file
184
api/kyverno/v2beta1/rule_types.go
Normal file
|
@ -0,0 +1,184 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// Rule defines a validation, mutation, or generation control for matching resources.
|
||||
// Each rules contains a match declaration to select resources, and an optional exclude
|
||||
// declaration to specify which resources to exclude.
|
||||
type Rule struct {
|
||||
// Name is a label to identify the rule, It must be unique within the policy.
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
|
||||
// Context defines variables and data sources that can be used during rule execution.
|
||||
// +optional
|
||||
Context []kyvernov1.ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
|
||||
// MatchResources defines when this policy rule should be applied. The match
|
||||
// criteria can include resource information (e.g. kind, name, namespace, labels)
|
||||
// and admission review request information like the user name or role.
|
||||
// At least one kind is required.
|
||||
MatchResources MatchResources `json:"match,omitempty" yaml:"match,omitempty"`
|
||||
|
||||
// ExcludeResources defines when this policy rule should not be applied. The exclude
|
||||
// criteria can include resource information (e.g. kind, name, namespace, labels)
|
||||
// and admission review request information like the name or role.
|
||||
// +optional
|
||||
ExcludeResources MatchResources `json:"exclude,omitempty" yaml:"exclude,omitempty"`
|
||||
|
||||
// ImageExtractors defines a mapping from kinds to ImageExtractorConfigs.
|
||||
// This config is only valid for verifyImages rules.
|
||||
// +optional
|
||||
ImageExtractors kyvernov1.ImageExtractorConfigs `json:"imageExtractors,omitempty" yaml:"imageExtractors,omitempty"`
|
||||
|
||||
// Preconditions are used to determine if a policy rule should be applied by evaluating a
|
||||
// set of conditions. The declaration can contain nested `any` or `all` statements. A direct list
|
||||
// of conditions (without `any` or `all` statements is supported for backwards compatibility but
|
||||
// See: https://kyverno.io/docs/writing-policies/preconditions/
|
||||
// +optional
|
||||
RawAnyAllConditions *AnyAllConditions `json:"preconditions,omitempty" yaml:"preconditions,omitempty"`
|
||||
|
||||
// Mutation is used to modify matching resources.
|
||||
// +optional
|
||||
Mutation kyvernov1.Mutation `json:"mutate,omitempty" yaml:"mutate,omitempty"`
|
||||
|
||||
// Validation is used to validate matching resources.
|
||||
// +optional
|
||||
Validation Validation `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||
|
||||
// Generation is used to create new resources.
|
||||
// +optional
|
||||
Generation kyvernov1.Generation `json:"generate,omitempty" yaml:"generate,omitempty"`
|
||||
|
||||
// VerifyImages is used to verify image signatures and mutate them to add a digest
|
||||
// +optional
|
||||
VerifyImages []ImageVerification `json:"verifyImages,omitempty" yaml:"verifyImages,omitempty"`
|
||||
}
|
||||
|
||||
// HasMutate checks for mutate rule
|
||||
func (r *Rule) HasMutate() bool {
|
||||
return !reflect.DeepEqual(r.Mutation, kyvernov1.Mutation{})
|
||||
}
|
||||
|
||||
// HasVerifyImages checks for verifyImages rule
|
||||
func (r *Rule) HasVerifyImages() bool {
|
||||
return r.VerifyImages != nil && !reflect.DeepEqual(r.VerifyImages, ImageVerification{})
|
||||
}
|
||||
|
||||
// HasYAMLSignatureVerify checks for validate.manifests rule
|
||||
func (r Rule) HasYAMLSignatureVerify() bool {
|
||||
return r.Validation.Manifests != nil && len(r.Validation.Manifests.Attestors) != 0
|
||||
}
|
||||
|
||||
// HasImagesValidationChecks checks whether the verifyImages rule has validation checks
|
||||
func (r *Rule) HasImagesValidationChecks() bool {
|
||||
for _, v := range r.VerifyImages {
|
||||
if v.VerifyDigest || v.Required {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasYAMLSignatureVerify checks for validate rule
|
||||
func (p *ClusterPolicy) HasYAMLSignatureVerify() bool {
|
||||
for _, rule := range p.Spec.Rules {
|
||||
if rule.HasYAMLSignatureVerify() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasValidate checks for validate rule
|
||||
func (r *Rule) HasValidate() bool {
|
||||
return !reflect.DeepEqual(r.Validation, Validation{})
|
||||
}
|
||||
|
||||
// HasGenerate checks for generate rule
|
||||
func (r *Rule) HasGenerate() bool {
|
||||
return !reflect.DeepEqual(r.Generation, kyvernov1.Generation{})
|
||||
}
|
||||
|
||||
// IsMutateExisting checks if the mutate rule applies to existing resources
|
||||
func (r *Rule) IsMutateExisting() bool {
|
||||
return r.Mutation.Targets != nil
|
||||
}
|
||||
|
||||
// IsCloneSyncGenerate checks if the generate rule has the clone block with sync=true
|
||||
func (r *Rule) GetCloneSyncForGenerate() (clone bool, sync bool) {
|
||||
if !r.HasGenerate() {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Generation.Clone.Name != "" {
|
||||
clone = true
|
||||
}
|
||||
|
||||
sync = r.Generation.Synchronize
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateRuleType checks only one type of rule is defined per rule
|
||||
func (r *Rule) ValidateRuleType(path *field.Path) (errs field.ErrorList) {
|
||||
ruleTypes := []bool{r.HasMutate(), r.HasValidate(), r.HasGenerate(), r.HasVerifyImages()}
|
||||
count := 0
|
||||
for _, v := range ruleTypes {
|
||||
if v {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
errs = append(errs, field.Invalid(path, r, fmt.Sprintf("No operation defined in the rule '%s'.(supported operations: mutate,validate,generate,verifyImages)", r.Name)))
|
||||
} else if count != 1 {
|
||||
errs = append(errs, field.Invalid(path, r, fmt.Sprintf("Multiple operations defined in the rule '%s', only one operation (mutate,validate,generate,verifyImages) is allowed per rule", r.Name)))
|
||||
}
|
||||
|
||||
if r.ImageExtractors != nil && !r.HasVerifyImages() {
|
||||
errs = append(errs, field.Invalid(path.Child("imageExtractors"), r, fmt.Sprintf("Invalid rule spec for rule '%s', imageExtractors can only be defined for verifyImages rule", r.Name)))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set
|
||||
func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) {
|
||||
if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 {
|
||||
return errs
|
||||
}
|
||||
// if both have any then no resource should be common
|
||||
if len(r.MatchResources.Any) > 0 && len(r.ExcludeResources.Any) > 0 {
|
||||
for _, rmr := range r.MatchResources.Any {
|
||||
for _, rer := range r.ExcludeResources.Any {
|
||||
if reflect.DeepEqual(rmr, rer) {
|
||||
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
if reflect.DeepEqual(r.ExcludeResources.Any, r.MatchResources.Any) {
|
||||
return errs
|
||||
}
|
||||
if reflect.DeepEqual(r.ExcludeResources.All, r.MatchResources.All) {
|
||||
return errs
|
||||
}
|
||||
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (r *Rule) Validate(path *field.Path, namespaced bool, clusterResources sets.String) (errs field.ErrorList) {
|
||||
errs = append(errs, r.ValidateRuleType(path)...)
|
||||
errs = append(errs, r.ValidateMatchExcludeConflict(path)...)
|
||||
errs = append(errs, r.MatchResources.Validate(path.Child("match"), namespaced, clusterResources)...)
|
||||
errs = append(errs, r.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
|
||||
return errs
|
||||
}
|
55
api/kyverno/v2beta1/spec_test.go
Normal file
55
api/kyverno/v2beta1/spec_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"gotest.tools/assert"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test_Validate_UniqueRuleName(t *testing.T) {
|
||||
subject := Spec{
|
||||
Rules: []Rule{{
|
||||
Name: "deny-privileged-disallowpriviligedescalation",
|
||||
MatchResources: MatchResources{
|
||||
Any: kyvernov1.ResourceFilters{{
|
||||
ResourceDescription: kyvernov1.ResourceDescription{
|
||||
Kinds: []string{
|
||||
"Pod",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
Validation: Validation{
|
||||
Message: "message",
|
||||
RawAnyPattern: &apiextv1.JSON{
|
||||
Raw: []byte("{"),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "deny-privileged-disallowpriviligedescalation",
|
||||
MatchResources: MatchResources{
|
||||
Any: kyvernov1.ResourceFilters{{
|
||||
ResourceDescription: kyvernov1.ResourceDescription{
|
||||
Kinds: []string{
|
||||
"Pod",
|
||||
},
|
||||
}},
|
||||
}},
|
||||
Validation: Validation{
|
||||
Message: "message",
|
||||
RawAnyPattern: &apiextv1.JSON{
|
||||
Raw: []byte("{"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
path := field.NewPath("dummy")
|
||||
errs := subject.Validate(path, false, nil)
|
||||
assert.Equal(t, len(errs), 1)
|
||||
assert.Equal(t, errs[0].Field, "dummy.rules[1].name")
|
||||
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
||||
assert.Equal(t, errs[0].Detail, "Duplicate rule name: 'deny-privileged-disallowpriviligedescalation'")
|
||||
}
|
234
api/kyverno/v2beta1/spec_types.go
Normal file
234
api/kyverno/v2beta1/spec_types.go
Normal file
|
@ -0,0 +1,234 @@
|
|||
package v2beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// Spec contains a list of Rule instances and other policy controls.
|
||||
type Spec struct {
|
||||
// Rules is a list of Rule instances. A Policy contains multiple rules and
|
||||
// each rule can validate, mutate, or generate resources.
|
||||
Rules []Rule `json:"rules,omitempty" yaml:"rules,omitempty"`
|
||||
|
||||
// ApplyRules controls how rules in a policy are applied. Rule are processed in
|
||||
// the order of declaration. When set to `One` processing stops after a rule has
|
||||
// been applied i.e. the rule matches and results in a pass, fail, or error. When
|
||||
// set to `All` all rules in the policy are processed. The default is `All`.
|
||||
// +optional
|
||||
ApplyRules *kyvernov1.ApplyRulesType `json:"applyRules,omitempty" yaml:"applyRules,omitempty"`
|
||||
|
||||
// FailurePolicy defines how unexpected policy errors and webhook response timeout errors are handled.
|
||||
// Rules within the same policy share the same failure behavior.
|
||||
// Allowed values are Ignore or Fail. Defaults to Fail.
|
||||
// +optional
|
||||
FailurePolicy *kyvernov1.FailurePolicyType `json:"failurePolicy,omitempty" yaml:"failurePolicy,omitempty"`
|
||||
|
||||
// ValidationFailureAction defines if a validation policy rule violation should block
|
||||
// the admission review request (enforce), or allow (audit) the admission review request
|
||||
// and report an error in a policy report. Optional.
|
||||
// Allowed values are audit or enforce. The default value is "audit".
|
||||
// +optional
|
||||
// +kubebuilder:validation:Enum=audit;enforce
|
||||
ValidationFailureAction kyvernov1.ValidationFailureAction `json:"validationFailureAction,omitempty" yaml:"validationFailureAction,omitempty"`
|
||||
|
||||
// ValidationFailureActionOverrides is a Cluster Policy attribute that specifies ValidationFailureAction
|
||||
// namespace-wise. It overrides ValidationFailureAction for the specified namespaces.
|
||||
// +optional
|
||||
ValidationFailureActionOverrides []kyvernov1.ValidationFailureActionOverride `json:"validationFailureActionOverrides,omitempty" yaml:"validationFailureActionOverrides,omitempty"`
|
||||
|
||||
// Background 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
|
||||
Background *bool `json:"background,omitempty" yaml:"background,omitempty"`
|
||||
|
||||
// SchemaValidation skips policy validation checks.
|
||||
// Optional. The default value is set to "true", it must be set to "false" to disable the validation checks.
|
||||
// +optional
|
||||
SchemaValidation *bool `json:"schemaValidation,omitempty" yaml:"schemaValidation,omitempty"`
|
||||
|
||||
// WebhookTimeoutSeconds 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.
|
||||
WebhookTimeoutSeconds *int32 `json:"webhookTimeoutSeconds,omitempty" yaml:"webhookTimeoutSeconds,omitempty"`
|
||||
|
||||
// MutateExistingOnPolicyUpdate controls if a mutateExisting policy is applied on policy events.
|
||||
// Default value is "false".
|
||||
// +optional
|
||||
MutateExistingOnPolicyUpdate bool `json:"mutateExistingOnPolicyUpdate,omitempty" yaml:"mutateExistingOnPolicyUpdate,omitempty"`
|
||||
|
||||
// GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources
|
||||
// If is set to "true" generate rule will be triggered and applied to existing matched resources.
|
||||
// Defaults to "false" if not specified.
|
||||
// +optional
|
||||
GenerateExistingOnPolicyUpdate bool `json:"generateExistingOnPolicyUpdate,omitempty" yaml:"generateExistingOnPolicyUpdate,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Spec) SetRules(rules []Rule) {
|
||||
s.Rules = rules
|
||||
}
|
||||
|
||||
// HasMutateOrValidateOrGenerate checks for rule types
|
||||
func (s *Spec) HasMutateOrValidateOrGenerate() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasMutate checks for mutate rule types
|
||||
func (s *Spec) HasMutate() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasMutate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasValidate checks for validate rule types
|
||||
func (s *Spec) HasValidate() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasValidate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasGenerate checks for generate rule types
|
||||
func (s *Spec) HasGenerate() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasGenerate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasImagesValidationChecks checks for image verification rules invoked during resource validation
|
||||
func (s *Spec) HasImagesValidationChecks() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasImagesValidationChecks() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasVerifyImages checks for image verification rules invoked during resource mutation
|
||||
func (s *Spec) HasVerifyImages() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasVerifyImages() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasYAMLSignatureVerify checks for image verification rules invoked during resource mutation
|
||||
func (s *Spec) HasYAMLSignatureVerify() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.HasYAMLSignatureVerify() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// BackgroundProcessingEnabled checks if background is set to true
|
||||
func (s *Spec) BackgroundProcessingEnabled() bool {
|
||||
if s.Background == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return *s.Background
|
||||
}
|
||||
|
||||
// IsMutateExisting checks if the mutate policy applies to existing resources
|
||||
func (s *Spec) IsMutateExisting() bool {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.IsMutateExisting() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetMutateExistingOnPolicyUpdate return MutateExistingOnPolicyUpdate set value
|
||||
func (s *Spec) GetMutateExistingOnPolicyUpdate() bool {
|
||||
return s.MutateExistingOnPolicyUpdate
|
||||
}
|
||||
|
||||
// IsGenerateExistingOnPolicyUpdate return GenerateExistingOnPolicyUpdate set value
|
||||
func (s *Spec) IsGenerateExistingOnPolicyUpdate() bool {
|
||||
return s.GenerateExistingOnPolicyUpdate
|
||||
}
|
||||
|
||||
// GetFailurePolicy returns the failure policy to be applied
|
||||
func (s *Spec) GetFailurePolicy() kyvernov1.FailurePolicyType {
|
||||
if s.FailurePolicy == nil {
|
||||
return kyvernov1.Fail
|
||||
}
|
||||
return *s.FailurePolicy
|
||||
}
|
||||
|
||||
// GetValidationFailureAction returns the validation failure action to be applied
|
||||
func (s *Spec) GetValidationFailureAction() kyvernov1.ValidationFailureAction {
|
||||
if s.ValidationFailureAction == "" {
|
||||
return kyvernov1.Audit
|
||||
}
|
||||
|
||||
return s.ValidationFailureAction
|
||||
}
|
||||
|
||||
// GetFailurePolicy returns the failure policy to be applied
|
||||
func (s *Spec) GetApplyRules() kyvernov1.ApplyRulesType {
|
||||
if s.ApplyRules == nil {
|
||||
return kyvernov1.ApplyAll
|
||||
}
|
||||
return *s.ApplyRules
|
||||
}
|
||||
|
||||
// ValidateRuleNames checks if the rule names are unique across a policy
|
||||
func (s *Spec) ValidateRuleNames(path *field.Path) (errs field.ErrorList) {
|
||||
names := sets.NewString()
|
||||
for i, rule := range s.Rules {
|
||||
rulePath := path.Index(i)
|
||||
if names.Has(rule.Name) {
|
||||
errs = append(errs, field.Invalid(rulePath.Child("name"), rule, fmt.Sprintf(`Duplicate rule name: '%s'`, rule.Name)))
|
||||
}
|
||||
names.Insert(rule.Name)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// ValidateRules implements programmatic validation of Rules
|
||||
func (s *Spec) ValidateRules(path *field.Path, namespaced bool, clusterResources sets.String) (errs field.ErrorList) {
|
||||
errs = append(errs, s.ValidateRuleNames(path)...)
|
||||
for i, rule := range s.Rules {
|
||||
errs = append(errs, rule.Validate(path.Index(i), namespaced, clusterResources)...)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (s *Spec) Validate(path *field.Path, namespaced bool, clusterResources sets.String) (errs field.ErrorList) {
|
||||
errs = append(errs, s.ValidateRules(path.Child("rules"), namespaced, clusterResources)...)
|
||||
if namespaced && len(s.ValidationFailureActionOverrides) > 0 {
|
||||
errs = append(errs, field.Forbidden(path.Child("validationFailureActionOverrides"), "Use of validationFailureActionOverrides is supported only with ClusterPolicy"))
|
||||
}
|
||||
return errs
|
||||
}
|
523
api/kyverno/v2beta1/zz_generated.deepcopy.go
Executable file
523
api/kyverno/v2beta1/zz_generated.deepcopy.go
Executable file
|
@ -0,0 +1,523 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
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 controller-gen. DO NOT EDIT.
|
||||
|
||||
package v2beta1
|
||||
|
||||
import (
|
||||
v1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnyAllConditions) DeepCopyInto(out *AnyAllConditions) {
|
||||
*out = *in
|
||||
if in.AnyConditions != nil {
|
||||
in, out := &in.AnyConditions, &out.AnyConditions
|
||||
*out = make([]Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.AllConditions != nil {
|
||||
in, out := &in.AllConditions, &out.AllConditions
|
||||
*out = make([]Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnyAllConditions.
|
||||
func (in *AnyAllConditions) DeepCopy() *AnyAllConditions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AnyAllConditions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterPolicy) DeepCopyInto(out *ClusterPolicy) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPolicy.
|
||||
func (in *ClusterPolicy) DeepCopy() *ClusterPolicy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterPolicy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ClusterPolicy) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterPolicyList) DeepCopyInto(out *ClusterPolicyList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ClusterPolicy, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPolicyList.
|
||||
func (in *ClusterPolicyList) DeepCopy() *ClusterPolicyList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterPolicyList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ClusterPolicyList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Condition) DeepCopyInto(out *Condition) {
|
||||
*out = *in
|
||||
if in.RawKey != nil {
|
||||
in, out := &in.RawKey, &out.RawKey
|
||||
*out = new(apiextensionsv1.JSON)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.RawValue != nil {
|
||||
in, out := &in.RawValue, &out.RawValue
|
||||
*out = new(apiextensionsv1.JSON)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
|
||||
func (in *Condition) DeepCopy() *Condition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Condition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Deny) DeepCopyInto(out *Deny) {
|
||||
*out = *in
|
||||
if in.RawAnyAllConditions != nil {
|
||||
in, out := &in.RawAnyAllConditions, &out.RawAnyAllConditions
|
||||
*out = new(AnyAllConditions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deny.
|
||||
func (in *Deny) DeepCopy() *Deny {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Deny)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImageVerification) DeepCopyInto(out *ImageVerification) {
|
||||
*out = *in
|
||||
if in.ImageReferences != nil {
|
||||
in, out := &in.ImageReferences, &out.ImageReferences
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Attestors != nil {
|
||||
in, out := &in.Attestors, &out.Attestors
|
||||
*out = make([]v1.AttestorSet, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Attestations != nil {
|
||||
in, out := &in.Attestations, &out.Attestations
|
||||
*out = make([]v1.Attestation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageVerification.
|
||||
func (in *ImageVerification) DeepCopy() *ImageVerification {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImageVerification)
|
||||
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
|
||||
if in.Any != nil {
|
||||
in, out := &in.Any, &out.Any
|
||||
*out = make(v1.ResourceFilters, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.All != nil {
|
||||
in, out := &in.All, &out.All
|
||||
*out = make(v1.ResourceFilters, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchResources.
|
||||
func (in *MatchResources) DeepCopy() *MatchResources {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MatchResources)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Policy) DeepCopyInto(out *Policy) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy.
|
||||
func (in *Policy) DeepCopy() *Policy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Policy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Policy) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PolicyList) DeepCopyInto(out *PolicyList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Policy, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList.
|
||||
func (in *PolicyList) DeepCopy() *PolicyList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PolicyList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *PolicyList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) {
|
||||
*out = *in
|
||||
if in.Kinds != nil {
|
||||
in, out := &in.Kinds, &out.Kinds
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Names != nil {
|
||||
in, out := &in.Names, &out.Names
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Selector != nil {
|
||||
in, out := &in.Selector, &out.Selector
|
||||
*out = new(metav1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NamespaceSelector != nil {
|
||||
in, out := &in.NamespaceSelector, &out.NamespaceSelector
|
||||
*out = new(metav1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceDescription.
|
||||
func (in *ResourceDescription) DeepCopy() *ResourceDescription {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceDescription)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceFilter) DeepCopyInto(out *ResourceFilter) {
|
||||
*out = *in
|
||||
in.UserInfo.DeepCopyInto(&out.UserInfo)
|
||||
in.ResourceDescription.DeepCopyInto(&out.ResourceDescription)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceFilter.
|
||||
func (in *ResourceFilter) DeepCopy() *ResourceFilter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceFilter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ResourceFilters) DeepCopyInto(out *ResourceFilters) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ResourceFilters, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceFilters.
|
||||
func (in ResourceFilters) DeepCopy() ResourceFilters {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceFilters)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Rule) DeepCopyInto(out *Rule) {
|
||||
*out = *in
|
||||
if in.Context != nil {
|
||||
in, out := &in.Context, &out.Context
|
||||
*out = make([]v1.ContextEntry, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
in.MatchResources.DeepCopyInto(&out.MatchResources)
|
||||
in.ExcludeResources.DeepCopyInto(&out.ExcludeResources)
|
||||
if in.ImageExtractors != nil {
|
||||
in, out := &in.ImageExtractors, &out.ImageExtractors
|
||||
*out = make(v1.ImageExtractorConfigs, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal []v1.ImageExtractorConfig
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = make([]v1.ImageExtractorConfig, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
if in.RawAnyAllConditions != nil {
|
||||
in, out := &in.RawAnyAllConditions, &out.RawAnyAllConditions
|
||||
*out = new(AnyAllConditions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Mutation.DeepCopyInto(&out.Mutation)
|
||||
in.Validation.DeepCopyInto(&out.Validation)
|
||||
in.Generation.DeepCopyInto(&out.Generation)
|
||||
if in.VerifyImages != nil {
|
||||
in, out := &in.VerifyImages, &out.VerifyImages
|
||||
*out = make([]ImageVerification, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
|
||||
func (in *Rule) DeepCopy() *Rule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Rule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Spec) DeepCopyInto(out *Spec) {
|
||||
*out = *in
|
||||
if in.Rules != nil {
|
||||
in, out := &in.Rules, &out.Rules
|
||||
*out = make([]Rule, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.ApplyRules != nil {
|
||||
in, out := &in.ApplyRules, &out.ApplyRules
|
||||
*out = new(v1.ApplyRulesType)
|
||||
**out = **in
|
||||
}
|
||||
if in.FailurePolicy != nil {
|
||||
in, out := &in.FailurePolicy, &out.FailurePolicy
|
||||
*out = new(v1.FailurePolicyType)
|
||||
**out = **in
|
||||
}
|
||||
if in.ValidationFailureActionOverrides != nil {
|
||||
in, out := &in.ValidationFailureActionOverrides, &out.ValidationFailureActionOverrides
|
||||
*out = make([]v1.ValidationFailureActionOverride, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Background != nil {
|
||||
in, out := &in.Background, &out.Background
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.SchemaValidation != nil {
|
||||
in, out := &in.SchemaValidation, &out.SchemaValidation
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.WebhookTimeoutSeconds != nil {
|
||||
in, out := &in.WebhookTimeoutSeconds, &out.WebhookTimeoutSeconds
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spec.
|
||||
func (in *Spec) DeepCopy() *Spec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Spec)
|
||||
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
|
||||
if in.Manifests != nil {
|
||||
in, out := &in.Manifests, &out.Manifests
|
||||
*out = new(v1.Manifests)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ForEachValidation != nil {
|
||||
in, out := &in.ForEachValidation, &out.ForEachValidation
|
||||
*out = make([]v1.ForEachValidation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.RawPattern != nil {
|
||||
in, out := &in.RawPattern, &out.RawPattern
|
||||
*out = new(apiextensionsv1.JSON)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.RawAnyPattern != nil {
|
||||
in, out := &in.RawAnyPattern, &out.RawAnyPattern
|
||||
*out = new(apiextensionsv1.JSON)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Deny != nil {
|
||||
in, out := &in.Deny, &out.Deny
|
||||
*out = new(Deny)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.PodSecurity != nil {
|
||||
in, out := &in.PodSecurity, &out.PodSecurity
|
||||
*out = new(v1.PodSecurity)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Validation.
|
||||
func (in *Validation) DeepCopy() *Validation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Validation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
7825
config/install.yaml
7825
config/install.yaml
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,8 @@ Resource Types:
|
|||
<ul><li>
|
||||
<a href="#kyverno.io/v1.ClusterPolicy">ClusterPolicy</a>
|
||||
</li><li>
|
||||
<a href="#kyverno.io/v1.GenerateRequest">GenerateRequest</a>
|
||||
</li><li>
|
||||
<a href="#kyverno.io/v1.Policy">Policy</a>
|
||||
</li></ul>
|
||||
<hr />
|
||||
|
@ -254,6 +256,121 @@ PolicyStatus
|
|||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.GenerateRequest">GenerateRequest
|
||||
</h3>
|
||||
<p>
|
||||
<p>GenerateRequest is a request to process generate rule.</p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>apiVersion</code><br/>
|
||||
string</td>
|
||||
<td>
|
||||
<code>
|
||||
kyverno.io/v1
|
||||
</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>kind</code><br/>
|
||||
string
|
||||
</td>
|
||||
<td><code>GenerateRequest</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>metadata</code><br/>
|
||||
<em>
|
||||
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta">
|
||||
Kubernetes meta/v1.ObjectMeta
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
Refer to the Kubernetes API documentation for the fields of the
|
||||
<code>metadata</code> field.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>spec</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GenerateRequestSpec">
|
||||
GenerateRequestSpec
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Spec is the information to identify the generate request.</p>
|
||||
<br/>
|
||||
<br/>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<td>
|
||||
<code>policy</code><br/>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Specifies the name of the policy.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>resource</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ResourceSpec">
|
||||
ResourceSpec
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>ResourceSpec is the information to identify the generate request.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>context</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GenerateRequestContext">
|
||||
GenerateRequestContext
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Context …</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>status</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GenerateRequestStatus">
|
||||
GenerateRequestStatus
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Status contains statistics related to generate request.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.Policy">Policy
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -1502,104 +1619,6 @@ Deny
|
|||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.GenerateRequest">GenerateRequest
|
||||
</h3>
|
||||
<p>
|
||||
<p>GenerateRequest is a request to process generate rule.</p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>metadata</code><br/>
|
||||
<em>
|
||||
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta">
|
||||
Kubernetes meta/v1.ObjectMeta
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
Refer to the Kubernetes API documentation for the fields of the
|
||||
<code>metadata</code> field.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>spec</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GenerateRequestSpec">
|
||||
GenerateRequestSpec
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Spec is the information to identify the generate request.</p>
|
||||
<br/>
|
||||
<br/>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<td>
|
||||
<code>policy</code><br/>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Specifies the name of the policy.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>resource</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ResourceSpec">
|
||||
ResourceSpec
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>ResourceSpec is the information to identify the generate request.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>context</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GenerateRequestContext">
|
||||
GenerateRequestContext
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Context …</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>status</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GenerateRequestStatus">
|
||||
GenerateRequestStatus
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Status contains statistics related to generate request.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.GenerateRequestContext">GenerateRequestContext
|
||||
</h3>
|
||||
<p>
|
||||
|
|
1351
docs/crd/v2beta1/index.html
Normal file
1351
docs/crd/v2beta1/index.html
Normal file
File diff suppressed because it is too large
Load diff
|
@ -29,7 +29,7 @@ var (
|
|||
policyRule = admissionregistrationv1.Rule{
|
||||
Resources: []string{"clusterpolicies/*", "policies/*"},
|
||||
APIGroups: []string{"kyverno.io"},
|
||||
APIVersions: []string{"v1"},
|
||||
APIVersions: []string{"v1", "v2beta1"},
|
||||
}
|
||||
verifyRule = admissionregistrationv1.Rule{
|
||||
Resources: []string{"leases"},
|
||||
|
|
Loading…
Reference in a new issue