diff --git a/.github/actions/kyverno-logs/action.yaml b/.github/actions/kyverno-logs/action.yaml
index 9cd0485c95..10ccde2156 100644
--- a/.github/actions/kyverno-logs/action.yaml
+++ b/.github/actions/kyverno-logs/action.yaml
@@ -14,15 +14,9 @@ runs:
run: |
kubectl -n kyverno get pod
kubectl -n kyverno describe pod | grep -i events -A10
- - shell: bash
- run: |
- kubectl -n kyverno logs deploy/kyverno-admission-controller --all-containers -p || true
- kubectl -n kyverno logs deploy/kyverno-reports-controller --all-containers -p || true
- kubectl -n kyverno logs deploy/kyverno-cleanup-controller --all-containers -p || true
- kubectl -n kyverno logs deploy/kyverno-background-controller --all-containers -p || true
- shell: bash
run: |
kubectl -n kyverno logs deploy/kyverno-admission-controller --all-containers
+ kubectl -n kyverno logs deploy/kyverno-background-controller --all-containers
kubectl -n kyverno logs deploy/kyverno-reports-controller --all-containers
kubectl -n kyverno logs deploy/kyverno-cleanup-controller --all-containers
- kubectl -n kyverno logs deploy/kyverno-background-controller --all-containers
diff --git a/Makefile b/Makefile
index 3a50e8fe0f..b607859d49 100644
--- a/Makefile
+++ b/Makefile
@@ -501,7 +501,7 @@ codegen-client-all: codegen-client-wrappers
codegen-crds-kyverno: ## Generate kyverno CRDs
@echo Generate kyverno crds... >&2
@rm -rf $(CRDS_PATH)/kyverno && mkdir -p $(CRDS_PATH)/kyverno
- @go run ./hack/controller-gen -- paths=./api/kyverno/... crd:crdVersions=v1,ignoreUnexportedFields=true,generateEmbeddedObjectMeta=false output:dir=$(CRDS_PATH)/kyverno
+ @go run ./hack/controller-gen -- paths=./api/kyverno/v1/... paths=./api/kyverno/v2/... paths=./api/kyverno/v2alpha1/... paths=./api/kyverno/v2beta1/... crd:crdVersions=v1,ignoreUnexportedFields=true,generateEmbeddedObjectMeta=false output:dir=$(CRDS_PATH)/kyverno
.PHONY: codegen-crds-policyreport
codegen-crds-policyreport: ## Generate policy reports CRDs
diff --git a/api/kyverno/v2/constants.go b/api/kyverno/v2/constants.go
index 09977515a3..d10de6903c 100644
--- a/api/kyverno/v2/constants.go
+++ b/api/kyverno/v2/constants.go
@@ -10,9 +10,5 @@ const (
// URGeneratePolicyLabel adds the policy name to URs for generate policies
URGeneratePolicyLabel = "generate.kyverno.io/policy-name"
- URGenerateResourceNameLabel = "generate.kyverno.io/resource-name"
- URGenerateResourceUIDLabel = "generate.kyverno.io/resource-uid"
- URGenerateResourceNSLabel = "generate.kyverno.io/resource-namespace"
- URGenerateResourceKindLabel = "generate.kyverno.io/resource-kind"
URGenerateRetryCountAnnotation = "generate.kyverno.io/retry-count"
)
diff --git a/api/kyverno/v2/updaterequest_types.go b/api/kyverno/v2/updaterequest_types.go
index 142b0ba8ca..99b8d7fb37 100644
--- a/api/kyverno/v2/updaterequest_types.go
+++ b/api/kyverno/v2/updaterequest_types.go
@@ -82,6 +82,31 @@ type UpdateRequestSpec struct {
// Specifies the name of the policy.
Policy string `json:"policy" yaml:"policy"`
+ // RuleContext is the associate context to apply rules.
+ // optional
+ RuleContext []RuleContext `json:"ruleContext,omitempty" yaml:"ruleContext,omitempty"`
+
+ // Rule is the associate rule name of the current UR.
+ Rule string `json:"rule" yaml:"rule"`
+
+ // DeleteDownstream represents whether the downstream needs to be deleted.
+ // Deprecated
+ DeleteDownstream bool `json:"deleteDownstream" yaml:"deleteDownstream"`
+
+ // Synchronize represents the sync behavior of the corresponding rule
+ // Optional. Defaults to "false" if not specified.
+ // Deprecated, will be removed in 1.14.
+ Synchronize bool `json:"synchronize,omitempty" yaml:"synchronize,omitempty"`
+
+ // ResourceSpec is the information to identify the trigger resource.
+ Resource kyvernov1.ResourceSpec `json:"resource" yaml:"resource"`
+
+ // Context represents admission request context.
+ // It is used upon admission review only and is shared across rules within the same UR.
+ Context UpdateRequestSpecContext `json:"context" yaml:"context"`
+}
+
+type RuleContext struct {
// Rule is the associate rule name of the current UR.
Rule string `json:"rule" yaml:"rule"`
@@ -93,10 +118,7 @@ type UpdateRequestSpec struct {
Synchronize bool `json:"synchronize,omitempty" yaml:"synchronize,omitempty"`
// ResourceSpec is the information to identify the trigger resource.
- Resource kyvernov1.ResourceSpec `json:"resource" yaml:"resource"`
-
- // Context ...
- Context UpdateRequestSpecContext `json:"context" yaml:"context"`
+ Trigger kyvernov1.ResourceSpec `json:"trigger" yaml:"resource"`
}
// UpdateRequestSpecContext stores the context to be shared.
diff --git a/api/kyverno/v2/zz_generated.deepcopy.go b/api/kyverno/v2/zz_generated.deepcopy.go
index 62dab042e0..afa073fb3f 100644
--- a/api/kyverno/v2/zz_generated.deepcopy.go
+++ b/api/kyverno/v2/zz_generated.deepcopy.go
@@ -433,6 +433,23 @@ func (in *RequestInfo) DeepCopy() *RequestInfo {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RuleContext) DeepCopyInto(out *RuleContext) {
+ *out = *in
+ out.Trigger = in.Trigger
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleContext.
+func (in *RuleContext) DeepCopy() *RuleContext {
+ if in == nil {
+ return nil
+ }
+ out := new(RuleContext)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpdateRequest) DeepCopyInto(out *UpdateRequest) {
*out = *in
@@ -497,6 +514,11 @@ func (in *UpdateRequestList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpdateRequestSpec) DeepCopyInto(out *UpdateRequestSpec) {
*out = *in
+ if in.RuleContext != nil {
+ in, out := &in.RuleContext, &out.RuleContext
+ *out = make([]RuleContext, len(*in))
+ copy(*out, *in)
+ }
out.Resource = in.Resource
in.Context.DeepCopyInto(&out.Context)
return
diff --git a/charts/kyverno/charts/crds/templates/kyverno.io/kyverno.io_updaterequests.yaml b/charts/kyverno/charts/crds/templates/kyverno.io/kyverno.io_updaterequests.yaml
index a9885c56e1..030e189ccf 100644
--- a/charts/kyverno/charts/crds/templates/kyverno.io/kyverno.io_updaterequests.yaml
+++ b/charts/kyverno/charts/crds/templates/kyverno.io/kyverno.io_updaterequests.yaml
@@ -24,392 +24,6 @@ spec:
singular: updaterequest
scope: Namespaced
versions:
- - additionalPrinterColumns:
- - jsonPath: .spec.policy
- name: Policy
- type: string
- - jsonPath: .spec.rule
- name: Rule
- type: string
- - jsonPath: .spec.requestType
- name: RuleType
- type: string
- - jsonPath: .spec.resource.kind
- name: ResourceKind
- type: string
- - jsonPath: .spec.resource.name
- name: ResourceName
- type: string
- - jsonPath: .spec.resource.namespace
- name: ResourceNamespace
- type: string
- - jsonPath: .status.state
- name: status
- type: string
- - jsonPath: .metadata.creationTimestamp
- name: Age
- type: date
- deprecated: true
- name: v1beta1
- schema:
- openAPIV3Schema:
- description: UpdateRequest is a request to process mutate and generate rules
- in background.
- properties:
- apiVersion:
- description: |-
- APIVersion defines the versioned schema of this representation of an object.
- Servers should convert recognized schemas to the latest internal value, and
- may reject unrecognized values.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
- type: string
- kind:
- description: |-
- Kind is a string value representing the REST resource this object represents.
- Servers may infer this from the endpoint the client submits requests to.
- Cannot be updated.
- In CamelCase.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
- type: string
- metadata:
- type: object
- spec:
- description: ResourceSpec is the information to identify the trigger resource.
- properties:
- context:
- description: Context ...
- properties:
- admissionRequestInfo:
- description: AdmissionRequestInfoObject stores the admission request
- and operation details
- properties:
- admissionRequest:
- description: AdmissionRequest describes the admission.Attributes
- for the admission request.
- properties:
- dryRun:
- description: |-
- DryRun indicates that modifications will definitely not be persisted for this request.
- Defaults to false.
- type: boolean
- kind:
- description: Kind is the fully-qualified type of object
- being submitted (for example, v1.Pod or autoscaling.v1.Scale)
- properties:
- group:
- type: string
- kind:
- type: string
- version:
- type: string
- required:
- - group
- - kind
- - version
- type: object
- name:
- description: |-
- Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and
- rely on the server to generate the name. If that is the case, this field will contain an empty string.
- type: string
- namespace:
- description: Namespace is the namespace associated with
- the request (if any).
- type: string
- object:
- description: Object is the object from the incoming request.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- oldObject:
- description: OldObject is the existing object. Only populated
- for DELETE and UPDATE requests.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- operation:
- description: |-
- Operation is the operation being performed. This may be different than the operation
- requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
- type: string
- options:
- description: |-
- Options is the operation option structure of the operation being performed.
- e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
- different than the options the caller provided. e.g. for a patch request the performed
- Operation might be a CREATE, in which case the Options will a
- `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- requestKind:
- description: |-
- RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
- If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
-
-
- For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
- `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
- an API request to apps/v1beta1 deployments would be converted and sent to the webhook
- with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
- and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
-
-
- See documentation for the "matchPolicy" field in the webhook configuration type for more details.
- properties:
- group:
- type: string
- kind:
- type: string
- version:
- type: string
- required:
- - group
- - kind
- - version
- type: object
- requestResource:
- description: |-
- RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
- If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
-
-
- For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
- `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
- an API request to apps/v1beta1 deployments would be converted and sent to the webhook
- with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
- and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
-
-
- See documentation for the "matchPolicy" field in the webhook configuration type.
- properties:
- group:
- type: string
- resource:
- type: string
- version:
- type: string
- required:
- - group
- - resource
- - version
- type: object
- requestSubResource:
- description: |-
- RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
- If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
- See documentation for the "matchPolicy" field in the webhook configuration type.
- type: string
- resource:
- description: Resource is the fully-qualified resource
- being requested (for example, v1.pods)
- properties:
- group:
- type: string
- resource:
- type: string
- version:
- type: string
- required:
- - group
- - resource
- - version
- type: object
- subResource:
- description: SubResource is the subresource being requested,
- if any (for example, "status" or "scale")
- type: string
- uid:
- description: |-
- UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
- otherwise identical (parallel requests, requests when earlier requests did not modify etc)
- The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
- It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
- type: string
- userInfo:
- description: UserInfo is information about the requesting
- user
- properties:
- extra:
- additionalProperties:
- description: ExtraValue masks the value so protobuf
- can generate
- items:
- type: string
- type: array
- description: Any additional information provided by
- the authenticator.
- type: object
- groups:
- description: The names of groups this user is a part
- of.
- items:
- type: string
- type: array
- x-kubernetes-list-type: atomic
- uid:
- description: |-
- A unique value that identifies this user across time. If this user is
- deleted and another user by the same name is added, they will have
- different UIDs.
- type: string
- username:
- description: The name that uniquely identifies this
- user among all active users.
- type: string
- type: object
- required:
- - kind
- - operation
- - resource
- - uid
- - userInfo
- type: object
- operation:
- description: Operation is the type of resource operation being
- checked for admission control
- type: string
- type: object
- userInfo:
- description: RequestInfo contains permission info carried in an
- admission request.
- properties:
- clusterRoles:
- description: ClusterRoles is a list of possible clusterRoles
- send the request.
- items:
- type: string
- nullable: true
- type: array
- roles:
- description: Roles is a list of possible role send the request.
- items:
- type: string
- nullable: true
- type: array
- userInfo:
- description: UserInfo is the userInfo carried in the admission
- request.
- properties:
- extra:
- additionalProperties:
- description: ExtraValue masks the value so protobuf
- can generate
- items:
- type: string
- type: array
- description: Any additional information provided by the
- authenticator.
- type: object
- groups:
- description: The names of groups this user is a part of.
- items:
- type: string
- type: array
- x-kubernetes-list-type: atomic
- uid:
- description: |-
- A unique value that identifies this user across time. If this user is
- deleted and another user by the same name is added, they will have
- different UIDs.
- type: string
- username:
- description: The name that uniquely identifies this user
- among all active users.
- type: string
- type: object
- type: object
- type: object
- deleteDownstream:
- description: DeleteDownstream represents whether the downstream needs
- to be deleted.
- type: boolean
- policy:
- description: Specifies the name of the policy.
- type: string
- requestType:
- description: Type represents request type for background processing
- enum:
- - mutate
- - generate
- type: string
- resource:
- description: ResourceSpec is the information to identify the trigger
- resource.
- properties:
- apiVersion:
- description: APIVersion specifies resource apiVersion.
- type: string
- kind:
- description: Kind specifies resource kind.
- type: string
- name:
- description: Name specifies the resource name.
- type: string
- namespace:
- description: Namespace specifies resource namespace.
- type: string
- uid:
- description: UID specifies the resource uid.
- type: string
- type: object
- rule:
- description: Rule is the associate rule name of the current UR.
- type: string
- synchronize:
- description: |-
- Synchronize represents the sync behavior of the corresponding rule
- Optional. Defaults to "false" if not specified.
- type: boolean
- required:
- - context
- - deleteDownstream
- - policy
- - resource
- - rule
- type: object
- status:
- description: Status contains statistics related to update request.
- properties:
- generatedResources:
- description: |-
- This will track the resources that are updated by the generate Policy.
- Will be used during clean up resources.
- items:
- properties:
- apiVersion:
- description: APIVersion specifies resource apiVersion.
- type: string
- kind:
- description: Kind specifies resource kind.
- type: string
- name:
- description: Name specifies the resource name.
- type: string
- namespace:
- description: Namespace specifies resource namespace.
- type: string
- uid:
- description: UID specifies the resource uid.
- type: string
- type: object
- type: array
- handler:
- description: Deprecated
- type: string
- message:
- description: Specifies request status message.
- type: string
- retryCount:
- type: integer
- state:
- description: State represents state of the update request.
- type: string
- required:
- - state
- type: object
- type: object
- served: true
- storage: false
- subresources:
- status: {}
- additionalPrinterColumns:
- jsonPath: .spec.policy
name: Policy
@@ -459,7 +73,9 @@ spec:
description: ResourceSpec is the information to identify the trigger resource.
properties:
context:
- description: Context ...
+ description: |-
+ Context represents admission request context.
+ It is used upon admission review only and is shared across rules within the same UR.
properties:
admissionRequestInfo:
description: AdmissionRequestInfoObject stores the admission request
@@ -700,8 +316,9 @@ spec:
type: object
type: object
deleteDownstream:
- description: DeleteDownstream represents whether the downstream needs
- to be deleted.
+ description: |-
+ DeleteDownstream represents whether the downstream needs to be deleted.
+ Deprecated
type: boolean
policy:
description: Specifies the name of the policy.
@@ -735,10 +352,56 @@ spec:
rule:
description: Rule is the associate rule name of the current UR.
type: string
+ ruleContext:
+ description: |-
+ RuleContext is the associate context to apply rules.
+ optional
+ items:
+ properties:
+ deleteDownstream:
+ description: DeleteDownstream represents whether the downstream
+ needs to be deleted.
+ type: boolean
+ rule:
+ description: Rule is the associate rule name of the current
+ UR.
+ type: string
+ synchronize:
+ description: |-
+ Synchronize represents the sync behavior of the corresponding rule
+ Optional. Defaults to "false" if not specified.
+ type: boolean
+ trigger:
+ description: ResourceSpec is the information to identify the
+ trigger resource.
+ properties:
+ apiVersion:
+ description: APIVersion specifies resource apiVersion.
+ type: string
+ kind:
+ description: Kind specifies resource kind.
+ type: string
+ name:
+ description: Name specifies the resource name.
+ type: string
+ namespace:
+ description: Namespace specifies resource namespace.
+ type: string
+ uid:
+ description: UID specifies the resource uid.
+ type: string
+ type: object
+ required:
+ - deleteDownstream
+ - rule
+ - trigger
+ type: object
+ type: array
synchronize:
description: |-
Synchronize represents the sync behavior of the corresponding rule
Optional. Defaults to "false" if not specified.
+ Deprecated, will be removed in 1.14.
type: boolean
required:
- context
diff --git a/cmd/cli/kubectl-kyverno/processor/generate.go b/cmd/cli/kubectl-kyverno/processor/generate.go
index 31bd1d56de..8510113456 100644
--- a/cmd/cli/kubectl-kyverno/processor/generate.go
+++ b/cmd/cli/kubectl-kyverno/processor/generate.go
@@ -5,8 +5,6 @@ import (
"io"
"strings"
- kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
- kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
@@ -80,23 +78,10 @@ func handleGeneratePolicy(out io.Writer, store *store.Store, generateResponse *e
return nil, err
}
- gr := kyvernov2.UpdateRequest{
- Spec: kyvernov2.UpdateRequestSpec{
- Type: kyvernov2.Generate,
- Policy: generateResponse.Policy().GetName(),
- Resource: kyvernov1.ResourceSpec{
- Kind: generateResponse.Resource.GetKind(),
- Namespace: generateResponse.Resource.GetNamespace(),
- Name: generateResponse.Resource.GetName(),
- APIVersion: generateResponse.Resource.GetAPIVersion(),
- },
- },
- }
-
var newRuleResponse []engineapi.RuleResponse
for _, rule := range generateResponse.PolicyResponse.Rules {
- genResource, err := c.ApplyGeneratePolicy(log.Log.V(2), &policyContext, gr, []string{rule.Name()})
+ genResource, err := c.ApplyGeneratePolicy(log.Log.V(2), &policyContext, []string{rule.Name()})
if err != nil {
return nil, err
}
diff --git a/config/crds/kyverno/kyverno.io_updaterequests.yaml b/config/crds/kyverno/kyverno.io_updaterequests.yaml
index a4e549d44c..20cbe100d1 100644
--- a/config/crds/kyverno/kyverno.io_updaterequests.yaml
+++ b/config/crds/kyverno/kyverno.io_updaterequests.yaml
@@ -18,392 +18,6 @@ spec:
singular: updaterequest
scope: Namespaced
versions:
- - additionalPrinterColumns:
- - jsonPath: .spec.policy
- name: Policy
- type: string
- - jsonPath: .spec.rule
- name: Rule
- type: string
- - jsonPath: .spec.requestType
- name: RuleType
- type: string
- - jsonPath: .spec.resource.kind
- name: ResourceKind
- type: string
- - jsonPath: .spec.resource.name
- name: ResourceName
- type: string
- - jsonPath: .spec.resource.namespace
- name: ResourceNamespace
- type: string
- - jsonPath: .status.state
- name: status
- type: string
- - jsonPath: .metadata.creationTimestamp
- name: Age
- type: date
- deprecated: true
- name: v1beta1
- schema:
- openAPIV3Schema:
- description: UpdateRequest is a request to process mutate and generate rules
- in background.
- properties:
- apiVersion:
- description: |-
- APIVersion defines the versioned schema of this representation of an object.
- Servers should convert recognized schemas to the latest internal value, and
- may reject unrecognized values.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
- type: string
- kind:
- description: |-
- Kind is a string value representing the REST resource this object represents.
- Servers may infer this from the endpoint the client submits requests to.
- Cannot be updated.
- In CamelCase.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
- type: string
- metadata:
- type: object
- spec:
- description: ResourceSpec is the information to identify the trigger resource.
- properties:
- context:
- description: Context ...
- properties:
- admissionRequestInfo:
- description: AdmissionRequestInfoObject stores the admission request
- and operation details
- properties:
- admissionRequest:
- description: AdmissionRequest describes the admission.Attributes
- for the admission request.
- properties:
- dryRun:
- description: |-
- DryRun indicates that modifications will definitely not be persisted for this request.
- Defaults to false.
- type: boolean
- kind:
- description: Kind is the fully-qualified type of object
- being submitted (for example, v1.Pod or autoscaling.v1.Scale)
- properties:
- group:
- type: string
- kind:
- type: string
- version:
- type: string
- required:
- - group
- - kind
- - version
- type: object
- name:
- description: |-
- Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and
- rely on the server to generate the name. If that is the case, this field will contain an empty string.
- type: string
- namespace:
- description: Namespace is the namespace associated with
- the request (if any).
- type: string
- object:
- description: Object is the object from the incoming request.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- oldObject:
- description: OldObject is the existing object. Only populated
- for DELETE and UPDATE requests.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- operation:
- description: |-
- Operation is the operation being performed. This may be different than the operation
- requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
- type: string
- options:
- description: |-
- Options is the operation option structure of the operation being performed.
- e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
- different than the options the caller provided. e.g. for a patch request the performed
- Operation might be a CREATE, in which case the Options will a
- `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- requestKind:
- description: |-
- RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
- If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
-
-
- For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
- `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
- an API request to apps/v1beta1 deployments would be converted and sent to the webhook
- with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
- and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
-
-
- See documentation for the "matchPolicy" field in the webhook configuration type for more details.
- properties:
- group:
- type: string
- kind:
- type: string
- version:
- type: string
- required:
- - group
- - kind
- - version
- type: object
- requestResource:
- description: |-
- RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
- If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
-
-
- For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
- `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
- an API request to apps/v1beta1 deployments would be converted and sent to the webhook
- with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
- and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
-
-
- See documentation for the "matchPolicy" field in the webhook configuration type.
- properties:
- group:
- type: string
- resource:
- type: string
- version:
- type: string
- required:
- - group
- - resource
- - version
- type: object
- requestSubResource:
- description: |-
- RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
- If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
- See documentation for the "matchPolicy" field in the webhook configuration type.
- type: string
- resource:
- description: Resource is the fully-qualified resource
- being requested (for example, v1.pods)
- properties:
- group:
- type: string
- resource:
- type: string
- version:
- type: string
- required:
- - group
- - resource
- - version
- type: object
- subResource:
- description: SubResource is the subresource being requested,
- if any (for example, "status" or "scale")
- type: string
- uid:
- description: |-
- UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
- otherwise identical (parallel requests, requests when earlier requests did not modify etc)
- The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
- It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
- type: string
- userInfo:
- description: UserInfo is information about the requesting
- user
- properties:
- extra:
- additionalProperties:
- description: ExtraValue masks the value so protobuf
- can generate
- items:
- type: string
- type: array
- description: Any additional information provided by
- the authenticator.
- type: object
- groups:
- description: The names of groups this user is a part
- of.
- items:
- type: string
- type: array
- x-kubernetes-list-type: atomic
- uid:
- description: |-
- A unique value that identifies this user across time. If this user is
- deleted and another user by the same name is added, they will have
- different UIDs.
- type: string
- username:
- description: The name that uniquely identifies this
- user among all active users.
- type: string
- type: object
- required:
- - kind
- - operation
- - resource
- - uid
- - userInfo
- type: object
- operation:
- description: Operation is the type of resource operation being
- checked for admission control
- type: string
- type: object
- userInfo:
- description: RequestInfo contains permission info carried in an
- admission request.
- properties:
- clusterRoles:
- description: ClusterRoles is a list of possible clusterRoles
- send the request.
- items:
- type: string
- nullable: true
- type: array
- roles:
- description: Roles is a list of possible role send the request.
- items:
- type: string
- nullable: true
- type: array
- userInfo:
- description: UserInfo is the userInfo carried in the admission
- request.
- properties:
- extra:
- additionalProperties:
- description: ExtraValue masks the value so protobuf
- can generate
- items:
- type: string
- type: array
- description: Any additional information provided by the
- authenticator.
- type: object
- groups:
- description: The names of groups this user is a part of.
- items:
- type: string
- type: array
- x-kubernetes-list-type: atomic
- uid:
- description: |-
- A unique value that identifies this user across time. If this user is
- deleted and another user by the same name is added, they will have
- different UIDs.
- type: string
- username:
- description: The name that uniquely identifies this user
- among all active users.
- type: string
- type: object
- type: object
- type: object
- deleteDownstream:
- description: DeleteDownstream represents whether the downstream needs
- to be deleted.
- type: boolean
- policy:
- description: Specifies the name of the policy.
- type: string
- requestType:
- description: Type represents request type for background processing
- enum:
- - mutate
- - generate
- type: string
- resource:
- description: ResourceSpec is the information to identify the trigger
- resource.
- properties:
- apiVersion:
- description: APIVersion specifies resource apiVersion.
- type: string
- kind:
- description: Kind specifies resource kind.
- type: string
- name:
- description: Name specifies the resource name.
- type: string
- namespace:
- description: Namespace specifies resource namespace.
- type: string
- uid:
- description: UID specifies the resource uid.
- type: string
- type: object
- rule:
- description: Rule is the associate rule name of the current UR.
- type: string
- synchronize:
- description: |-
- Synchronize represents the sync behavior of the corresponding rule
- Optional. Defaults to "false" if not specified.
- type: boolean
- required:
- - context
- - deleteDownstream
- - policy
- - resource
- - rule
- type: object
- status:
- description: Status contains statistics related to update request.
- properties:
- generatedResources:
- description: |-
- This will track the resources that are updated by the generate Policy.
- Will be used during clean up resources.
- items:
- properties:
- apiVersion:
- description: APIVersion specifies resource apiVersion.
- type: string
- kind:
- description: Kind specifies resource kind.
- type: string
- name:
- description: Name specifies the resource name.
- type: string
- namespace:
- description: Namespace specifies resource namespace.
- type: string
- uid:
- description: UID specifies the resource uid.
- type: string
- type: object
- type: array
- handler:
- description: Deprecated
- type: string
- message:
- description: Specifies request status message.
- type: string
- retryCount:
- type: integer
- state:
- description: State represents state of the update request.
- type: string
- required:
- - state
- type: object
- type: object
- served: true
- storage: false
- subresources:
- status: {}
- additionalPrinterColumns:
- jsonPath: .spec.policy
name: Policy
@@ -453,7 +67,9 @@ spec:
description: ResourceSpec is the information to identify the trigger resource.
properties:
context:
- description: Context ...
+ description: |-
+ Context represents admission request context.
+ It is used upon admission review only and is shared across rules within the same UR.
properties:
admissionRequestInfo:
description: AdmissionRequestInfoObject stores the admission request
@@ -694,8 +310,9 @@ spec:
type: object
type: object
deleteDownstream:
- description: DeleteDownstream represents whether the downstream needs
- to be deleted.
+ description: |-
+ DeleteDownstream represents whether the downstream needs to be deleted.
+ Deprecated
type: boolean
policy:
description: Specifies the name of the policy.
@@ -729,10 +346,56 @@ spec:
rule:
description: Rule is the associate rule name of the current UR.
type: string
+ ruleContext:
+ description: |-
+ RuleContext is the associate context to apply rules.
+ optional
+ items:
+ properties:
+ deleteDownstream:
+ description: DeleteDownstream represents whether the downstream
+ needs to be deleted.
+ type: boolean
+ rule:
+ description: Rule is the associate rule name of the current
+ UR.
+ type: string
+ synchronize:
+ description: |-
+ Synchronize represents the sync behavior of the corresponding rule
+ Optional. Defaults to "false" if not specified.
+ type: boolean
+ trigger:
+ description: ResourceSpec is the information to identify the
+ trigger resource.
+ properties:
+ apiVersion:
+ description: APIVersion specifies resource apiVersion.
+ type: string
+ kind:
+ description: Kind specifies resource kind.
+ type: string
+ name:
+ description: Name specifies the resource name.
+ type: string
+ namespace:
+ description: Namespace specifies resource namespace.
+ type: string
+ uid:
+ description: UID specifies the resource uid.
+ type: string
+ type: object
+ required:
+ - deleteDownstream
+ - rule
+ - trigger
+ type: object
+ type: array
synchronize:
description: |-
Synchronize represents the sync behavior of the corresponding rule
Optional. Defaults to "false" if not specified.
+ Deprecated, will be removed in 1.14.
type: boolean
required:
- context
diff --git a/config/install-latest-testing.yaml b/config/install-latest-testing.yaml
index 2825cb4fc6..65b752f2ac 100644
--- a/config/install-latest-testing.yaml
+++ b/config/install-latest-testing.yaml
@@ -41380,392 +41380,6 @@ spec:
singular: updaterequest
scope: Namespaced
versions:
- - additionalPrinterColumns:
- - jsonPath: .spec.policy
- name: Policy
- type: string
- - jsonPath: .spec.rule
- name: Rule
- type: string
- - jsonPath: .spec.requestType
- name: RuleType
- type: string
- - jsonPath: .spec.resource.kind
- name: ResourceKind
- type: string
- - jsonPath: .spec.resource.name
- name: ResourceName
- type: string
- - jsonPath: .spec.resource.namespace
- name: ResourceNamespace
- type: string
- - jsonPath: .status.state
- name: status
- type: string
- - jsonPath: .metadata.creationTimestamp
- name: Age
- type: date
- deprecated: true
- name: v1beta1
- schema:
- openAPIV3Schema:
- description: UpdateRequest is a request to process mutate and generate rules
- in background.
- properties:
- apiVersion:
- description: |-
- APIVersion defines the versioned schema of this representation of an object.
- Servers should convert recognized schemas to the latest internal value, and
- may reject unrecognized values.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
- type: string
- kind:
- description: |-
- Kind is a string value representing the REST resource this object represents.
- Servers may infer this from the endpoint the client submits requests to.
- Cannot be updated.
- In CamelCase.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
- type: string
- metadata:
- type: object
- spec:
- description: ResourceSpec is the information to identify the trigger resource.
- properties:
- context:
- description: Context ...
- properties:
- admissionRequestInfo:
- description: AdmissionRequestInfoObject stores the admission request
- and operation details
- properties:
- admissionRequest:
- description: AdmissionRequest describes the admission.Attributes
- for the admission request.
- properties:
- dryRun:
- description: |-
- DryRun indicates that modifications will definitely not be persisted for this request.
- Defaults to false.
- type: boolean
- kind:
- description: Kind is the fully-qualified type of object
- being submitted (for example, v1.Pod or autoscaling.v1.Scale)
- properties:
- group:
- type: string
- kind:
- type: string
- version:
- type: string
- required:
- - group
- - kind
- - version
- type: object
- name:
- description: |-
- Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and
- rely on the server to generate the name. If that is the case, this field will contain an empty string.
- type: string
- namespace:
- description: Namespace is the namespace associated with
- the request (if any).
- type: string
- object:
- description: Object is the object from the incoming request.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- oldObject:
- description: OldObject is the existing object. Only populated
- for DELETE and UPDATE requests.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- operation:
- description: |-
- Operation is the operation being performed. This may be different than the operation
- requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
- type: string
- options:
- description: |-
- Options is the operation option structure of the operation being performed.
- e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
- different than the options the caller provided. e.g. for a patch request the performed
- Operation might be a CREATE, in which case the Options will a
- `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- requestKind:
- description: |-
- RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
- If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
-
-
- For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
- `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
- an API request to apps/v1beta1 deployments would be converted and sent to the webhook
- with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
- and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
-
-
- See documentation for the "matchPolicy" field in the webhook configuration type for more details.
- properties:
- group:
- type: string
- kind:
- type: string
- version:
- type: string
- required:
- - group
- - kind
- - version
- type: object
- requestResource:
- description: |-
- RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
- If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
-
-
- For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
- `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
- an API request to apps/v1beta1 deployments would be converted and sent to the webhook
- with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
- and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
-
-
- See documentation for the "matchPolicy" field in the webhook configuration type.
- properties:
- group:
- type: string
- resource:
- type: string
- version:
- type: string
- required:
- - group
- - resource
- - version
- type: object
- requestSubResource:
- description: |-
- RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
- If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
- See documentation for the "matchPolicy" field in the webhook configuration type.
- type: string
- resource:
- description: Resource is the fully-qualified resource
- being requested (for example, v1.pods)
- properties:
- group:
- type: string
- resource:
- type: string
- version:
- type: string
- required:
- - group
- - resource
- - version
- type: object
- subResource:
- description: SubResource is the subresource being requested,
- if any (for example, "status" or "scale")
- type: string
- uid:
- description: |-
- UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
- otherwise identical (parallel requests, requests when earlier requests did not modify etc)
- The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
- It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
- type: string
- userInfo:
- description: UserInfo is information about the requesting
- user
- properties:
- extra:
- additionalProperties:
- description: ExtraValue masks the value so protobuf
- can generate
- items:
- type: string
- type: array
- description: Any additional information provided by
- the authenticator.
- type: object
- groups:
- description: The names of groups this user is a part
- of.
- items:
- type: string
- type: array
- x-kubernetes-list-type: atomic
- uid:
- description: |-
- A unique value that identifies this user across time. If this user is
- deleted and another user by the same name is added, they will have
- different UIDs.
- type: string
- username:
- description: The name that uniquely identifies this
- user among all active users.
- type: string
- type: object
- required:
- - kind
- - operation
- - resource
- - uid
- - userInfo
- type: object
- operation:
- description: Operation is the type of resource operation being
- checked for admission control
- type: string
- type: object
- userInfo:
- description: RequestInfo contains permission info carried in an
- admission request.
- properties:
- clusterRoles:
- description: ClusterRoles is a list of possible clusterRoles
- send the request.
- items:
- type: string
- nullable: true
- type: array
- roles:
- description: Roles is a list of possible role send the request.
- items:
- type: string
- nullable: true
- type: array
- userInfo:
- description: UserInfo is the userInfo carried in the admission
- request.
- properties:
- extra:
- additionalProperties:
- description: ExtraValue masks the value so protobuf
- can generate
- items:
- type: string
- type: array
- description: Any additional information provided by the
- authenticator.
- type: object
- groups:
- description: The names of groups this user is a part of.
- items:
- type: string
- type: array
- x-kubernetes-list-type: atomic
- uid:
- description: |-
- A unique value that identifies this user across time. If this user is
- deleted and another user by the same name is added, they will have
- different UIDs.
- type: string
- username:
- description: The name that uniquely identifies this user
- among all active users.
- type: string
- type: object
- type: object
- type: object
- deleteDownstream:
- description: DeleteDownstream represents whether the downstream needs
- to be deleted.
- type: boolean
- policy:
- description: Specifies the name of the policy.
- type: string
- requestType:
- description: Type represents request type for background processing
- enum:
- - mutate
- - generate
- type: string
- resource:
- description: ResourceSpec is the information to identify the trigger
- resource.
- properties:
- apiVersion:
- description: APIVersion specifies resource apiVersion.
- type: string
- kind:
- description: Kind specifies resource kind.
- type: string
- name:
- description: Name specifies the resource name.
- type: string
- namespace:
- description: Namespace specifies resource namespace.
- type: string
- uid:
- description: UID specifies the resource uid.
- type: string
- type: object
- rule:
- description: Rule is the associate rule name of the current UR.
- type: string
- synchronize:
- description: |-
- Synchronize represents the sync behavior of the corresponding rule
- Optional. Defaults to "false" if not specified.
- type: boolean
- required:
- - context
- - deleteDownstream
- - policy
- - resource
- - rule
- type: object
- status:
- description: Status contains statistics related to update request.
- properties:
- generatedResources:
- description: |-
- This will track the resources that are updated by the generate Policy.
- Will be used during clean up resources.
- items:
- properties:
- apiVersion:
- description: APIVersion specifies resource apiVersion.
- type: string
- kind:
- description: Kind specifies resource kind.
- type: string
- name:
- description: Name specifies the resource name.
- type: string
- namespace:
- description: Namespace specifies resource namespace.
- type: string
- uid:
- description: UID specifies the resource uid.
- type: string
- type: object
- type: array
- handler:
- description: Deprecated
- type: string
- message:
- description: Specifies request status message.
- type: string
- retryCount:
- type: integer
- state:
- description: State represents state of the update request.
- type: string
- required:
- - state
- type: object
- type: object
- served: true
- storage: false
- subresources:
- status: {}
- additionalPrinterColumns:
- jsonPath: .spec.policy
name: Policy
@@ -41815,7 +41429,9 @@ spec:
description: ResourceSpec is the information to identify the trigger resource.
properties:
context:
- description: Context ...
+ description: |-
+ Context represents admission request context.
+ It is used upon admission review only and is shared across rules within the same UR.
properties:
admissionRequestInfo:
description: AdmissionRequestInfoObject stores the admission request
@@ -42056,8 +41672,9 @@ spec:
type: object
type: object
deleteDownstream:
- description: DeleteDownstream represents whether the downstream needs
- to be deleted.
+ description: |-
+ DeleteDownstream represents whether the downstream needs to be deleted.
+ Deprecated
type: boolean
policy:
description: Specifies the name of the policy.
@@ -42091,10 +41708,56 @@ spec:
rule:
description: Rule is the associate rule name of the current UR.
type: string
+ ruleContext:
+ description: |-
+ RuleContext is the associate context to apply rules.
+ optional
+ items:
+ properties:
+ deleteDownstream:
+ description: DeleteDownstream represents whether the downstream
+ needs to be deleted.
+ type: boolean
+ rule:
+ description: Rule is the associate rule name of the current
+ UR.
+ type: string
+ synchronize:
+ description: |-
+ Synchronize represents the sync behavior of the corresponding rule
+ Optional. Defaults to "false" if not specified.
+ type: boolean
+ trigger:
+ description: ResourceSpec is the information to identify the
+ trigger resource.
+ properties:
+ apiVersion:
+ description: APIVersion specifies resource apiVersion.
+ type: string
+ kind:
+ description: Kind specifies resource kind.
+ type: string
+ name:
+ description: Name specifies the resource name.
+ type: string
+ namespace:
+ description: Namespace specifies resource namespace.
+ type: string
+ uid:
+ description: UID specifies the resource uid.
+ type: string
+ type: object
+ required:
+ - deleteDownstream
+ - rule
+ - trigger
+ type: object
+ type: array
synchronize:
description: |-
Synchronize represents the sync behavior of the corresponding rule
Optional. Defaults to "false" if not specified.
+ Deprecated, will be removed in 1.14.
type: boolean
required:
- context
diff --git a/docs/user/crd/index.html b/docs/user/crd/index.html
index 98908837ed..47308c9ebf 100644
--- a/docs/user/crd/index.html
+++ b/docs/user/crd/index.html
@@ -3559,6 +3559,7 @@ ResourceDescription
TargetResourceSpec,
UpdateRequestSpec,
UpdateRequestStatus,
+RuleContext,
UpdateRequestSpec,
UpdateRequestStatus)
@@ -5877,6 +5878,20 @@ string
+ruleContext
+
+
+[]RuleContext
+
+
+ |
+
+ RuleContext is the associate context to apply rules.
+optional
+ |
+
+
+
rule
string
@@ -5894,7 +5909,8 @@ bool
|
- DeleteDownstream represents whether the downstream needs to be deleted.
+DeleteDownstream represents whether the downstream needs to be deleted.
+Deprecated
|
@@ -5906,7 +5922,8 @@ bool
Synchronize represents the sync behavior of the corresponding rule
-Optional. Defaults to “false” if not specified.
+Optional. Defaults to “false” if not specified.
+Deprecated, will be removed in 1.14.
|
@@ -5932,7 +5949,8 @@ UpdateRequestSpecContext
- Context …
+Context represents admission request context.
+It is used upon admission review only and is shared across rules within the same UR.
|
@@ -6475,6 +6493,72 @@ Kubernetes authentication/v1.UserInfo
+RuleContext
+
+
+(Appears on:
+UpdateRequestSpec)
+
+
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+rule
+
+string
+
+ |
+
+ Rule is the associate rule name of the current UR.
+ |
+
+
+
+deleteDownstream
+
+bool
+
+ |
+
+ DeleteDownstream represents whether the downstream needs to be deleted.
+ |
+
+
+
+synchronize
+
+bool
+
+ |
+
+ Synchronize represents the sync behavior of the corresponding rule
+Optional. Defaults to “false” if not specified.
+ |
+
+
+
+trigger
+
+
+ResourceSpec
+
+
+ |
+
+ ResourceSpec is the information to identify the trigger resource.
+ |
+
+
+
+
UpdateRequestSpec
@@ -6518,6 +6602,20 @@ string
+ruleContext
+
+
+[]RuleContext
+
+
+ |
+
+ RuleContext is the associate context to apply rules.
+optional
+ |
+
+
+
rule
string
@@ -6535,7 +6633,8 @@ bool
|
- DeleteDownstream represents whether the downstream needs to be deleted.
+DeleteDownstream represents whether the downstream needs to be deleted.
+Deprecated
|
@@ -6547,7 +6646,8 @@ bool
Synchronize represents the sync behavior of the corresponding rule
-Optional. Defaults to “false” if not specified.
+Optional. Defaults to “false” if not specified.
+Deprecated, will be removed in 1.14.
|
@@ -6573,7 +6673,8 @@ UpdateRequestSpecContext
- Context …
+Context represents admission request context.
+It is used upon admission review only and is shared across rules within the same UR.
|
diff --git a/pkg/background/common/context.go b/pkg/background/common/context.go
index 22fa697e93..f4cc5ca15f 100644
--- a/pkg/background/common/context.go
+++ b/pkg/background/common/context.go
@@ -17,7 +17,7 @@ import (
func NewBackgroundContext(
logger logr.Logger,
dclient dclient.Interface,
- ur *kyvernov2.UpdateRequest,
+ urContext kyvernov2.UpdateRequestSpecContext,
policy kyvernov1.PolicyInterface,
trigger *unstructured.Unstructured,
cfg config.Configuration,
@@ -27,15 +27,15 @@ func NewBackgroundContext(
var new, old unstructured.Unstructured
var err error
- if ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest != nil {
- new, old, err = admissionutils.ExtractResources(nil, *ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest)
+ if urContext.AdmissionRequestInfo.AdmissionRequest != nil {
+ new, old, err = admissionutils.ExtractResources(nil, *urContext.AdmissionRequestInfo.AdmissionRequest)
if err != nil {
return nil, fmt.Errorf("failed to load request in context: %w", err)
}
if new.Object != nil {
if !check(&new, trigger) {
- err := fmt.Errorf("resources don't match")
- return nil, fmt.Errorf("resource %v: %w", ur.Spec.GetResource().String(), err)
+ return nil, fmt.Errorf("resources don't match, want: %v/%v, got: %v/%v",
+ trigger.GetNamespace(), trigger.GetName(), new.GetNamespace(), new.GetName())
}
}
}
@@ -47,19 +47,19 @@ func NewBackgroundContext(
}
var policyContext *engine.PolicyContext
- if ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest == nil {
+ if urContext.AdmissionRequestInfo.AdmissionRequest == nil {
policyContext, err = engine.NewPolicyContext(
jp,
*trigger,
- kyvernov1.AdmissionOperation(ur.Spec.Context.AdmissionRequestInfo.Operation),
- &ur.Spec.Context.UserRequestInfo,
+ kyvernov1.AdmissionOperation(urContext.AdmissionRequestInfo.Operation),
+ &urContext.UserRequestInfo,
cfg,
)
} else {
policyContext, err = engine.NewPolicyContextFromAdmissionRequest(
jp,
- *ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest,
- ur.Spec.Context.UserRequestInfo,
+ *urContext.AdmissionRequestInfo.AdmissionRequest,
+ urContext.UserRequestInfo,
trigger.GroupVersionKind(),
cfg,
)
diff --git a/pkg/background/common/labels.go b/pkg/background/common/labels.go
index 14b29e9459..8157c243ff 100644
--- a/pkg/background/common/labels.go
+++ b/pkg/background/common/labels.go
@@ -53,18 +53,12 @@ func MutateLabelsSet(policyKey string, trigger Object) pkglabels.Set {
return set
}
-func GenerateLabelsSet(policyKey string, trigger Object) pkglabels.Set {
+func GenerateLabelsSet(policyKey string) pkglabels.Set {
_, policyName, _ := cache.SplitMetaNamespaceKey(policyKey)
set := pkglabels.Set{
kyvernov2.URGeneratePolicyLabel: policyName,
}
- isNil := trigger == nil || (reflect.ValueOf(trigger).Kind() == reflect.Ptr && reflect.ValueOf(trigger).IsNil())
- if !isNil {
- set[kyvernov2.URGenerateResourceUIDLabel] = string(trigger.GetUID())
- set[kyvernov2.URGenerateResourceNSLabel] = trigger.GetNamespace()
- set[kyvernov2.URGenerateResourceKindLabel] = trigger.GetKind()
- }
return set
}
diff --git a/pkg/background/common/resource.go b/pkg/background/common/resource.go
index f4c00d39d0..c02bfb3b4b 100644
--- a/pkg/background/common/resource.go
+++ b/pkg/background/common/resource.go
@@ -3,8 +3,10 @@ package common
import (
"context"
"fmt"
+ "reflect"
"github.com/go-logr/logr"
+ kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
"github.com/kyverno/kyverno/pkg/clients/dclient"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
@@ -13,10 +15,13 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
-func GetResource(client dclient.Interface, urSpec kyvernov2.UpdateRequestSpec, log logr.Logger) (resource *unstructured.Unstructured, err error) {
- resourceSpec := urSpec.GetResource()
+func GetResource(client dclient.Interface, resourceSpec kyvernov1.ResourceSpec, urSpec kyvernov2.UpdateRequestSpec, log logr.Logger) (resource *unstructured.Unstructured, err error) {
+ obj := resourceSpec
+ if reflect.DeepEqual(obj, kyvernov1.ResourceSpec{}) {
+ obj = urSpec.GetResource()
+ }
- if urSpec.GetResource().GetUID() != "" {
+ if obj.GetUID() != "" {
triggers, err := client.ListResource(context.TODO(), resourceSpec.GetAPIVersion(), resourceSpec.GetKind(), resourceSpec.GetNamespace(), nil)
if err != nil {
return nil, fmt.Errorf("failed to list trigger resources: %v", err)
@@ -27,7 +32,7 @@ func GetResource(client dclient.Interface, urSpec kyvernov2.UpdateRequestSpec, l
return &trigger, nil
}
}
- } else if urSpec.GetResource().GetName() != "" {
+ } else if obj.GetName() != "" {
if resourceSpec.Kind == "Namespace" {
resourceSpec.Namespace = ""
}
@@ -44,7 +49,7 @@ func GetResource(client dclient.Interface, urSpec kyvernov2.UpdateRequestSpec, l
return resource, nil
}
- if resource == nil && urSpec.Context.AdmissionRequestInfo.AdmissionRequest != nil {
+ if urSpec.Context.AdmissionRequestInfo.AdmissionRequest != nil {
request := urSpec.Context.AdmissionRequestInfo.AdmissionRequest
raw := request.Object.Raw
if request.Operation == admissionv1.Delete {
@@ -52,8 +57,12 @@ func GetResource(client dclient.Interface, urSpec kyvernov2.UpdateRequestSpec, l
}
resource, err = kubeutils.BytesToUnstructured(raw)
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert raw object to unstructured: %v", err)
+ } else {
+ return resource, nil
+ }
}
- log.V(3).Info("fetched trigger resource", "resourceSpec", resourceSpec)
- return resource, err
+ return nil, fmt.Errorf("resource not found")
}
diff --git a/pkg/background/generate/cleanup.go b/pkg/background/generate/cleanup.go
index 40ec3204f0..f6049bd75d 100644
--- a/pkg/background/generate/cleanup.go
+++ b/pkg/background/generate/cleanup.go
@@ -14,14 +14,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
-func (c *GenerateController) deleteDownstream(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest) (err error) {
- if !ur.Spec.DeleteDownstream {
- return nil
- }
-
+func (c *GenerateController) deleteDownstream(policy kyvernov1.PolicyInterface, ruleContext kyvernov2.RuleContext, ur *kyvernov2.UpdateRequest) (err error) {
// handle data policy/rule deletion
if ur.Status.GeneratedResources != nil {
- c.log.V(4).Info("policy/rule no longer exists, deleting the downstream resource based on synchronize", "ur", ur.Name, "policy", ur.Spec.Policy, "rule", ur.Spec.Rule)
+ c.log.V(4).Info("policy/rule no longer exists, deleting the downstream resource based on synchronize", "ur", ur.Name, "policy", ur.Spec.Policy)
var errs []error
failedDownstreams := []kyvernov1.ResourceSpec{}
for _, e := range ur.Status.GeneratedResources {
@@ -46,18 +42,17 @@ func (c *GenerateController) deleteDownstream(policy kyvernov1.PolicyInterface,
return nil
}
- return c.handleNonPolicyChanges(policy, ur)
+ return c.handleNonPolicyChanges(policy, ruleContext, ur)
}
-func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest) error {
- if !ur.Spec.DeleteDownstream {
- return nil
- }
-
+func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInterface, ruleContext kyvernov2.RuleContext, ur *kyvernov2.UpdateRequest) error {
+ logger := c.log.V(4).WithValues("ur", ur.Name, "policy", ur.Spec.Policy, "rule", ruleContext.Rule)
+ logger.Info("synchronize for none-policy changes")
for _, rule := range policy.GetSpec().Rules {
- if ur.Spec.Rule != rule.Name {
+ if ruleContext.Rule != rule.Name {
continue
}
+ logger.Info("deleting the downstream resource based on synchronize")
labels := map[string]string{
common.GeneratePolicyLabel: policy.GetName(),
common.GeneratePolicyNamespaceLabel: policy.GetNamespace(),
@@ -65,7 +60,7 @@ func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInter
kyverno.LabelAppManagedBy: kyverno.ValueKyvernoApp,
}
- downstreams, err := c.getDownstreams(rule, labels, ur)
+ downstreams, err := c.getDownstreams(rule, labels, &ruleContext)
if err != nil {
return fmt.Errorf("failed to fetch downstream resources: %v", err)
}
@@ -77,7 +72,7 @@ func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInter
failedDownstreams = append(failedDownstreams, spec)
errs = append(errs, err)
} else {
- c.log.V(4).Info("downstream resource deleted", "spec", spec.String())
+ logger.Info("downstream resource deleted", "spec", spec.String())
}
}
if len(errs) != 0 {
@@ -88,22 +83,22 @@ func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInter
_, err = c.statusControl.Success(ur.GetName(), nil)
}
if err != nil {
- c.log.Error(err, "failed to update ur status")
+ logger.Error(err, "failed to update ur status")
}
}
return nil
}
-func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[string]string, ur *kyvernov2.UpdateRequest) (*unstructured.UnstructuredList, error) {
- gv, err := ur.Spec.GetResource().GetGroupVersion()
+func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[string]string, ruleContext *kyvernov2.RuleContext) (*unstructured.UnstructuredList, error) {
+ gv, err := ruleContext.Trigger.GetGroupVersion()
if err != nil {
return nil, err
}
- selector[common.GenerateTriggerUIDLabel] = string(ur.Spec.GetResource().GetUID())
- selector[common.GenerateTriggerNSLabel] = ur.Spec.GetResource().GetNamespace()
- selector[common.GenerateTriggerKindLabel] = ur.Spec.GetResource().GetKind()
+ selector[common.GenerateTriggerUIDLabel] = string(ruleContext.Trigger.GetUID())
+ selector[common.GenerateTriggerNSLabel] = ruleContext.Trigger.GetNamespace()
+ selector[common.GenerateTriggerKindLabel] = ruleContext.Trigger.GetKind()
selector[common.GenerateTriggerGroupLabel] = gv.Group
selector[common.GenerateTriggerVersionLabel] = gv.Version
if rule.Generation.GetKind() != "" {
@@ -117,7 +112,7 @@ func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[st
if len(downstreamList.Items) == 0 {
// Fetch downstream resources using the trigger name label
delete(selector, common.GenerateTriggerUIDLabel)
- selector[common.GenerateTriggerNameLabel] = ur.Spec.GetResource().GetName()
+ selector[common.GenerateTriggerNameLabel] = ruleContext.Trigger.GetName()
c.log.V(4).Info("fetching downstream resource by the name", "APIVersion", rule.Generation.GetAPIVersion(), "kind", rule.Generation.GetKind(), "selector", selector)
dsList, err := common.FindDownstream(c.client, rule.Generation.GetAPIVersion(), rule.Generation.GetKind(), selector)
if err != nil {
@@ -140,7 +135,7 @@ func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[st
if len(dsList.Items) == 0 {
delete(selector, common.GenerateTriggerUIDLabel)
- selector[common.GenerateTriggerNameLabel] = ur.Spec.GetResource().GetName()
+ selector[common.GenerateTriggerNameLabel] = ruleContext.Trigger.GetName()
c.log.V(4).Info("fetching downstream resource by the name", "APIVersion", rule.Generation.GetAPIVersion(), "kind", rule.Generation.GetKind(), "selector", selector)
dsList, err = common.FindDownstream(c.client, rule.Generation.GetAPIVersion(), rule.Generation.GetKind(), selector)
if err != nil {
diff --git a/pkg/background/generate/clone.go b/pkg/background/generate/clone.go
index 49bb522204..bd85a80006 100644
--- a/pkg/background/generate/clone.go
+++ b/pkg/background/generate/clone.go
@@ -6,7 +6,6 @@ import (
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
- kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
"github.com/kyverno/kyverno/pkg/clients/dclient"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
@@ -14,7 +13,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, policy kyvernov1.PolicyInterface, ur kyvernov2.UpdateRequest, rule kyvernov1.Rule, client dclient.Interface) generateResponse {
+func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, client dclient.Interface) generateResponse {
source := sourceSpec
clone := rule.Generation
if clone.Clone.Name != "" {
@@ -60,15 +59,9 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, pol
sourceObjCopy.SetResourceVersion("")
targetObj, err := client.GetResource(context.TODO(), target.GetAPIVersion(), target.GetKind(), target.GetNamespace(), target.GetName())
- if err != nil {
- if apierrors.IsNotFound(err) && len(ur.Status.GeneratedResources) != 0 && !clone.Synchronize {
- log.V(4).Info("synchronization is disabled, recreation will be skipped", "target resource", targetObj)
- return newSkipGenerateResponse(nil, target, nil)
- }
- if apierrors.IsNotFound(err) {
- return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
- }
- return newSkipGenerateResponse(nil, target, fmt.Errorf("failed to get the target source: %v", err))
+ if err != nil && apierrors.IsNotFound(err) {
+ // the target resource should always exist regardless of synchronize settings
+ return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
}
if targetObj != nil {
@@ -88,7 +81,7 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, pol
return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
}
-func manageCloneList(log logr.Logger, targetNamespace string, ur kyvernov2.UpdateRequest, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, client dclient.Interface) []generateResponse {
+func manageCloneList(log logr.Logger, targetNamespace string, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, client dclient.Interface) []generateResponse {
var responses []generateResponse
cloneList := rule.Generation.CloneList
sourceNamespace := cloneList.Namespace
@@ -109,7 +102,7 @@ func manageCloneList(log logr.Logger, targetNamespace string, ur kyvernov2.Updat
for _, source := range sources.Items {
target := newResourceSpec(source.GetAPIVersion(), source.GetKind(), targetNamespace, source.GetName())
responses = append(responses,
- manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), policy, ur, rule, client))
+ manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), policy, rule, client))
}
}
return responses
diff --git a/pkg/background/generate/data.go b/pkg/background/generate/data.go
index f6a374dc7f..32097b718b 100644
--- a/pkg/background/generate/data.go
+++ b/pkg/background/generate/data.go
@@ -2,18 +2,16 @@ package generate
import (
"context"
- "fmt"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
- kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
"github.com/kyverno/kyverno/pkg/clients/dclient"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
-func manageData(log logr.Logger, target kyvernov1.ResourceSpec, data interface{}, synchronize bool, ur kyvernov2.UpdateRequest, client dclient.Interface) generateResponse {
+func manageData(log logr.Logger, target kyvernov1.ResourceSpec, data interface{}, synchronize bool, client dclient.Interface) generateResponse {
if data == nil {
log.V(4).Info("data is nil - skipping update")
return newSkipGenerateResponse(nil, target, nil)
@@ -25,16 +23,9 @@ func manageData(log logr.Logger, target kyvernov1.ResourceSpec, data interface{}
}
targetObj, err := client.GetResource(context.TODO(), target.GetAPIVersion(), target.GetKind(), target.GetNamespace(), target.GetName())
- if err != nil {
- if apierrors.IsNotFound(err) && len(ur.Status.GeneratedResources) != 0 && !synchronize {
- log.V(4).Info("synchronize is disable - skip re-create")
- return newSkipGenerateResponse(nil, target, nil)
- }
- if apierrors.IsNotFound(err) {
- return newCreateGenerateResponse(resource, target, nil)
- }
-
- return newSkipGenerateResponse(nil, target, fmt.Errorf("failed to get the target source: %v", err))
+ if err != nil && apierrors.IsNotFound(err) {
+ // the target resource should always exist regardless of synchronize settings
+ return newCreateGenerateResponse(resource, target, nil)
}
log.V(4).Info("found target resource")
diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go
index 408f532fed..cff081e90b 100644
--- a/pkg/background/generate/generate.go
+++ b/pkg/background/generate/generate.go
@@ -20,7 +20,6 @@ import (
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
- enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/engine/validate"
"github.com/kyverno/kyverno/pkg/engine/variables"
@@ -31,13 +30,11 @@ import (
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
validationpolicy "github.com/kyverno/kyverno/pkg/validation/policy"
"github.com/pkg/errors"
+ "go.uber.org/multierr"
admissionv1 "k8s.io/api/admission/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/apimachinery/pkg/selection"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
)
@@ -95,58 +92,56 @@ func NewGenerateController(
}
func (c *GenerateController) ProcessUR(ur *kyvernov2.UpdateRequest) error {
- logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey(), "rule", ur.Spec.GetRuleName(), "resource", ur.Spec.GetResource().String())
- var err error
+ logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey())
var genResources []kyvernov1.ResourceSpec
logger.Info("start processing UR", "ur", ur.Name, "resourceVersion", ur.GetResourceVersion())
- trigger, err := c.getTrigger(ur.Spec)
- if err != nil || trigger == nil {
- logger.V(3).Info("the trigger resource does not exist or is pending creation")
- if err := updateStatus(c.statusControl, *ur, err, nil); err != nil {
- return err
- }
- return nil
- }
-
- namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger)
- genResources, err = c.applyGenerate(*trigger, *ur, namespaceLabels)
- if err != nil {
- if strings.Contains(err.Error(), doesNotApply) {
- ur.Status.State = kyvernov2.Completed
- logger.V(4).Info(fmt.Sprintf("%s, updating UR status to Completed", err.Error()))
- _, err := c.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), ur, metav1.UpdateOptions{})
- return err
+ var failures []error
+ for i := 0; i < len(ur.Spec.RuleContext); i++ {
+ rule := ur.Spec.RuleContext[i]
+ trigger, err := c.getTrigger(ur.Spec, i)
+ if err != nil || trigger == nil {
+ logger.V(4).Info("the trigger resource does not exist or is pending creation")
+ failures = append(failures, fmt.Errorf("rule %s failed: failed to fetch trigger resource: %v", rule.Rule, err))
+ continue
}
- policy, err := c.getPolicySpec(*ur)
+ namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger)
+ genResources, err = c.applyGenerate(*trigger, *ur, i, namespaceLabels)
if err != nil {
- return err
+ if strings.Contains(err.Error(), doesNotApply) {
+ logger.V(4).Info(fmt.Sprintf("skipping rule %s: %v", rule.Rule, err.Error()))
+ }
+
+ policy, err := c.getPolicyObject(*ur)
+ if err != nil {
+ failures = append(failures, fmt.Errorf("rule %v failed: failed to get policy object: %s", rule.Rule, err))
+ continue
+ }
+
+ events := event.NewBackgroundFailedEvent(err, policy, ur.Spec.RuleContext[i].Rule, event.GeneratePolicyController,
+ kyvernov1.ResourceSpec{Kind: trigger.GetKind(), Namespace: trigger.GetNamespace(), Name: trigger.GetName()})
+ c.eventGen.Add(events...)
}
-
- events := event.NewBackgroundFailedEvent(err, policy, ur.Spec.Rule, event.GeneratePolicyController,
- kyvernov1.ResourceSpec{Kind: trigger.GetKind(), Namespace: trigger.GetNamespace(), Name: trigger.GetName()})
- c.eventGen.Add(events...)
}
- if err = updateStatus(c.statusControl, *ur, err, genResources); err != nil {
- return err
- }
- return err
+ return updateStatus(c.statusControl, *ur, multierr.Combine(failures...), genResources)
}
const doesNotApply = "policy does not apply to resource"
-func (c *GenerateController) getTrigger(spec kyvernov2.UpdateRequestSpec) (*unstructured.Unstructured, error) {
+func (c *GenerateController) getTrigger(spec kyvernov2.UpdateRequestSpec, i int) (*unstructured.Unstructured, error) {
+ resourceSpec := spec.RuleContext[i].Trigger
+ c.log.V(4).Info("fetching trigger", "trigger", resourceSpec.String())
admissionRequest := spec.Context.AdmissionRequestInfo.AdmissionRequest
if admissionRequest == nil {
- return common.GetResource(c.client, spec, c.log)
+ return common.GetResource(c.client, resourceSpec, spec, c.log)
} else {
operation := spec.Context.AdmissionRequestInfo.Operation
if operation == admissionv1.Delete {
- return c.getTriggerForDeleteOperation(spec)
+ return c.getTriggerForDeleteOperation(spec, i)
} else if operation == admissionv1.Create {
- return c.getTriggerForCreateOperation(spec)
+ return c.getTriggerForCreateOperation(spec, i)
} else {
newResource, oldResource, err := admissionutils.ExtractResources(nil, *admissionRequest)
if err != nil {
@@ -163,24 +158,26 @@ func (c *GenerateController) getTrigger(spec kyvernov2.UpdateRequestSpec) (*unst
}
}
-func (c *GenerateController) getTriggerForDeleteOperation(spec kyvernov2.UpdateRequestSpec) (*unstructured.Unstructured, error) {
+func (c *GenerateController) getTriggerForDeleteOperation(spec kyvernov2.UpdateRequestSpec, i int) (*unstructured.Unstructured, error) {
request := spec.Context.AdmissionRequestInfo.AdmissionRequest
_, oldResource, err := admissionutils.ExtractResources(nil, *request)
if err != nil {
return nil, fmt.Errorf("failed to load resource from context: %w", err)
}
labels := oldResource.GetLabels()
+ resourceSpec := spec.RuleContext[i].Trigger
if labels[common.GeneratePolicyLabel] != "" {
// non-trigger deletion, get trigger from ur spec
c.log.V(4).Info("non-trigger resource is deleted, fetching the trigger from the UR spec", "trigger", spec.Resource.String())
- return common.GetResource(c.client, spec, c.log)
+ return common.GetResource(c.client, resourceSpec, spec, c.log)
}
return &oldResource, nil
}
-func (c *GenerateController) getTriggerForCreateOperation(spec kyvernov2.UpdateRequestSpec) (*unstructured.Unstructured, error) {
+func (c *GenerateController) getTriggerForCreateOperation(spec kyvernov2.UpdateRequestSpec, i int) (*unstructured.Unstructured, error) {
admissionRequest := spec.Context.AdmissionRequestInfo.AdmissionRequest
- trigger, err := common.GetResource(c.client, spec, c.log)
+ resourceSpec := spec.RuleContext[i].Trigger
+ trigger, err := common.GetResource(c.client, resourceSpec, spec, c.log)
if err != nil || trigger == nil {
if admissionRequest.SubResource == "" {
return nil, err
@@ -197,22 +194,36 @@ func (c *GenerateController) getTriggerForCreateOperation(spec kyvernov2.UpdateR
return trigger, err
}
-func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, ur kyvernov2.UpdateRequest, namespaceLabels map[string]string) ([]kyvernov1.ResourceSpec, error) {
- logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey(), "rule", ur.Spec.GetRuleName(), "resource", ur.Spec.GetResource().String())
- logger.V(3).Info("applying generate policy rule")
+func (c *GenerateController) applyGenerate(trigger unstructured.Unstructured, ur kyvernov2.UpdateRequest, i int, namespaceLabels map[string]string) ([]kyvernov1.ResourceSpec, error) {
+ logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey())
+ logger.V(3).Info("applying generate policy")
- policy, err := c.getPolicySpec(ur)
+ policy, err := c.getPolicyObject(ur)
if err != nil && !apierrors.IsNotFound(err) {
logger.Error(err, "error in fetching policy")
return nil, err
}
- if ur.Spec.DeleteDownstream || apierrors.IsNotFound(err) {
- err = c.deleteDownstream(policy, &ur)
+ ruleContext := ur.Spec.RuleContext[i]
+ if ruleContext.DeleteDownstream || apierrors.IsNotFound(err) {
+ err = c.deleteDownstream(policy, ruleContext, &ur)
return nil, err
}
- policyContext, err := common.NewBackgroundContext(logger, c.client, &ur, policy, &resource, c.configuration, c.jp, namespaceLabels)
+ var rule *kyvernov1.Rule
+ p := policy.CreateDeepCopy()
+ for j := range p.GetSpec().Rules {
+ if p.GetSpec().Rules[j].Name == ruleContext.Rule {
+ rule = &p.GetSpec().Rules[j]
+ break
+ }
+ }
+ if rule == nil {
+ logger.Info("skip rule application as the rule does not exist in the updaterequest", "rule", ruleContext.Rule)
+ return nil, nil
+ }
+ p.GetSpec().SetRules([]kyvernov1.Rule{*rule})
+ policyContext, err := common.NewBackgroundContext(logger, c.client, ur.Spec.Context, p, &trigger, c.configuration, c.jp, namespaceLabels)
if err != nil {
return nil, err
}
@@ -235,62 +246,17 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
}
var applicableRules []string
- // Removing UR if rule is failed. Used when the generate condition failed but ur exist
for _, r := range engineResponse.PolicyResponse.Rules {
- if r.Name() != ur.Spec.GetRuleName() {
- continue
- }
-
- if r.Status() != engineapi.RuleStatusPass {
- logger.V(4).Info("querying all update requests")
- selector := labels.SelectorFromSet(labels.Set(map[string]string{
- kyvernov2.URGeneratePolicyLabel: engineResponse.Policy().GetName(),
- kyvernov2.URGenerateResourceKindLabel: engineResponse.Resource.GetKind(),
- kyvernov2.URGenerateResourceNSLabel: engineResponse.Resource.GetNamespace(),
- }))
- // get update requests that have the resource UID label
- requirement, err := labels.NewRequirement(kyvernov2.URGenerateResourceUIDLabel, selection.Equals, []string{string(engineResponse.Resource.GetUID())})
- if err != nil {
- logger.Error(err, "failed to add the resource UID label")
- }
- selectorWithResUID := selector.Add(*requirement)
- urList, err := c.urLister.List(selectorWithResUID)
- if err != nil {
- logger.Error(err, "failed to get update request for the resource", "kind", engineResponse.Resource.GetKind(), "name", engineResponse.Resource.GetName(), "namespace", engineResponse.Resource.GetNamespace())
- continue
- }
-
- if len(urList) == 0 {
- // get update requests that have the resource name label
- requirement, err = labels.NewRequirement(kyvernov2.URGenerateResourceNameLabel, selection.Equals, []string{engineResponse.Resource.GetName()})
- if err != nil {
- logger.Error(err, "failed to add the resource name label")
- continue
- }
- selectorWithResName := selector.Add(*requirement)
- urList, err = c.urLister.List(selectorWithResName)
- if err != nil {
- logger.Error(err, "failed to get update request for the resource", "kind", engineResponse.Resource.GetKind(), "name", engineResponse.Resource.GetName(), "namespace", engineResponse.Resource.GetNamespace())
- continue
- }
- }
-
- for _, v := range urList {
- err := c.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), v.GetName(), metav1.DeleteOptions{})
- if err != nil {
- logger.Error(err, "failed to delete update request")
- }
- }
- } else {
+ if r.Status() == engineapi.RuleStatusPass {
applicableRules = append(applicableRules, r.Name())
}
}
// Apply the generate rule on resource
- genResources, err := c.ApplyGeneratePolicy(logger, policyContext, ur, applicableRules)
+ genResources, err := c.ApplyGeneratePolicy(logger, policyContext, applicableRules)
if err == nil {
for _, res := range genResources {
- e := event.NewResourceGenerationEvent(ur.Spec.Policy, ur.Spec.Rule, event.GeneratePolicyController, res)
+ e := event.NewResourceGenerationEvent(ur.Spec.Policy, ur.Spec.RuleContext[i].Rule, event.GeneratePolicyController, res)
c.eventGen.Add(e)
}
@@ -301,8 +267,8 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
return genResources, err
}
-// getPolicySpec gets the policy spec from the ClusterPolicy/Policy
-func (c *GenerateController) getPolicySpec(ur kyvernov2.UpdateRequest) (kyvernov1.PolicyInterface, error) {
+// getPolicyObject gets the policy spec from the ClusterPolicy/Policy
+func (c *GenerateController) getPolicyObject(ur kyvernov2.UpdateRequest) (kyvernov1.PolicyInterface, error) {
pNamespace, pName, err := cache.SplitMetaNamespaceKey(ur.Spec.Policy)
if err != nil {
return nil, err
@@ -335,12 +301,9 @@ func updateStatus(statusControl common.StatusControlInterface, ur kyvernov2.Upda
return nil
}
-func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, ur kyvernov2.UpdateRequest, applicableRules []string) (genResources []kyvernov1.ResourceSpec, err error) {
- // Get the response as the actions to be performed on the resource
- // - - substitute values
+func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, applicableRules []string) (genResources []kyvernov1.ResourceSpec, err error) {
policy := policyContext.Policy()
resource := policyContext.NewResource()
- jsonContext := policyContext.JSONContext()
// To manage existing resources, we compare the creation time for the default resource to be generated and policy creation time
ruleNameToProcessingTime := make(map[string]time.Duration)
applyRules := policy.GetSpec().GetApplyRules()
@@ -355,7 +318,6 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
if !slices.Contains(applicableRules, rule.Name) {
continue
}
-
if rule.Generation.Synchronize {
ruleRaw, err := json.Marshal(rule.DeepCopy())
if err != nil {
@@ -377,7 +339,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
if applyRules == kyvernov1.ApplyOne && applyCount > 0 {
break
}
-
+ logger := log.WithValues("rule", rule.Name)
// add configmap json data to context
if err := c.engine.ContextLoader(policyContext.Policy(), rule)(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil {
log.Error(err, "cannot add configmaps to context")
@@ -389,7 +351,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
return nil, err
}
- genResource, err = applyRule(log, c.client, rule, resource, jsonContext, policy, ur)
+ genResource, err = applyRule(logger, c.client, rule, resource, policy)
if err != nil {
log.Error(err, "failed to apply generate rule", "policy", policy.GetName(), "rule", rule.Name, "resource", resource.GetName())
return nil, err
@@ -402,7 +364,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
return genResources, nil
}
-func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, trigger unstructured.Unstructured, ctx enginecontext.EvalInterface, policy kyvernov1.PolicyInterface, ur kyvernov2.UpdateRequest) ([]kyvernov1.ResourceSpec, error) {
+func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, trigger unstructured.Unstructured, policy kyvernov1.PolicyInterface) ([]kyvernov1.ResourceSpec, error) {
responses := []generateResponse{}
var err error
var newGenResources []kyvernov1.ResourceSpec
@@ -411,12 +373,12 @@ func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, t
logger := log.WithValues("target", target.String())
if rule.Generation.Clone.Name != "" {
- resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, policy, ur, rule, client)
+ resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, policy, rule, client)
responses = append(responses, resp)
} else if len(rule.Generation.CloneList.Kinds) != 0 {
- responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), ur, policy, rule, client)
+ responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), policy, rule, client)
} else {
- resp := manageData(logger.WithValues("type", "data"), target, rule.Generation.RawData, rule.Generation.Synchronize, ur, client)
+ resp := manageData(logger.WithValues("type", "data"), target, rule.Generation.RawData, rule.Generation.Synchronize, client)
responses = append(responses, resp)
}
@@ -464,7 +426,7 @@ func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, t
} else if response.GetAction() == Update {
generatedObj, err := client.GetResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName())
if err != nil {
- logger.V(2).Info("target resource not found, creating new target")
+ logger.V(2).Info("creating new target due to the failure when fetching", "err", err.Error())
if policy.GetSpec().UseServerSideApply {
_, err = client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate")
} else {
diff --git a/pkg/background/generate/validate.go b/pkg/background/generate/validate.go
index e2fa32ef71..8d1522856e 100644
--- a/pkg/background/generate/validate.go
+++ b/pkg/background/generate/validate.go
@@ -43,7 +43,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
return path, fmt.Errorf("pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
}
- return validateMap(log, typedResourceElement, typedPatternElement, originPattern, path)
+ return validateMap(typedResourceElement, typedPatternElement, originPattern, path)
// array
case []interface{}:
typedResourceElement, ok := resourceElement.([]interface{})
@@ -67,7 +67,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
// If validateResourceElement detects map element inside resource and pattern trees, it goes to validateMap
// For each element of the map we must detect the type again, so we pass these elements to validateResourceElement
-func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) (string, error) {
+func validateMap(resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) (string, error) {
patternMap = wildcards.ExpandInMetadata(patternMap, resourceMap)
sortedResourceKeys := list.New()
for k := range patternMap {
diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go
index e4d37a9336..0d2e6130e2 100644
--- a/pkg/background/mutate/mutate.go
+++ b/pkg/background/mutate/mutate.go
@@ -94,7 +94,7 @@ func (c *mutateExistingController) ProcessUR(ur *kyvernov2.UpdateRequest) error
var trigger *unstructured.Unstructured
admissionRequest := ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest
if admissionRequest == nil {
- trigger, err = common.GetResource(c.client, ur.Spec, c.log)
+ trigger, err = common.GetResource(c.client, ur.Spec.Resource, ur.Spec, c.log)
if err != nil || trigger == nil {
logger.WithName(rule.Name).Error(err, "failed to get trigger resource")
if err := updateURStatus(c.statusControl, *ur, err); err != nil {
@@ -104,7 +104,7 @@ func (c *mutateExistingController) ProcessUR(ur *kyvernov2.UpdateRequest) error
}
} else {
if admissionRequest.Operation == admissionv1.Create {
- trigger, err = common.GetResource(c.client, ur.Spec, c.log)
+ trigger, err = common.GetResource(c.client, ur.Spec.Resource, ur.Spec, c.log)
if err != nil || trigger == nil {
if admissionRequest.SubResource == "" {
logger.WithName(rule.Name).Error(err, "failed to get trigger resource")
@@ -139,7 +139,7 @@ func (c *mutateExistingController) ProcessUR(ur *kyvernov2.UpdateRequest) error
}
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger)
- policyContext, err := common.NewBackgroundContext(logger, c.client, ur, policy, trigger, c.configuration, c.jp, namespaceLabels)
+ policyContext, err := common.NewBackgroundContext(logger, c.client, ur.Spec.Context, policy, trigger, c.configuration, c.jp, namespaceLabels)
if err != nil {
logger.WithName(rule.Name).Error(err, "failed to build policy context")
errs = append(errs, err)
diff --git a/pkg/client/applyconfigurations/kyverno/v2/rulecontext.go b/pkg/client/applyconfigurations/kyverno/v2/rulecontext.go
new file mode 100644
index 0000000000..373ea59966
--- /dev/null
+++ b/pkg/client/applyconfigurations/kyverno/v2/rulecontext.go
@@ -0,0 +1,70 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v2
+
+import (
+ v1 "github.com/kyverno/kyverno/pkg/client/applyconfigurations/kyverno/v1"
+)
+
+// RuleContextApplyConfiguration represents an declarative configuration of the RuleContext type for use
+// with apply.
+type RuleContextApplyConfiguration struct {
+ Rule *string `json:"rule,omitempty"`
+ DeleteDownstream *bool `json:"deleteDownstream,omitempty"`
+ Synchronize *bool `json:"synchronize,omitempty"`
+ Trigger *v1.ResourceSpecApplyConfiguration `json:"trigger,omitempty"`
+}
+
+// RuleContextApplyConfiguration constructs an declarative configuration of the RuleContext type for use with
+// apply.
+func RuleContext() *RuleContextApplyConfiguration {
+ return &RuleContextApplyConfiguration{}
+}
+
+// WithRule sets the Rule field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Rule field is set to the value of the last call.
+func (b *RuleContextApplyConfiguration) WithRule(value string) *RuleContextApplyConfiguration {
+ b.Rule = &value
+ return b
+}
+
+// WithDeleteDownstream sets the DeleteDownstream field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the DeleteDownstream field is set to the value of the last call.
+func (b *RuleContextApplyConfiguration) WithDeleteDownstream(value bool) *RuleContextApplyConfiguration {
+ b.DeleteDownstream = &value
+ return b
+}
+
+// WithSynchronize sets the Synchronize field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Synchronize field is set to the value of the last call.
+func (b *RuleContextApplyConfiguration) WithSynchronize(value bool) *RuleContextApplyConfiguration {
+ b.Synchronize = &value
+ return b
+}
+
+// WithTrigger sets the Trigger field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Trigger field is set to the value of the last call.
+func (b *RuleContextApplyConfiguration) WithTrigger(value *v1.ResourceSpecApplyConfiguration) *RuleContextApplyConfiguration {
+ b.Trigger = value
+ return b
+}
diff --git a/pkg/client/applyconfigurations/kyverno/v2/updaterequestspec.go b/pkg/client/applyconfigurations/kyverno/v2/updaterequestspec.go
index 0f1977f6ce..6e6bf59bdb 100644
--- a/pkg/client/applyconfigurations/kyverno/v2/updaterequestspec.go
+++ b/pkg/client/applyconfigurations/kyverno/v2/updaterequestspec.go
@@ -28,6 +28,7 @@ import (
type UpdateRequestSpecApplyConfiguration struct {
Type *v2.RequestType `json:"requestType,omitempty"`
Policy *string `json:"policy,omitempty"`
+ RuleContext []RuleContextApplyConfiguration `json:"ruleContext,omitempty"`
Rule *string `json:"rule,omitempty"`
DeleteDownstream *bool `json:"deleteDownstream,omitempty"`
Synchronize *bool `json:"synchronize,omitempty"`
@@ -57,6 +58,19 @@ func (b *UpdateRequestSpecApplyConfiguration) WithPolicy(value string) *UpdateRe
return b
}
+// WithRuleContext adds the given value to the RuleContext field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the RuleContext field.
+func (b *UpdateRequestSpecApplyConfiguration) WithRuleContext(values ...*RuleContextApplyConfiguration) *UpdateRequestSpecApplyConfiguration {
+ for i := range values {
+ if values[i] == nil {
+ panic("nil value passed to WithRuleContext")
+ }
+ b.RuleContext = append(b.RuleContext, *values[i])
+ }
+ return b
+}
+
// WithRule sets the Rule field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Rule field is set to the value of the last call.
diff --git a/pkg/client/applyconfigurations/utils.go b/pkg/client/applyconfigurations/utils.go
index f0c396bac2..576c664969 100644
--- a/pkg/client/applyconfigurations/utils.go
+++ b/pkg/client/applyconfigurations/utils.go
@@ -185,6 +185,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &kyvernov2.PolicyExceptionSpecApplyConfiguration{}
case v2.SchemeGroupVersion.WithKind("RequestInfo"):
return &kyvernov2.RequestInfoApplyConfiguration{}
+ case v2.SchemeGroupVersion.WithKind("RuleContext"):
+ return &kyvernov2.RuleContextApplyConfiguration{}
case v2.SchemeGroupVersion.WithKind("UpdateRequest"):
return &kyvernov2.UpdateRequestApplyConfiguration{}
case v2.SchemeGroupVersion.WithKind("UpdateRequestSpec"):
diff --git a/pkg/event/events.go b/pkg/event/events.go
index 2251f5d59d..7b88a4b4e2 100644
--- a/pkg/event/events.go
+++ b/pkg/event/events.go
@@ -167,6 +167,12 @@ func NewBackgroundFailedEvent(err error, policy kyvernov1.PolicyInterface, rule
Namespace: policy.GetNamespace(),
UID: policy.GetUID(),
}
+ var msg string
+ if rule == "" {
+ msg = fmt.Sprintf("policy %s error: %v", policy.GetName(), err)
+ } else {
+ msg = fmt.Sprintf("policy %s/%s error: %v", policy.GetName(), rule, err)
+ }
events = append(events, Info{
Regarding: regarding,
Related: &corev1.ObjectReference{
@@ -178,7 +184,7 @@ func NewBackgroundFailedEvent(err error, policy kyvernov1.PolicyInterface, rule
},
Source: source,
Reason: PolicyError,
- Message: fmt.Sprintf("policy %s/%s error: %v", policy.GetName(), rule, err),
+ Message: msg,
Action: None,
})
@@ -366,6 +372,12 @@ func NewValidatingAdmissionPolicyEvent(policy kyvernov1.PolicyInterface, vapName
}
func NewFailedEvent(err error, policy, rule string, source Source, resource kyvernov1.ResourceSpec) Info {
+ var msg string
+ if rule == "" {
+ msg = fmt.Sprintf("policy %s error: %v", policy, err)
+ } else {
+ msg = fmt.Sprintf("policy %s/%s error: %v", policy, rule, err)
+ }
return Info{
Regarding: corev1.ObjectReference{
APIVersion: resource.APIVersion,
@@ -376,7 +388,7 @@ func NewFailedEvent(err error, policy, rule string, source Source, resource kyve
},
Source: source,
Reason: PolicyError,
- Message: fmt.Sprintf("policy %s/%s error: %v", policy, rule, err),
+ Message: msg,
Action: None,
}
}
diff --git a/pkg/policy/generate.go b/pkg/policy/generate.go
index 9ba2d61644..40c62c921e 100644
--- a/pkg/policy/generate.go
+++ b/pkg/policy/generate.go
@@ -11,6 +11,7 @@ import (
"github.com/kyverno/kyverno/pkg/background/common"
generateutils "github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/config"
+ engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
"go.uber.org/multierr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -25,10 +26,6 @@ func (pc *policyController) handleGenerate(policyKey string, policy kyvernov1.Po
return err
}
- if !policy.GetSpec().IsGenerateExisting() {
- return nil
- }
-
logger.V(4).Info("reconcile policy with generateExisting enabled")
if err := pc.handleGenerateForExisting(policy); err != nil {
logger.Error(err, "failed to create UR for generateExisting")
@@ -37,65 +34,9 @@ func (pc *policyController) handleGenerate(policyKey string, policy kyvernov1.Po
return nil
}
-func (pc *policyController) handleGenerateForExisting(policy kyvernov1.PolicyInterface) error {
- var errors []error
- var triggers []*unstructured.Unstructured
- ruleType := kyvernov2.Generate
- spec := policy.GetSpec()
- policyNew := policy.CreateDeepCopy()
- policyNew.GetSpec().Rules = nil
-
- for _, rule := range spec.Rules {
- // check if the rule sets the generateExisting field.
- // if not, use the policy level setting
- generateExisting := rule.Generation.GenerateExisting
- if generateExisting != nil {
- if !*generateExisting {
- continue
- }
- } else if !spec.GenerateExisting {
- continue
- }
-
- triggers = getTriggers(pc.client, rule, policy.IsNamespaced(), policy.GetNamespace(), pc.log)
- policyNew.GetSpec().SetRules([]kyvernov1.Rule{rule})
- for _, trigger := range triggers {
- ur := newUR(policyNew, common.ResourceSpecFromUnstructured(*trigger), rule.Name, ruleType, false)
- skip, err := pc.handleUpdateRequest(ur, trigger, rule.Name, policyNew)
- if err != nil {
- pc.log.Error(err, "failed to create new UR on policy update", "policy", policyNew.GetName(), "rule", rule.Name, "rule type", ruleType,
- "target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
- errors = append(errors, err)
- continue
- }
-
- if skip {
- continue
- }
-
- pc.log.V(4).Info("successfully created UR on policy update", "policy", policyNew.GetName(), "rule", rule.Name, "rule type", ruleType,
- "target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
- }
- }
- return multierr.Combine(errors...)
-}
-
-func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.PolicyInterface) error {
- var errs []error
- rules := autogen.ComputeRules(policy, "")
- for _, r := range rules {
- generateType, sync, orphanDownstreamOnPolicyDelete := r.GetTypeAndSyncAndOrphanDownstream()
- if sync && (generateType == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
- if err := pc.syncDataPolicyChanges(policy, true); err != nil {
- errs = append(errs, err)
- }
- }
- }
- return multierr.Combine(errs...)
-}
-
func (pc *policyController) syncDataPolicyChanges(policy kyvernov1.PolicyInterface, deleteDownstream bool) error {
- var errorList []error
+ var errs []error
+ ur := newGenerateUR(policy)
for _, rule := range policy.GetSpec().Rules {
generate := rule.Generation
if !generate.Synchronize {
@@ -104,14 +45,139 @@ func (pc *policyController) syncDataPolicyChanges(policy kyvernov1.PolicyInterfa
if generate.GetData() == nil {
continue
}
- if err := pc.syncDataRulechanges(policy, rule, deleteDownstream); err != nil {
- errorList = append(errorList, err)
+ var err error
+ if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule, deleteDownstream, false); err != nil {
+ errs = append(errs, err)
}
}
- return multierr.Combine(errorList...)
+
+ if len(ur.Spec.RuleContext) == 0 {
+ return multierr.Combine(errs...)
+ }
+ pc.log.V(2).WithName("syncDataPolicyChanges").Info("creating new UR for generate")
+ created, err := pc.urGenerator.Generate(context.TODO(), pc.kyvernoClient, ur, pc.log)
+ if err != nil {
+ errs = append(errs, err)
+ }
+ if created != nil {
+ updated := created.DeepCopy()
+ updated.Status.State = kyvernov2.Pending
+ _, err = pc.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{})
+ if err != nil {
+ errs = append(errs, err)
+ }
+ }
+ return multierr.Combine(errs...)
}
-func (pc *policyController) syncDataRulechanges(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, deleteDownstream bool) error {
+func (pc *policyController) handleGenerateForExisting(policy kyvernov1.PolicyInterface) error {
+ var errors []error
+ var triggers []*unstructured.Unstructured
+ policyNew := policy.CreateDeepCopy()
+ policyNew.GetSpec().Rules = nil
+ ur := newGenerateUR(policy)
+ logger := pc.log.WithName("handleGenerateForExisting")
+ for _, rule := range policy.GetSpec().Rules {
+ if !rule.HasGenerate() {
+ continue
+ }
+
+ // check if the rule sets the generateExisting field.
+ // if not, use the policy level setting
+ generateExisting := rule.Generation.GenerateExisting
+ if generateExisting != nil {
+ if !*generateExisting {
+ continue
+ }
+ } else if !policy.GetSpec().GenerateExisting {
+ continue
+ }
+
+ triggers = getTriggers(pc.client, rule, policy.IsNamespaced(), policy.GetNamespace(), pc.log)
+ policyNew.GetSpec().SetRules([]kyvernov1.Rule{rule})
+ for _, trigger := range triggers {
+ namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), pc.nsLister, pc.log)
+ policyContext, err := common.NewBackgroundContext(pc.log, pc.client, ur.Spec.Context, policy, trigger, pc.configuration, pc.jp, namespaceLabels)
+ if err != nil {
+ errors = append(errors, fmt.Errorf("failed to build policy context for rule %s: %w", rule.Name, err))
+ continue
+ }
+
+ engineResponse := pc.engine.ApplyBackgroundChecks(context.TODO(), policyContext)
+ if len(engineResponse.PolicyResponse.Rules) == 0 {
+ continue
+ }
+ logger.V(4).Info("adding rule context", "rule", rule.Name, "trigger", trigger.GetNamespace()+"/"+trigger.GetName())
+ addRuleContext(ur, rule.Name, common.ResourceSpecFromUnstructured(*trigger), false)
+ }
+ }
+
+ if len(ur.Spec.RuleContext) == 0 {
+ return multierr.Combine(errors...)
+ }
+
+ logger.V(2).Info("creating new UR for generate")
+ created, err := pc.urGenerator.Generate(context.TODO(), pc.kyvernoClient, ur, pc.log)
+ if err != nil {
+ errors = append(errors, err)
+ return multierr.Combine(errors...)
+ }
+ if created != nil {
+ updated := created.DeepCopy()
+ updated.Status.State = kyvernov2.Pending
+ _, err = pc.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{})
+ if err != nil {
+ errors = append(errors, err)
+ return multierr.Combine(errors...)
+ }
+ pc.log.V(4).Info("successfully created UR on policy update", "policy", policyNew.GetName())
+ }
+ return multierr.Combine(errors...)
+}
+
+func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.PolicyInterface) error {
+ var errs []error
+ rules := autogen.ComputeRules(policy, "")
+ ur := newGenerateUR(policy)
+ for _, r := range rules {
+ generate := r.Generation
+ if !generate.Synchronize {
+ continue
+ }
+ if generate.GetData() == nil {
+ continue
+ }
+ generateType, sync, orphanDownstreamOnPolicyDelete := r.GetTypeAndSyncAndOrphanDownstream()
+ if sync && (generateType == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
+ var err error
+ if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r, true, true); err != nil {
+ errs = append(errs, err)
+ }
+ }
+ }
+
+ if len(ur.Spec.RuleContext) == 0 {
+ return multierr.Combine(errs...)
+ }
+
+ pc.log.V(2).WithName("createURForDownstreamDeletion").Info("creating new UR for generate")
+ created, err := pc.urGenerator.Generate(context.TODO(), pc.kyvernoClient, ur, pc.log)
+ if err != nil {
+ errs = append(errs, err)
+ }
+ if created != nil {
+ updated := created.DeepCopy()
+ updated.Status.State = kyvernov2.Pending
+ updated.Status.GeneratedResources = ur.Status.GeneratedResources
+ _, err = pc.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{})
+ if err != nil {
+ errs = append(errs, err)
+ }
+ }
+ return multierr.Combine(errs...)
+}
+
+func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest, rule kyvernov1.Rule, deleteDownstream, policyDeletion bool) (*kyvernov2.UpdateRequest, error) {
labels := map[string]string{
common.GeneratePolicyLabel: policy.GetName(),
common.GeneratePolicyNamespaceLabel: policy.GetNamespace(),
@@ -121,29 +187,24 @@ func (pc *policyController) syncDataRulechanges(policy kyvernov1.PolicyInterface
downstreams, err := common.FindDownstream(pc.client, rule.Generation.GetAPIVersion(), rule.Generation.GetKind(), labels)
if err != nil {
- return err
+ return ur, err
+ }
+
+ if len(downstreams.Items) == 0 {
+ return ur, nil
}
pc.log.V(4).Info("sync data rule changes to downstream targets")
- var errorList []error
for _, downstream := range downstreams.Items {
labels := downstream.GetLabels()
trigger := generateutils.TriggerFromLabels(labels)
- ur := newUR(policy, trigger, rule.Name, kyvernov2.Generate, deleteDownstream)
- created, err := pc.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).Create(context.TODO(), ur, metav1.CreateOptions{})
- if err != nil {
- errorList = append(errorList, err)
- continue
- }
- updated := created.DeepCopy()
- updated.Status = newURStatus(downstream)
- _, err = pc.kyvernoClient.KyvernoV2().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{})
- if err != nil {
- errorList = append(errorList, err)
- continue
+ addRuleContext(ur, rule.Name, trigger, deleteDownstream)
+ if policyDeletion {
+ addGeneratedResources(ur, downstream)
}
}
- return multierr.Combine(errorList...)
+
+ return ur, nil
}
// ruleDeletion returns true if any rule is deleted, along with deleted rules
diff --git a/pkg/policy/mutate.go b/pkg/policy/mutate.go
index d52a5beb75..401031a47c 100644
--- a/pkg/policy/mutate.go
+++ b/pkg/policy/mutate.go
@@ -43,7 +43,7 @@ func (pc *policyController) handleMutate(policyKey string, policy kyvernov1.Poli
}
logger.Info("creating new UR for mutate")
- ur := newUR(policy, backgroundcommon.ResourceSpecFromUnstructured(*trigger), rule.Name, ruleType, false)
+ ur := newMutateUR(policy, backgroundcommon.ResourceSpecFromUnstructured(*trigger), rule.Name)
skip, err := pc.handleUpdateRequest(ur, trigger, rule.Name, policyNew)
if err != nil {
pc.log.Error(err, "failed to create new UR on policy update", "policy", policyNew.GetName(), "rule", rule.Name, "rule type", ruleType,
diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go
index 4eb10025fe..eb31677f66 100644
--- a/pkg/policy/policy_controller.go
+++ b/pkg/policy/policy_controller.go
@@ -398,7 +398,7 @@ func (pc *policyController) requeuePolicies() {
func (pc *policyController) handleUpdateRequest(ur *kyvernov2.UpdateRequest, triggerResource *unstructured.Unstructured, ruleName string, policy kyvernov1.PolicyInterface) (skip bool, err error) {
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(triggerResource.GetKind(), triggerResource.GetNamespace(), pc.nsLister, pc.log)
- policyContext, err := backgroundcommon.NewBackgroundContext(pc.log, pc.client, ur, policy, triggerResource, pc.configuration, pc.jp, namespaceLabels)
+ policyContext, err := backgroundcommon.NewBackgroundContext(pc.log, pc.client, ur.Spec.Context, policy, triggerResource, pc.configuration, pc.jp, namespaceLabels)
if err != nil {
return false, fmt.Errorf("failed to build policy context for rule %s: %w", ruleName, err)
}
diff --git a/pkg/policy/updaterequest.go b/pkg/policy/updaterequest.go
index fade189e0e..88a15c0b7e 100644
--- a/pkg/policy/updaterequest.go
+++ b/pkg/policy/updaterequest.go
@@ -7,25 +7,37 @@ import (
"github.com/kyverno/kyverno/pkg/config"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/labels"
)
-func newUR(policy kyvernov1.PolicyInterface, trigger kyvernov1.ResourceSpec, ruleName string, ruleType kyvernov2.RequestType, deleteDownstream bool) *kyvernov2.UpdateRequest {
- var policyNameNamespaceKey string
-
- if policy.IsNamespaced() {
- policyNameNamespaceKey = policy.GetNamespace() + "/" + policy.GetName()
- } else {
- policyNameNamespaceKey = policy.GetName()
+func newMutateUR(policy kyvernov1.PolicyInterface, trigger kyvernov1.ResourceSpec, ruleName string) *kyvernov2.UpdateRequest {
+ ur := newUrMeta()
+ ur.Labels = common.MutateLabelsSet(policyKey(policy), trigger)
+ ur.Spec = kyvernov2.UpdateRequestSpec{
+ Type: kyvernov2.Mutate,
+ Policy: policyKey(policy),
+ Rule: ruleName,
+ Resource: kyvernov1.ResourceSpec{
+ Kind: trigger.GetKind(),
+ Namespace: trigger.GetNamespace(),
+ Name: trigger.GetName(),
+ APIVersion: trigger.GetAPIVersion(),
+ UID: trigger.GetUID(),
+ },
}
+ return ur
+}
- var label labels.Set
- if ruleType == kyvernov2.Mutate {
- label = common.MutateLabelsSet(policyNameNamespaceKey, trigger)
- } else {
- label = common.GenerateLabelsSet(policyNameNamespaceKey, trigger)
+func newGenerateUR(policy kyvernov1.PolicyInterface) *kyvernov2.UpdateRequest {
+ ur := newUrMeta()
+ ur.Labels = common.GenerateLabelsSet(policyKey(policy))
+ ur.Spec = kyvernov2.UpdateRequestSpec{
+ Type: kyvernov2.Generate,
+ Policy: policyKey(policy),
}
+ return ur
+}
+func newUrMeta() *kyvernov2.UpdateRequest {
return &kyvernov2.UpdateRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: kyvernov2.SchemeGroupVersion.String(),
@@ -34,35 +46,32 @@ func newUR(policy kyvernov1.PolicyInterface, trigger kyvernov1.ResourceSpec, rul
ObjectMeta: metav1.ObjectMeta{
GenerateName: "ur-",
Namespace: config.KyvernoNamespace(),
- Labels: label,
- },
- Spec: kyvernov2.UpdateRequestSpec{
- Type: ruleType,
- Policy: policyNameNamespaceKey,
- Rule: ruleName,
- Resource: kyvernov1.ResourceSpec{
- Kind: trigger.GetKind(),
- Namespace: trigger.GetNamespace(),
- Name: trigger.GetName(),
- APIVersion: trigger.GetAPIVersion(),
- UID: trigger.GetUID(),
- },
- DeleteDownstream: deleteDownstream,
},
}
}
-func newURStatus(downstream unstructured.Unstructured) kyvernov2.UpdateRequestStatus {
- return kyvernov2.UpdateRequestStatus{
- State: kyvernov2.Pending,
- GeneratedResources: []kyvernov1.ResourceSpec{
- {
- APIVersion: downstream.GetAPIVersion(),
- Kind: downstream.GetKind(),
- Namespace: downstream.GetNamespace(),
- Name: downstream.GetName(),
- UID: downstream.GetUID(),
- },
+func addGeneratedResources(ur *kyvernov2.UpdateRequest, downstream unstructured.Unstructured) {
+ ur.Status.GeneratedResources = append(ur.Status.GeneratedResources,
+ kyvernov1.ResourceSpec{
+ APIVersion: downstream.GetAPIVersion(),
+ Kind: downstream.GetKind(),
+ Namespace: downstream.GetNamespace(),
+ Name: downstream.GetName(),
+ UID: downstream.GetUID(),
},
- }
+ )
+}
+
+func addRuleContext(ur *kyvernov2.UpdateRequest, ruleName string, trigger kyvernov1.ResourceSpec, deleteDownstream bool) {
+ ur.Spec.RuleContext = append(ur.Spec.RuleContext, kyvernov2.RuleContext{
+ Rule: ruleName,
+ Trigger: kyvernov1.ResourceSpec{
+ Kind: trigger.GetKind(),
+ Namespace: trigger.GetNamespace(),
+ Name: trigger.GetName(),
+ APIVersion: trigger.GetAPIVersion(),
+ UID: trigger.GetUID(),
+ },
+ DeleteDownstream: deleteDownstream,
+ })
}
diff --git a/pkg/policy/utils.go b/pkg/policy/utils.go
index be3f6a60ac..bf8b6e08aa 100644
--- a/pkg/policy/utils.go
+++ b/pkg/policy/utils.go
@@ -37,3 +37,14 @@ func castPolicy(p interface{}) kyvernov1.PolicyInterface {
}
return policy
}
+
+func policyKey(policy kyvernov1.PolicyInterface) string {
+ var policyNameNamespaceKey string
+
+ if policy.IsNamespaced() {
+ policyNameNamespaceKey = policy.GetNamespace() + "/" + policy.GetName()
+ } else {
+ policyNameNamespaceKey = policy.GetName()
+ }
+ return policyNameNamespaceKey
+}
diff --git a/pkg/webhooks/resource/generation/handler.go b/pkg/webhooks/resource/generation/handler.go
index 5f59e48f3f..6e52d32868 100644
--- a/pkg/webhooks/resource/generation/handler.go
+++ b/pkg/webhooks/resource/generation/handler.go
@@ -89,7 +89,7 @@ func (h *generationHandler) Handle(
if h.backgroundServiceAccountName == policyContext.AdmissionInfo().AdmissionUserInfo.Username {
return
}
- h.handleNonTrigger(ctx, policyContext, request)
+ h.handleNonTrigger(ctx, policyContext)
}
func getAppliedRules(policy kyvernov1.PolicyInterface, applied []engineapi.RuleResponse) []kyvernov1.Rule {
@@ -137,13 +137,12 @@ func (h *generationHandler) handleTrigger(
func (h *generationHandler) handleNonTrigger(
ctx context.Context,
policyContext *engine.PolicyContext,
- request admissionv1.AdmissionRequest,
) {
resource := policyContext.OldResource()
labels := resource.GetLabels()
if _, ok := labels[common.GenerateTypeCloneSourceLabel]; ok || labels[common.GeneratePolicyLabel] != "" {
h.log.V(4).Info("handle non-trigger resource operation for generate")
- if err := h.processRequest(ctx, policyContext, request); err != nil {
+ if err := h.processRequest(ctx, policyContext); err != nil {
h.log.Error(err, "failed to create the UR on non-trigger admission request")
}
}
@@ -171,16 +170,14 @@ func (h *generationHandler) applyGeneration(
}
rules := getAppliedRules(policy, appliedRules)
- for _, rule := range rules {
- h.log.V(4).Info("creating the UR to generate downstream on trigger's operation", "operation", request.Operation, "rule", rule.Name)
- urSpec := buildURSpec(kyvernov2.Generate, pKey, rule.Name, triggerSpec, false)
- urSpec.Context = buildURContext(request, policyContext)
- if err := h.urGenerator.Apply(ctx, urSpec); err != nil {
- h.log.Error(err, "failed to create the UR to create downstream on trigger's operation", "operation", request.Operation, "rule", rule.Name)
- e := event.NewFailedEvent(err, pKey, rule.Name, event.GeneratePolicyController,
- kyvernov1.ResourceSpec{Kind: policy.GetKind(), Namespace: policy.GetNamespace(), Name: policy.GetName()})
- h.eventGen.Add(e)
- }
+ h.log.V(4).Info("creating the UR to generate downstream on trigger's operation", "operation", request.Operation, "policy", pKey)
+ urSpec := buildURSpecNew(kyvernov2.Generate, pKey, rules, triggerSpec, false)
+ urSpec.Context = buildURContext(request, policyContext)
+ if err := h.urGenerator.Apply(ctx, urSpec); err != nil {
+ h.log.Error(err, "failed to create the UR to create downstream on trigger's operation", "operation", request.Operation, "policy", pKey)
+ e := event.NewFailedEvent(err, pKey, "", event.GeneratePolicyController,
+ kyvernov1.ResourceSpec{Kind: policy.GetKind(), Namespace: policy.GetNamespace(), Name: policy.GetName()})
+ h.eventGen.Add(e)
}
}
@@ -199,7 +196,7 @@ func (h *generationHandler) syncTriggerAction(
pKey := common.PolicyKey(policy.GetNamespace(), policy.GetName())
trigger := policyContext.OldResource()
- urSpec := kyvernov1.ResourceSpec{
+ triggerSpec := kyvernov1.ResourceSpec{
APIVersion: trigger.GetAPIVersion(),
Kind: trigger.GetKind(),
Namespace: trigger.GetNamespace(),
@@ -208,38 +205,39 @@ func (h *generationHandler) syncTriggerAction(
}
rules := getAppliedRules(policy, failedRules)
+ urSpec := kyvernov2.UpdateRequestSpec{
+ Type: kyvernov2.Generate,
+ Policy: pKey,
+ RuleContext: make([]kyvernov2.RuleContext, 0),
+ Context: buildURContext(request, policyContext),
+ }
for _, rule := range rules {
// fire generation on trigger deletion
if (request.Operation == admissionv1.Delete) && webhookutils.MatchDeleteOperation(rule) {
h.log.V(4).Info("creating the UR to generate downstream on trigger's deletion", "operation", request.Operation, "rule", rule.Name)
- ur := buildURSpec(kyvernov2.Generate, pKey, rule.Name, urSpec, false)
- ur.Context = buildURContext(request, policyContext)
- if err := h.urGenerator.Apply(ctx, ur); err != nil {
- h.log.Error(err, "failed to create the UR to generate downstream on trigger's deletion", "operation", request.Operation, "rule", rule.Name)
- e := event.NewFailedEvent(err, pKey, rule.Name, event.GeneratePolicyController,
- kyvernov1.ResourceSpec{Kind: policy.GetKind(), Namespace: policy.GetNamespace(), Name: policy.GetName()})
- h.eventGen.Add(e)
- }
+ ruleCtx := buildRuleContext(rule, triggerSpec, false)
+ urSpec.RuleContext = append(urSpec.RuleContext, ruleCtx)
continue
}
// delete downstream on trigger deletion
if rule.Generation.Synchronize {
h.log.V(4).Info("creating the UR to delete downstream on trigger's event", "operation", request.Operation, "rule", rule.Name)
- ur := buildURSpec(kyvernov2.Generate, pKey, rule.Name, urSpec, true)
- ur.Context = buildURContext(request, policyContext)
- if err := h.urGenerator.Apply(ctx, ur); err != nil {
- h.log.Error(err, "failed to create the UR to delete downstream on trigger's event", "operation", request.Operation, "rule", rule.Name)
- e := event.NewFailedEvent(err, pKey, rule.Name, event.GeneratePolicyController,
- kyvernov1.ResourceSpec{Kind: policy.GetKind(), Namespace: policy.GetNamespace(), Name: policy.GetName()})
- h.eventGen.Add(e)
- }
+ ruleCtx := buildRuleContext(rule, triggerSpec, true)
+ urSpec.RuleContext = append(urSpec.RuleContext, ruleCtx)
}
}
+
+ if err := h.urGenerator.Apply(ctx, urSpec); err != nil {
+ h.log.Error(err, "failed to create the UR on trigger's event", "operation", request.Operation, "policy", pKey)
+ e := event.NewFailedEvent(err, pKey, "", event.GeneratePolicyController,
+ kyvernov1.ResourceSpec{Kind: policy.GetKind(), Namespace: policy.GetNamespace(), Name: policy.GetName()})
+ h.eventGen.Add(e)
+ }
}
// processRequest determine if it needs to re-apply the generate rule to the source or the target changes
-func (h *generationHandler) processRequest(ctx context.Context, policyContext *engine.PolicyContext, request admissionv1.AdmissionRequest) (err error) {
+func (h *generationHandler) processRequest(ctx context.Context, policyContext *engine.PolicyContext) (err error) {
var policy kyvernov1.PolicyInterface
var labelsList []map[string]string
var deleteDownstream bool
@@ -310,6 +308,12 @@ func (h *generationHandler) processRequest(ctx context.Context, policyContext *e
}
pKey := common.PolicyKey(pNamespace, pName)
+ urSpec := kyvernov2.UpdateRequestSpec{
+ Type: kyvernov2.Generate,
+ Policy: pKey,
+ RuleContext: make([]kyvernov2.RuleContext, 0),
+ }
+
for _, rule := range policy.GetSpec().Rules {
if rule.Name == pRuleName && rule.Generation.Synchronize {
gvk, subresource := policyContext.ResourceKind()
@@ -327,15 +331,16 @@ func (h *generationHandler) processRequest(ctx context.Context, policyContext *e
continue
}
- ur := buildURSpec(kyvernov2.Generate, pKey, rule.Name, generateutils.TriggerFromLabels(labels), deleteDownstream)
- if err := h.urGenerator.Apply(ctx, ur); err != nil {
- e := event.NewBackgroundFailedEvent(err, policy, pRuleName, event.GeneratePolicyController,
- kyvernov1.ResourceSpec{Kind: new.GetKind(), Namespace: new.GetNamespace(), Name: new.GetName()})
- h.eventGen.Add(e...)
- return err
- }
+ ruleCtx := buildRuleContext(rule, generateutils.TriggerFromLabels(labels), deleteDownstream)
+ urSpec.RuleContext = append(urSpec.RuleContext, ruleCtx)
}
}
+ if err := h.urGenerator.Apply(ctx, urSpec); err != nil {
+ e := event.NewBackgroundFailedEvent(err, policy, "", event.GeneratePolicyController,
+ kyvernov1.ResourceSpec{Kind: new.GetKind(), Namespace: new.GetNamespace(), Name: new.GetName()})
+ h.eventGen.Add(e...)
+ return err
+ }
}
return nil
}
diff --git a/pkg/webhooks/resource/generation/utils.go b/pkg/webhooks/resource/generation/utils.go
index 83e9319140..d217f854c8 100644
--- a/pkg/webhooks/resource/generation/utils.go
+++ b/pkg/webhooks/resource/generation/utils.go
@@ -7,12 +7,23 @@ import (
admissionv1 "k8s.io/api/admission/v1"
)
-func buildURSpec(requestType kyvernov2.RequestType, policyKey, ruleName string, resource kyvernov1.ResourceSpec, deleteDownstream bool) kyvernov2.UpdateRequestSpec {
+func buildURSpecNew(requestType kyvernov2.RequestType, policyKey string, rules []kyvernov1.Rule, trigger kyvernov1.ResourceSpec, deleteDownstream bool) kyvernov2.UpdateRequestSpec {
+ ruleCtx := make([]kyvernov2.RuleContext, 0)
+ for _, rule := range rules {
+ ctx := buildRuleContext(rule, trigger, deleteDownstream)
+ ruleCtx = append(ruleCtx, ctx)
+ }
return kyvernov2.UpdateRequestSpec{
- Type: requestType,
- Policy: policyKey,
- Rule: ruleName,
- Resource: resource,
+ Type: requestType,
+ Policy: policyKey,
+ RuleContext: ruleCtx,
+ }
+}
+
+func buildRuleContext(rule kyvernov1.Rule, trigger kyvernov1.ResourceSpec, deleteDownstream bool) kyvernov2.RuleContext {
+ return kyvernov2.RuleContext{
+ Rule: rule.Name,
+ Trigger: trigger,
DeleteDownstream: deleteDownstream,
}
}
diff --git a/pkg/webhooks/updaterequest/generator.go b/pkg/webhooks/updaterequest/generator.go
index 1cd8fd9828..c6cec2de05 100644
--- a/pkg/webhooks/updaterequest/generator.go
+++ b/pkg/webhooks/updaterequest/generator.go
@@ -70,7 +70,7 @@ func (g *generator) tryApplyResource(ctx context.Context, urSpec kyvernov2.Updat
if urSpec.GetRequestType() == kyvernov2.Mutate {
queryLabels = common.MutateLabelsSet(urSpec.Policy, urSpec.GetResource())
} else if urSpec.GetRequestType() == kyvernov2.Generate {
- queryLabels = common.GenerateLabelsSet(urSpec.Policy, urSpec.GetResource())
+ queryLabels = common.GenerateLabelsSet(urSpec.Policy)
}
l.V(4).Info("creating new UpdateRequest")
diff --git a/scripts/config/standard/kyverno.yaml b/scripts/config/standard/kyverno.yaml
index 8cb4dcc7af..b85c35e10e 100644
--- a/scripts/config/standard/kyverno.yaml
+++ b/scripts/config/standard/kyverno.yaml
@@ -5,6 +5,8 @@ features:
eventTypes: []
backgroundController:
+ extraArgs:
+ v: 4
rbac:
clusterRole:
extraResources:
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/cornercases/clone-list-sync-same-trigger-source-delete-source/chainsaw-test.yaml b/test/conformance/chainsaw/generate/clusterpolicy/cornercases/clone-list-sync-same-trigger-source-delete-source/chainsaw-test.yaml
index 7869202284..df15d93cba 100755
--- a/test/conformance/chainsaw/generate/clusterpolicy/cornercases/clone-list-sync-same-trigger-source-delete-source/chainsaw-test.yaml
+++ b/test/conformance/chainsaw/generate/clusterpolicy/cornercases/clone-list-sync-same-trigger-source-delete-source/chainsaw-test.yaml
@@ -19,9 +19,15 @@ spec:
try:
- apply:
file: trigger.yaml
+ - name: step-03
+ try:
+ - sleep:
+ duration: 2s
+ - name: step-04
+ try:
- assert:
file: target.yaml
- - name: step-03
+ - name: step-05
try:
- delete:
ref:
@@ -29,11 +35,11 @@ spec:
kind: Secret
name: mysecret
namespace: clone-list-sync-same-trigger-source-trigger-ns
- - name: step-04
+ - name: step-06
try:
- sleep:
duration: 3s
- - name: step-05
+ - name: step-07
try:
- error:
file: target.yaml
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/README.md b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/README.md
new file mode 100644
index 0000000000..f183346bb5
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/README.md
@@ -0,0 +1,17 @@
+## Description
+
+This test ensures that a generate policy works as expected in case the rules have a different value for the `generateExisting` field.
+
+## Expected Behavior
+
+1. Create two Namespaces named `red-ns` and `green-ns`.
+
+2. Create a policy with two generate rules:
+ - The first rule named `generate-network-policy` matches Namespaces sets the `generateExisting` to `true`.
+ - The second rule named `generate-config-map` matches Namespaces sets the `generateExisting` to `false`.
+
+3. It is expected that a NetworkPolicy will be generated for each Namespace whereas ConfigMaps will not be generated.
+
+## Reference Issue(s)
+
+N/A
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/chainsaw-test.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/chainsaw-test.yaml
new file mode 100755
index 0000000000..231349992e
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/chainsaw-test.yaml
@@ -0,0 +1,27 @@
+apiVersion: chainsaw.kyverno.io/v1alpha1
+kind: Test
+metadata:
+ creationTimestamp: null
+ name: different-generate-existing-values
+spec:
+ steps:
+ - name: step-01
+ try:
+ - apply:
+ file: existing-resources.yaml
+ - name: step-02
+ try:
+ - apply:
+ file: policy.yaml
+ - assert:
+ file: policy-ready.yaml
+ - name: step-03
+ try:
+ - sleep:
+ duration: 3s
+ - name: step-04
+ try:
+ - assert:
+ file: generated-resources.yaml
+ - error:
+ file: fail-generated-resources.yaml
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/existing-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/existing-resources.yaml
new file mode 100644
index 0000000000..ab3740d2bd
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/existing-resources.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: red-ns
+ labels:
+ color: red
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: green-ns
+ labels:
+ color: green
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/fail-generated-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/fail-generated-resources.yaml
new file mode 100644
index 0000000000..fc1fbc5222
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/fail-generated-resources.yaml
@@ -0,0 +1,34 @@
+apiVersion: v1
+data:
+ KAFKA_ADDRESS: 192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092
+ ZK_ADDRESS: 192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181
+kind: ConfigMap
+metadata:
+ labels:
+ somekey: somevalue
+ name: zk-kafka-address
+ namespace: red-ns
+---
+apiVersion: v1
+data:
+ KAFKA_ADDRESS: 192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092
+ ZK_ADDRESS: 192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181
+kind: ConfigMap
+metadata:
+ labels:
+ somekey: somevalue
+ name: zk-kafka-address
+ namespace: green-ns
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ created-by: kyverno
+ name: default-deny
+ namespace: red-ns
+spec:
+ podSelector: {}
+ policyTypes:
+ - Ingress
+ - Egress
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/generated-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/generated-resources.yaml
new file mode 100644
index 0000000000..31a31fa1b6
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/generated-resources.yaml
@@ -0,0 +1,12 @@
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ created-by: kyverno
+ name: default-deny
+ namespace: green-ns
+spec:
+ podSelector: {}
+ policyTypes:
+ - Ingress
+ - Egress
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/policy-ready.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/policy-ready.yaml
new file mode 100644
index 0000000000..73a7bc1dfb
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/policy-ready.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+ name: different-generate-existing-values-reorder
+status:
+ conditions:
+ - reason: Succeeded
+ status: "True"
+ type: Ready
\ No newline at end of file
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/policy.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/policy.yaml
new file mode 100644
index 0000000000..ac3ca4cad3
--- /dev/null
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values-reorder/policy.yaml
@@ -0,0 +1,53 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+ name: different-generate-existing-values-reorder
+spec:
+ rules:
+ - name: generate-config-map
+ match:
+ any:
+ - resources:
+ kinds:
+ - Namespace
+ names:
+ - red-ns
+ generate:
+ generateExisting: false
+ synchronize: true
+ apiVersion: v1
+ kind: ConfigMap
+ name: zk-kafka-address
+ namespace: "{{request.object.metadata.name}}"
+ data:
+ kind: ConfigMap
+ metadata:
+ labels:
+ somekey: somevalue
+ data:
+ ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
+ KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"
+ - name: generate-network-policy
+ match:
+ any:
+ - resources:
+ kinds:
+ - Namespace
+ names:
+ - green-ns
+ generate:
+ generateExisting: true
+ kind: NetworkPolicy
+ apiVersion: networking.k8s.io/v1
+ name: default-deny
+ namespace: "{{request.object.metadata.name}}"
+ synchronize: true
+ data:
+ metadata:
+ labels:
+ created-by: kyverno
+ spec:
+ podSelector: {}
+ policyTypes:
+ - Ingress
+ - Egress
\ No newline at end of file
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/fail-generated-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/fail-generated-resources.yaml
index 96b86fb5dc..fc1fbc5222 100644
--- a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/fail-generated-resources.yaml
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/fail-generated-resources.yaml
@@ -19,3 +19,16 @@ metadata:
somekey: somevalue
name: zk-kafka-address
namespace: green-ns
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ created-by: kyverno
+ name: default-deny
+ namespace: red-ns
+spec:
+ podSelector: {}
+ policyTypes:
+ - Ingress
+ - Egress
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/generated-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/generated-resources.yaml
index f61700ca9f..31a31fa1b6 100644
--- a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/generated-resources.yaml
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/generated-resources.yaml
@@ -1,18 +1,5 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
-metadata:
- labels:
- created-by: kyverno
- name: default-deny
- namespace: red-ns
-spec:
- podSelector: {}
- policyTypes:
- - Ingress
- - Egress
----
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
metadata:
labels:
created-by: kyverno
diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/policy.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/policy.yaml
index 302c9f5712..08dd7e15fe 100644
--- a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/policy.yaml
+++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/different-generate-existing-values/policy.yaml
@@ -10,6 +10,8 @@ spec:
- resources:
kinds:
- Namespace
+ names:
+ - green-ns
generate:
generateExisting: true
kind: NetworkPolicy
@@ -32,6 +34,8 @@ spec:
- resources:
kinds:
- Namespace
+ names:
+ - red-ns
generate:
generateExisting: false
synchronize: true