mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-26 08:02:31 +00:00
* fix: check custom_instance_user can connect during instance database setup Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: mark the custom_instance_user login ok before tearing the connection down Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
4.7 KiB
TypeScript
95 lines
4.7 KiB
TypeScript
import type { CustomInstanceDb } from '$lib/gen'
|
|
import { runningFrom, type SetupStep } from '../wizards/SetupChecklist.svelte'
|
|
|
|
/**
|
|
* The same checks as [`instanceDbSteps`], in the vocabulary the wizard's checklist speaks.
|
|
* Nothing is reported until the call returns, so an unreported step is either the failure
|
|
* (when the call errored) or simply not reached yet.
|
|
*/
|
|
export function instanceSetupSteps(
|
|
dbname: string,
|
|
status: CustomInstanceDb | undefined,
|
|
running: boolean
|
|
): SetupStep[] {
|
|
let firstUnreported = true
|
|
const steps = instanceDbSteps(dbname, status).map((step): SetupStep => {
|
|
if (step.status === 'OK') return { ...step, status: 'done' }
|
|
if (step.status === 'FAIL') return { ...step, status: 'failed' }
|
|
if (step.status === 'SKIP') return { ...step, status: 'skipped' }
|
|
const failed = firstUnreported && !!status?.error
|
|
firstUnreported = false
|
|
return { ...step, status: failed ? 'failed' : 'pending' }
|
|
})
|
|
return runningFrom(steps, running)
|
|
}
|
|
|
|
/**
|
|
* The checks `setup_custom_instance_db` reports, in the order it runs them. Shared so the
|
|
* setup modal and the data table wizard describe the same failure the same way.
|
|
*/
|
|
export function instanceDbSteps(dbname: string, status: CustomInstanceDb | undefined) {
|
|
return [
|
|
{
|
|
title: 'Super admin required',
|
|
status: status?.logs.super_admin,
|
|
description:
|
|
'You need to be a super admin to create a new database in the Windmill PostgreSQL instance'
|
|
},
|
|
{
|
|
title: 'Retrieve and parse database credentials',
|
|
status: status?.logs.database_credentials,
|
|
description:
|
|
'Windmill uses the DATABASE_URL or DATABASE_URL_FILE environment variable to connect to the PostgreSQL instance. Make sure it is correctly set'
|
|
},
|
|
{
|
|
title: 'Database name is valid',
|
|
status: status?.logs.valid_dbname,
|
|
description:
|
|
'The database name must be alphanumeric (underscores and hyphens allowed) and cannot be named the same as the Windmill database (usually "windmill")'
|
|
},
|
|
{
|
|
title:
|
|
'Create database' +
|
|
(status?.logs.created_database === 'SKIP' ? ' (already exists, skipped)' : ''),
|
|
status: status?.logs.created_database,
|
|
description: `In the Windmill PostgreSQL instance, run: CREATE DATABASE "${dbname}".`
|
|
},
|
|
{
|
|
title: `Connect to the ${dbname} database`,
|
|
status: status?.logs.db_connect,
|
|
description:
|
|
"Connect to the newly created database with the default admin user (the one in DATABASE_URL, usually 'postgres') to run the next commands"
|
|
},
|
|
{
|
|
title: 'Grant permissions to custom_instance_user',
|
|
status: status?.logs.grant_permissions,
|
|
description:
|
|
'Gives custom_instance_user the required permissions to use the database. custom_instance_user is already created during a migration and has an auto-generated password stored in global_settings.custom_instance_pg_databases.user_pwd. These are the commands : \n\n' +
|
|
`GRANT CONNECT ON DATABASE "${dbname}" TO custom_instance_user;\n` +
|
|
'GRANT USAGE ON SCHEMA public TO custom_instance_user;\n' +
|
|
'GRANT CREATE ON SCHEMA public TO custom_instance_user;\n' +
|
|
`GRANT CREATE ON DATABASE "${dbname}" TO custom_instance_user;\n` +
|
|
'ALTER DEFAULT PRIVILEGES IN SCHEMA public \n' +
|
|
' GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES\n TO custom_instance_user;\n' +
|
|
'ALTER ROLE custom_instance_user CREATEROLE;'
|
|
},
|
|
{
|
|
title: 'Grant replication to custom_instance_replication_user',
|
|
status: status?.logs.replication_user,
|
|
description:
|
|
'Postgres triggers on custom-instance datatables connect as custom_instance_replication_user, whose password is stored in global_settings.custom_instance_replication_pwd. The role is cluster-wide, so it is created on the Windmill PostgreSQL instance rather than on this database : \n\n' +
|
|
'ALTER ROLE custom_instance_replication_user REPLICATION;\n' +
|
|
'GRANT custom_instance_user TO custom_instance_replication_user;\n\n' +
|
|
'Setting REPLICATION requires a superuser on PostgreSQL 15 and older. Managed instances never grant one, so on AWS RDS Windmill falls back to GRANT rds_replication TO custom_instance_replication_user. The database stays usable for datatables if this step fails, but postgres triggers on them do not.' +
|
|
(status?.logs.replication_user_error
|
|
? `\n\nError: ${status.logs.replication_user_error}`
|
|
: '')
|
|
},
|
|
{
|
|
title: 'Connect as custom_instance_user',
|
|
status: status?.logs.user_connect,
|
|
description: `The steps above run as the user in DATABASE_URL, but data tables and DuckLake catalogs on ${dbname} log in as custom_instance_user. This checks that login reaches the database too. A connection pooler that routes on the login name, such as Supabase's shared pooler, refuses it: use your own Postgres database instead, or point DATABASE_URL at the Postgres server directly.`
|
|
}
|
|
]
|
|
}
|