diff --git a/api/formance.com/v1beta1/database_types.go b/api/formance.com/v1beta1/database_types.go index 3f92098b7..4e7d67ab6 100644 --- a/api/formance.com/v1beta1/database_types.go +++ b/api/formance.com/v1beta1/database_types.go @@ -47,6 +47,7 @@ type DatabaseStatus struct { // // It uses the settings `postgres..uri` which must have the following uri format: `postgresql://[@]@/` // Additionally, the uri can define a query param `secret` indicating a k8s secret, than must be used to retrieve database credentials. +// Credentials in the secret are expected to be URL-encoded by default. Set `secretCredentialsEncoding=raw` to let the operator encode them. // // On creation, the reconciler behind the Database object will create the database on the postgresql server using a k8s job. // On Deletion, by default, the reconciler will let the database untouched. diff --git a/config/crd/bases/formance.com_databases.yaml b/config/crd/bases/formance.com_databases.yaml index 396976e01..b88fda06c 100644 --- a/config/crd/bases/formance.com_databases.yaml +++ b/config/crd/bases/formance.com_databases.yaml @@ -39,6 +39,7 @@ spec: It uses the settings `postgres..uri` which must have the following uri format: `postgresql://[@]@/` Additionally, the uri can define a query param `secret` indicating a k8s secret, than must be used to retrieve database credentials. + Credentials in the secret are expected to be URL-encoded by default. Set `secretCredentialsEncoding=raw` to let the operator encode them. On creation, the reconciler behind the Database object will create the database on the postgresql server using a k8s job. On Deletion, by default, the reconciler will let the database untouched. diff --git a/docs/07-Upgrade/01-Upgrade from the operator.md b/docs/07-Upgrade/01-Upgrade from the operator.md index d11527390..50e0f2723 100644 --- a/docs/07-Upgrade/01-Upgrade from the operator.md +++ b/docs/07-Upgrade/01-Upgrade from the operator.md @@ -13,6 +13,20 @@ Once the upgrade is complete, you can verify the operator is running the latest kubectl -n formance-system describe deployments operator ``` +## PostgreSQL secret credentials in Operator v3.12.0 + +Operator `v3.12.0`, shipped with the Formance Helm chart `formance-1.15.0`, introduced URL encoding for PostgreSQL credentials read from Kubernetes Secrets. Earlier operator versions injected these values directly into the connection URI, so deployments with special characters commonly stored credentials in URL-encoded form already. + +To preserve compatibility with these deployments, credentials referenced by the `secret` PostgreSQL URI parameter are treated as URL-encoded by default. No configuration change is required when the Secret already contains encoded values such as `p%5Ess%20word`. + +When the Secret contains raw credentials, opt in to operator-managed encoding with `secretCredentialsEncoding=raw`: + +```text +postgresql://postgres:5432?secret=postgres&secretCredentialsEncoding=raw +``` + +The operator consumes both `secret` and `secretCredentialsEncoding`; neither parameter is included in the final PostgreSQL connection URI. + ## Components upgrade The upgrade process is managed by the operator, who will upgrade the components one by one, as specified in the `versions` CRD. Any migration that needs to be carried out will also be managed by the operator. @@ -58,4 +72,4 @@ helm upgrade regions oci://ghcr.io/formancehq/helm/regions \ As soon as Operator v2 starts, it will begin migrating v1 objects to v2 objects, then create the new associated resources. All steps have been designed to be carried out without any service interruption. However, we recommend performing this update in Staging before doing it in Production. -If you encounter any issues, do not hesitate to contact us. We will be happy to help you with the migration process. \ No newline at end of file +If you encounter any issues, do not hesitate to contact us. We will be happy to help you with the migration process. diff --git a/docs/09-Configuration reference/01-Settings.md b/docs/09-Configuration reference/01-Settings.md index c744bdea1..8a3ec2044 100644 --- a/docs/09-Configuration reference/01-Settings.md +++ b/docs/09-Configuration reference/01-Settings.md @@ -92,10 +92,11 @@ Scheme: postgresql Query params : -| Name | Type | Default | Description | -| -------------- | ------ | ------- | ---------------------------------------------- | -| secret | string | | Specify a secret where credentials are defined | -| disableSSLMode | bool | false | Disable SSL on Postgres connection | +| Name | Type | Default | Description | +| ------------------------- | ------ | ------------ | ------------------------------------------------------------------------------ | +| secret | string | | Specify a secret where credentials are defined | +| secretCredentialsEncoding | string | `urlEncoded` | Use `raw` to let the operator URL-encode the credentials read from the secret | +| disableSSLMode | bool | false | Disable SSL on Postgres connection | In addition to the parameters above, any extra query parameters included in the URI are passed through to the final `POSTGRES_URI` environment variable. This is useful for managed PostgreSQL providers (e.g. Google Cloud SQL, Azure Database) that require additional connection parameters. @@ -105,7 +106,9 @@ For example: postgresql://user:pass@host:5432?sslmode=require&tcpKeepAlive=true ``` -The `secret` and `disableSSLMode` parameters are consumed by the operator and will not appear in the resulting connection string. When `disableSSLMode=true` is set, it overrides any `sslmode` parameter with `sslmode=disable`. +The `secret`, `secretCredentialsEncoding`, and `disableSSLMode` parameters are consumed by the operator and will not appear in the resulting connection string. When `disableSSLMode=true` is set, it overrides any `sslmode` parameter with `sslmode=disable`. + +Credentials stored in the referenced secret are assumed to be URL-encoded by default for compatibility with operator versions before `v3.12.0`. Set `secretCredentialsEncoding=raw` when the `username` and `password` keys contain raw values that the operator must encode before building the PostgreSQL URI. ### ElasticSearch URI format diff --git a/docs/09-Configuration reference/02-Custom Resource Definitions.md b/docs/09-Configuration reference/02-Custom Resource Definitions.md index ce7e0cb75..014329e1a 100644 --- a/docs/09-Configuration reference/02-Custom Resource Definitions.md +++ b/docs/09-Configuration reference/02-Custom Resource Definitions.md @@ -2141,6 +2141,7 @@ Database represent a concrete database on a PostgreSQL server, it is created by It uses the settings `postgres..uri` which must have the following uri format: `postgresql://[@]@/` Additionally, the uri can define a query param `secret` indicating a k8s secret, than must be used to retrieve database credentials. +Credentials in the secret are expected to be URL-encoded by default. Set `secretCredentialsEncoding=raw` to let the operator encode them. On creation, the reconciler behind the Database object will create the database on the postgresql server using a k8s job. On Deletion, by default, the reconciler will let the database untouched. diff --git a/docs/09-Configuration reference/settings.catalog.json b/docs/09-Configuration reference/settings.catalog.json index 91fc9e96b..5d4cd3895 100644 --- a/docs/09-Configuration reference/settings.catalog.json +++ b/docs/09-Configuration reference/settings.catalog.json @@ -43,7 +43,7 @@ "key": "clear-database", "valueType": "bool", "sources": [ - "internal/resources/databases/init.go:139" + "internal/resources/databases/init.go:143" ] }, { @@ -519,7 +519,7 @@ } ], "sources": [ - "internal/resources/databases/env.go:80" + "internal/resources/databases/env.go:85" ] }, { diff --git a/helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_databases.formance.com.yaml b/helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_databases.formance.com.yaml index 9aff0aa1b..a44fda4e5 100644 --- a/helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_databases.formance.com.yaml +++ b/helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_databases.formance.com.yaml @@ -42,6 +42,7 @@ spec: It uses the settings `postgres..uri` which must have the following uri format: `postgresql://[@]@/` Additionally, the uri can define a query param `secret` indicating a k8s secret, than must be used to retrieve database credentials. + Credentials in the secret are expected to be URL-encoded by default. Set `secretCredentialsEncoding=raw` to let the operator encode them. On creation, the reconciler behind the Database object will create the database on the postgresql server using a k8s job. On Deletion, by default, the reconciler will let the database untouched. diff --git a/internal/resources/databases/env.go b/internal/resources/databases/env.go index bfd0381d7..fa9ae70b1 100644 --- a/internal/resources/databases/env.go +++ b/internal/resources/databases/env.go @@ -31,6 +31,10 @@ func GetPostgresEnvVars(ctx core.Context, stack *v1beta1.Stack, database *v1beta ) } else { secret := database.Status.URI.Query().Get("secret") + credentialsEncoding, err := parsePostgresCredentialsEncoding(database.Status.URI.Query().Get(postgresCredentialsEncodingQueryParam)) + if err != nil { + return nil, err + } postgresURIUsernameEnv = "POSTGRES_URL_ENCODED_USERNAME" postgresURIPasswordEnv = "POSTGRES_URL_ENCODED_PASSWORD" encodedSecretName := getEncodedPostgresCredentialsSecretName(database, secret) @@ -39,6 +43,7 @@ func GetPostgresEnvVars(ctx core.Context, stack *v1beta1.Stack, database *v1beta core.EnvFromSecret("POSTGRES_PASSWORD", secret, postgresCredentialsPasswordKey), core.EnvFromSecret("POSTGRES_URL_ENCODED_USERNAME", encodedSecretName, postgresCredentialsUsernameKey), core.EnvFromSecret("POSTGRES_URL_ENCODED_PASSWORD", encodedSecretName, postgresCredentialsPasswordKey), + core.Env("POSTGRES_CREDENTIALS_ENCODING", string(credentialsEncoding)), ) } ret = append(ret, @@ -130,6 +135,7 @@ func BuildPostgresQueryString(rawQuery url.Values) string { delete(params, "disableSSLMode") delete(params, "secret") delete(params, "awsRole") + delete(params, postgresCredentialsEncodingQueryParam) if settings.IsTrue(rawQuery.Get("disableSSLMode")) { params.Set("sslmode", "disable") } diff --git a/internal/resources/databases/env_test.go b/internal/resources/databases/env_test.go index 3d09f32a1..38c671e8e 100644 --- a/internal/resources/databases/env_test.go +++ b/internal/resources/databases/env_test.go @@ -69,6 +69,11 @@ func TestBuildPostgresQueryString(t *testing.T) { uri: "postgresql://user:pass@host:5432?awsRole=my-role&sslmode=require", expectedQuery: "sslmode=require", }, + { + name: "secret credentials encoding is filtered out", + uri: "postgresql://host:5432?secret=creds&secretCredentialsEncoding=raw&sslmode=require", + expectedQuery: "sslmode=require", + }, } for _, tc := range testCases { @@ -196,6 +201,55 @@ func TestGetPostgresEnvVarsUsesEncodedSecretForURI(t *testing.T) { require.Equal(t, "$(POSTGRES_NO_DATABASE_URI)/$(POSTGRES_DATABASE)", envByName["POSTGRES_URI"].Value) } +func TestGetPostgresEnvVarsIncludesCredentialsEncodingRolloutSignal(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + uri string + expected string + }{ + { + name: "defaults to url encoded", + uri: "postgresql://postgres:5432?secret=postgres", + expected: "urlEncoded", + }, + { + name: "raw", + uri: "postgresql://postgres:5432?secret=postgres&secretCredentialsEncoding=raw", + expected: "raw", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + postgresURI, err := v1beta1.ParseURL(tc.uri) + require.NoError(t, err) + database := &v1beta1.Database{ + ObjectMeta: metav1.ObjectMeta{Name: "stack-ledger"}, + Spec: v1beta1.DatabaseSpec{ + Service: "ledger", + }, + Status: v1beta1.DatabaseStatus{ + URI: postgresURI, + Database: "ledger", + }, + } + + envVars, err := GetPostgresEnvVars(newTestContext(t), &v1beta1.Stack{ObjectMeta: metav1.ObjectMeta{Name: "stack"}}, database) + require.NoError(t, err) + + envByName := make(map[string]corev1.EnvVar, len(envVars)) + for _, envVar := range envVars { + envByName[envVar.Name] = envVar + } + require.Equal(t, tc.expected, envByName["POSTGRES_CREDENTIALS_ENCODING"].Value) + }) + } +} + func TestGetPostgresEnvVarsAvoidsEncodedSecretNameCollision(t *testing.T) { t.Parallel() diff --git a/internal/resources/databases/init.go b/internal/resources/databases/init.go index 390beea8b..7da6bf657 100644 --- a/internal/resources/databases/init.go +++ b/internal/resources/databases/init.go @@ -57,7 +57,11 @@ func Reconcile(ctx core.Context, stack *v1beta1.Stack, database *v1beta1.Databas if err != nil { return err } - if err := reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, secret); err != nil { + credentialsEncoding, err := parsePostgresCredentialsEncoding(databaseURL.Query().Get(postgresCredentialsEncodingQueryParam)) + if err != nil { + return err + } + if err := reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, secret, credentialsEncoding); err != nil { return err } } else { @@ -208,7 +212,11 @@ func reconcileEncodedPostgresCredentialsSecretBeforeDatabaseDelete(ctx core.Cont if sourceSecretName == "" { return nil } - return reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecretName) + credentialsEncoding, err := parsePostgresCredentialsEncoding(database.Status.URI.Query().Get(postgresCredentialsEncodingQueryParam)) + if err != nil { + return err + } + return reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecretName, credentialsEncoding) } func handleDatabaseJob(ctx core.Context, stack *v1beta1.Stack, database *v1beta1.Database, name string, args ...string) error { diff --git a/internal/resources/databases/secret.go b/internal/resources/databases/secret.go index c3f146888..f604fb89b 100644 --- a/internal/resources/databases/secret.go +++ b/internal/resources/databases/secret.go @@ -23,8 +23,14 @@ const ( encodedPostgresCredentialsSecretSuffix = "postgres-uri-credentials" collisionSafeEncodedPostgresCredentialsSecretSuffix = "encoded-postgres-uri-credentials" encodedPostgresCredentialsSecretAnnotation = "formance.com/encoded-postgres-credentials-secret" + postgresCredentialsEncodingQueryParam = "secretCredentialsEncoding" + + postgresCredentialsEncodingURLEncoded postgresCredentialsEncoding = "urlEncoded" + postgresCredentialsEncodingRaw postgresCredentialsEncoding = "raw" ) +type postgresCredentialsEncoding string + func getEncodedPostgresCredentialsSecretName(database *v1beta1.Database, sourceSecretName ...string) string { name := fmt.Sprintf("%s-%s", database.Name, encodedPostgresCredentialsSecretSuffix) if len(sourceSecretName) > 0 && sourceSecretName[0] == name { @@ -64,7 +70,29 @@ func dedupePostgresCredentialsSecretNames(names []string) []string { return ret } -func reconcileEncodedPostgresCredentialsSecret(ctx core.Context, stack *v1beta1.Stack, database *v1beta1.Database, secretName string) error { +func parsePostgresCredentialsEncoding(value string) (postgresCredentialsEncoding, error) { + switch postgresCredentialsEncoding(value) { + case "", postgresCredentialsEncodingURLEncoded: + return postgresCredentialsEncodingURLEncoded, nil + case postgresCredentialsEncodingRaw: + return postgresCredentialsEncodingRaw, nil + default: + return "", fmt.Errorf("invalid %s %q: expected %q or %q", + postgresCredentialsEncodingQueryParam, + value, + postgresCredentialsEncodingURLEncoded, + postgresCredentialsEncodingRaw, + ) + } +} + +func reconcileEncodedPostgresCredentialsSecret( + ctx core.Context, + stack *v1beta1.Stack, + database *v1beta1.Database, + secretName string, + credentialsEncoding postgresCredentialsEncoding, +) error { sourceSecret := &corev1.Secret{} if err := ctx.GetClient().Get(ctx, types.NamespacedName{ Namespace: stack.Name, @@ -84,6 +112,10 @@ func reconcileEncodedPostgresCredentialsSecret(ctx core.Context, stack *v1beta1. if !ok { return fmt.Errorf("postgres credentials secret %s/%s is missing %q", stack.Name, secretName, postgresCredentialsPasswordKey) } + if credentialsEncoding == postgresCredentialsEncodingRaw { + username = []byte(escapePostgresCredentialForURI(string(username))) + password = []byte(escapePostgresCredentialForURI(string(password))) + } encodedSecretName := getEncodedPostgresCredentialsSecretName(database, secretName) if err := ensureEncodedPostgresCredentialsSecretCanBeWritten(ctx, stack, database, encodedSecretName); err != nil { @@ -99,8 +131,8 @@ func reconcileEncodedPostgresCredentialsSecret(ctx core.Context, stack *v1beta1. } secret.Annotations[encodedPostgresCredentialsSecretAnnotation] = "true" secret.Data = map[string][]byte{ - postgresCredentialsUsernameKey: []byte(escapePostgresCredentialForURI(string(username))), - postgresCredentialsPasswordKey: []byte(escapePostgresCredentialForURI(string(password))), + postgresCredentialsUsernameKey: username, + postgresCredentialsPasswordKey: password, } return nil }, core.WithController[*corev1.Secret](ctx.GetScheme(), database)) diff --git a/internal/resources/databases/secret_test.go b/internal/resources/databases/secret_test.go index 1acf256ba..4816388a9 100644 --- a/internal/resources/databases/secret_test.go +++ b/internal/resources/databases/secret_test.go @@ -21,43 +21,126 @@ import ( func TestReconcileEncodedPostgresCredentialsSecret(t *testing.T) { t.Parallel() - stack := &v1beta1.Stack{ - ObjectMeta: metav1.ObjectMeta{Name: "stack"}, - } - database := &v1beta1.Database{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1beta1.GroupVersion.String(), - Kind: "Database", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "stack-ledger", - UID: types.UID("database-uid"), + testCases := []struct { + name string + credentials map[string][]byte + encoding postgresCredentialsEncoding + expectedUsername string + expectedPassword string + }{ + { + name: "encode raw credentials", + credentials: map[string][]byte{ + postgresCredentialsUsernameKey: []byte("user^name"), + postgresCredentialsPasswordKey: []byte("p^ss word"), + }, + encoding: postgresCredentialsEncodingRaw, + expectedUsername: "user%5Ename", + expectedPassword: "p%5Ess%20word", + }, + { + name: "preserve pre-encoded credentials", + credentials: map[string][]byte{ + postgresCredentialsUsernameKey: []byte("user%5Ename"), + postgresCredentialsPasswordKey: []byte("p%5Ess%20word"), + }, + encoding: postgresCredentialsEncodingURLEncoded, + expectedUsername: "user%5Ename", + expectedPassword: "p%5Ess%20word", }, } - sourceSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: stack.Name, - Name: "postgres", - }, - Data: map[string][]byte{ - postgresCredentialsUsernameKey: []byte("user^name"), - postgresCredentialsPasswordKey: []byte("p^ss word"), - }, + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + stack := &v1beta1.Stack{ + ObjectMeta: metav1.ObjectMeta{Name: "stack"}, + } + database := &v1beta1.Database{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1beta1.GroupVersion.String(), + Kind: "Database", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "stack-ledger", + UID: types.UID("database-uid"), + }, + } + sourceSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: stack.Name, + Name: "postgres", + }, + Data: tc.credentials, + } + ctx := newTestContext(t, sourceSecret) + + require.NoError(t, reconcileEncodedPostgresCredentialsSecret( + ctx, + stack, + database, + sourceSecret.Name, + tc.encoding, + )) + + encodedSecret := &corev1.Secret{} + require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ + Namespace: stack.Name, + Name: getEncodedPostgresCredentialsSecretName(database), + }, encodedSecret)) + require.Equal(t, tc.expectedUsername, string(encodedSecret.Data[postgresCredentialsUsernameKey])) + require.Equal(t, tc.expectedPassword, string(encodedSecret.Data[postgresCredentialsPasswordKey])) + require.Equal(t, "true", encodedSecret.Annotations[encodedPostgresCredentialsSecretAnnotation]) + require.Len(t, encodedSecret.OwnerReferences, 1) + require.Equal(t, database.Name, encodedSecret.OwnerReferences[0].Name) + }) } - ctx := newTestContext(t, sourceSecret) +} - require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name)) +func TestParsePostgresCredentialsEncoding(t *testing.T) { + t.Parallel() - encodedSecret := &corev1.Secret{} - require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ - Namespace: stack.Name, - Name: getEncodedPostgresCredentialsSecretName(database), - }, encodedSecret)) - require.Equal(t, []byte("user%5Ename"), encodedSecret.Data[postgresCredentialsUsernameKey]) - require.Equal(t, []byte("p%5Ess%20word"), encodedSecret.Data[postgresCredentialsPasswordKey]) - require.Equal(t, "true", encodedSecret.Annotations[encodedPostgresCredentialsSecretAnnotation]) - require.Len(t, encodedSecret.OwnerReferences, 1) - require.Equal(t, database.Name, encodedSecret.OwnerReferences[0].Name) + testCases := []struct { + name string + value string + expected postgresCredentialsEncoding + wantErr bool + }{ + { + name: "defaults to url encoded", + expected: postgresCredentialsEncodingURLEncoded, + }, + { + name: "url encoded", + value: "urlEncoded", + expected: postgresCredentialsEncodingURLEncoded, + }, + { + name: "raw", + value: "raw", + expected: postgresCredentialsEncodingRaw, + }, + { + name: "invalid", + value: "automatic", + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + actual, err := parsePostgresCredentialsEncoding(tc.value) + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.expected, actual) + }) + } } func TestReconcileEncodedPostgresCredentialsSecretAvoidsSourceSecretNameCollision(t *testing.T) { @@ -88,7 +171,7 @@ func TestReconcileEncodedPostgresCredentialsSecretAvoidsSourceSecretNameCollisio } ctx := newTestContext(t, sourceSecret) - require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name)) + require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name, postgresCredentialsEncodingRaw)) preservedSourceSecret := &corev1.Secret{} require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ @@ -107,7 +190,7 @@ func TestReconcileEncodedPostgresCredentialsSecretAvoidsSourceSecretNameCollisio require.Equal(t, []byte("user%5Ename"), encodedSecret.Data[postgresCredentialsUsernameKey]) require.Equal(t, []byte("p%5Ess%20word"), encodedSecret.Data[postgresCredentialsPasswordKey]) - require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name)) + require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name, postgresCredentialsEncodingRaw)) require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ Namespace: stack.Name, @@ -162,7 +245,7 @@ func TestReconcileEncodedPostgresCredentialsSecretKeepsUncontrolledLegacyFallbac } ctx := newTestContext(t, sourceSecret, legacyFallbackSecret) - require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name)) + require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name, postgresCredentialsEncodingRaw)) preservedLegacySecret := &corev1.Secret{} require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ @@ -222,7 +305,7 @@ func TestReconcileEncodedPostgresCredentialsSecretRejectsUncontrolledEncodedSecr } ctx := newTestContext(t, sourceSecret, existingTargetSecret) - err := reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name) + err := reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name, postgresCredentialsEncodingRaw) require.ErrorContains(t, err, "already exists and is not controlled") preservedTargetSecret := &corev1.Secret{} @@ -265,7 +348,7 @@ func TestReconcileEncodedPostgresCredentialsSecretRemovesStaleSourceSecretContro require.NoError(t, controllerutil.SetControllerReference(database, sourceSecret, ctx.GetScheme())) require.NoError(t, ctx.GetClient().Create(ctx, sourceSecret)) - require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name)) + require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name, postgresCredentialsEncodingRaw)) preservedSourceSecret := &corev1.Secret{} require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ @@ -340,7 +423,7 @@ func TestReconcileEncodedPostgresCredentialsSecretDeletesStaleControlledEncodedS require.NoError(t, ctx.GetClient().Create(ctx, staleEncodedSecret)) require.NoError(t, ctx.GetClient().Create(ctx, staleHashedEncodedSecret)) - require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name)) + require.NoError(t, reconcileEncodedPostgresCredentialsSecret(ctx, stack, database, sourceSecret.Name, postgresCredentialsEncodingRaw)) currentEncodedSecret := &corev1.Secret{} require.NoError(t, ctx.GetClient().Get(ctx, types.NamespacedName{ @@ -701,7 +784,7 @@ func TestReconcileEncodedPostgresCredentialsSecretBeforeDatabaseDeleteCreatesCol stack := &v1beta1.Stack{ ObjectMeta: metav1.ObjectMeta{Name: "stack"}, } - databaseURI, err := v1beta1.ParseURL("postgresql://postgres:5432?secret=stack-ledger-postgres-uri-credentials") + databaseURI, err := v1beta1.ParseURL("postgresql://postgres:5432?secret=stack-ledger-postgres-uri-credentials&secretCredentialsEncoding=raw") require.NoError(t, err) database := &v1beta1.Database{ TypeMeta: metav1.TypeMeta{