Ctrl K
ComunidadGuías…

pkg/finalizers/catalog_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package finalizers

import (
	"testing"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func TestOwnerMatchesWorkload(t *testing.T) {
	vm := &unstructured.Unstructured{}
	vm.SetAPIVersion("operator.victoriametrics.com/v1beta1")
	argo := &unstructured.Unstructured{}
	argo.SetAPIVersion("argoproj.io/v1alpha1")
	kustomization := &unstructured.Unstructured{}
	kustomization.SetAPIVersion("kustomize.toolkit.fluxcd.io/v1")
	renamed := map[string]string{"app.kubernetes.io/name": "vmop"}
	containers := func(image string, args ...string) corev1.PodTemplateSpec {
		return corev1.PodTemplateSpec{ObjectMeta: metav1.ObjectMeta{Labels: renamed}, Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "operator", Image: image, Args: args}}}}
	}
	for _, tt := range []struct {
		name, finalizer, workload string
		root                      *unstructured.Unstructured
		labels                    map[string]string
		template                  corev1.PodTemplateSpec
		want                      bool
	}{
		{name: "AWS node class", finalizer: "karpenter.k8s.aws/termination", root: vm, workload: "karpenter", want: true},
		{name: "Azure node class", finalizer: "karpenter.azure.com/termination", root: vm, workload: "karpenter", want: true},
		{name: "karpenter deployment", finalizer: "karpenter.sh/termination", root: vm, workload: "karpenter", want: true},
		{name: "catalog label", finalizer: "apps.victoriametrics.com/finalizer", root: vm, workload: "custom", labels: map[string]string{"app.kubernetes.io/name": "victoria-metrics-operator"}, want: true},
		{name: "catalog name", finalizer: "apps.victoriametrics.com/finalizer", root: vm, workload: "vm-victoria-metrics-operator", want: true},
		{name: "renamed install by Docker Hub image", finalizer: "apps.victoriametrics.com/finalizer", root: vm, workload: "vmop", labels: renamed, template: containers("victoriametrics/operator:v0.50.0"), want: true},
		{name: "renamed install by mirrored image", finalizer: "apps.victoriametrics.com/finalizer", root: vm, workload: "vmop", labels: renamed, template: containers("mirror.example.com:5000/victoriametrics/operator:v0.50.0@sha256:0123"), want: true},
		{name: "mirror that prefixes the path", finalizer: "apps.victoriametrics.com/finalizer", root: vm, workload: "vmop", labels: renamed, template: containers("mirror.example.com/dockerhub/victoriametrics/operator:v0.50.0"), want: true},
		{name: "renamed install of another image", finalizer: "apps.victoriametrics.com/finalizer", root: vm, workload: "vmop", labels: renamed, template: containers("victoriametrics/vmagent:v1.100.0")},
		{name: "flux controller by ghcr image", finalizer: "finalizers.kustomize.toolkit.fluxcd.io", root: kustomization, workload: "kc", template: containers("ghcr.io/fluxcd/kustomize-controller:v1.4.0"), want: true},
		{name: "flux controller image of a sibling", finalizer: "finalizers.kustomize.toolkit.fluxcd.io", root: kustomization, workload: "hc", template: containers("ghcr.io/fluxcd/helm-controller:v1.1.0")},
		{name: "argo cd application controller by image and binary", finalizer: "resources-finalizer.argocd.argoproj.io", root: argo, workload: "ctrl", template: containers("quay.io/argoproj/argocd:v2.13.0", "/usr/local/bin/argocd-application-controller", "--metrics-port=8082"), want: true},
		{name: "argo cd server shares the image", finalizer: "resources-finalizer.argocd.argoproj.io", root: argo, workload: "web", template: containers("quay.io/argoproj/argocd:v2.13.0", "/usr/local/bin/argocd-server")},
	} {
		t.Run(tt.name, func(t *testing.T) {
			owner := ResolveOwner(tt.finalizer, tt.root)
			if owner == nil {
				t.Fatal("expected a catalog owner")
			}
			workload := &metav1.ObjectMeta{Name: tt.workload, Labels: tt.labels}
			if got := owner.MatchesWorkload(workload, tt.template); got != tt.want {
				t.Fatalf("got %v want %v", got, tt.want)
			}
		})
	}
}

func TestVictoriaMetricsReloaderImageIsNotOperator(t *testing.T) {
	root := &unstructured.Unstructured{}
	root.SetAPIVersion("operator.victoriametrics.com/v1beta1")
	owner := ResolveOwner("apps.victoriametrics.com/finalizer", root)
	for _, tc := range []struct {
		name       string
		containers []corev1.Container
		want       bool
	}{
		{name: "default reloader", containers: []corev1.Container{{Name: "config-reloader", Image: "victoriametrics/operator:config-reloader-v0.66.1"}}},
		{name: "renamed reloader container", containers: []corev1.Container{{Name: "reload", Image: "victoriametrics/operator:config-reloader-v0.66.1"}}},
		{name: "mirrored tagged digest reloader", containers: []corev1.Container{{Name: "reload", Image: "mirror.example:5000/victoriametrics/operator:config-reloader-v0.66.1@sha256:0123"}}},
		{name: "digest-only reloader", containers: []corev1.Container{{Name: "config-reloader", Image: "mirror.example:5000/victoriametrics/operator@sha256:0123"}}},
		{name: "retagged reloader", containers: []corev1.Container{{Name: "config-reloader", Image: "victoriametrics/operator:custom"}}},
		{name: "operator with default entrypoint", containers: []corev1.Container{{Name: "manager", Image: "victoriametrics/operator:v0.66.1"}}, want: true},
		{name: "digest-only operator", containers: []corev1.Container{{Name: "manager", Image: "mirror.example:5000/victoriametrics/operator@sha256:0123"}}, want: true},
		{name: "operator beside reloader", containers: []corev1.Container{{Name: "config-reloader", Image: "victoriametrics/operator:config-reloader-v0.66.1"}, {Name: "manager", Image: "victoriametrics/operator:v0.66.1"}}, want: true},
	} {
		t.Run(tc.name, func(t *testing.T) {
			spec := corev1.PodSpec{Containers: tc.containers}
			if got := owner.MatchesPod(nil, spec); got != tc.want {
				t.Errorf("pod match = %v, want %v", got, tc.want)
			}
			if got := owner.MatchesWorkload(&metav1.ObjectMeta{Name: "custom"}, corev1.PodTemplateSpec{Spec: spec}); got != tc.want {
				t.Errorf("workload match = %v, want %v", got, tc.want)
			}
		})
	}
}

func TestImageRepositoryPath(t *testing.T) {
	for image, want := range map[string]string{
		"victoriametrics/operator":                                 "victoriametrics/operator",
		"victoriametrics/operator:v0.50.0":                         "victoriametrics/operator",
		"docker.io/victoriametrics/operator:v0.50.0":               "victoriametrics/operator",
		"ghcr.io/fluxcd/helm-controller:v1.1.0@sha256:abc":         "fluxcd/helm-controller",
		"localhost/fluxcd/helm-controller":                         "fluxcd/helm-controller",
		"registry:5000/fluxcd/helm-controller:v1":                  "fluxcd/helm-controller",
		"public.ecr.aws/karpenter/controller:1.0.0":                "karpenter/controller",
		"mcr.microsoft.com/aks/karpenter/controller:v1.0.0":        "aks/karpenter/controller",
		"mirror.example.com/dockerhub/victoriametrics/operator:v1": "dockerhub/victoriametrics/operator",
	} {
		if got := imageRepositoryPath(image); got != want {
			t.Errorf("%s: got %q want %q", image, got, want)
		}
	}
}

// A first-label name match is too weak to identify an owner: it may only
// name a candidate.
func TestControllerCandidate(t *testing.T) {
	root := &unstructured.Unstructured{}
	for _, tt := range []struct {
		name, group, finalizer, workload string
		labels                           map[string]string
		want                             bool
	}{
		{name: "name", group: "widgets.example.com", finalizer: "widgets.example.com/cleanup", workload: "release-widgets-operator", want: true},
		{name: "part-of", group: "widgets.example.com", finalizer: "widgets.example.com/cleanup", workload: "custom", labels: map[string]string{"app.kubernetes.io/part-of": "widgets-operator"}, want: true},
		{name: "unrelated domain", group: "other.example.com", finalizer: "widgets.example.com/cleanup", workload: "widgets-operator"},
		{name: "domain only", group: "widgets.example.com", finalizer: "widgets.example.com/cleanup", workload: "widgets-server"},
		{name: "substring", group: "widgets.example.com", finalizer: "widgets.example.com/cleanup", workload: "notwidgets-operator"},
		{name: "reserved domain", group: "k8s.io", finalizer: "k8s.io/cleanup", workload: "k8s-operator"},
		{name: "catalog finalizers are never candidates", group: "karpenter.sh", finalizer: "karpenter.sh/termination", workload: "karpenter-operator"},
	} {
		t.Run(tt.name, func(t *testing.T) {
			root.SetAPIVersion(tt.group + "/v1")
			workload := &metav1.ObjectMeta{Name: tt.workload, Labels: tt.labels}
			if got := ControllerCandidate(tt.finalizer, root, workload); got != tt.want {
				t.Fatalf("got %v want %v", got, tt.want)
			}
		})
	}
}

func TestResolveKarpenterProviderOwner(t *testing.T) {
	for _, key := range []string{"karpenter.sh/termination", "karpenter.sh/custom", "karpenter.k8s.aws/termination", "karpenter.azure.com/termination"} {
		t.Run(key, func(t *testing.T) {
			owner := ResolveOwner(key, &unstructured.Unstructured{})
			if owner == nil || owner.Controller != "karpenter" || owner.Namespace != "" || owner.SelectorKey != "app.kubernetes.io/name" || owner.SelectorValue != "karpenter" || owner.OffClusterOfferings == "" || !owner.ReleasesInfrastructure {
				t.Fatalf("got owner %+v", owner)
			}
		})
	}
}

func TestOwnerOffClusterAndInfrastructure(t *testing.T) {
	argo := ResolveOwner("resources-finalizer.argocd.argoproj.io", &unstructured.Unstructured{})
	if argo == nil || argo.OffClusterOfferings != "Amazon EKS Capabilities" || argo.ReleasesInfrastructure {
		t.Fatalf("argo=%+v", argo)
	}
	vm := &unstructured.Unstructured{}
	vm.SetAPIVersion("operator.victoriametrics.com/v1beta1")
	for _, owner := range []*Owner{ResolveOwner("apps.victoriametrics.com/finalizer", vm), ResolveOwner("finalizers.helm.toolkit.fluxcd.io", vm)} {
		if owner == nil || owner.OffClusterOfferings != "" || owner.ReleasesInfrastructure {
			t.Fatalf("in-cluster catalog entry=%+v", owner)
		}
	}
}

// Infrastructure operators stay out of the catalog: absent an identified
// controller, callers must not treat a missing pod as permission to remove
// the finalizer.
func TestInfrastructureOperatorFinalizersAreNotIdentified(t *testing.T) {
	for finalizer, group := range map[string]string{
		"finalizer.managedresource.crossplane.io": "ec2.aws.upbound.io",
		"composite.apiextensions.crossplane.io":   "platform.example.org",
		"cluster.cluster.x-k8s.io":                "cluster.x-k8s.io",
		"cnrm.cloud.google.com/finalizer":         "sql.cnrm.cloud.google.com",
		"finalizers.s3.services.k8s.aws":          "s3.services.k8s.aws",
		"strimzi.io/topic-operator":               "kafka.strimzi.io",
	} {
		root := &unstructured.Unstructured{}
		root.SetAPIVersion(group + "/v1")
		if owner := ResolveOwner(finalizer, root); owner != nil {
			t.Errorf("%s resolved to %+v", finalizer, owner)
		}
		for _, workload := range []string{"crossplane", "capi-controller-manager", "cnrm-controller-manager", "ack-s3-controller", "strimzi-cluster-operator"} {
			if ControllerCandidate(finalizer, root, &metav1.ObjectMeta{Name: workload}) {
				t.Errorf("%s matched workload %s", finalizer, workload)
			}
		}
	}
}

func TestFluxImageOwnerByKind(t *testing.T) {
	for kind, want := range map[string]string{"ImageUpdateAutomation": "image-automation-controller", "ImagePolicy": "image-reflector-controller", "ImageRepository": "image-reflector-controller"} {
		root := &unstructured.Unstructured{}
		root.SetAPIVersion("image.toolkit.fluxcd.io/v1beta2")
		root.SetKind(kind)
		for _, finalizer := range []string{"finalizers.fluxcd.io", "finalizers.image.toolkit.fluxcd.io"} {
			if owner := ResolveOwner(finalizer, root); owner == nil || owner.Controller != want || owner.SelectorValue != want {
				t.Errorf("%s %s: got %+v, want %s", kind, finalizer, owner, want)
			}
		}
	}
}

// TestResolveOwner pins the static finalizer→controller mapping.
// New Flux/Argo versions may introduce new keys; this test catches
// regressions when entries get accidentally renamed.
func TestResolveOwner(t *testing.T) {
	mkObj := func(api string) *unstructured.Unstructured {
		return &unstructured.Unstructured{Object: map[string]any{
			"apiVersion": api,
			"kind":       "Whatever",
		}}
	}
	tests := []struct {
		name      string
		finalizer string
		obj       *unstructured.Unstructured
		want      string // expected Controller name; "" means nil owner
	}{
		{"argo standard", "resources-finalizer.argocd.argoproj.io", mkObj(""), "argocd-application-controller"},
		{"argo legacy cascade", "foreground-cascade.argocd.argoproj.io", mkObj(""), "argocd-application-controller"},
		{"flux generic on Kustomization", "finalizers.fluxcd.io", mkObj("kustomize.toolkit.fluxcd.io/v1"), "kustomize-controller"},
		{"flux generic on HelmRelease", "finalizers.fluxcd.io", mkObj("helm.toolkit.fluxcd.io/v2"), "helm-controller"},
		{"flux generic on GitRepository", "finalizers.fluxcd.io", mkObj("source.toolkit.fluxcd.io/v1"), "source-controller"},
		{"flux specific kustomize", "finalizers.kustomize.toolkit.fluxcd.io", mkObj(""), "kustomize-controller"},
		{"flux specific helm", "finalizers.helm.toolkit.fluxcd.io", mkObj(""), "helm-controller"},
		{"flux specific source", "finalizers.source.toolkit.fluxcd.io", mkObj(""), "source-controller"},
		{"unknown finalizer returns nil", "example.com/cleanup", mkObj(""), ""},
		{"unknown apiVersion for generic flux returns nil", "finalizers.fluxcd.io", mkObj("custom.example.com/v1"), ""},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := ResolveOwner(tt.finalizer, tt.obj)
			if tt.want == "" {
				if got != nil {
					t.Fatalf("expected nil owner, got %+v", got)
				}
				return
			}
			if got == nil {
				t.Fatalf("expected owner with controller %q, got nil", tt.want)
			}
			if got.Controller != tt.want {
				t.Fatalf("Controller = %q, want %q", got.Controller, tt.want)
			}
		})
	}
}

func TestCatalogConditionalCleanupPolicy(t *testing.T) {
	for _, tc := range []struct {
		finalizer, group, kind string
		allow                  bool
	}{
		{"apps.victoriametrics.com/finalizer", "operator.victoriametrics.com", "VMAgent", true},
		{"finalizers.kustomize.toolkit.fluxcd.io", "kustomize.toolkit.fluxcd.io", "Kustomization", true},
		{"finalizers.helm.toolkit.fluxcd.io", "helm.toolkit.fluxcd.io", "HelmRelease", true},
		{"finalizers.source.toolkit.fluxcd.io", "source.toolkit.fluxcd.io", "GitRepository", true},
		{"finalizers.notification.toolkit.fluxcd.io", "notification.toolkit.fluxcd.io", "Alert", true},
		{"finalizers.image.toolkit.fluxcd.io", "image.toolkit.fluxcd.io", "ImageRepository", true},
		{"finalizers.image.toolkit.fluxcd.io", "image.toolkit.fluxcd.io", "ImageUpdateAutomation", true},
		{"resources-finalizer.argocd.argoproj.io", "argoproj.io", "Application", false},
		{"karpenter.sh/termination", "karpenter.sh", "NodeClaim", false},
		{"karpenter.k8s.aws/termination", "karpenter.k8s.aws", "EC2NodeClass", false},
		{"karpenter.azure.com/termination", "karpenter.azure.com", "AKSNodeClass", false},
	} {
		t.Run(tc.finalizer+"/"+tc.kind, func(t *testing.T) {
			root := &unstructured.Unstructured{}
			root.SetAPIVersion(tc.group + "/v1")
			root.SetKind(tc.kind)
			owner := ResolveOwner(tc.finalizer, root)
			if owner == nil || owner.AllowConditionalCleanup != tc.allow {
				t.Fatalf("owner=%+v, want cleanup opt-in %v", owner, tc.allow)
			}
		})
	}
}

func TestFluxHelmControllerRequiresPublishedImage(t *testing.T) {
	root := &unstructured.Unstructured{}
	root.SetAPIVersion("helm.toolkit.fluxcd.io/v2")
	owner := ResolveOwner("finalizers.helm.toolkit.fluxcd.io", root)
	for _, tc := range []struct {
		name, image string
		want        bool
	}{
		{"unrelated k3s controller", "rancher/helm-controller:v0.16.0", false},
		{"unrelated private controller", "private.example/team/helm-controller:v1", false},
		{"Flux controller", "ghcr.io/fluxcd/helm-controller:v1.1.0", true},
		{"Flux controller mirrored", "mirror.example/fluxcd/helm-controller@sha256:abc", true},
		{"Flux controller proxy cache", "harbor.example/ghcr-proxy/fluxcd/helm-controller:v1", true},
		{"Flux controller prefix mirror", "registry.example/oss/fluxcd/helm-controller:v1", true},
		{"vendor segment must match", "registry.example/team/notfluxcd/helm-controller:v1", false},
	} {
		t.Run(tc.name, func(t *testing.T) {
			identity := metav1.ObjectMeta{Name: "helm-controller", Labels: map[string]string{"app": "helm-controller"}}
			template := corev1.PodTemplateSpec{ObjectMeta: identity, Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "manager", Image: tc.image}}}}
			if got := owner.MatchesWorkload(&identity, template); got != tc.want {
				t.Fatalf("workload match = %v, want %v", got, tc.want)
			}
			if got := owner.MatchesPod(identity.Labels, template.Spec); got != tc.want {
				t.Fatalf("pod match = %v, want %v", got, tc.want)
			}
		})
	}
}