1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/background/generate/clone.go
shuting 2cd462570a
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>
2024-08-29 11:59:22 +00:00

112 lines
4.4 KiB
Go

package generate
import (
"context"
"fmt"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
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{
APIVersion: target.GetAPIVersion(),
Kind: target.GetKind(),
Namespace: pattern.Clone.Namespace,
Name: pattern.Clone.Name,
}
}
if source.GetNamespace() == "" {
log.V(4).Info("namespace is optional in case of cluster scope resource", "source namespace", source.GetNamespace())
}
if source.GetNamespace() == target.GetNamespace() && source.GetName() == target.GetName() {
log.V(4).Info("skip resource self-clone")
return newSkipGenerateResponse(nil, target, nil)
}
sourceObj, err := client.GetResource(context.TODO(), source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName())
if err != nil {
return newSkipGenerateResponse(nil, target, fmt.Errorf("source resource %s not found: %v", target.String(), err))
}
if err := updateSourceLabel(client, sourceObj); err != nil {
log.Error(err, "failed to add labels to the source", "kind", sourceObj.GetKind(), "namespace", sourceObj.GetNamespace(), "name", sourceObj.GetName())
}
sourceObjCopy := sourceObj.DeepCopy()
addSourceLabels(sourceObjCopy)
if sourceObjCopy.GetNamespace() != target.GetNamespace() && sourceObjCopy.GetOwnerReferences() != nil {
sourceObjCopy.SetOwnerReferences(nil)
}
// Clean up parameters that shouldn't be copied
sourceObjCopy.SetUID("")
sourceObjCopy.SetSelfLink("")
var emptyTime metav1.Time
sourceObjCopy.SetCreationTimestamp(emptyTime)
sourceObjCopy.SetManagedFields(nil)
sourceObjCopy.SetResourceVersion("")
targetObj, err := client.GetResource(context.TODO(), target.GetAPIVersion(), target.GetKind(), target.GetNamespace(), target.GetName())
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 {
if !severSideApply {
sourceObjCopy.SetUID(targetObj.GetUID())
sourceObjCopy.SetSelfLink(targetObj.GetSelfLink())
sourceObjCopy.SetCreationTimestamp(targetObj.GetCreationTimestamp())
sourceObjCopy.SetManagedFields(targetObj.GetManagedFields())
sourceObjCopy.SetResourceVersion(targetObj.GetResourceVersion())
if datautils.DeepEqual(sourceObjCopy, targetObj) {
return newSkipGenerateResponse(nil, target, nil)
}
}
return newUpdateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
}
return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
}
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
for _, kind := range kinds {
apiVersion, kind := kubeutils.GetKindFromGVK(kind)
sources, err := client.ListResource(context.TODO(), apiVersion, kind, sourceNamespace, pattern.CloneList.Selector)
if err != nil {
responses = append(responses,
newSkipGenerateResponse(
nil,
newResourceSpec(apiVersion, kind, targetNamespace, ""),
fmt.Errorf("failed to list source resource for cloneList %s %s/%s. %v", apiVersion, kind, sourceNamespace, err),
),
)
}
for _, source := range sources.Items {
target := newResourceSpec(source.GetAPIVersion(), source.GetKind(), targetNamespace, source.GetName())
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, pattern, client))
}
}
return responses
}