From 845024ea3ad8be1719c9eb41247a1e3d9c95fac4 Mon Sep 17 00:00:00 2001 From: Michael Gagliardo Date: Mon, 14 Nov 2022 10:40:05 -0500 Subject: [PATCH] feat: allow custom annotations Signed-off-by: Michael Gagliardo --- pkg/config/config.go | 20 ++++- pkg/config/config_test.go | 107 +++++++++++++++++++++++++ pkg/config/keys.go | 20 ++--- pkg/config/keys_test.go | 6 +- pkg/controller/validate_config_test.go | 14 ++-- 5 files changed, 145 insertions(+), 22 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index dcc92480..10df0f17 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -57,7 +57,8 @@ var ( datastoreEngineKey = newStringKey("datastoreEngine") replicasKey = newIntOrStringKey("replicas", 2) replicasKeyForMemory = newIntOrStringKey("replicas", 1) - extraPodLabelsKey = labelSetKey("extraPodLabels") + extraPodLabelsKey = metadataSetKey("extraPodLabels") + extraPodAnnotationsKey = metadataSetKey("extraPodAnnotations") grpcTLSKeyPathKey = newKey("grpcTLSKeyPath", DefaultTLSKeyFile) grpcTLSCertPathKey = newKey("grpcTLSCertPath", DefaultTLSCrtFile) dispatchClusterTLSKeyPathKey = newKey("dispatchClusterTLSKeyPath", DefaultTLSKeyFile) @@ -129,6 +130,7 @@ type SpiceConfig struct { TelemetryTLSCASecretName string SecretName string ExtraPodLabels map[string]string + ExtraPodAnnotations map[string]string Passthrough map[string]string } @@ -152,6 +154,7 @@ func NewConfig(nn types.NamespacedName, uid types.UID, currentState *SpiceDBMigr EnvPrefix: envPrefixKey.pop(config), SpiceDBCmd: spiceDBCmdKey.pop(config), ExtraPodLabels: make(map[string]string, 0), + ExtraPodAnnotations: make(map[string]string, 0), LogLevel: logLevelKey.pop(config), } migrationConfig := MigrationConfig{ @@ -233,15 +236,25 @@ func NewConfig(nn types.NamespacedName, uid types.UID, currentState *SpiceDBMigr } var labelWarnings []error - spiceConfig.ExtraPodLabels, labelWarnings, err = extraPodLabelsKey.pop(config) + spiceConfig.ExtraPodLabels, labelWarnings, err = extraPodLabelsKey.pop(config, "label") if err != nil { errs = append(errs, err) } - if len(warnings) > 0 { + if len(labelWarnings) > 0 { warnings = append(warnings, labelWarnings...) } + var annotationWarnings []error + spiceConfig.ExtraPodAnnotations, annotationWarnings, err = extraPodAnnotationsKey.pop(config, "annotation") + if err != nil { + errs = append(errs, err) + } + + if len(annotationWarnings) > 0 { + warnings = append(warnings, annotationWarnings...) + } + // generate secret refs for tls if specified if len(spiceConfig.TLSSecretName) > 0 { passthroughKeys := []*key[string]{ @@ -648,6 +661,7 @@ func (c *Config) Deployment(migrationHash, secretHash string) *applyappsv1.Deplo WithLabels(map[string]string{"app.kubernetes.io/instance": name}). WithLabels(metadata.LabelsForComponent(c.Name, metadata.ComponentSpiceDBLabelValue)). WithLabels(c.ExtraPodLabels). + WithAnnotations(c.ExtraPodAnnotations). WithSpec(applycorev1.PodSpec().WithServiceAccountName(c.Name).WithContainers( applycorev1.Container().WithName(name).WithImage(c.TargetSpiceDBImage). WithCommand(c.SpiceConfig.SpiceDBCmd, "serve"). diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index cb17fe50..727f3ba8 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -769,6 +769,113 @@ func TestNewConfig(t *testing.T) { }, }, }, + { + name: "set extra annotations as string", + args: args{ + nn: types.NamespacedName{Namespace: "test", Name: "test"}, + uid: types.UID("1"), + globalConfig: OperatorConfig{ + ImageName: "image", + AllowedImages: []string{"image"}, + }, + rawConfig: json.RawMessage(` + { + "datastoreEngine": "cockroachdb", + "extraPodAnnotations": "app.kubernetes.io/name=test,app.kubernetes.io/managed-by=test-owner" + } + `), + secret: &corev1.Secret{Data: map[string][]byte{ + "datastore_uri": []byte("uri"), + "preshared_key": []byte("psk"), + }}, + }, + wantWarnings: []error{fmt.Errorf("no TLS configured, consider setting \"tlsSecretName\"")}, + want: &Config{ + MigrationConfig: MigrationConfig{ + MigrationLogLevel: "debug", + DatastoreEngine: "cockroachdb", + DatastoreURI: "uri", + TargetSpiceDBImage: "image", + EnvPrefix: "SPICEDB", + SpiceDBCmd: "spicedb", + TargetMigration: "head", + }, + SpiceConfig: SpiceConfig{ + LogLevel: "info", + SkipMigrations: false, + Name: "test", + Namespace: "test", + UID: "1", + Replicas: 2, + PresharedKey: "psk", + EnvPrefix: "SPICEDB", + SpiceDBCmd: "spicedb", + ExtraPodAnnotations: map[string]string{ + "app.kubernetes.io/name": "test", + "app.kubernetes.io/managed-by": "test-owner", + }, + Passthrough: map[string]string{ + "datastoreEngine": "cockroachdb", + "dispatchClusterEnabled": "true", + }, + }, + }, + }, + { + name: "set extra annotations as map", + args: args{ + nn: types.NamespacedName{Namespace: "test", Name: "test"}, + uid: types.UID("1"), + globalConfig: OperatorConfig{ + ImageName: "image", + AllowedImages: []string{"image"}, + }, + rawConfig: json.RawMessage(` + { + "datastoreEngine": "cockroachdb", + "extraPodAnnotations": { + "app.kubernetes.io/name": "test", + "app.kubernetes.io/managed-by": "test-owner" + } + } + `), + secret: &corev1.Secret{Data: map[string][]byte{ + "datastore_uri": []byte("uri"), + "preshared_key": []byte("psk"), + }}, + }, + wantWarnings: []error{fmt.Errorf("no TLS configured, consider setting \"tlsSecretName\"")}, + want: &Config{ + MigrationConfig: MigrationConfig{ + MigrationLogLevel: "debug", + DatastoreEngine: "cockroachdb", + DatastoreURI: "uri", + TargetSpiceDBImage: "image", + EnvPrefix: "SPICEDB", + SpiceDBCmd: "spicedb", + TargetMigration: "head", + }, + SpiceConfig: SpiceConfig{ + LogLevel: "info", + SkipMigrations: false, + Name: "test", + Namespace: "test", + UID: "1", + Replicas: 2, + PresharedKey: "psk", + EnvPrefix: "SPICEDB", + SpiceDBCmd: "spicedb", + ExtraPodAnnotations: map[string]string{ + "app.kubernetes.io/name": "test", + "app.kubernetes.io/managed-by": "test-owner", + }, + Passthrough: map[string]string{ + "datastoreEngine": "cockroachdb", + "dispatchClusterEnabled": "true", + }, + }, + }, + }, { name: "skip migrations bool", args: args{ diff --git a/pkg/config/keys.go b/pkg/config/keys.go index 89af19ed..7ceab283 100644 --- a/pkg/config/keys.go +++ b/pkg/config/keys.go @@ -102,38 +102,38 @@ func (k *boolOrStringKey) pop(config RawConfig) (out bool, err error) { return } -type labelSetKey string +type metadataSetKey string -func (k labelSetKey) pop(config RawConfig) (podLabels map[string]string, warnings []error, err error) { +func (k metadataSetKey) pop(config RawConfig, metadataType string) (podMetadata map[string]string, warnings []error, err error) { v, ok := config[string(k)] delete(config, string(k)) if !ok { return } - podLabels = make(map[string]string) + podMetadata = make(map[string]string) switch value := v.(type) { case string: if len(value) > 0 { - extraPodLabelPairs := strings.Split(value, ",") - for _, p := range extraPodLabelPairs { + extraPodMetadataPairs := strings.Split(value, ",") + for _, p := range extraPodMetadataPairs { k, v, ok := strings.Cut(p, "=") if !ok { - warnings = append(warnings, fmt.Errorf("couldn't parse extra pod label %q: labels should be of the form k=v,k2=v2", p)) + warnings = append(warnings, fmt.Errorf("couldn't parse extra pod %s %q: values should be of the form k=v,k2=v2", metadataType, p)) continue } - podLabels[k] = v + podMetadata[k] = v } } case map[string]any: for k, v := range value { - labelValue, ok := v.(string) + metadataValue, ok := v.(string) if !ok { - warnings = append(warnings, fmt.Errorf("couldn't parse extra pod label %v", v)) + warnings = append(warnings, fmt.Errorf("couldn't parse extra pod %s %v", metadataType, v)) continue } - podLabels[k] = labelValue + podMetadata[k] = metadataValue } default: err = fmt.Errorf("expected string or map for key %s", k) diff --git a/pkg/config/keys_test.go b/pkg/config/keys_test.go index f25be0f2..12648a4d 100644 --- a/pkg/config/keys_test.go +++ b/pkg/config/keys_test.go @@ -103,7 +103,7 @@ func TestIntOrStringKey(t *testing.T) { } } -func TestLabelSetKey(t *testing.T) { +func TestMetadataSetKey(t *testing.T) { input := map[string]any{"k": "v", "k2": "v2"} invalidInput := map[string]any{"k": 1, "k2": "v2"} empty := map[string]string{} @@ -125,12 +125,12 @@ func TestLabelSetKey(t *testing.T) { {"recovers and warns on invalid map value", invalidInput, empty, map[string]string{"k2": "v2"}, true, false}, } { t.Run(val.description, func(t *testing.T) { - k := labelSetKey("test") + k := metadataSetKey("test") config := emptyConfig if val.value != nil { config = RawConfig{"test": val.value} } - result, warns, err := k.pop(config) + result, warns, err := k.pop(config, "metadata") if val.value != nil { require.Empty(t, config) } diff --git a/pkg/controller/validate_config_test.go b/pkg/controller/validate_config_test.go index 69d163c6..d7aa158c 100644 --- a/pkg/controller/validate_config_test.go +++ b/pkg/controller/validate_config_test.go @@ -77,11 +77,12 @@ func TestValidateConfigHandler(t *testing.T) { expectNext: nextKey, }, { - name: "valid config with warnings", + name: "valid config with label warnings", currentStatus: &v1alpha1.SpiceDBCluster{Status: v1alpha1.ClusterStatus{Image: "image"}}, rawConfig: json.RawMessage(`{ "datastoreEngine": "cockroachdb", - "extraPodLabels": "wrong:format" + "extraPodLabels": "wrong:format", + "extraPodAnnotations": "annotation:bad" }`), existingSecret: &corev1.Secret{ Data: map[string][]byte{ @@ -159,12 +160,13 @@ func TestValidateConfigHandler(t *testing.T) { currentStatus: &v1alpha1.SpiceDBCluster{Status: v1alpha1.ClusterStatus{Image: "image", Conditions: []metav1.Condition{{ Type: "ConfigurationWarning", Status: metav1.ConditionTrue, - Message: "[couldn't parse extra pod label \"wrong:format\": labels should be of the form k=v,k2=v2, no TLS configured, consider setting \"tlsSecretName\"]", + Message: "[couldn't parse extra pod label \"wrong:format\": values should be of the form k=v,k2=v2, couldn't parse extra pod annotation \"annotation:bad\": values should be of the form k=v,k2=v2, no TLS configured, consider setting \"tlsSecretName\"]", }}}}, rawConfig: json.RawMessage(`{ - "datastoreEngine": "cockroachdb", - "tlsSecretName": "secret", - "extraPodLabels": "correct=format,good=value" + "datastoreEngine": "cockroachdb", + "tlsSecretName": "secret", + "extraPodLabels": "correct=format,good=value", + "extraPodAnnotations": "annotation=works" }`), existingSecret: &corev1.Secret{ Data: map[string][]byte{