@@ -14,8 +14,9 @@ import { readFileSync, existsSync } from "fs";
1414
1515import { Client } from "pg" ;
1616
17- import { base64Encode } from "./helper" ;
17+ import { base64Encode , resolveInstallMethod } from "./helper" ;
1818import { KubeClient , BACKSTAGE_BACKEND_CONTAINER } from "./kube-client" ;
19+ import { BACKSTAGE_CR_API_VERSION } from "./runtime-config" ;
1920import type { AppConfigYaml } from "./runtime-config" ;
2021
2122/**
@@ -128,6 +129,15 @@ export async function configurePostgresCredentials(
128129 data,
129130 } ;
130131 await kubeClient . createOrUpdateSecret ( secret , namespace ) ;
132+
133+ // For operator installs, ensure the Backstage CR includes postgres-cred in
134+ // extraEnvs.secrets so the operator injects the credentials as env vars.
135+ // This is done here (not in prepareForExternalDatabase) because the secret
136+ // must contain real credentials before the operator reconciles — placeholder
137+ // values would break the DB connection and leave readiness at 503.
138+ if ( resolveInstallMethod ( ) === "operator" ) {
139+ await addPostgresCredToBackstageCR ( kubeClient , namespace ) ;
140+ }
131141}
132142
133143const SYSTEM_DATABASES = [
@@ -297,7 +307,11 @@ export async function prepareForExternalDatabase(
297307 // Schema-mode tests may have added individual secretKeyRef env vars pointing
298308 // to a *-postgresql secret. These override the bulk envFrom injection from
299309 // postgres-cred and must be removed before external DB tests.
300- await removeSchemaModePatchedEnvVars ( kubeClient , deploymentName , namespace ) ;
310+ // Skip for operator — the operator manages env vars via extraEnvs.secrets
311+ // in the Backstage CR, so there are no direct deployment patches to remove.
312+ if ( resolveInstallMethod ( ) !== "operator" ) {
313+ await removeSchemaModePatchedEnvVars ( kubeClient , deploymentName , namespace ) ;
314+ }
301315
302316 // --- 2. Patch app-config ConfigMap to use external DB connection ---
303317 console . log ( "Patching app-config to use external database connection (env var placeholders)..." ) ;
@@ -314,11 +328,20 @@ export async function prepareForExternalDatabase(
314328 } ) ;
315329 console . log ( "App-config patched for external database connection" ) ;
316330
317- // --- 3. Add POSTGRES_* env vars to the deployment via secretKeyRef ---
318- // The deployment starts with internal DB (no postgres-cred env vars).
319- // Add individual env vars pointing to the postgres-cred secret so the
320- // app-config ${POSTGRES_HOST} etc. placeholders resolve correctly.
321- await ensurePostgresCredEnvVars ( kubeClient , deploymentName , namespace ) ;
331+ // --- 3. Add POSTGRES_* env vars to the deployment ---
332+ // Helm: patch individual env vars pointing to the postgres-cred secret so
333+ // the app-config ${POSTGRES_HOST} etc. placeholders resolve correctly.
334+ // Operator: the Backstage CR is patched later by configurePostgresCredentials()
335+ // after real credentials are written to the postgres-cred secret. Patching the
336+ // CR here with placeholder values would trigger operator reconciliation and
337+ // break the DB connection (readiness 503).
338+ if ( resolveInstallMethod ( ) === "operator" ) {
339+ console . log (
340+ "Skipping env var setup (operator CR will be patched by configurePostgresCredentials)" ,
341+ ) ;
342+ } else {
343+ await ensurePostgresCredEnvVars ( kubeClient , deploymentName , namespace ) ;
344+ }
322345}
323346
324347/**
@@ -371,3 +394,52 @@ async function ensurePostgresCredEnvVars(
371394 ) ;
372395 console . log ( "POSTGRES_* env vars added to deployment from postgres-cred" ) ;
373396}
397+
398+ /**
399+ * Patch the operator Backstage CR to add postgres-cred to extraEnvs.secrets.
400+ * This causes the operator to inject all keys from the postgres-cred secret
401+ * as env vars into the RHDH container.
402+ *
403+ * The postgres-cred secret is NOT included in the CR at deployment time —
404+ * it contains placeholder values that would override the operator-managed
405+ * internal PostgreSQL credentials. It is only added here, when external DB
406+ * tests need the real credentials injected.
407+ *
408+ * Uses JSON merge-patch so the secrets array is replaced wholesale.
409+ * The CR is created by generateBackstageCR() with only rhdh-runtime-config
410+ * in extraEnvs.secrets, so we set both here explicitly.
411+ */
412+ async function addPostgresCredToBackstageCR (
413+ kubeClient : KubeClient ,
414+ namespace : string ,
415+ ) : Promise < void > {
416+ const releaseName =
417+ process . env . RELEASE_NAME !== undefined && process . env . RELEASE_NAME !== ""
418+ ? process . env . RELEASE_NAME
419+ : "rhdh" ;
420+ const [ group , version ] = BACKSTAGE_CR_API_VERSION . split ( "/" ) ;
421+
422+ const patch = {
423+ spec : {
424+ application : {
425+ extraEnvs : {
426+ secrets : [ { name : "rhdh-runtime-config" } , { name : "postgres-cred" } ] ,
427+ } ,
428+ } ,
429+ } ,
430+ } ;
431+
432+ await kubeClient . customObjectsApi . patchNamespacedCustomObject (
433+ group ,
434+ version ,
435+ namespace ,
436+ "backstages" ,
437+ releaseName ,
438+ patch ,
439+ undefined ,
440+ undefined ,
441+ undefined ,
442+ { headers : { "Content-Type" : "application/merge-patch+json" } } ,
443+ ) ;
444+ console . log ( "Patched Backstage CR: added postgres-cred to extraEnvs.secrets" ) ;
445+ }
0 commit comments