UNPKG

@fairmint/canton-node-sdk

Version:
144 lines (125 loc) 4.61 kB
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. // SPDX-License-Identifier: Apache-2.0 import * as cloudsql from '@google-cloud/sql'; import * as gcp from '@pulumi/gcp'; import * as automation from '@pulumi/pulumi/automation'; import * as readline from 'readline'; import { program } from 'commander'; import { CLUSTER_BASENAME, config } from '@lfdecentralizedtrust/splice-pulumi-common'; import { stack } from './pulumi'; import { runSvCantonForAllMigrations } from './sv-canton/pulumi'; const gcpSqlClient = new cloudsql.SqlInstancesServiceClient({ fallback: 'rest', }); async function getDBsInStack(stack: automation.Stack): Promise<gcp.sql.DatabaseInstance[]> { const exported = await stack.exportStack(); const resources = exported.deployment.resources; if (!resources) { return Promise.resolve([]); } const res = resources.filter((r: any) => r.type === 'gcp:sql/databaseInstance:DatabaseInstance'); console.log( `In ${stack.name} got ${JSON.stringify( res.map((x: any) => x.id), null, 2 )}` ); return res; } async function getAllPulumiDbs(): Promise<gcp.sql.DatabaseInstance[]> { const projects = ['canton-network', 'sv-runbook', 'splitwell', 'validator1']; const coreDbs = await Promise.all( projects.map(async project => await getDBsInStack(await stack(project, project, true, {}))) ).then(dbs => dbs.flat()); const readDbsForAllStacks = runSvCantonForAllMigrations( 'get_dbs', async stack => { return getDBsInStack(stack); }, false, true ); const migrationDbsRet = await Promise.all(readDbsForAllStacks.map(res => res.promise)).then( result => Array.from(result.values()).flat() ); return [...coreDbs, ...migrationDbsRet]; } async function getAllGcpDbs(): Promise<cloudsql.protos.google.cloud.sql.v1.IDatabaseInstance[]> { const gcp_project = config.requireEnv('CLOUDSDK_CORE_PROJECT'); const filter = `settings.userLabels.cluster:${CLUSTER_BASENAME}`; // console.log(filter); const request = { project: gcp_project, filter: filter, }; const result = await gcpSqlClient.list(request); return result[0].items ?? []; } function prettyPrintDb(db: cloudsql.protos.google.cloud.sql.v1.IDatabaseInstance) { const createTimeSeconds = db.createTime?.seconds; const createTime = createTimeSeconds ? new Date((createTimeSeconds as number) * 1000).toDateString() : 'unknown'; const size = `${db.settings?.dataDiskSizeGb?.value ?? 'unknown'} GB`; console.log(`* Database: ${db.name} (State: ${db.state}, created: ${createTime}, size: ${size})`); } async function deleteDb(db: cloudsql.protos.google.cloud.sql.v1.IDatabaseInstance) { const request = { instance: db.name, project: db.project, }; console.log(`Deleting ${db.name}...`); await gcpSqlClient.delete(request); console.log(`Done deleting ${db.name}`); } async function runPurgeUnusedDbs() { program.option('-y, --yes', 'Auto-accept all prompts').parse(process.argv); const options = program.opts(); const autoAccept = options.yes; const usedDbs = await getAllPulumiDbs(); // DatabaseInstance.id is a string, but ts insists on it being an Output<string>, so we force-cast it via an unknown cast const usedDbNames: string[] = usedDbs.map( (db: gcp.sql.DatabaseInstance) => db.id ) as unknown as string[]; const allDbs = await getAllGcpDbs(); const unusedDbs = allDbs.filter(db => !usedDbNames.some(usedDb => usedDb == db.name)); if (unusedDbs.length != allDbs.length - usedDbNames.length) { console.warn('Warning: There are some databases in Pulumi that were not found in GCP'); } if (unusedDbs.length == 0) { console.log('No unused databases found'); return; } console.log(`About to delete the following ${unusedDbs.length} database instances:`); unusedDbs.forEach(db => prettyPrintDb(db)); if (autoAccept) { console.log('Auto-accepting'); for (const db of unusedDbs) { await deleteDb(db); } } else { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question( '\nDo you want to proceed with deleting these DB instances? [y/n] ', async answer => { if (answer === 'y') { console.log('Deleting databases'); for (const db of unusedDbs) { await deleteDb(db); } } else { console.log('Aborting'); } rl.close(); } ); } } runPurgeUnusedDbs().catch(e => { console.error(e); process.exit(1); });