UNPKG

@catladder/cli

Version:

Panter cli tool for cloud CI/CD and DevOps

136 lines (125 loc) 4.26 kB
import { exec } from "child-process-promise"; import { defineCommand } from "../../core/defineCommand"; import { getProjectPvcs } from "../../kubernetes"; import { logError } from "../../utils/log"; import { getProjectNamespace } from "../../utils/projects"; import { ensureCluster } from "../../apps/cli/commands/project/utils/ensureCluster"; import { getMongoDbPodsWithReplInfo } from "../../apps/cli/commands/mongodb/utils"; import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions"; import { hasDeployType } from "../availability"; const removeFinalizer = async ( namespace: string, type: "pod" | "pv" | "pvc", name: string, ) => exec( `kubectl patch --namespace ${namespace} ${type} ${name} -p '{"metadata":{"finalizers":null}}'`, ); const deleteResource = async ( namespace: string, type: "pod" | "pv" | "pvc", name: string, ) => exec(`kubectl delete --namespace ${namespace} ${type} ${name}`); const removeFinalizerAndDelete = async ( namespace: string, type: "pod" | "pv" | "pvc", name: string, ) => { return Promise.all([ removeFinalizer(namespace, type, name), await deleteResource(namespace, type, name), ]); }; export const commandMongoDestroyMember = defineCommand({ name: "project mongo destroy-member", description: "DESTROY a member of a replicaset in order to reinitialize it", group: "mongodb", isAvailable: hasDeployType("kubernetes"), inputs: { envComponent: { type: "string", message: "environment:component", positional: true, choices: async () => envAndComponents(), }, podName: { type: "string", message: "Which pod? 🤔", choices: async (ctx) => { const envComponent = await ctx.get("envComponent"); const mongodbPods = await getMongoDbPodsWithReplInfo(envComponent); const secondaries = mongodbPods.filter((pod: any) => !pod.isMaster); return secondaries.map((p: any) => ({ name: `[ secondary ] ${p.podName}`, value: p.podName, })); }, }, }, execute: async (ctx) => { const envComponent = await ctx.get("envComponent"); await ensureCluster(ctx, envComponent); ctx.log( "this command tries to delete a (secondary) member of the replicaset, it's persistent volume claim (pvc) and the volume", ); ctx.log(""); ctx.log( "this is useful, if you update the stateful set with new volume configuration (different size or storage class)", ); ctx.log(""); ctx.log( "Kubernetes will usually recreate the missing member with the updated config and mongodb will start synchronizing the new member.", ); ctx.log(""); ctx.log( "This works without downtime, but should be done when load on the db is low. Also it has not been tested with large dbs (> 10gi)", ); ctx.log(""); ctx.log( "Deleting the volume and claim often stuck, just wait or cancel and restart.", ); ctx.log(""); const understood = await ctx.confirm("DO YOU UNDERSTAND?"); if (!understood) { throw new Error("abort"); } const namespace = await getProjectNamespace(envComponent); const pvcs = await getProjectPvcs(envComponent); const podName = await ctx.get("podName"); const thePvc = pvcs.find( (pvc: any) => pvc.metadata.name === `datadir-${podName}`, ); if (!thePvc) { logError(ctx, `sorry, no pvc found for ${podName}`); return; } const shouldContinue = await ctx.confirm( "THIS WILL DESTROY THE POD, ITS VOLUME AND ALL ITS DATA 🙀🙀🙀!!! continue? 🤔", ); if (!shouldContinue) { throw new Error("abort"); } ctx.log("destroying volume..."); try { await removeFinalizerAndDelete( namespace, "pv", (thePvc as any).spec.volumeName, ); } catch (e: any) { ctx.log(e.message); } ctx.log("destroying volume claim..."); try { await removeFinalizerAndDelete( namespace, "pvc", (thePvc as any).metadata.name, ); } catch (e: any) { ctx.log(e.message); } ctx.log("destroying pod..."); await removeFinalizerAndDelete(namespace, "pod", podName); }, });