diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index 009a85239f..4cd8606fc0 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -17,7 +17,7 @@ import knex from 'knex'; import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; -import { createPgDatabaseClient } from './postgres'; +import { createPgDatabaseClient, ensurePgDatabaseExists } from './postgres'; import { createSqliteDatabaseClient } from './sqlite3'; type DatabaseClient = 'pg' | 'sqlite3' | string; @@ -48,3 +48,19 @@ export function createDatabaseClient( * @deprecated Use createDatabaseClient instead */ export const createDatabase = createDatabaseClient; + +/** + * Ensures that the given databases all exist, creating them if they do not. + */ +export async function ensureDatabaseExists( + dbConfig: Config, + ...databases: Array +) { + const client: DatabaseClient = dbConfig.getString('client'); + + if (client === 'pg') { + return ensurePgDatabaseExists(dbConfig, ...databases); + } + + return undefined; +} diff --git a/packages/backend-common/src/database/postgres.ts b/packages/backend-common/src/database/postgres.ts index 03a74156c7..82e971f081 100644 --- a/packages/backend-common/src/database/postgres.ts +++ b/packages/backend-common/src/database/postgres.ts @@ -95,3 +95,39 @@ function requirePgConnectionString() { throw new Error(`${message}\n${e.message}`); } } + +/** + * Creates the missing Postgres database if it does not exist + * + * @param dbConfig The database config + * @param databases The name of the databases to create + */ +export async function ensurePgDatabaseExists( + dbConfig: Config, + ...databases: Array +) { + const admin = createPgDatabaseClient(dbConfig, { + connection: { + database: 'postgres', + }, + }); + + try { + const ensureDatabase = async (database: string) => { + const result = await admin + .from('pg_database') + .where('datname', database) + .count>(); + + if (parseInt(result[0].count, 10) > 0) { + return; + } + + await admin.raw(`CREATE DATABASE ??`, [database]); + }; + + await Promise.all(databases.map(ensureDatabase)); + } finally { + await admin.destroy(); + } +} diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index dbd7842f8b..e86034dd29 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -24,6 +24,7 @@ import Router from 'express-promise-router'; import { + ensureDatabaseExists, createDatabaseClient, createServiceBuilder, loadBackendConfig, @@ -68,6 +69,12 @@ async function main() { const configs = await loadBackendConfig(); const configReader = ConfigReader.fromConfigs(configs); const createEnv = makeCreateEnv(configs); + await ensureDatabaseExists( + configReader.getConfig('backend.database'), + 'backstage_plugin_catalog', + 'backstage_plugin_auth', + ); + const healthcheckEnv = useHotMemoize(module, () => createEnv('healthcheck')); const catalogEnv = useHotMemoize(module, () => createEnv('catalog')); const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder')); diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index 07f1b1c99a..47ab2c5993 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -8,6 +8,7 @@ import Router from 'express-promise-router'; import { + ensureDatabaseExists, createDatabaseClient, createServiceBuilder, loadBackendConfig, @@ -46,6 +47,11 @@ async function main() { const configs = await loadBackendConfig(); const configReader = ConfigReader.fromConfigs(configs); const createEnv = makeCreateEnv(configs); + await ensureDatabaseExists( + configReader.getConfig('backend.database'), + 'backstage_plugin_catalog', + 'backstage_plugin_auth', + ); const catalogEnv = useHotMemoize(module, () => createEnv('catalog')); const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));