diff --git a/pkg/background/generate/clone.go b/pkg/background/generate/clone.go index fbfe04f54ff6..76235fdc975f 100644 --- a/pkg/background/generate/clone.go +++ b/pkg/background/generate/clone.go @@ -13,15 +13,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, rule kyvernov1.Rule, client dclient.Interface) generateResponse { +func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, pattern kyvernov1.GeneratePatterns, client dclient.Interface) generateResponse { source := sourceSpec - clone := rule.Generation - if clone.Clone.Name != "" { + if pattern.Clone.Name != "" { source = kyvernov1.ResourceSpec{ APIVersion: target.GetAPIVersion(), Kind: target.GetKind(), - Namespace: clone.Clone.Namespace, - Name: clone.Clone.Name, + Namespace: pattern.Clone.Namespace, + Name: pattern.Clone.Name, } } @@ -80,14 +79,13 @@ 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, rule kyvernov1.Rule, client dclient.Interface) []generateResponse { +func manageCloneList(log logr.Logger, targetNamespace string, severSideApply bool, pattern kyvernov1.GeneratePatterns, client dclient.Interface) []generateResponse { var responses []generateResponse - cloneList := rule.Generation.CloneList - sourceNamespace := cloneList.Namespace - kinds := cloneList.Kinds + sourceNamespace := pattern.CloneList.Namespace + kinds := pattern.CloneList.Kinds for _, kind := range kinds { apiVersion, kind := kubeutils.GetKindFromGVK(kind) - sources, err := client.ListResource(context.TODO(), apiVersion, kind, sourceNamespace, cloneList.Selector) + sources, err := client.ListResource(context.TODO(), apiVersion, kind, sourceNamespace, pattern.CloneList.Selector) if err != nil { responses = append(responses, newSkipGenerateResponse( @@ -101,13 +99,13 @@ func manageCloneList(log logr.Logger, targetNamespace string, severSideApply boo for _, source := range sources.Items { target := newResourceSpec(source.GetAPIVersion(), source.GetKind(), targetNamespace, source.GetName()) - if (cloneList.Kinds != nil) && (source.GetNamespace() == target.GetNamespace()) { + if (pattern.CloneList.Kinds != nil) && (source.GetNamespace() == target.GetNamespace()) { log.V(4).Info("skip resource self-clone") responses = append(responses, newSkipGenerateResponse(nil, target, nil)) continue } responses = append(responses, - manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), severSideApply, rule, client)) + manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), severSideApply, pattern, client)) } } return responses diff --git a/pkg/background/generate/controller.go b/pkg/background/generate/controller.go index 132fadfe1968..6694c6ec20c7 100644 --- a/pkg/background/generate/controller.go +++ b/pkg/background/generate/controller.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-logr/logr" + gojmespath "github.com/kyverno/go-jmespath" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2" "github.com/kyverno/kyverno/pkg/background/common" @@ -311,16 +312,20 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext } logger := log.WithValues("rule", rule.Name) contextLoader := c.engine.ContextLoader(policy, rule) - // if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext(), rule); err != nil { - // log.Error(err, "variable substitution failed for rule", "rule", rule.Name) - // return nil, err - // } + if err := contextLoader(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil { + if _, ok := err.(gojmespath.NotFoundError); ok { + logger.V(3).Info("failed to load rule level context", "reason", err.Error()) + } else { + logger.Error(err, "failed to load rule level context") + } + return nil, fmt.Errorf("failed to load rule level context: %v", err) + } if rule.Generation.ForEachGeneration != nil { 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(), contextLoader) + g := newGenerator(c.client, logger, policyContext, policy, rule, rule.Context, rule.GetAnyAllConditions(), policyContext.NewResource(), rule.Generation.GeneratePatterns, contextLoader) genResource, err = g.generate() } diff --git a/pkg/background/generate/generator.go b/pkg/background/generate/generator.go index d1c725675476..acde93fc7977 100644 --- a/pkg/background/generate/generator.go +++ b/pkg/background/generate/generator.go @@ -28,6 +28,7 @@ type generator struct { anyAllConditions any trigger unstructured.Unstructured forEach []kyvernov1.ForEachGeneration + pattern kyvernov1.GeneratePatterns contextLoader engineapi.EngineContextLoader } @@ -39,6 +40,7 @@ func newGenerator(client dclient.Interface, contextEntries []kyvernov1.ContextEntry, anyAllConditions any, trigger unstructured.Unstructured, + pattern kyvernov1.GeneratePatterns, contextLoader engineapi.EngineContextLoader, ) *generator { return &generator{ @@ -50,6 +52,7 @@ func newGenerator(client dclient.Interface, contextEntries: contextEntries, anyAllConditions: anyAllConditions, trigger: trigger, + pattern: pattern, contextLoader: contextLoader, } } @@ -65,10 +68,18 @@ func newForeachGenerator(client dclient.Interface, forEach []kyvernov1.ForEachGeneration, contextLoader engineapi.EngineContextLoader, ) *generator { - - g := newGenerator(client, logger, policyContext, policy, rule, contextEntries, anyAllConditions, trigger, contextLoader) - g.forEach = forEach - return g + return &generator{ + client: client, + logger: logger, + policyContext: policyContext, + policy: policy, + rule: rule, + contextEntries: contextEntries, + anyAllConditions: anyAllConditions, + trigger: trigger, + forEach: forEach, + contextLoader: contextLoader, + } } func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) { @@ -95,22 +106,22 @@ func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) { return newGenResources, nil } - rule, err := variables.SubstituteAllInRule(g.logger, g.policyContext.JSONContext(), g.rule) + pattern, err := variables.SubstituteAllInType(g.logger, g.policyContext.JSONContext(), &g.pattern) if err != nil { - g.logger.Error(err, "variable substitution failed for rule", "rule", rule.Name) + g.logger.Error(err, "variable substitution failed for rule", "rule", g.rule.Name) return nil, err } - target := rule.Generation.ResourceSpec + target := pattern.ResourceSpec logger := g.logger.WithValues("target", target.String()) - if rule.Generation.Clone.Name != "" { - resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, g.policy.GetSpec().UseServerSideApply, rule, g.client) + if pattern.Clone.Name != "" { + resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, g.policy.GetSpec().UseServerSideApply, *pattern, g.client) responses = append(responses, resp) - } else if len(rule.Generation.CloneList.Kinds) != 0 { - responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), g.policy.GetSpec().UseServerSideApply, rule, g.client) + } else if len(pattern.CloneList.Kinds) != 0 { + responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), g.policy.GetSpec().UseServerSideApply, *pattern, g.client) } else { - resp := manageData(logger.WithValues("type", "data"), target, rule.Generation.RawData, rule.Generation.Synchronize, g.client) + resp := manageData(logger.WithValues("type", "data"), target, pattern.RawData, g.rule.Generation.Synchronize, g.client) responses = append(responses, resp) } @@ -140,7 +151,7 @@ func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) { } newResource.SetAPIVersion(targetMeta.GetAPIVersion()) - common.ManageLabels(newResource, g.trigger, g.policy, rule.Name) + common.ManageLabels(newResource, g.trigger, g.policy, g.rule.Name) if response.GetAction() == Create { newResource.SetResourceVersion("") if g.policy.GetSpec().UseServerSideApply { @@ -169,7 +180,7 @@ func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) { } newGenResources = append(newGenResources, targetMeta) } else { - if !rule.Generation.Synchronize { + if !g.rule.Generation.Synchronize { logger.V(4).Info("synchronize disabled, skip syncing changes") continue } @@ -253,6 +264,7 @@ func (g *generator) generateElements(foreach kyvernov1.ForEachGeneration, elemen foreach.Context, foreach.AnyAllConditions, g.trigger, + foreach.GeneratePatterns, g.contextLoader). generate() if err != nil {