mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-13 19:28:55 +00:00
feat: foreach
support for clone
(#10888)
* chore: add chainsaw tests for foreach clone Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: update webhooks for foreach generate Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: rename generatePattern Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: chainsaw tests for generateExisting Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: add missing files Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: add chainsaw tests for foreach clone, sync=true Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: linter issues Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: add chainsaw test foreach clonelist, sync=true, delete source Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: sync deletion for cloneList Signed-off-by: ShutingZhao <shuting@nirmata.com> --------- Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
parent
d733ea3bb0
commit
2cd462570a
48 changed files with 736 additions and 110 deletions
|
@ -763,14 +763,14 @@ type Generation struct {
|
|||
OrphanDownstreamOnPolicyDelete bool `json:"orphanDownstreamOnPolicyDelete,omitempty" yaml:"orphanDownstreamOnPolicyDelete,omitempty"`
|
||||
|
||||
// +optional
|
||||
GeneratePatterns `json:",omitempty" yaml:",omitempty"`
|
||||
GeneratePattern `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// ForEach applies generate rules to a list of sub-elements by creating a context for each entry in the list and looping over it to apply the specified logic.
|
||||
// +optional
|
||||
ForEachGeneration []ForEachGeneration `json:"foreach,omitempty" yaml:"foreach,omitempty"`
|
||||
}
|
||||
|
||||
type GeneratePatterns struct {
|
||||
type GeneratePattern struct {
|
||||
// ResourceSpec contains information to select the resource.
|
||||
// +kubebuilder:validation:Optional
|
||||
ResourceSpec `json:",omitempty" yaml:",omitempty"`
|
||||
|
@ -808,7 +808,7 @@ type ForEachGeneration struct {
|
|||
// +optional
|
||||
AnyAllConditions *AnyAllConditions `json:"preconditions,omitempty" yaml:"preconditions,omitempty"`
|
||||
|
||||
GeneratePatterns `json:",omitempty" yaml:",omitempty"`
|
||||
GeneratePattern `json:",omitempty" yaml:",omitempty"`
|
||||
}
|
||||
|
||||
type CloneList struct {
|
||||
|
@ -845,16 +845,16 @@ func (g *Generation) Validate(path *field.Path, namespaced bool, policyNamespace
|
|||
|
||||
if g.ForEachGeneration != nil {
|
||||
for i, foreach := range g.ForEachGeneration {
|
||||
err := foreach.GeneratePatterns.Validate(path.Child("foreach").Index(i), namespaced, policyNamespace, clusterResources)
|
||||
err := foreach.GeneratePattern.Validate(path.Child("foreach").Index(i), namespaced, policyNamespace, clusterResources)
|
||||
errs = append(errs, err...)
|
||||
}
|
||||
return errs
|
||||
} else {
|
||||
return g.GeneratePatterns.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||
return g.GeneratePattern.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (g *GeneratePattern) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if namespaced {
|
||||
if err := g.validateNamespacedTargetsScope(clusterResources, policyNamespace); err != nil {
|
||||
errs = append(errs, field.Forbidden(path.Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
|
||||
|
@ -873,7 +873,7 @@ func (g *GeneratePatterns) Validate(path *field.Path, namespaced bool, policyNam
|
|||
}
|
||||
}
|
||||
|
||||
newGeneration := GeneratePatterns{
|
||||
newGeneration := GeneratePattern{
|
||||
ResourceSpec: ResourceSpec{
|
||||
Kind: g.ResourceSpec.GetKind(),
|
||||
APIVersion: g.ResourceSpec.GetAPIVersion(),
|
||||
|
@ -901,7 +901,7 @@ func (g *GeneratePatterns) Validate(path *field.Path, namespaced bool, policyNam
|
|||
return append(errs, g.ValidateCloneList(path, namespaced, policyNamespace, clusterResources)...)
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) ValidateCloneList(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (g *GeneratePattern) ValidateCloneList(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if len(g.CloneList.Kinds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -938,7 +938,7 @@ func (g *GeneratePatterns) ValidateCloneList(path *field.Path, namespaced bool,
|
|||
return errs
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) GetType() GenerateType {
|
||||
func (g *GeneratePattern) GetType() GenerateType {
|
||||
if g.RawData != nil {
|
||||
return Data
|
||||
}
|
||||
|
@ -946,15 +946,15 @@ func (g *GeneratePatterns) GetType() GenerateType {
|
|||
return Clone
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) GetData() apiextensions.JSON {
|
||||
func (g *GeneratePattern) GetData() apiextensions.JSON {
|
||||
return FromJSON(g.RawData)
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) SetData(in apiextensions.JSON) {
|
||||
func (g *GeneratePattern) SetData(in apiextensions.JSON) {
|
||||
g.RawData = ToJSON(in)
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) validateNamespacedTargetsScope(clusterResources sets.Set[string], policyNamespace string) error {
|
||||
func (g *GeneratePattern) validateNamespacedTargetsScope(clusterResources sets.Set[string], policyNamespace string) error {
|
||||
target := g.ResourceSpec
|
||||
if clusterResources.Has(target.GetAPIVersion() + "/" + target.GetKind()) {
|
||||
return fmt.Errorf("the target must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
|
||||
|
|
|
@ -552,7 +552,7 @@ func (in *ForEachGeneration) DeepCopyInto(out *ForEachGeneration) {
|
|||
*out = new(AnyAllConditions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.GeneratePatterns.DeepCopyInto(&out.GeneratePatterns)
|
||||
in.GeneratePattern.DeepCopyInto(&out.GeneratePattern)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -660,7 +660,7 @@ func (in *ForEachValidation) DeepCopy() *ForEachValidation {
|
|||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GeneratePatterns) DeepCopyInto(out *GeneratePatterns) {
|
||||
func (in *GeneratePattern) DeepCopyInto(out *GeneratePattern) {
|
||||
*out = *in
|
||||
out.ResourceSpec = in.ResourceSpec
|
||||
if in.RawData != nil {
|
||||
|
@ -673,12 +673,12 @@ func (in *GeneratePatterns) DeepCopyInto(out *GeneratePatterns) {
|
|||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GeneratePatterns.
|
||||
func (in *GeneratePatterns) DeepCopy() *GeneratePatterns {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GeneratePattern.
|
||||
func (in *GeneratePattern) DeepCopy() *GeneratePattern {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GeneratePatterns)
|
||||
out := new(GeneratePattern)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
@ -691,7 +691,7 @@ func (in *Generation) DeepCopyInto(out *Generation) {
|
|||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
in.GeneratePatterns.DeepCopyInto(&out.GeneratePatterns)
|
||||
in.GeneratePattern.DeepCopyInto(&out.GeneratePattern)
|
||||
if in.ForEachGeneration != nil {
|
||||
in, out := &in.ForEachGeneration, &out.ForEachGeneration
|
||||
*out = make([]ForEachGeneration, len(*in))
|
||||
|
|
|
@ -1201,7 +1201,7 @@ Timestamps (SCTs). If the value is unset, the default behavior by Cosign is used
|
|||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">GeneratePatterns</a>)
|
||||
<a href="#kyverno.io/v1.GeneratePattern">GeneratePattern</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>CloneFrom provides the location of the source resource used to generate target resources.
|
||||
|
@ -1707,10 +1707,10 @@ See: <a href="https://kyverno.io/docs/writing-policies/preconditions/">https://k
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>GeneratePatterns</code><br/>
|
||||
<code>GeneratePattern</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">
|
||||
GeneratePatterns
|
||||
<a href="#kyverno.io/v1.GeneratePattern">
|
||||
GeneratePattern
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
|
@ -2051,7 +2051,7 @@ ForEachValidationWrapper
|
|||
<p>
|
||||
<p>ForeachOrder specifies the iteration order in foreach statements.</p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.GeneratePatterns">GeneratePatterns
|
||||
<h3 id="kyverno.io/v1.GeneratePattern">GeneratePattern
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
|
@ -2197,10 +2197,10 @@ Defaults to “false” if not specified.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>GeneratePatterns</code><br/>
|
||||
<code>GeneratePattern</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">
|
||||
GeneratePatterns
|
||||
<a href="#kyverno.io/v1.GeneratePattern">
|
||||
GeneratePattern
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
|
@ -3699,7 +3699,7 @@ ResourceDescription
|
|||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">GeneratePatterns</a>,
|
||||
<a href="#kyverno.io/v1.GeneratePattern">GeneratePattern</a>,
|
||||
<a href="#kyverno.io/v1.TargetResourceSpec">TargetResourceSpec</a>,
|
||||
<a href="#kyverno.io/v1beta1.UpdateRequestSpec">UpdateRequestSpec</a>,
|
||||
<a href="#kyverno.io/v1beta1.UpdateRequestStatus">UpdateRequestStatus</a>,
|
||||
|
|
|
@ -2455,7 +2455,7 @@ Timestamps (SCTs). If the value is unset, the default behavior by Cosign is used
|
|||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">GeneratePatterns</a>)
|
||||
<a href="#kyverno-io-v1-GeneratePattern">GeneratePattern</a>)
|
||||
</p>
|
||||
|
||||
|
||||
|
@ -3503,7 +3503,7 @@ See: https://kyverno.io/docs/writing-policies/preconditions/</p>
|
|||
|
||||
|
||||
<tr>
|
||||
<td><code>GeneratePatterns</code>
|
||||
<td><code>GeneratePattern</code>
|
||||
|
||||
<span style="color:blue;"> *</span>
|
||||
|
||||
|
@ -3512,8 +3512,8 @@ See: https://kyverno.io/docs/writing-policies/preconditions/</p>
|
|||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">
|
||||
<span style="font-family: monospace">GeneratePatterns</span>
|
||||
<a href="#kyverno-io-v1-GeneratePattern">
|
||||
<span style="font-family: monospace">GeneratePattern</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
@ -4193,7 +4193,7 @@ must be satisfied for the validation rule to succeed.</p>
|
|||
|
||||
|
||||
|
||||
<H3 id="kyverno-io-v1-GeneratePatterns">GeneratePatterns
|
||||
<H3 id="kyverno-io-v1-GeneratePattern">GeneratePattern
|
||||
</H3>
|
||||
|
||||
|
||||
|
@ -4470,15 +4470,15 @@ Defaults to "false" if not specified.</p>
|
|||
|
||||
|
||||
<tr>
|
||||
<td><code>GeneratePatterns</code>
|
||||
<td><code>GeneratePattern</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">
|
||||
<span style="font-family: monospace">GeneratePatterns</span>
|
||||
<a href="#kyverno-io-v1-GeneratePattern">
|
||||
<span style="font-family: monospace">GeneratePattern</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
@ -7330,7 +7330,7 @@ does not match an empty label set.</p>
|
|||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">GeneratePatterns</a>,
|
||||
<a href="#kyverno-io-v1-GeneratePattern">GeneratePattern</a>,
|
||||
<a href="#kyverno-io-v1-TargetResourceSpec">TargetResourceSpec</a>)
|
||||
</p>
|
||||
|
||||
|
|
|
@ -56,17 +56,22 @@ func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInter
|
|||
labels := map[string]string{
|
||||
common.GeneratePolicyLabel: policy.GetName(),
|
||||
common.GeneratePolicyNamespaceLabel: policy.GetNamespace(),
|
||||
common.GenerateRuleLabel: rule.Name,
|
||||
kyverno.LabelAppManagedBy: kyverno.ValueKyvernoApp,
|
||||
// common.GenerateRuleLabel: rule.Name,
|
||||
kyverno.LabelAppManagedBy: kyverno.ValueKyvernoApp,
|
||||
}
|
||||
|
||||
downstreams, err := c.getDownstreams(rule, labels, &ruleContext)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch downstream resources: %v", err)
|
||||
}
|
||||
|
||||
if len(downstreams) == 0 {
|
||||
logger.V(4).Info("no downstream resources found by label selectors", "labels", labels)
|
||||
return nil
|
||||
}
|
||||
var errs []error
|
||||
failedDownstreams := []kyvernov1.ResourceSpec{}
|
||||
for _, downstream := range downstreams.Items {
|
||||
for _, downstream := range downstreams {
|
||||
spec := common.ResourceSpecFromUnstructured(downstream)
|
||||
if err := c.client.DeleteResource(context.TODO(), downstream.GetAPIVersion(), downstream.GetKind(), downstream.GetNamespace(), downstream.GetName(), false); err != nil && !apierrors.IsNotFound(err) {
|
||||
failedDownstreams = append(failedDownstreams, spec)
|
||||
|
@ -90,7 +95,7 @@ func (c *GenerateController) handleNonPolicyChanges(policy kyvernov1.PolicyInter
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[string]string, ruleContext *kyvernov2.RuleContext) (*unstructured.UnstructuredList, error) {
|
||||
func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[string]string, ruleContext *kyvernov2.RuleContext) ([]unstructured.Unstructured, error) {
|
||||
gv, err := ruleContext.Trigger.GetGroupVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -101,34 +106,43 @@ func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[st
|
|||
selector[common.GenerateTriggerKindLabel] = ruleContext.Trigger.GetKind()
|
||||
selector[common.GenerateTriggerGroupLabel] = gv.Group
|
||||
selector[common.GenerateTriggerVersionLabel] = gv.Version
|
||||
if rule.Generation.GetKind() != "" {
|
||||
|
||||
for _, g := range rule.Generation.ForEachGeneration {
|
||||
return c.fetch(g.GeneratePattern, selector, ruleContext)
|
||||
}
|
||||
|
||||
return c.fetch(rule.Generation.GeneratePattern, selector, ruleContext)
|
||||
}
|
||||
|
||||
func (c *GenerateController) fetch(generatePattern kyvernov1.GeneratePattern, selector map[string]string, ruleContext *kyvernov2.RuleContext) ([]unstructured.Unstructured, error) {
|
||||
downstreamResources := []unstructured.Unstructured{}
|
||||
if generatePattern.GetKind() != "" {
|
||||
// Fetch downstream resources using trigger uid label
|
||||
c.log.V(4).Info("fetching downstream resource by the UID", "APIVersion", rule.Generation.GetAPIVersion(), "kind", rule.Generation.GetKind(), "selector", selector)
|
||||
downstreamList, err := common.FindDownstream(c.client, rule.Generation.GetAPIVersion(), rule.Generation.GetKind(), selector)
|
||||
c.log.V(4).Info("fetching downstream resource by the UID", "APIVersion", generatePattern.GetAPIVersion(), "kind", generatePattern.GetKind(), "selector", selector)
|
||||
dsList, err := common.FindDownstream(c.client, generatePattern.GetAPIVersion(), generatePattern.GetKind(), selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(downstreamList.Items) == 0 {
|
||||
if len(dsList.Items) == 0 {
|
||||
// Fetch downstream resources using the trigger name label
|
||||
delete(selector, common.GenerateTriggerUIDLabel)
|
||||
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)
|
||||
c.log.V(4).Info("fetching downstream resource by the name", "APIVersion", generatePattern.GetAPIVersion(), "kind", generatePattern.GetKind(), "selector", selector)
|
||||
dsList, err = common.FindDownstream(c.client, generatePattern.GetAPIVersion(), generatePattern.GetKind(), selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
downstreamList.Items = append(downstreamList.Items, dsList.Items...)
|
||||
}
|
||||
downstreamResources = append(downstreamResources, dsList.Items...)
|
||||
|
||||
return downstreamList, err
|
||||
return downstreamResources, err
|
||||
}
|
||||
|
||||
dsList := &unstructured.UnstructuredList{}
|
||||
for _, kind := range rule.Generation.CloneList.Kinds {
|
||||
for _, kind := range generatePattern.CloneList.Kinds {
|
||||
apiVersion, kind := kubeutils.GetKindFromGVK(kind)
|
||||
c.log.V(4).Info("fetching downstream cloneList resources by the UID", "APIVersion", apiVersion, "kind", kind, "selector", selector)
|
||||
dsList, err = common.FindDownstream(c.client, apiVersion, kind, selector)
|
||||
dsList, err := common.FindDownstream(c.client, apiVersion, kind, selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -136,12 +150,14 @@ func (c *GenerateController) getDownstreams(rule kyvernov1.Rule, selector map[st
|
|||
if len(dsList.Items) == 0 {
|
||||
delete(selector, common.GenerateTriggerUIDLabel)
|
||||
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)
|
||||
c.log.V(4).Info("fetching downstream resource by the name", "APIVersion", generatePattern.GetAPIVersion(), "kind", generatePattern.GetKind(), "selector", selector)
|
||||
dsList, err = common.FindDownstream(c.client, generatePattern.GetAPIVersion(), generatePattern.GetKind(), selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
downstreamResources = append(downstreamResources, dsList.Items...)
|
||||
}
|
||||
return dsList, nil
|
||||
|
||||
return downstreamResources, nil
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, pattern kyvernov1.GeneratePatterns, client dclient.Interface) generateResponse {
|
||||
func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, pattern kyvernov1.GeneratePattern, client dclient.Interface) generateResponse {
|
||||
source := sourceSpec
|
||||
if pattern.Clone.Name != "" {
|
||||
source = kyvernov1.ResourceSpec{
|
||||
|
@ -79,7 +79,7 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, sev
|
|||
return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
|
||||
}
|
||||
|
||||
func manageCloneList(log logr.Logger, targetNamespace string, severSideApply bool, pattern kyvernov1.GeneratePatterns, client dclient.Interface) []generateResponse {
|
||||
func manageCloneList(log logr.Logger, targetNamespace string, severSideApply bool, pattern kyvernov1.GeneratePattern, client dclient.Interface) []generateResponse {
|
||||
var responses []generateResponse
|
||||
sourceNamespace := pattern.CloneList.Namespace
|
||||
kinds := pattern.CloneList.Kinds
|
||||
|
|
|
@ -325,7 +325,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
|
|||
g := newForeachGenerator(c.client, logger, policyContext, policy, rule, rule.Context, rule.GetAnyAllConditions(), policyContext.NewResource(), rule.Generation.ForEachGeneration, contextLoader)
|
||||
genResource, err = g.generateForeach()
|
||||
} else {
|
||||
g := newGenerator(c.client, logger, policyContext, policy, rule, rule.Context, rule.GetAnyAllConditions(), policyContext.NewResource(), rule.Generation.GeneratePatterns, contextLoader)
|
||||
g := newGenerator(c.client, logger, policyContext, policy, rule, rule.Context, rule.GetAnyAllConditions(), policyContext.NewResource(), rule.Generation.GeneratePattern, contextLoader)
|
||||
genResource, err = g.generate()
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ type generator struct {
|
|||
anyAllConditions any
|
||||
trigger unstructured.Unstructured
|
||||
forEach []kyvernov1.ForEachGeneration
|
||||
pattern kyvernov1.GeneratePatterns
|
||||
pattern kyvernov1.GeneratePattern
|
||||
contextLoader engineapi.EngineContextLoader
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ func newGenerator(client dclient.Interface,
|
|||
contextEntries []kyvernov1.ContextEntry,
|
||||
anyAllConditions any,
|
||||
trigger unstructured.Unstructured,
|
||||
pattern kyvernov1.GeneratePatterns,
|
||||
pattern kyvernov1.GeneratePattern,
|
||||
contextLoader engineapi.EngineContextLoader,
|
||||
) *generator {
|
||||
return &generator{
|
||||
|
@ -264,7 +264,7 @@ func (g *generator) generateElements(foreach kyvernov1.ForEachGeneration, elemen
|
|||
foreach.Context,
|
||||
foreach.AnyAllConditions,
|
||||
g.trigger,
|
||||
foreach.GeneratePatterns,
|
||||
foreach.GeneratePattern,
|
||||
g.contextLoader).
|
||||
generate()
|
||||
if err != nil {
|
||||
|
|
|
@ -26,10 +26,10 @@ import (
|
|||
// ForEachGenerationApplyConfiguration represents an declarative configuration of the ForEachGeneration type for use
|
||||
// with apply.
|
||||
type ForEachGenerationApplyConfiguration struct {
|
||||
List *string `json:"list,omitempty"`
|
||||
Context []ContextEntryApplyConfiguration `json:"context,omitempty"`
|
||||
AnyAllConditions *AnyAllConditionsApplyConfiguration `json:"preconditions,omitempty"`
|
||||
*GeneratePatternsApplyConfiguration `json:"GeneratePatterns,omitempty"`
|
||||
List *string `json:"list,omitempty"`
|
||||
Context []ContextEntryApplyConfiguration `json:"context,omitempty"`
|
||||
AnyAllConditions *AnyAllConditionsApplyConfiguration `json:"preconditions,omitempty"`
|
||||
*GeneratePatternApplyConfiguration `json:"GeneratePattern,omitempty"`
|
||||
}
|
||||
|
||||
// ForEachGenerationApplyConfiguration constructs an declarative configuration of the ForEachGeneration type for use with
|
||||
|
@ -122,7 +122,7 @@ func (b *ForEachGenerationApplyConfiguration) ensureResourceSpecApplyConfigurati
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the RawData field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.ensureGeneratePatternApplyConfigurationExists()
|
||||
b.RawData = &value
|
||||
return b
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func (b *ForEachGenerationApplyConfiguration) WithRawData(value apiextensionsv1.
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Clone field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.ensureGeneratePatternApplyConfigurationExists()
|
||||
b.Clone = value
|
||||
return b
|
||||
}
|
||||
|
@ -140,13 +140,13 @@ func (b *ForEachGenerationApplyConfiguration) WithClone(value *CloneFromApplyCon
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CloneList field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.ensureGeneratePatternApplyConfigurationExists()
|
||||
b.CloneList = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ForEachGenerationApplyConfiguration) ensureGeneratePatternsApplyConfigurationExists() {
|
||||
if b.GeneratePatternsApplyConfiguration == nil {
|
||||
b.GeneratePatternsApplyConfiguration = &GeneratePatternsApplyConfiguration{}
|
||||
func (b *ForEachGenerationApplyConfiguration) ensureGeneratePatternApplyConfigurationExists() {
|
||||
if b.GeneratePatternApplyConfiguration == nil {
|
||||
b.GeneratePatternApplyConfiguration = &GeneratePatternApplyConfiguration{}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,25 +23,25 @@ import (
|
|||
types "k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// GeneratePatternsApplyConfiguration represents an declarative configuration of the GeneratePatterns type for use
|
||||
// GeneratePatternApplyConfiguration represents an declarative configuration of the GeneratePattern type for use
|
||||
// with apply.
|
||||
type GeneratePatternsApplyConfiguration struct {
|
||||
type GeneratePatternApplyConfiguration struct {
|
||||
*ResourceSpecApplyConfiguration `json:"ResourceSpec,omitempty"`
|
||||
RawData *apiextensionsv1.JSON `json:"data,omitempty"`
|
||||
Clone *CloneFromApplyConfiguration `json:"clone,omitempty"`
|
||||
CloneList *CloneListApplyConfiguration `json:"cloneList,omitempty"`
|
||||
}
|
||||
|
||||
// GeneratePatternsApplyConfiguration constructs an declarative configuration of the GeneratePatterns type for use with
|
||||
// GeneratePatternApplyConfiguration constructs an declarative configuration of the GeneratePattern type for use with
|
||||
// apply.
|
||||
func GeneratePatterns() *GeneratePatternsApplyConfiguration {
|
||||
return &GeneratePatternsApplyConfiguration{}
|
||||
func GeneratePattern() *GeneratePatternApplyConfiguration {
|
||||
return &GeneratePatternApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithAPIVersion sets the APIVersion 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 APIVersion field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithAPIVersion(value string) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithAPIVersion(value string) *GeneratePatternApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.APIVersion = &value
|
||||
return b
|
||||
|
@ -50,7 +50,7 @@ func (b *GeneratePatternsApplyConfiguration) WithAPIVersion(value string) *Gener
|
|||
// WithKind sets the Kind 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 Kind field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithKind(value string) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithKind(value string) *GeneratePatternApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Kind = &value
|
||||
return b
|
||||
|
@ -59,7 +59,7 @@ func (b *GeneratePatternsApplyConfiguration) WithKind(value string) *GeneratePat
|
|||
// WithNamespace sets the Namespace 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 Namespace field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithNamespace(value string) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithNamespace(value string) *GeneratePatternApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Namespace = &value
|
||||
return b
|
||||
|
@ -68,7 +68,7 @@ func (b *GeneratePatternsApplyConfiguration) WithNamespace(value string) *Genera
|
|||
// WithName sets the Name 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 Name field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithName(value string) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithName(value string) *GeneratePatternApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Name = &value
|
||||
return b
|
||||
|
@ -77,13 +77,13 @@ func (b *GeneratePatternsApplyConfiguration) WithName(value string) *GeneratePat
|
|||
// WithUID sets the UID 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 UID field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithUID(value types.UID) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithUID(value types.UID) *GeneratePatternApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.UID = &value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *GeneratePatternsApplyConfiguration) ensureResourceSpecApplyConfigurationExists() {
|
||||
func (b *GeneratePatternApplyConfiguration) ensureResourceSpecApplyConfigurationExists() {
|
||||
if b.ResourceSpecApplyConfiguration == nil {
|
||||
b.ResourceSpecApplyConfiguration = &ResourceSpecApplyConfiguration{}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func (b *GeneratePatternsApplyConfiguration) ensureResourceSpecApplyConfiguratio
|
|||
// WithRawData sets the RawData 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 RawData field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *GeneratePatternApplyConfiguration {
|
||||
b.RawData = &value
|
||||
return b
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ func (b *GeneratePatternsApplyConfiguration) WithRawData(value apiextensionsv1.J
|
|||
// WithClone sets the Clone 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 Clone field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *GeneratePatternApplyConfiguration {
|
||||
b.Clone = value
|
||||
return b
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func (b *GeneratePatternsApplyConfiguration) WithClone(value *CloneFromApplyConf
|
|||
// WithCloneList sets the CloneList 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 CloneList field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *GeneratePatternsApplyConfiguration {
|
||||
func (b *GeneratePatternApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *GeneratePatternApplyConfiguration {
|
||||
b.CloneList = value
|
||||
return b
|
||||
}
|
|
@ -26,11 +26,11 @@ import (
|
|||
// GenerationApplyConfiguration represents an declarative configuration of the Generation type for use
|
||||
// with apply.
|
||||
type GenerationApplyConfiguration struct {
|
||||
GenerateExisting *bool `json:"generateExisting,omitempty"`
|
||||
Synchronize *bool `json:"synchronize,omitempty"`
|
||||
OrphanDownstreamOnPolicyDelete *bool `json:"orphanDownstreamOnPolicyDelete,omitempty"`
|
||||
*GeneratePatternsApplyConfiguration `json:"GeneratePatterns,omitempty"`
|
||||
ForEachGeneration []ForEachGenerationApplyConfiguration `json:"foreach,omitempty"`
|
||||
GenerateExisting *bool `json:"generateExisting,omitempty"`
|
||||
Synchronize *bool `json:"synchronize,omitempty"`
|
||||
OrphanDownstreamOnPolicyDelete *bool `json:"orphanDownstreamOnPolicyDelete,omitempty"`
|
||||
*GeneratePatternApplyConfiguration `json:"GeneratePattern,omitempty"`
|
||||
ForEachGeneration []ForEachGenerationApplyConfiguration `json:"foreach,omitempty"`
|
||||
}
|
||||
|
||||
// GenerationApplyConfiguration constructs an declarative configuration of the Generation type for use with
|
||||
|
@ -118,7 +118,7 @@ func (b *GenerationApplyConfiguration) ensureResourceSpecApplyConfigurationExist
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the RawData field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *GenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.ensureGeneratePatternApplyConfigurationExists()
|
||||
b.RawData = &value
|
||||
return b
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ func (b *GenerationApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Clone field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *GenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.ensureGeneratePatternApplyConfigurationExists()
|
||||
b.Clone = value
|
||||
return b
|
||||
}
|
||||
|
@ -136,14 +136,14 @@ func (b *GenerationApplyConfiguration) WithClone(value *CloneFromApplyConfigurat
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CloneList field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *GenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.ensureGeneratePatternApplyConfigurationExists()
|
||||
b.CloneList = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *GenerationApplyConfiguration) ensureGeneratePatternsApplyConfigurationExists() {
|
||||
if b.GeneratePatternsApplyConfiguration == nil {
|
||||
b.GeneratePatternsApplyConfiguration = &GeneratePatternsApplyConfiguration{}
|
||||
func (b *GenerationApplyConfiguration) ensureGeneratePatternApplyConfigurationExists() {
|
||||
if b.GeneratePatternApplyConfiguration == nil {
|
||||
b.GeneratePatternApplyConfiguration = &GeneratePatternApplyConfiguration{}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,8 +83,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
|
|||
return &kyvernov1.ForEachMutationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("ForEachValidation"):
|
||||
return &kyvernov1.ForEachValidationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("GeneratePatterns"):
|
||||
return &kyvernov1.GeneratePatternsApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("GeneratePattern"):
|
||||
return &kyvernov1.GeneratePatternApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("Generation"):
|
||||
return &kyvernov1.GenerationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("GlobalContextEntryReference"):
|
||||
|
|
|
@ -981,11 +981,19 @@ func (c *controller) mergeWebhook(dst *webhook, policy kyvernov1.PolicyInterface
|
|||
// matching kinds in generate policies need to be added to both webhook
|
||||
if rule.HasGenerate() {
|
||||
matchedGVK = append(matchedGVK, rule.MatchResources.GetKinds()...)
|
||||
for _, g := range rule.Generation.ForEachGeneration {
|
||||
if g.GeneratePattern.ResourceSpec.Kind != "" {
|
||||
matchedGVK = append(matchedGVK, g.GeneratePattern.ResourceSpec.Kind)
|
||||
} else {
|
||||
matchedGVK = append(matchedGVK, g.GeneratePattern.CloneList.Kinds...)
|
||||
}
|
||||
}
|
||||
if rule.Generation.ResourceSpec.Kind != "" {
|
||||
matchedGVK = append(matchedGVK, rule.Generation.ResourceSpec.Kind)
|
||||
} else {
|
||||
matchedGVK = append(matchedGVK, rule.Generation.CloneList.Kinds...)
|
||||
continue
|
||||
}
|
||||
matchedGVK = append(matchedGVK, rule.Generation.CloneList.Kinds...)
|
||||
continue
|
||||
}
|
||||
if (updateValidate && rule.HasValidate() || rule.HasVerifyImageChecks()) ||
|
||||
(updateValidate && rule.HasMutateExisting()) ||
|
||||
|
|
|
@ -44,14 +44,14 @@ func (pc *policyController) syncDataPolicyChanges(policy kyvernov1.PolicyInterfa
|
|||
continue
|
||||
}
|
||||
if generate.GetData() != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule.Name, generate.GeneratePatterns, deleteDownstream, false); err != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule.Name, generate.GeneratePattern, deleteDownstream, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, foreach := range generate.ForEachGeneration {
|
||||
if foreach.GetData() != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule.Name, foreach.GeneratePatterns, deleteDownstream, false); err != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule.Name, foreach.GeneratePattern, deleteDownstream, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.Polic
|
|||
sync, orphanDownstreamOnPolicyDelete := r.GetSyncAndOrphanDownstream()
|
||||
if generate.GetData() != nil {
|
||||
if sync && (generate.GetType() == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r.Name, r.Generation.GeneratePatterns, true, true); err != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r.Name, r.Generation.GeneratePattern, true, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.Polic
|
|||
for _, foreach := range generate.ForEachGeneration {
|
||||
if foreach.GetData() != nil {
|
||||
if sync && (foreach.GetType() == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r.Name, foreach.GeneratePatterns, true, true); err != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r.Name, foreach.GeneratePattern, true, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.Polic
|
|||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest, ruleName string, pattern kyvernov1.GeneratePatterns, deleteDownstream, policyDeletion bool) (*kyvernov2.UpdateRequest, error) {
|
||||
func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest, ruleName string, pattern kyvernov1.GeneratePattern, deleteDownstream, policyDeletion bool) (*kyvernov2.UpdateRequest, error) {
|
||||
labels := map[string]string{
|
||||
common.GeneratePolicyLabel: policy.GetName(),
|
||||
common.GeneratePolicyNamespaceLabel: policy.GetNamespace(),
|
||||
|
|
|
@ -59,20 +59,19 @@ func (g *Generate) Validate(ctx context.Context) (warnings []string, path string
|
|||
// If kind and namespace contain variables, then we cannot resolve then so we skip the processing
|
||||
if rule.ForEachGeneration != nil {
|
||||
for _, forEach := range rule.ForEachGeneration {
|
||||
if err := g.validateAuth(ctx, forEach.GeneratePatterns); err != nil {
|
||||
if err := g.validateAuth(ctx, forEach.GeneratePattern); err != nil {
|
||||
return nil, "foreach", err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := g.validateAuth(ctx, rule.GeneratePatterns); err != nil {
|
||||
if err := g.validateAuth(ctx, rule.GeneratePattern); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
// validateAuth returns a error if kyverno cannot perform operations
|
||||
func (g *Generate) validateAuth(ctx context.Context, generate kyvernov1.GeneratePatterns) error {
|
||||
func (g *Generate) validateAuth(ctx context.Context, generate kyvernov1.GeneratePattern) error {
|
||||
if len(generate.CloneList.Kinds) != 0 {
|
||||
for _, kind := range generate.CloneList.Kinds {
|
||||
gvk, sub := parseCloneKind(kind)
|
||||
|
|
|
@ -217,7 +217,7 @@ func (h *generationHandler) syncTriggerAction(
|
|||
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)
|
||||
h.log.V(4).Info("creating the UR to generate downstream on trigger's deletion", "operation", request.Operation, "rule", rule.Name, "trigger", triggerSpec.String())
|
||||
ruleCtx := buildRuleContext(rule, triggerSpec, false)
|
||||
urSpec.RuleContext = append(urSpec.RuleContext, ruleCtx)
|
||||
continue
|
||||
|
@ -225,7 +225,7 @@ func (h *generationHandler) syncTriggerAction(
|
|||
|
||||
// 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)
|
||||
h.log.V(4).Info("creating the UR to delete downstream on trigger's event", "operation", request.Operation, "rule", rule.Name, "trigger", triggerSpec.String())
|
||||
ruleCtx := buildRuleContext(rule, triggerSpec, true)
|
||||
urSpec.RuleContext = append(urSpec.RuleContext, ruleCtx)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,24 @@ features:
|
|||
omitEvents:
|
||||
eventTypes: []
|
||||
|
||||
admissionController:
|
||||
extraArgs:
|
||||
v: 4
|
||||
rbac:
|
||||
clusterRole:
|
||||
extraResources:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
|
||||
backgroundController:
|
||||
extraArgs:
|
||||
v: 4
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-cpol-clone-list-sync-delete-source-existing-ns
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
allowedToBeCloned: "true"
|
||||
location: europe
|
||||
name: mysecret-1
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-existing-ns
|
||||
type: Opaque
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
allowedToBeCloned: "false"
|
||||
location: europe
|
||||
name: mysecret-2
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-existing-ns
|
||||
type: Opaque
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-cpol-clone-list-sync-delete-source-target-ns-1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-cpol-clone-list-sync-delete-source-target-ns-2
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-list-sync-delete-source
|
||||
spec:
|
||||
rules:
|
||||
- match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
name: k-kafka-address
|
||||
context:
|
||||
- name: configmapns
|
||||
variable:
|
||||
jmesPath: request.object.metadata.namespace
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{configmapns}}'
|
||||
operator: Equals
|
||||
value: '{{request.object.metadata.namespace}}'
|
||||
generate:
|
||||
generateExisting: false
|
||||
synchronize: true
|
||||
foreach:
|
||||
- list: request.object.data.namespaces | split(@, ',')
|
||||
context:
|
||||
- name: ns
|
||||
variable:
|
||||
jmesPath: element
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{ ns }}'
|
||||
operator: AnyIn
|
||||
value:
|
||||
- foreach-cpol-clone-list-sync-delete-source-target-ns-1
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
namespace: '{{ ns }}'
|
||||
cloneList:
|
||||
kinds:
|
||||
- v1/Secret
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-existing-ns
|
||||
selector:
|
||||
matchLabels:
|
||||
allowedToBeCloned: "true"
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v2beta1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-list-sync-delete-source
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-cpol-clone-list-sync-delete-source-trigger-ns
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-trigger-ns
|
||||
data:
|
||||
namespaces: foreach-cpol-clone-list-sync-delete-source-target-ns-1,foreach-cpol-clone-list-sync-delete-source-target-ns-2
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
allowedToBeCloned: "true"
|
||||
location: europe
|
||||
name: mysecret-1
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-target-ns-1
|
||||
type: Opaque
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
allowedToBeCloned: "true"
|
||||
location: europe
|
||||
name: mysecret-2
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-target-ns-2
|
||||
type: Opaque
|
|
@ -0,0 +1,11 @@
|
|||
## Description
|
||||
|
||||
This test ensures the corresponding downstream target is deleted when its trigger is deleted, for a generate foreach cloneList type of policy.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
If the downstream resources `mysecret-1` is remained in the namespace `cpol-clone-list-sync-delete-source-trigger-ns-1`, the test fails. If not, the test passes.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/3542
|
|
@ -0,0 +1,37 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-clone-list-sync-delete-source
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: 1-0-existing.yaml
|
||||
- apply:
|
||||
file: 1-1-policy.yaml
|
||||
- assert:
|
||||
file: 1-2-policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: 2-1-trigger.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- assert:
|
||||
file: 3-1-target-expected.yaml
|
||||
- error:
|
||||
file: 3-2-target-none-expected.yaml
|
||||
- name: step-04
|
||||
try:
|
||||
- delete:
|
||||
ref:
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
name: mysecret-1
|
||||
namespace: foreach-cpol-clone-list-sync-delete-source-existing-ns
|
||||
- name: step-05
|
||||
try:
|
||||
- error:
|
||||
file: 3-1-target-expected.yaml
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-2
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: source-secret
|
||||
namespace: default
|
||||
type: Opaque
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-sync-create
|
||||
spec:
|
||||
rules:
|
||||
- match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
name: k-kafka-address
|
||||
context:
|
||||
- name: configmapns
|
||||
variable:
|
||||
jmesPath: request.object.metadata.namespace
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{configmapns}}'
|
||||
operator: Equals
|
||||
value: 'default'
|
||||
generate:
|
||||
generateExisting: false
|
||||
synchronize: true
|
||||
foreach:
|
||||
- list: request.object.data.namespaces | split(@, ',')
|
||||
context:
|
||||
- name: ns
|
||||
variable:
|
||||
jmesPath: element
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{ ns }}'
|
||||
operator: AnyIn
|
||||
value:
|
||||
- foreach-ns-1
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
name: cloned-secret-{{ elementIndex }}-{{ ns }}
|
||||
namespace: '{{ ns }}'
|
||||
clone:
|
||||
namespace: default
|
||||
name: source-secret
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-sync-create
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,8 @@
|
|||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: default
|
||||
data:
|
||||
namespaces: foreach-ns-1,foreach-ns-2
|
||||
fo: bar
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloned-secret-0-foreach-ns-1
|
||||
namespace: foreach-ns-1
|
||||
type: Opaque
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloned-secret-0-foreach-ns-2
|
||||
namespace: foreach-ns-2
|
||||
type: Opaque
|
|
@ -0,0 +1,11 @@
|
|||
## Description
|
||||
|
||||
This is a basic creation test for a "generate foreach clone" policy with preconditions and context variables. It checks that the basic functionality works whereby installation of the policy causes correct evaluation of the match and preconditions blocks.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
If only the `foreach-ns-1` Namespace receives a cloned Secret, the test passes. If either it does not or `foreach-ns-2` receives a cloned Secret, the test fails.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/3542
|
|
@ -0,0 +1,44 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-data-sync-create
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: 1-1-source.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: 2-1-policy.yaml
|
||||
- assert:
|
||||
file: 2-2-policy-assert.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: 3-1-trigger.yaml
|
||||
- name: step-04
|
||||
try:
|
||||
- apply:
|
||||
file: 4-1-cloned-target.yaml
|
||||
- error:
|
||||
file: 4-2-no-cloned-target.yaml
|
||||
- name: step-05
|
||||
try:
|
||||
- delete:
|
||||
ref:
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
name: source-secret
|
||||
namespace: default
|
||||
- name: step-06
|
||||
try:
|
||||
- sleep:
|
||||
duration: 1s
|
||||
- name: step-07
|
||||
try:
|
||||
- error:
|
||||
file: 4-1-cloned-target.yaml
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-2
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: source-secret
|
||||
namespace: default
|
||||
type: Opaque
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-sync-create
|
||||
spec:
|
||||
rules:
|
||||
- match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
name: k-kafka-address
|
||||
context:
|
||||
- name: configmapns
|
||||
variable:
|
||||
jmesPath: request.object.metadata.namespace
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{configmapns}}'
|
||||
operator: Equals
|
||||
value: 'default'
|
||||
generate:
|
||||
generateExisting: false
|
||||
synchronize: true
|
||||
foreach:
|
||||
- list: request.object.data.namespaces | split(@, ',')
|
||||
context:
|
||||
- name: ns
|
||||
variable:
|
||||
jmesPath: element
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{ ns }}'
|
||||
operator: AnyIn
|
||||
value:
|
||||
- foreach-ns-1
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
name: cloned-secret-{{ elementIndex }}-{{ ns }}
|
||||
namespace: '{{ ns }}'
|
||||
clone:
|
||||
namespace: default
|
||||
name: source-secret
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-sync-create
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,7 @@
|
|||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: default
|
||||
data:
|
||||
namespaces: foreach-ns-1,foreach-ns-2
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloned-secret-0-foreach-ns-1
|
||||
namespace: foreach-ns-1
|
||||
type: Opaque
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloned-secret-0-foreach-ns-2
|
||||
namespace: foreach-ns-2
|
||||
type: Opaque
|
|
@ -0,0 +1,11 @@
|
|||
## Description
|
||||
|
||||
This is a basic creation test for a "generate foreach clone" policy with preconditions and context variables. It checks that the basic functionality works whereby installation of the policy causes correct evaluation of the match and preconditions blocks.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
If only the `foreach-ns-1` Namespace receives a cloned Secret, the test passes. If either it does not or `foreach-ns-2` receives a cloned Secret, the test fails.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/3542
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-data-sync-create
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: 1-1-source.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: 2-1-policy.yaml
|
||||
- assert:
|
||||
file: 2-2-policy-assert.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: 3-1-trigger.yaml
|
||||
- name: step-04
|
||||
try:
|
||||
- apply:
|
||||
file: 4-1-cloned-target.yaml
|
||||
- error:
|
||||
file: 4-2-no-cloned-target.yaml
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-2
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: source-secret
|
||||
namespace: default
|
||||
type: Opaque
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: default
|
||||
data:
|
||||
namespaces: foreach-ns-1,foreach-ns-2
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-sync-create
|
||||
spec:
|
||||
rules:
|
||||
- match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
name: k-kafka-address
|
||||
context:
|
||||
- name: configmapns
|
||||
variable:
|
||||
jmesPath: request.object.metadata.namespace
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{configmapns}}'
|
||||
operator: Equals
|
||||
value: 'default'
|
||||
generate:
|
||||
generateExisting: true
|
||||
synchronize: true
|
||||
foreach:
|
||||
- list: request.object.data.namespaces | split(@, ',')
|
||||
context:
|
||||
- name: ns
|
||||
variable:
|
||||
jmesPath: element
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{ ns }}'
|
||||
operator: AnyIn
|
||||
value:
|
||||
- foreach-ns-1
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
name: cloned-secret-{{ elementIndex }}-{{ ns }}
|
||||
namespace: '{{ ns }}'
|
||||
clone:
|
||||
namespace: default
|
||||
name: source-secret
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-clone-sync-create
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloned-secret-0-foreach-ns-1
|
||||
namespace: foreach-ns-1
|
||||
type: Opaque
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
foo: YmFy
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloned-secret-0-foreach-ns-2
|
||||
namespace: foreach-ns-2
|
||||
type: Opaque
|
|
@ -0,0 +1,11 @@
|
|||
## Description
|
||||
|
||||
This is a basic test for generate existing foreach clone policy, with preconditions and context variables. It checks that the basic functionality works whereby installation of the policy causes correct evaluation of the match and preconditions blocks.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
If only the `foreach-ns-1` Namespace receives a cloned Secret upon policy creation, the test passes. If either it does not or `foreach-ns-2` receives a cloned Secret, the test fails.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/3542
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-data-sync-create
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: 1-1-source.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: 2-1-policy.yaml
|
||||
- assert:
|
||||
file: 2-2-policy-assert.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: 3-1-cloned-target.yaml
|
||||
- error:
|
||||
file: 3-2-no-cloned-target.yaml
|
Loading…
Add table
Reference in a new issue