1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

refactor: update updaterequest to be created for each policy (#10793)

* chore: remove v1beta1 updaterequest definitions

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: update UR to map a policy instead a rule; adapt UR mapping changes for admission review

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: update code-gen

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: linter

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: remove unused function

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: add missing files

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: add missing files

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update ur in policy controller

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: update crds

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: adapt ur changes in the background controller

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: linter

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: more linter

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: modify mapping relationship for deletion events

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: remedy missing target for policy application

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: fetching logic for triggers

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: clean up targets upon policy deletion

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update crds

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* merge main

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* merge main

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: adds delay before assertion

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update docs

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: wrong yaml format

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: update error handling logic

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix(attempt): enable more debug info

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix(attempt): enable debug log

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix(attempt): enable debug log

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix(attempt): enable debug log

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: makefile to update ur crds

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: generate existing

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: skip empty ur generation

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: update install.yaml

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2024-08-14 01:14:06 +08:00 committed by GitHub
parent de37a045be
commit 481798c836
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1002 additions and 1577 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3559,6 +3559,7 @@ ResourceDescription
<a href="#kyverno.io/v1.TargetResourceSpec">TargetResourceSpec</a>,
<a href="#kyverno.io/v1beta1.UpdateRequestSpec">UpdateRequestSpec</a>,
<a href="#kyverno.io/v1beta1.UpdateRequestStatus">UpdateRequestStatus</a>,
<a href="#kyverno.io/v2.RuleContext">RuleContext</a>,
<a href="#kyverno.io/v2.UpdateRequestSpec">UpdateRequestSpec</a>,
<a href="#kyverno.io/v2.UpdateRequestStatus">UpdateRequestStatus</a>)
</p>
@ -5877,6 +5878,20 @@ string
</tr>
<tr>
<td>
<code>ruleContext</code><br/>
<em>
<a href="#kyverno.io/v2.RuleContext">
[]RuleContext
</a>
</em>
</td>
<td>
<p>RuleContext is the associate context to apply rules.
optional</p>
</td>
</tr>
<tr>
<td>
<code>rule</code><br/>
<em>
string
@ -5894,7 +5909,8 @@ bool
</em>
</td>
<td>
<p>DeleteDownstream represents whether the downstream needs to be deleted.</p>
<p>DeleteDownstream represents whether the downstream needs to be deleted.
Deprecated</p>
</td>
</tr>
<tr>
@ -5906,7 +5922,8 @@ bool
</td>
<td>
<p>Synchronize represents the sync behavior of the corresponding rule
Optional. Defaults to &ldquo;false&rdquo; if not specified.</p>
Optional. Defaults to &ldquo;false&rdquo; if not specified.
Deprecated, will be removed in 1.14.</p>
</td>
</tr>
<tr>
@ -5932,7 +5949,8 @@ UpdateRequestSpecContext
</em>
</td>
<td>
<p>Context &hellip;</p>
<p>Context represents admission request context.
It is used upon admission review only and is shared across rules within the same UR.</p>
</td>
</tr>
</table>
@ -6475,6 +6493,72 @@ Kubernetes authentication/v1.UserInfo
</p>
<p>
</p>
<h3 id="kyverno.io/v2.RuleContext">RuleContext
</h3>
<p>
(<em>Appears on:</em>
<a href="#kyverno.io/v2.UpdateRequestSpec">UpdateRequestSpec</a>)
</p>
<p>
</p>
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>rule</code><br/>
<em>
string
</em>
</td>
<td>
<p>Rule is the associate rule name of the current UR.</p>
</td>
</tr>
<tr>
<td>
<code>deleteDownstream</code><br/>
<em>
bool
</em>
</td>
<td>
<p>DeleteDownstream represents whether the downstream needs to be deleted.</p>
</td>
</tr>
<tr>
<td>
<code>synchronize</code><br/>
<em>
bool
</em>
</td>
<td>
<p>Synchronize represents the sync behavior of the corresponding rule
Optional. Defaults to &ldquo;false&rdquo; if not specified.</p>
</td>
</tr>
<tr>
<td>
<code>trigger</code><br/>
<em>
<a href="#kyverno.io/v1.ResourceSpec">
ResourceSpec
</a>
</em>
</td>
<td>
<p>ResourceSpec is the information to identify the trigger resource.</p>
</td>
</tr>
</tbody>
</table>
<hr />
<h3 id="kyverno.io/v2.UpdateRequestSpec">UpdateRequestSpec
</h3>
<p>
@ -6518,6 +6602,20 @@ string
</tr>
<tr>
<td>
<code>ruleContext</code><br/>
<em>
<a href="#kyverno.io/v2.RuleContext">
[]RuleContext
</a>
</em>
</td>
<td>
<p>RuleContext is the associate context to apply rules.
optional</p>
</td>
</tr>
<tr>
<td>
<code>rule</code><br/>
<em>
string
@ -6535,7 +6633,8 @@ bool
</em>
</td>
<td>
<p>DeleteDownstream represents whether the downstream needs to be deleted.</p>
<p>DeleteDownstream represents whether the downstream needs to be deleted.
Deprecated</p>
</td>
</tr>
<tr>
@ -6547,7 +6646,8 @@ bool
</td>
<td>
<p>Synchronize represents the sync behavior of the corresponding rule
Optional. Defaults to &ldquo;false&rdquo; if not specified.</p>
Optional. Defaults to &ldquo;false&rdquo; if not specified.
Deprecated, will be removed in 1.14.</p>
</td>
</tr>
<tr>
@ -6573,7 +6673,8 @@ UpdateRequestSpecContext
</em>
</td>
<td>
<p>Context &hellip;</p>
<p>Context represents admission request context.
It is used upon admission review only and is shared across rules within the same UR.</p>
</td>
</tr>
</tbody>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,6 +5,8 @@ features:
eventTypes: []
backgroundController:
extraArgs:
v: 4
rbac:
clusterRole:
extraResources:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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