1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 09:26:54 +00:00
kyverno/pkg/engine/handlers/mutation/load_targets.go
Ammar Yasser d61f87e0f9
Context vars with labelselector (#11608)
* refactor: Treat all of the target spec as a single object while replacing variables

Serialize it into a map string interface then back to a target seletor before returning

Signed-off-by: aerosouund <aerosound161@gmail.com>

* test: Add scenario for variables in the label selector test

Signed-off-by: aerosouund <aerosound161@gmail.com>

* Fix: Capitalize error message

Co-authored-by: shuting <shuting@nirmata.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>

* Fix: Adjust error message specification to mention target rather than selector

Co-authored-by: shuting <shuting@nirmata.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>

* fix: Pass the target selector only during variable replacement

Signed-off-by: aerosouund <aerosound161@gmail.com>

---------

Signed-off-by: aerosouund <aerosound161@gmail.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
Co-authored-by: shuting <shuting@nirmata.com>
2024-11-20 10:36:55 +00:00

131 lines
4.1 KiB
Go

package mutation
import (
"context"
"encoding/json"
"fmt"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/ext/wildcard"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/variables"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"go.uber.org/multierr"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// resourceInfo contains the Unstructured resource, and if the resource is a subresource, it contains its name and its
// parentResource's group-version-resource
type resourceInfo struct {
unstructured unstructured.Unstructured
subresource string
parentResourceGVR metav1.GroupVersionResource
}
type target struct {
resourceInfo
context []kyvernov1.ContextEntry
preconditions apiextensions.JSON
}
func loadTargets(ctx context.Context, client engineapi.Client, targets []kyvernov1.TargetResourceSpec, policyCtx engineapi.PolicyContext, logger logr.Logger) ([]target, error) {
var targetObjects []target
var errors []error
for i := range targets {
preconditions := targets[i].GetAnyAllConditions()
spec, err := resolveSpec(i, targets[i], policyCtx, logger)
if err != nil {
errors = append(errors, err)
continue
}
objs, err := getTargets(ctx, client, spec.ResourceSpec, policyCtx, spec.Selector)
if err != nil {
errors = append(errors, err)
continue
}
for _, obj := range objs {
targetObjects = append(targetObjects, target{
resourceInfo: obj,
context: targets[i].Context,
preconditions: preconditions,
})
}
}
return targetObjects, multierr.Combine(errors...)
}
func resolveSpec(i int, target kyvernov1.TargetResourceSpec, ctx engineapi.PolicyContext, logger logr.Logger) (kyvernov1.TargetSelector, error) {
var s kyvernov1.TargetSelector
jsonData, err := json.Marshal(target.TargetSelector)
if err != nil {
return kyvernov1.TargetSelector{}, fmt.Errorf("failed to marshal the mutation target to JSON: %s", err)
}
var result map[string]interface{}
if err := json.Unmarshal(jsonData, &result); err != nil {
return kyvernov1.TargetSelector{}, err
}
selector, err := variables.SubstituteAll(logger, ctx.JSONContext(), result)
if err != nil || selector == nil {
return kyvernov1.TargetSelector{}, fmt.Errorf("failed to substitute variables in target[%d]: %v", i, err)
}
substitutedJson, err := json.Marshal(selector)
if err != nil {
return kyvernov1.TargetSelector{}, err
}
if err := json.Unmarshal(substitutedJson, &s); err != nil {
return kyvernov1.TargetSelector{}, err
}
return s, nil
}
func getTargets(ctx context.Context, client engineapi.Client, target kyvernov1.ResourceSpec, policyCtx engineapi.PolicyContext, lselector *metav1.LabelSelector) ([]resourceInfo, error) {
namespace := target.Namespace
name := target.Name
policy := policyCtx.Policy()
// if it's namespaced policy, targets has to be loaded only from the policy's namespace
if policy.IsNamespaced() {
namespace = policy.GetNamespace()
}
group, version, kind, subresource := kubeutils.ParseKindSelector(target.APIVersion + "/" + target.Kind)
resources, err := client.GetResources(ctx, group, version, kind, subresource, namespace, name, lselector)
if err != nil {
return nil, err
}
targetObjects := make([]resourceInfo, 0, len(resources))
for _, resource := range resources {
targetObjects = append(targetObjects, resourceInfo{
unstructured: resource.Unstructured,
subresource: resource.SubResource,
parentResourceGVR: metav1.GroupVersionResource{
Group: resource.Group,
Version: resource.Version,
Resource: resource.Resource,
},
})
}
return targetObjects, nil
}
func match(namespacePattern, namePattern, namespace, name string) bool {
if namespacePattern == "" && namePattern == "" {
return true
} else if namespacePattern == "" {
if wildcard.Match(namePattern, name) {
return true
}
} else if wildcard.Match(namespacePattern, namespace) {
if namePattern == "" || wildcard.Match(namePattern, name) {
return true
}
}
return false
}