Browse Source

Feature: Add secret metadata templating from secret values (#1740)

* handle template data for secret labels & annotations

Signed-off-by: Steven Bressey <steven.bressey@artifakt.io>
Steven Bressey 3 years ago
parent
commit
b5be79de98

+ 20 - 1
pkg/controllers/externalsecret/externalsecret_controller_template.go

@@ -55,11 +55,30 @@ func (r *Reconciler) applyTemplate(ctx context.Context, es *esv1beta1.ExternalSe
 	}
 	r.Log.V(1).Info("found template data", "tpl_data", tplMap)
 
+	tplMapLabels := make(map[string][]byte)
+	tplMapAnnotations := make(map[string][]byte)
+
+	// get template data for labels
+	if es.Spec.Target.Template.Metadata.Labels != nil {
+		for k, v := range es.Spec.Target.Template.Metadata.Labels {
+			tplMapLabels[k] = []byte(v)
+		}
+		r.Log.V(1).Info("found template metadata (labels)", "tpl_labels", tplMapLabels)
+	}
+
+	// get template data for annotations
+	if es.Spec.Target.Template.Metadata.Annotations != nil {
+		for k, v := range es.Spec.Target.Template.Metadata.Annotations {
+			tplMapAnnotations[k] = []byte(v)
+		}
+		r.Log.V(1).Info("found template metadata (annotations)", "tpl_annotations", tplMapAnnotations)
+	}
+
 	execute, err := template.EngineForVersion(es.Spec.Target.Template.EngineVersion)
 	if err != nil {
 		return err
 	}
-	err = execute(tplMap, dataMap, secret)
+	err = execute(tplMap, tplMapLabels, tplMapAnnotations, dataMap, secret)
 	if err != nil {
 		return fmt.Errorf(errExecTpl, err)
 	}

+ 1 - 1
pkg/template/engine.go

@@ -20,7 +20,7 @@ import (
 	v2 "github.com/external-secrets/external-secrets/pkg/template/v2"
 )
 
-type ExecFunc func(tpl, data map[string][]byte, secret *corev1.Secret) error
+type ExecFunc func(tpl, labelsTpl, annotationsTpl, data map[string][]byte, secret *corev1.Secret) error
 
 func EngineForVersion(version esapi.TemplateEngineVersion) (ExecFunc, error) {
 	switch version {

+ 1 - 1
pkg/template/v1/template.go

@@ -70,7 +70,7 @@ const (
 )
 
 // Execute renders the secret data as template. If an error occurs processing is stopped immediately.
-func Execute(tpl, data map[string][]byte, secret *corev1.Secret) error {
+func Execute(tpl, labelsTpl, annotationsTpl, data map[string][]byte, secret *corev1.Secret) error {
 	if tpl == nil {
 		return nil
 	}

+ 4 - 2
pkg/template/v1/template_test.go

@@ -19,6 +19,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 const (
@@ -345,9 +346,10 @@ func TestExecute(t *testing.T) {
 		row := tbl[i]
 		t.Run(row.name, func(t *testing.T) {
 			sec := &corev1.Secret{
-				Data: make(map[string][]byte),
+				Data:       make(map[string][]byte),
+				ObjectMeta: v1.ObjectMeta{Labels: make(map[string]string), Annotations: make(map[string]string)},
 			}
-			err := Execute(row.tpl, row.data, sec)
+			err := Execute(row.tpl, nil, nil, row.data, sec)
 			if !ErrorContains(err, row.expErr) {
 				t.Errorf("unexpected error: %s, expected: %s", err, row.expErr)
 			}

+ 18 - 2
pkg/template/v2/template.go

@@ -63,8 +63,8 @@ func init() {
 }
 
 // Execute renders the secret data as template. If an error occurs processing is stopped immediately.
-func Execute(tpl, data map[string][]byte, secret *corev1.Secret) error {
-	if tpl == nil {
+func Execute(tpl, labelsTpl, annotationsTpl, data map[string][]byte, secret *corev1.Secret) error {
+	if tpl == nil && labelsTpl == nil && annotationsTpl == nil {
 		return nil
 	}
 	for k, v := range tpl {
@@ -74,6 +74,22 @@ func Execute(tpl, data map[string][]byte, secret *corev1.Secret) error {
 		}
 		secret.Data[k] = val
 	}
+
+	for k, v := range labelsTpl {
+		val, err := execute(k, string(v), data)
+		if err != nil {
+			return fmt.Errorf(errExecute, k, err)
+		}
+		secret.ObjectMeta.Labels[k] = string(val)
+	}
+
+	for k, v := range annotationsTpl {
+		val, err := execute(k, string(v), data)
+		if err != nil {
+			return fmt.Errorf(errExecute, k, err)
+		}
+		secret.ObjectMeta.Annotations[k] = string(val)
+	}
 	return nil
 }
 

+ 64 - 10
pkg/template/v2/template_test.go

@@ -21,6 +21,7 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 const (
@@ -135,16 +136,22 @@ KfMtQkBmCFTNk3fOtz3sgTiv0OHbokplsICEc4tUT5RWU0frwAjJT4Pk
 
 func TestExecute(t *testing.T) {
 	tbl := []struct {
-		name        string
-		tpl         map[string][]byte
-		data        map[string][]byte
-		expetedData map[string][]byte
-		expErr      string
+		name                string
+		tpl                 map[string][]byte
+		labelsTpl           map[string][]byte
+		annotationsTpl      map[string][]byte
+		data                map[string][]byte
+		expetedData         map[string][]byte
+		expectedLabels      map[string]string
+		expectedAnnotations map[string]string
+		expErr              string
 	}{
 		{
-			name: "test empty",
-			tpl:  nil,
-			data: nil,
+			name:           "test empty",
+			tpl:            nil,
+			labelsTpl:      nil,
+			annotationsTpl: nil,
+			data:           nil,
 		},
 		{
 			name: "b64dec func",
@@ -401,15 +408,54 @@ func TestExecute(t *testing.T) {
 				"fn": []byte(pkcs12Cert),
 			},
 		},
+		{
+			name: "labels",
+			tpl: map[string][]byte{
+				"foo": []byte("{{ .secret | b64dec }}"),
+			},
+			labelsTpl: map[string][]byte{
+				"bar": []byte("{{ .env | b64dec }}"),
+			},
+			data: map[string][]byte{
+				"secret": []byte("MTIzNA=="),
+				"env":    []byte("ZGV2"),
+			},
+			expetedData: map[string][]byte{
+				"foo": []byte("1234"),
+			},
+			expectedLabels: map[string]string{
+				"bar": "dev",
+			},
+		},
+		{
+			name: "annotations",
+			tpl: map[string][]byte{
+				"foo": []byte("{{ .secret | b64dec }}"),
+			},
+			annotationsTpl: map[string][]byte{
+				"bar": []byte("{{ .env | b64dec }}"),
+			},
+			data: map[string][]byte{
+				"secret": []byte("MTIzNA=="),
+				"env":    []byte("ZGV2"),
+			},
+			expetedData: map[string][]byte{
+				"foo": []byte("1234"),
+			},
+			expectedAnnotations: map[string]string{
+				"bar": "dev",
+			},
+		},
 	}
 
 	for i := range tbl {
 		row := tbl[i]
 		t.Run(row.name, func(t *testing.T) {
 			sec := &corev1.Secret{
-				Data: make(map[string][]byte),
+				Data:       make(map[string][]byte),
+				ObjectMeta: v1.ObjectMeta{Labels: make(map[string]string), Annotations: make(map[string]string)},
 			}
-			err := Execute(row.tpl, row.data, sec)
+			err := Execute(row.tpl, row.labelsTpl, row.annotationsTpl, row.data, sec)
 			if !ErrorContains(err, row.expErr) {
 				t.Errorf("unexpected error: %s, expected: %s", err, row.expErr)
 			}
@@ -417,6 +463,14 @@ func TestExecute(t *testing.T) {
 				return
 			}
 			assert.EqualValues(t, row.expetedData, sec.Data)
+			if row.expectedLabels == nil {
+				return
+			}
+			assert.EqualValues(t, row.expectedLabels, sec.ObjectMeta.Labels)
+			if row.expectedAnnotations == nil {
+				return
+			}
+			assert.EqualValues(t, row.expectedAnnotations, sec.ObjectMeta.Annotations)
 		})
 	}
 }