2023-03-27 10:09:46 +02:00
|
|
|
package mutation
|
2022-04-25 20:20:40 +08:00
|
|
|
|
|
|
|
import (
|
2022-11-29 14:59:40 +01:00
|
|
|
"context"
|
2022-04-25 20:20:40 +08:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/go-logr/logr"
|
2022-05-17 13:12:43 +02:00
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2023-02-07 16:09:15 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
2023-01-31 16:28:48 +01:00
|
|
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
2022-04-25 20:20:40 +08:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/variables"
|
2022-12-09 22:15:23 +05:30
|
|
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
2022-08-25 07:23:01 +02:00
|
|
|
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
2022-08-02 07:54:02 -07:00
|
|
|
"go.uber.org/multierr"
|
2023-04-03 21:58:58 +02:00
|
|
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
2022-12-09 22:15:23 +05:30
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2022-04-25 20:20:40 +08:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
)
|
|
|
|
|
2022-12-09 22:15:23 +05:30
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2023-04-03 21:58:58 +02:00
|
|
|
type target struct {
|
|
|
|
resourceInfo
|
|
|
|
context []kyvernov1.ContextEntry
|
|
|
|
preconditions apiextensions.JSON
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadTargets(client dclient.Interface, targets []kyvernov1.TargetResourceSpec, ctx engineapi.PolicyContext, logger logr.Logger) ([]target, error) {
|
|
|
|
var targetObjects []target
|
2022-04-25 20:20:40 +08:00
|
|
|
var errors []error
|
2022-05-06 13:46:36 +08:00
|
|
|
for i := range targets {
|
2023-04-03 21:58:58 +02:00
|
|
|
preconditions := targets[i].GetAnyAllConditions()
|
2022-05-06 13:46:36 +08:00
|
|
|
spec, err := resolveSpec(i, targets[i], ctx, logger)
|
2022-04-25 20:20:40 +08:00
|
|
|
if err != nil {
|
2022-05-06 13:46:36 +08:00
|
|
|
errors = append(errors, err)
|
2022-04-25 20:20:40 +08:00
|
|
|
continue
|
|
|
|
}
|
2023-02-07 16:09:15 +01:00
|
|
|
objs, err := getTargets(client, spec, ctx)
|
2022-04-25 20:20:40 +08:00
|
|
|
if err != nil {
|
2022-05-06 13:46:36 +08:00
|
|
|
errors = append(errors, err)
|
2022-04-25 20:20:40 +08:00
|
|
|
continue
|
|
|
|
}
|
2023-04-03 21:58:58 +02:00
|
|
|
for _, obj := range objs {
|
|
|
|
targetObjects = append(targetObjects, target{
|
|
|
|
resourceInfo: obj,
|
|
|
|
context: targets[i].Context,
|
|
|
|
preconditions: preconditions,
|
|
|
|
})
|
|
|
|
}
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
2022-08-02 07:54:02 -07:00
|
|
|
return targetObjects, multierr.Combine(errors...)
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
2022-04-25 20:20:40 +08:00
|
|
|
|
2023-04-03 21:58:58 +02:00
|
|
|
func resolveSpec(i int, target kyvernov1.TargetResourceSpec, ctx engineapi.PolicyContext, logger logr.Logger) (kyvernov1.ResourceSpec, error) {
|
2023-01-31 16:28:48 +01:00
|
|
|
kind, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.Kind)
|
2022-05-06 13:46:36 +08:00
|
|
|
if err != nil {
|
2022-05-17 13:12:43 +02:00
|
|
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Kind %s: %v", i, target.Kind, err)
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
2023-01-31 16:28:48 +01:00
|
|
|
apiversion, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.APIVersion)
|
2022-05-06 13:46:36 +08:00
|
|
|
if err != nil {
|
2022-05-17 13:12:43 +02:00
|
|
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].APIVersion %s: %v", i, target.APIVersion, err)
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
2023-01-31 16:28:48 +01:00
|
|
|
namespace, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.Namespace)
|
2022-05-06 13:46:36 +08:00
|
|
|
if err != nil {
|
2022-05-17 13:12:43 +02:00
|
|
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Namespace %s: %v", i, target.Namespace, err)
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
2023-01-31 16:28:48 +01:00
|
|
|
name, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.Name)
|
2022-05-06 13:46:36 +08:00
|
|
|
if err != nil {
|
2022-05-17 13:12:43 +02:00
|
|
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Name %s: %v", i, target.Name, err)
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
2022-05-17 13:12:43 +02:00
|
|
|
return kyvernov1.ResourceSpec{
|
2022-05-06 13:46:36 +08:00
|
|
|
APIVersion: apiversion.(string),
|
|
|
|
Kind: kind.(string),
|
|
|
|
Namespace: namespace.(string),
|
|
|
|
Name: name.(string),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-03-15 11:47:25 +01:00
|
|
|
func getTargets(client dclient.Interface, target kyvernov1.ResourceSpec, ctx engineapi.PolicyContext) ([]resourceInfo, error) {
|
2022-12-09 22:15:23 +05:30
|
|
|
var targetObjects []resourceInfo
|
2022-05-06 13:46:36 +08:00
|
|
|
namespace := target.Namespace
|
|
|
|
name := target.Name
|
2023-01-31 16:28:48 +01:00
|
|
|
policy := ctx.Policy()
|
2022-09-26 20:24:13 +05:30
|
|
|
// if it's namespaced policy, targets has to be loaded only from the policy's namespace
|
2023-01-31 16:28:48 +01:00
|
|
|
if policy.IsNamespaced() {
|
|
|
|
namespace = policy.GetNamespace()
|
2022-09-26 20:24:13 +05:30
|
|
|
}
|
2023-03-15 11:47:25 +01:00
|
|
|
group, version, kind, subresource := kubeutils.ParseKindSelector(target.APIVersion + "/" + target.Kind)
|
|
|
|
gvrss, err := client.Discovery().FindResources(group, version, kind, subresource)
|
2022-12-09 22:15:23 +05:30
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-03-17 12:21:26 +01:00
|
|
|
for gvrs := range gvrss {
|
2023-03-21 14:28:00 +01:00
|
|
|
dyn := client.GetDynamicInterface().Resource(gvrs.GroupVersionResource())
|
2023-03-15 11:47:25 +01:00
|
|
|
var sub []string
|
|
|
|
if gvrs.SubResource != "" {
|
|
|
|
sub = []string{gvrs.SubResource}
|
2022-12-09 22:15:23 +05:30
|
|
|
}
|
2023-03-15 11:47:25 +01:00
|
|
|
// we can use `GET` directly
|
|
|
|
if namespace != "" && name != "" && !wildcard.ContainsWildcard(namespace) && !wildcard.ContainsWildcard(name) {
|
|
|
|
var obj *unstructured.Unstructured
|
|
|
|
var err error
|
|
|
|
obj, err = dyn.Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{}, sub...)
|
2022-12-09 22:15:23 +05:30
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-03-21 14:28:00 +01:00
|
|
|
targetObjects = append(targetObjects, resourceInfo{
|
|
|
|
unstructured: *obj,
|
|
|
|
subresource: gvrs.SubResource,
|
|
|
|
parentResourceGVR: metav1.GroupVersionResource(gvrs.GroupVersionResource()),
|
|
|
|
})
|
2023-03-15 11:47:25 +01:00
|
|
|
} else {
|
|
|
|
// we can use `LIST`
|
|
|
|
if gvrs.SubResource == "" {
|
|
|
|
list, err := dyn.List(context.TODO(), metav1.ListOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, obj := range list.Items {
|
|
|
|
if match(namespace, name, obj.GetNamespace(), obj.GetName()) {
|
|
|
|
targetObjects = append(targetObjects, resourceInfo{unstructured: obj})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// we need to use `LIST` / `GET`
|
|
|
|
list, err := dyn.List(context.TODO(), metav1.ListOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var parentObjects []unstructured.Unstructured
|
|
|
|
for _, obj := range list.Items {
|
|
|
|
if match(namespace, name, obj.GetNamespace(), obj.GetName()) {
|
|
|
|
parentObjects = append(parentObjects, obj)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, parentObject := range parentObjects {
|
|
|
|
var obj *unstructured.Unstructured
|
|
|
|
var err error
|
|
|
|
if parentObject.GetNamespace() == "" {
|
|
|
|
obj, err = dyn.Get(context.TODO(), name, metav1.GetOptions{}, sub...)
|
|
|
|
} else {
|
|
|
|
obj, err = dyn.Namespace(parentObject.GetNamespace()).Get(context.TODO(), name, metav1.GetOptions{}, sub...)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-03-21 14:28:00 +01:00
|
|
|
targetObjects = append(targetObjects, resourceInfo{
|
|
|
|
unstructured: *obj,
|
|
|
|
subresource: gvrs.SubResource,
|
|
|
|
parentResourceGVR: metav1.GroupVersionResource(gvrs.GroupVersionResource()),
|
|
|
|
})
|
2023-03-15 11:47:25 +01:00
|
|
|
}
|
2022-12-09 22:15:23 +05:30
|
|
|
}
|
2022-04-25 20:20:40 +08:00
|
|
|
}
|
2022-05-06 13:46:36 +08:00
|
|
|
}
|
|
|
|
return targetObjects, nil
|
|
|
|
}
|
2022-04-25 20:20:40 +08:00
|
|
|
|
2022-05-06 13:46:36 +08:00
|
|
|
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
|
|
|
|
}
|
2022-04-25 20:20:40 +08:00
|
|
|
}
|
2022-05-06 13:46:36 +08:00
|
|
|
return false
|
2022-04-25 20:20:40 +08:00
|
|
|
}
|