1import * as _ from 'lodash-es'2import { tsSec } from '../../serverF/secretsF/tsSecF.mts'3import { getDbBackupDir } from './backupDbConstantsF.mts'4import { dbActions, runDbAction } from './migrateDbF.mts'5import { createPgDbFromQs } from '../vanillaPgF.mts'6import { sleep } from '../../sharedF/sleepF.mts'7import { guardProdAction } from '../readlineF/guardPromptF.mts'8import { getDefaultAction } from '../../serverF/getDefaultActionF.mts'9import { getFilename } from '../../serverF/libFs/lsFilesPathsRecF.mts'10import { cliHelp } from '../cliF/cliHelpF.mts'11import { isActionSupported, resourcesActionsH } from '../k8sF/isActionSupportedF.mts'12import { chalkGreen } from '../../serverF/libChalkF.mts'13import { deployDbCtx } from '../ctxF/deployDbCtxF.mts'14import { module1ToObj } from '../../sharedF/objF.mts'15import { decomposeQsFromSec } from '../../serverF/secretsF/decomposeQsFromSecF.mts'16import { getProcArgv, isDirectlyRun } from '../../serverF/isDirectlyRunF.mts'17import { isDbUp } from './dbToolsF.mts'18import { mkProgressLogger } from '../cliF/cliProgressAI.mts'19import { throwDebugH } from '../../serverF/throwErrorF/throwDebugHF.mts'20import { genPlainSecretIfMissing } from '../secretF/genSecretAI.mts'21import { scryptSync } from 'node:crypto'23const vanillaPgPwSeedSecretName = 'vanilla-pg-pw-seed'25const getVanillaPgDeterministicPw = async (dbName: string) => {26 const seed = await genPlainSecretIfMissing({secretName: vanillaPgPwSeedSecretName, autoYes: true})27 if (!seed) return null28 return scryptSync(dbName, seed, 16).toString('base64url')29}31const buildVanillaPgQs = ({svcName, password}: {svcName: string, password: string}) => `postgres://postgres:${password}@${svcName}`33export const eptVanillaPgK8sRes = {kind: 'Cluster', apiVersion: 'postgresql.cnpg.io/v1'}34export const eptVanillaPg = async ({35 importMetaUrl,36 action=getDefaultAction(),37 ...props38}: {importMetaUrl: string, action?: string, pvcNameOffset?: number, dbQsCfg: {dbQsName: string, gpIsProd?: boolean}, sizeGb: number, migrationOpts?: Record<string, unknown>, dbType?: string, kyselyOutFile?: string, [k: string]: unknown}) => {39 if (!isDirectlyRun(importMetaUrl)) return {importMetaUrl}41 let {pvcNameOffset, dbQsCfg, sizeGb, migrationOpts, dbType, kyselyOutFile} = props43 const {dbQsName: qsName, gpIsProd} = dbQsCfg44 const svcName = qsName.replace(/_qs$/, '')46 const deterministicPw = await getVanillaPgDeterministicPw(svcName)47 await genPlainSecretIfMissing({48 secretName: qsName,49 genValue: () => buildVanillaPgQs({svcName, password: deterministicPw!}),50 autoYes: true,51 })53 await guardProdAction({54 gpIsProd: !!gpIsProd, action, name: qsName,55 dangerousActions: ['delete', 'apply'],56 warnOnlyActions: ['dbconsole'],57 })58 const {extQs, dbQs, cluster_name, svcName: decomposedSvcName, nodePort, password} = await decomposeQsFromSec(tsSec(qsName))60 deployDbCtx.enterWith({migrationOpts, gpIsProd, dbType, qsName, kyselyOutFile}) // ctx:clear62 const dbName = svcName64 if (action == 'info') {65 const backupsDir = getDbBackupDir({dbName})66 console.log({qsName, extQs, dbQs, backupsDir})67 }69 if (action != 'info' && await runDbAction({action, qsName})) {70 return71 }73 if (isActionSupported(action)) {75 const resources = await createPgDbFromQs({qsName, sizeGb, action, dbType, cluster_name, pvcNameOffset: pvcNameOffset != null ? String(pvcNameOffset) : undefined})77 if (action == 'apply' && cluster_name != 'minik') {78 const dbWaitTimeoutMs = 60 * 1000 // 1 min79 const logProgress = mkProgressLogger('waiting for db to come online..', dbWaitTimeoutMs)80 const startTime = Date.now()81 const timeoutMs = dbWaitTimeoutMs82 while (true) {83 if (Date.now() - startTime > timeoutMs) {84 console.log(' timeout')85 throwDebugH({qsName, timeoutMs, message: 'db failed to come online within 60 seconds'})86 }88 if (isUp) {89 console.log(' ready')90 break91 }92 logProgress('Connecting')94 }95 console.log(`96${chalkGreen(`db created & online!`)}97now run:98node ${getFilename(getProcArgv()[1])} migrate # migrate a new db99OR100node ${getFilename(getProcArgv()[1])} restore # restore latest db backup from ${getDbBackupDir({dbName})}101`)102 }103 return resources104 }106 if (action != 'help') {107 console.log(`unknown action "${action}"`)108 }109 const actionsH = {...resourcesActionsH(), ...module1ToObj(dbActions)}111}