| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- /*
- Copyright © 2025 ESO Maintainer Team
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package externalsecret
- import (
- "context"
- "testing"
- "github.com/go-logr/logr"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- v1 "k8s.io/api/core/v1"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/client-go/kubernetes/scheme"
- "k8s.io/utils/ptr"
- ctrl "sigs.k8s.io/controller-runtime"
- fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- "github.com/external-secrets/external-secrets/runtime/esutils"
- )
- func TestIsGenericTarget(t *testing.T) {
- tests := []struct {
- name string
- es *esv1.ExternalSecret
- expected bool
- }{
- {
- name: "nil manifest - Secret target",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: nil,
- },
- },
- },
- expected: false,
- },
- {
- name: "ConfigMap manifest target",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- },
- expected: true,
- },
- {
- name: "Custom Resource manifest target",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "argoproj.io/v1alpha1",
- Kind: "Application",
- },
- },
- },
- },
- expected: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := isGenericTarget(tt.es)
- assert.Equal(t, tt.expected, result)
- })
- }
- }
- func TestValidateGenericTarget(t *testing.T) {
- tests := []struct {
- name string
- es *esv1.ExternalSecret
- allowGenericTargets bool
- expectedError bool
- errorContains string
- }{
- {
- name: "ConfigMap target - flag enabled - valid",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- },
- allowGenericTargets: true,
- expectedError: false,
- },
- {
- name: "ConfigMap target - flag disabled",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- },
- allowGenericTargets: false,
- expectedError: true,
- errorContains: "generic targets are disabled",
- },
- {
- name: "Missing APIVersion",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "",
- Kind: "ConfigMap",
- },
- },
- },
- },
- allowGenericTargets: true,
- expectedError: true,
- errorContains: "apiVersion is required",
- },
- {
- name: "Missing Kind",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "",
- },
- },
- },
- },
- allowGenericTargets: true,
- expectedError: true,
- errorContains: "kind is required",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- r := &Reconciler{
- AllowGenericTargets: tt.allowGenericTargets,
- }
- log := ctrl.Log.WithName("test")
- err := r.validateGenericTarget(log, tt.es)
- if tt.expectedError {
- assert.Error(t, err)
- if tt.errorContains != "" {
- assert.Contains(t, err.Error(), tt.errorContains)
- }
- } else {
- assert.NoError(t, err)
- }
- })
- }
- }
- func TestGetTargetGVK(t *testing.T) {
- tests := []struct {
- name string
- es *esv1.ExternalSecret
- expected schema.GroupVersionKind
- }{
- {
- name: "ConfigMap target",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- },
- expected: schema.GroupVersionKind{
- Group: "",
- Version: "v1",
- Kind: "ConfigMap",
- },
- },
- {
- name: "ArgoCD Application target",
- es: &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Manifest: &esv1.ManifestReference{
- APIVersion: "argoproj.io/v1alpha1",
- Kind: "Application",
- },
- },
- },
- },
- expected: schema.GroupVersionKind{
- Group: "argoproj.io",
- Version: "v1alpha1",
- Kind: "Application",
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := getTargetGVK(tt.es)
- assert.Equal(t, tt.expected, result)
- })
- }
- }
- func TestGetTargetName(t *testing.T) {
- tests := []struct {
- name string
- es *esv1.ExternalSecret
- expected string
- }{
- {
- name: "Use target name when specified",
- es: &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "my-external-secret",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "custom-target-name",
- },
- },
- },
- expected: "custom-target-name",
- },
- {
- name: "Use ExternalSecret name when target name not specified",
- es: &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "my-external-secret",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "",
- },
- },
- },
- expected: "my-external-secret",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := getTargetName(tt.es)
- assert.Equal(t, tt.expected, result)
- })
- }
- }
- func TestCreateSimpleManifest(t *testing.T) {
- tests := []struct {
- name string
- kind string
- dataMap map[string][]byte
- validate func(t *testing.T, obj *unstructured.Unstructured)
- }{
- {
- name: "ConfigMap with data",
- kind: "ConfigMap",
- dataMap: map[string][]byte{
- "key1": []byte("value1"),
- "key2": []byte("value2"),
- },
- validate: func(t *testing.T, obj *unstructured.Unstructured) {
- // Directly access the data field
- data, ok := obj.Object["data"].(map[string]string)
- require.True(t, ok, "data should be map[string]string")
- assert.Equal(t, "value1", data["key1"])
- assert.Equal(t, "value2", data["key2"])
- },
- },
- {
- name: "Custom resource with spec.data",
- kind: "CustomResource",
- dataMap: map[string][]byte{
- "config": []byte("my-config"),
- },
- validate: func(t *testing.T, obj *unstructured.Unstructured) {
- spec, ok := obj.Object["spec"].(map[string]interface{})
- require.True(t, ok, "spec should be map[string]interface{}")
- data, ok := spec["data"].(map[string]string)
- require.True(t, ok, "spec.data should be map[string]string")
- assert.Equal(t, "my-config", data["config"])
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- r := &Reconciler{}
- obj := &unstructured.Unstructured{
- Object: make(map[string]interface{}),
- }
- obj.SetKind(tt.kind)
- result := r.createSimpleManifest(obj, tt.dataMap)
- assert.NotNil(t, result)
- if tt.validate != nil {
- tt.validate(t, result)
- }
- })
- }
- }
- func TestApplyTemplateToManifest_SimpleConfigMap(t *testing.T) {
- // Setup
- _ = esv1.AddToScheme(scheme.Scheme)
- fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme.Scheme).Build()
- r := &Reconciler{
- Client: fakeClient,
- }
- es := &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-es",
- Namespace: "default",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "test-configmap",
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- }
- dataMap := map[string][]byte{
- "key1": []byte("value1"),
- "key2": []byte("value2"),
- }
- // Execute
- result, err := r.applyTemplateToManifest(context.Background(), es, dataMap, nil)
- // Verify
- require.NoError(t, err)
- assert.NotNil(t, result)
- assert.Equal(t, "ConfigMap", result.GetKind())
- assert.Equal(t, "test-configmap", result.GetName())
- assert.Equal(t, "default", result.GetNamespace())
- // Verify data
- data, ok := result.Object["data"].(map[string]string)
- require.True(t, ok, "data should be map[string]string")
- assert.Equal(t, "value1", data["key1"])
- assert.Equal(t, "value2", data["key2"])
- // Verify managed label
- labels := result.GetLabels()
- assert.Equal(t, esv1.LabelManagedValue, labels[esv1.LabelManaged])
- }
- func TestApplyTemplateToManifest_WithMetadata(t *testing.T) {
- // Setup
- _ = esv1.AddToScheme(scheme.Scheme)
- fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme.Scheme).Build()
- r := &Reconciler{
- Client: fakeClient,
- }
- es := &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-es",
- Namespace: "default",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "test-configmap",
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- Template: &esv1.ExternalSecretTemplate{
- EngineVersion: esv1.TemplateEngineV2, // Set engine version
- Metadata: esv1.ExternalSecretTemplateMetadata{
- Labels: map[string]string{
- "app": "myapp",
- "tier": "backend",
- },
- Annotations: map[string]string{
- "description": "This is a test",
- },
- },
- },
- },
- },
- }
- dataMap := map[string][]byte{
- "config": []byte("test-config"),
- }
- // Execute
- result, err := r.applyTemplateToManifest(context.Background(), es, dataMap, nil)
- // Verify
- require.NoError(t, err)
- assert.NotNil(t, result)
- // Verify labels
- labels := result.GetLabels()
- assert.Equal(t, "myapp", labels["app"])
- assert.Equal(t, "backend", labels["tier"])
- assert.Equal(t, esv1.LabelManagedValue, labels[esv1.LabelManaged])
- // Verify annotations
- annotations := result.GetAnnotations()
- assert.Equal(t, "This is a test", annotations["description"])
- }
- func TestGetGenericResource(t *testing.T) {
- // Setup
- _ = esv1.AddToScheme(scheme.Scheme)
- // Create a ConfigMap to find
- existingConfigMap := &unstructured.Unstructured{
- Object: map[string]interface{}{
- "apiVersion": "v1",
- "kind": "ConfigMap",
- "metadata": map[string]interface{}{
- "name": "test-cm",
- "namespace": "default",
- },
- "data": map[string]interface{}{
- "key": "value",
- },
- },
- }
- _ = esv1.AddToScheme(scheme.Scheme)
- fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(existingConfigMap).Build()
- r := &Reconciler{
- Client: fakeClient,
- }
- es := &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-es",
- Namespace: "default",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "test-cm",
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- }
- // Execute
- result, err := r.getGenericResource(context.Background(), logr.Discard(), es)
- // Verify
- require.NoError(t, err)
- assert.NotNil(t, result)
- assert.Equal(t, "ConfigMap", result.GetKind())
- assert.Equal(t, "test-cm", result.GetName())
- // Verify data
- data, found, err := unstructured.NestedStringMap(result.Object, "data")
- require.NoError(t, err)
- require.True(t, found)
- assert.Equal(t, "value", data["key"])
- }
- func TestGetGenericResource_NotFound(t *testing.T) {
- // Setup
- _ = esv1.AddToScheme(scheme.Scheme)
- fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme.Scheme).Build()
- r := &Reconciler{
- Client: fakeClient,
- }
- es := &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-es",
- Namespace: "default",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "nonexistent-cm",
- Manifest: &esv1.ManifestReference{
- APIVersion: "v1",
- Kind: "ConfigMap",
- },
- },
- },
- }
- // Execute
- result, err := r.getGenericResource(context.Background(), logr.Discard(), es)
- // Verify - should return an error and nil result when resource doesn't exist
- assert.Error(t, err)
- assert.True(t, apierrors.IsNotFound(err))
- assert.Nil(t, result)
- }
- func init() {
- // Initialize scheme for tests
- _ = esv1.AddToScheme(scheme.Scheme)
- _ = v1.AddToScheme(scheme.Scheme)
- }
- func TestApplyTemplateToManifest_LiteralWithDeployment(t *testing.T) {
- // Test that literal templates work with complex objects like Deployments
- _ = esv1.AddToScheme(scheme.Scheme)
- fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme.Scheme).Build()
- r := &Reconciler{
- Client: fakeClient,
- }
- es := &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-es",
- Namespace: "default",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "test-deployment",
- Manifest: &esv1.ManifestReference{
- APIVersion: "apps/v1",
- Kind: "Deployment",
- },
- Template: &esv1.ExternalSecretTemplate{
- EngineVersion: esv1.TemplateEngineV2,
- TemplateFrom: []esv1.TemplateFrom{
- {
- Target: "spec",
- Literal: ptr.To(`
- replicas: {{ .replicas }}
- selector:
- matchLabels:
- app: myapp
- template:
- metadata:
- labels:
- app: myapp
- spec:
- containers:
- - name: nginx
- image: nginx:{{ .version }}
- ports:
- - containerPort: 80
- `),
- },
- },
- },
- },
- },
- }
- dataMap := map[string][]byte{
- "replicas": []byte("3"),
- "version": []byte("1.21"),
- }
- result, err := r.applyTemplateToManifest(context.Background(), es, dataMap, nil)
- require.NoError(t, err)
- assert.NotNil(t, result)
- assert.Equal(t, "Deployment", result.GetKind())
- assert.Equal(t, "test-deployment", result.GetName())
- spec, found, err := unstructured.NestedMap(result.Object, "spec")
- require.NoError(t, err)
- require.True(t, found, "spec should exist")
- replicas, found, err := unstructured.NestedInt64(result.Object, "spec", "replicas")
- require.NoError(t, err)
- require.True(t, found, "spec.replicas should exist")
- assert.Equal(t, int64(3), replicas)
- containers, found, err := unstructured.NestedSlice(result.Object, "spec", "template", "spec", "containers")
- require.NoError(t, err)
- require.True(t, found, "containers should exist")
- require.Len(t, containers, 1, "should have 1 container")
- container, ok := containers[0].(map[string]any)
- require.True(t, ok, "container should be a map")
- assert.Equal(t, "nginx:1.21", container["image"])
- t.Logf("Result spec: %+v", spec)
- }
- func TestApplyTemplateToManifest_MergeBehavior(t *testing.T) {
- _ = esv1.AddToScheme(scheme.Scheme)
- fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme.Scheme).Build()
- r := &Reconciler{
- Client: fakeClient,
- }
- es := &esv1.ExternalSecret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-es",
- Namespace: "default",
- },
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- Name: "test-slack-config",
- Manifest: &esv1.ManifestReference{
- APIVersion: "notification.toolkit.fluxcd.io/v1beta1",
- Kind: "Provider",
- },
- Template: &esv1.ExternalSecretTemplate{
- EngineVersion: esv1.TemplateEngineV2,
- TemplateFrom: []esv1.TemplateFrom{
- {
- Target: "spec.slack",
- Literal: ptr.To(`api_url: {{ .url }}`),
- },
- },
- },
- },
- },
- }
- existingResource := &unstructured.Unstructured{
- Object: map[string]interface{}{
- "apiVersion": "notification.toolkit.fluxcd.io/v1beta1",
- "kind": "Provider",
- "metadata": map[string]interface{}{
- "name": "test-slack-config",
- "namespace": "default",
- "resourceVersion": "12345",
- "uid": "test-uid-123",
- },
- "spec": map[string]interface{}{
- "type": "slack",
- "slack": map[string]interface{}{
- "channel": "general",
- "username": "bot",
- },
- },
- },
- }
- dataMap := map[string][]byte{
- "url": []byte("https://hooks.slack.com/services/XXX"),
- }
- result, err := r.applyTemplateToManifest(context.Background(), es, dataMap, existingResource)
- require.NoError(t, err)
- assert.NotNil(t, result)
- assert.Equal(t, "Provider", result.GetKind())
- assert.Equal(t, "test-slack-config", result.GetName())
- specType, found, err := unstructured.NestedString(result.Object, "spec", "type")
- require.NoError(t, err)
- require.True(t, found, "spec.type should be preserved")
- assert.Equal(t, "slack", specType, "spec.type should be preserved from existing resource")
- slackChannel, found, err := unstructured.NestedString(result.Object, "spec", "slack", "channel")
- require.NoError(t, err)
- require.True(t, found, "spec.slack.channel should be preserved")
- assert.Equal(t, "general", slackChannel, "spec.slack.channel should be preserved from existing resource")
- slackUsername, found, err := unstructured.NestedString(result.Object, "spec", "slack", "username")
- require.NoError(t, err)
- require.True(t, found, "spec.slack.username should be preserved")
- assert.Equal(t, "bot", slackUsername, "spec.slack.username should be preserved from existing resource")
- apiURL, found, err := unstructured.NestedString(result.Object, "spec", "slack", "api_url")
- require.NoError(t, err)
- require.True(t, found, "spec.slack.api_url should be added from template")
- assert.Equal(t, "https://hooks.slack.com/services/XXX", apiURL, "spec.slack.api_url should come from template")
- assert.Equal(t, "12345", result.GetResourceVersion(), "resourceVersion should be preserved")
- assert.Equal(t, "test-uid-123", string(result.GetUID()), "uid should be preserved")
- t.Logf("Result spec: %+v", result.Object["spec"])
- }
- func TestGenericTargetContentHash(t *testing.T) {
- tests := []struct {
- name string
- obj *unstructured.Unstructured
- wantErr bool
- }{
- {
- name: "hashes spec field",
- obj: &unstructured.Unstructured{
- Object: map[string]interface{}{
- "spec": map[string]interface{}{"key": "val"},
- },
- },
- },
- {
- name: "hashes data field when no spec",
- obj: &unstructured.Unstructured{
- Object: map[string]interface{}{
- "data": map[string]interface{}{"key": "val"},
- },
- },
- },
- {
- name: "prefers spec over data",
- obj: &unstructured.Unstructured{
- Object: map[string]interface{}{
- "spec": map[string]interface{}{"a": "1"},
- "data": map[string]interface{}{"b": "2"},
- },
- },
- },
- {
- name: "errors when neither spec nor data",
- obj: &unstructured.Unstructured{
- Object: map[string]interface{}{
- "status": map[string]interface{}{"ready": true},
- },
- },
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- hash, err := genericTargetContentHash(tt.obj)
- if tt.wantErr {
- assert.Error(t, err)
- assert.Empty(t, hash)
- return
- }
- require.NoError(t, err)
- assert.NotEmpty(t, hash)
- })
- }
- t.Run("spec preferred over data produces spec hash", func(t *testing.T) {
- specData := map[string]interface{}{"a": "1"}
- obj := &unstructured.Unstructured{
- Object: map[string]interface{}{
- "spec": specData,
- "data": map[string]interface{}{"b": "2"},
- },
- }
- hash, err := genericTargetContentHash(obj)
- require.NoError(t, err)
- assert.Equal(t, esutils.ObjectHash(specData), hash)
- })
- }
- func TestIsGenericTargetValid(t *testing.T) {
- makeES := func(policy esv1.ExternalSecretCreationPolicy) *esv1.ExternalSecret {
- return &esv1.ExternalSecret{
- Spec: esv1.ExternalSecretSpec{
- Target: esv1.ExternalSecretTarget{
- CreationPolicy: policy,
- },
- },
- }
- }
- makeTarget := func(uid string, labels map[string]string, annotations map[string]string, obj map[string]interface{}) *unstructured.Unstructured {
- u := &unstructured.Unstructured{Object: obj}
- if uid != "" {
- u.SetUID(types.UID(uid))
- }
- u.SetLabels(labels)
- u.SetAnnotations(annotations)
- return u
- }
- t.Run("orphan policy always valid", func(t *testing.T) {
- valid, err := isGenericTargetValid(nil, makeES(esv1.CreatePolicyOrphan))
- require.NoError(t, err)
- assert.True(t, valid)
- })
- t.Run("nil target is invalid", func(t *testing.T) {
- valid, err := isGenericTargetValid(nil, makeES(esv1.CreatePolicyOwner))
- require.NoError(t, err)
- assert.False(t, valid)
- })
- t.Run("empty UID is invalid", func(t *testing.T) {
- obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
- valid, err := isGenericTargetValid(obj, makeES(esv1.CreatePolicyOwner))
- require.NoError(t, err)
- assert.False(t, valid)
- })
- t.Run("not managed is invalid", func(t *testing.T) {
- obj := makeTarget("some-uid", map[string]string{}, nil, map[string]interface{}{
- "spec": map[string]interface{}{"key": "val"},
- })
- valid, err := isGenericTargetValid(obj, makeES(esv1.CreatePolicyOwner))
- require.NoError(t, err)
- assert.False(t, valid)
- })
- t.Run("hash mismatch is invalid", func(t *testing.T) {
- obj := makeTarget(
- "some-uid",
- map[string]string{esv1.LabelManaged: esv1.LabelManagedValue},
- map[string]string{esv1.AnnotationDataHash: "wrong-hash"},
- map[string]interface{}{"spec": map[string]interface{}{"key": "val"}},
- )
- valid, err := isGenericTargetValid(obj, makeES(esv1.CreatePolicyOwner))
- require.NoError(t, err)
- assert.False(t, valid)
- })
- t.Run("matching hash is valid", func(t *testing.T) {
- specData := map[string]interface{}{"key": "val"}
- hash := esutils.ObjectHash(specData)
- obj := makeTarget(
- "some-uid",
- map[string]string{esv1.LabelManaged: esv1.LabelManagedValue},
- map[string]string{esv1.AnnotationDataHash: hash},
- map[string]interface{}{"spec": specData},
- )
- valid, err := isGenericTargetValid(obj, makeES(esv1.CreatePolicyOwner))
- require.NoError(t, err)
- assert.True(t, valid)
- })
- t.Run("errors when target has no spec or data", func(t *testing.T) {
- obj := makeTarget(
- "some-uid",
- map[string]string{esv1.LabelManaged: esv1.LabelManagedValue},
- nil,
- map[string]interface{}{"status": map[string]interface{}{}},
- )
- _, err := isGenericTargetValid(obj, makeES(esv1.CreatePolicyOwner))
- assert.Error(t, err)
- })
- }
|