Skip to content

Commit

Permalink
feat: physical migration runbook
Browse files Browse the repository at this point in the history
  • Loading branch information
kirinnee committed Oct 19, 2024
1 parent 6040b23 commit 8622ca9
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 73 deletions.
4 changes: 2 additions & 2 deletions src/books/graceful-physical-cluster-destruction/aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { KubectlUtil, type ResourceSearch } from '../../lib/utility/kubectl-util
import type { TaskRunner } from '../../tasks/tasks.ts';
import type { YamlManipulator } from '../../lib/utility/yaml-manipulator.ts';
import type { UtilPrompter } from '../../lib/prompts/util-prompter.ts';
import type { GracefulClusterCloudDestructor } from './cloud.ts';
import type { GracefulPhysicalClusterCloudDestructor } from './cloud.ts';

class AwsGracefulPhysicalClusterDestructor implements GracefulClusterCloudDestructor {
class AwsGracefulPhysicalClusterDestructor implements GracefulPhysicalClusterCloudDestructor {
slug: string;

constructor(
Expand Down
4 changes: 2 additions & 2 deletions src/books/graceful-physical-cluster-destruction/cloud.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { LandscapeCluster } from '../../lib/service-tree-def.ts';

interface GracefulClusterCloudDestructor {
interface GracefulPhysicalClusterCloudDestructor {
slug: string;

Run(phy: LandscapeCluster, admin: LandscapeCluster): Promise<void>;
}

export type { GracefulClusterCloudDestructor };
export type { GracefulPhysicalClusterCloudDestructor };
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { KubectlUtil, type ResourceSearch } from '../../lib/utility/kubectl-util
import type { TaskRunner } from '../../tasks/tasks.ts';
import type { YamlManipulator } from '../../lib/utility/yaml-manipulator.ts';
import type { UtilPrompter } from '../../lib/prompts/util-prompter.ts';
import type { GracefulClusterCloudDestructor } from './cloud.ts';
import type { GracefulPhysicalClusterCloudDestructor } from './cloud.ts';

class DigitalOceanGracefulPhysicalClusterDestructor implements GracefulClusterCloudDestructor {
class DigitalOceanGracefulPhysicalClusterDestructor implements GracefulPhysicalClusterCloudDestructor {
slug: string;

constructor(
Expand Down
4 changes: 2 additions & 2 deletions src/books/graceful-physical-cluster-destruction/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RunBook } from '../run-book.ts';
import type { ServiceTreePrompter } from '../../lib/prompts/landscape.ts';
import type { ServiceTreePrinter } from '../../lib/utility/service-tree-printer.ts';
import type { GracefulClusterCloudDestructor } from './cloud.ts';
import type { GracefulPhysicalClusterCloudDestructor } from './cloud.ts';

class GracefulPhysicalClusterDestructor implements RunBook {
name: string = 'Graceful Physical Cluster Destruction';
Expand All @@ -10,7 +10,7 @@ class GracefulPhysicalClusterDestructor implements RunBook {
constructor(
private stp: ServiceTreePrompter,
private p: ServiceTreePrinter,
private clouds: GracefulClusterCloudDestructor[],
private clouds: GracefulPhysicalClusterCloudDestructor[],
) {}

async Run(): Promise<void> {
Expand Down
4 changes: 2 additions & 2 deletions src/books/graceful-physical-cluster-destruction/vultr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { KubectlUtil, type ResourceSearch } from '../../lib/utility/kubectl-util
import type { TaskRunner } from '../../tasks/tasks.ts';
import type { YamlManipulator } from '../../lib/utility/yaml-manipulator.ts';
import type { UtilPrompter } from '../../lib/prompts/util-prompter.ts';
import type { GracefulClusterCloudDestructor } from './cloud.ts';
import type { GracefulPhysicalClusterCloudDestructor } from './cloud.ts';

class VultrGracefulPhysicalClusterDestructor implements GracefulClusterCloudDestructor {
class VultrGracefulPhysicalClusterDestructor implements GracefulPhysicalClusterCloudDestructor {
slug: string;

constructor(
Expand Down
2 changes: 1 addition & 1 deletion src/books/physical-cluster-creation/digital-ocean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class DigitalOceanPhysicalClusterCreator implements PhysicalClusterCloudCreator
context: aCtx,
namespace: aNs,
selector: [
['atomi.cloud/sync-wave', 'wave-5'],
['atomi.cloud/sync-wave', 'wave-4'],
['atomi.cloud/landscape', phyLandscape.slug],
['atomi.cloud/cluster', phyCluster.principal.slug],
],
Expand Down
56 changes: 56 additions & 0 deletions src/books/physical-cluster-migration/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { RunBook } from "../run-book.ts";
import type { ServiceTreePrompter } from "../../lib/prompts/landscape.ts";
import type { ServiceTreePrinter } from "../../lib/utility/service-tree-printer.ts";
import { PhysicalClusterTransitioner } from "./transition.ts";
import type { PhysicalClusterCloudCreator } from "../physical-cluster-creation/cloud.ts";
import type { GracefulPhysicalClusterCloudDestructor } from "../graceful-physical-cluster-destruction/cloud.ts";

class PhysicalClusterMigrator implements RunBook {
name: string = 'Physical Cluster Migration';
desc: string = 'Migrate an physical cluster to a new cluster';

constructor(
private stp: ServiceTreePrompter,
private printer: ServiceTreePrinter,
private creators: PhysicalClusterCloudCreator[],
private destructors: GracefulPhysicalClusterCloudDestructor[],
private transition: PhysicalClusterTransitioner,
) {}

async Run(): Promise<void> {
const [adminLandscape, adminCluster] = await this.stp.AdminLandscapeCluster();

const [fromLandscape, fromCluster] = await this.stp.PhysicalLandscapeCluster(
'Select the physical landscape to migrate from',
'Select the physical cloud to migrate from',
'Select the physical cluster to migrate from',
);
const [toLandscape, toCluster] = await this.stp.PhysicalLandscapeCluster(
'Select the physical landscape to migrate to',
'Select the physical cloud to migrate to',
'Select the physical cluster to migrate to',
);

console.log('🎯 Selected Service Tree to migrate');
this.printer.Print('Admin', [adminLandscape, adminCluster]);
this.printer.Print('From', [fromLandscape, fromCluster]);
this.printer.Print('To ', [toLandscape, toCluster]);

// select the cloud to create the new cluster in
const c = this.creators.find(x => x.slug === toCluster.cloud.slug);
if (!c) return console.log('⚠️ Cloud not supported');

await c.Run([toLandscape, toCluster], [adminLandscape, adminCluster]);

// perform transition
await this.transition.Run([fromLandscape, fromCluster], [toLandscape, toCluster]);

// destroy old cluster
const d = this.destructors.find(x => x.slug === fromCluster.cloud.slug);
if (!d) return console.log('⚠️ Cloud not supported');

await d.Run([fromLandscape, fromCluster], [adminLandscape, adminCluster]);
}
}

export { PhysicalClusterMigrator };
17 changes: 17 additions & 0 deletions src/books/physical-cluster-migration/transition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { LandscapeCluster } from '../../lib/service-tree-def.ts';
import type { TaskRunner } from '../../tasks/tasks.ts';
import type { LoadBalancerDNSSwitcher } from '../../tasks/lb-dns-switcher.ts';

class PhysicalClusterTransitioner {
constructor(
private t: TaskRunner,
private dnsSwitcher: LoadBalancerDNSSwitcher,
) {}

async Run([, fC]: LandscapeCluster, [, tC]: LandscapeCluster): Promise<void> {
const task = this.dnsSwitcher.task(fC.principal, tC.principal);
await this.t.Run(task);
}
}

export { PhysicalClusterTransitioner };
112 changes: 61 additions & 51 deletions src/books/purge-admin-cluster/cloud.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import type { LandscapeCluster, ServiceTreeService } from "../../lib/service-tree-def.ts";
import { $ } from "bun";
import postgres from "postgres";
import type { TaskRunner } from "../../tasks/tasks.ts";
import type { LandscapeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts';
import { $ } from 'bun';
import postgres from 'postgres';
import type { TaskRunner } from '../../tasks/tasks.ts';

class GenericAdminClusterCloudPurger {

constructor(
private task: TaskRunner,
private projectId: string,
private env: string,
private tofuKey: string,
private kubernetesAccess: ServiceTreeService,
private sulfoxideHelium: ServiceTreeService
) {
}
private sulfoxideHelium: ServiceTreeService,
) {}

async Run([landscape, cluster]: LandscapeCluster): Promise<void> {

const l = landscape.slug;
const c = cluster.principal.slug;

Expand All @@ -29,57 +26,70 @@ class GenericAdminClusterCloudPurger {
const HePID = He.principal.projectId;
const HeKey = `${l.toUpperCase()}_${c.toUpperCase()}_KUBECONFIG`;


const rootFlags = `--projectId=${this.projectId} --env=${this.env} ${this.tofuKey}`;
const secret = await $`infisical secrets get --plain ${{ raw: rootFlags }}`.text();

// setup connection to tofu
const sql = postgres(secret);

await this.task.Run(["Check database reachability", async () => {
try {
await sql`SELECT 1`;
} catch (e) {
console.log("❌ Database not reachable");
await sql.end();
throw e;
}
}]);

await this.task.Run(["Deleting L0 States", async () => {
const table = `${l}-l0-${c}`;
try {
await sql`DELETE FROM ${ sql(table) }.states`;
} catch (e) {
await sql.end();
throw e;
}
}]);

await this.task.Run(["Deleting L1 States", async () => {
const table = `${l}-l1-${c}`;
try {
await sql`DELETE FROM ${ sql(table) }.states`;
} catch (e) {
await sql.end();
throw e;
}
}]);
await this.task.Run([
'Check database reachability',
async () => {
try {
await sql`SELECT 1`;
} catch (e) {
console.log('❌ Database not reachable');
await sql.end();
throw e;
}
},
]);

await this.task.Run([
'Deleting L0 States',
async () => {
const table = `${l}-l0-${c}`;
try {
await sql`DELETE FROM ${sql(table)}.states`;
} catch (e) {
await sql.end();
throw e;
}
},
]);

await this.task.Run([
'Deleting L1 States',
async () => {
const table = `${l}-l1-${c}`;
try {
await sql`DELETE FROM ${sql(table)}.states`;
} catch (e) {
await sql.end();
throw e;
}
},
]);

await sql.end();

await this.task.Run([`Deleting Kubernetes Access for ${l} ${c}`, async () => {
const kaFlag = `--projectId=${KaPID} --env=${landscape.slug} --type=shared ${KaKey}`;
console.log("⛳ Flags: ", kaFlag);
await $`infisical secrets delete ${{ raw: kaFlag }}`;
}]);

await this.task.Run([`Deleting Sulfoxide Helium for ${l} ${c}`, async () => {
const heFlag = `--projectId=${HePID} --env=${landscape.slug} --type=shared ${HeKey}`;
console.log("⛳ Flags: ", heFlag);
await $`infisical secrets delete ${{ raw: heFlag }}`;
}]);

await this.task.Run([
`Deleting Kubernetes Access for ${l} ${c}`,
async () => {
const kaFlag = `--projectId=${KaPID} --env=${landscape.slug} --type=shared ${KaKey}`;
console.log('⛳ Flags: ', kaFlag);
await $`infisical secrets delete ${{ raw: kaFlag }}`;
},
]);

await this.task.Run([
`Deleting Sulfoxide Helium for ${l} ${c}`,
async () => {
const heFlag = `--projectId=${HePID} --env=${landscape.slug} --type=shared ${HeKey}`;
console.log('⛳ Flags: ', heFlag);
await $`infisical secrets delete ${{ raw: heFlag }}`;
},
]);
}
}

Expand Down
33 changes: 23 additions & 10 deletions src/init/runbooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { GenericAdminClusterCloudRestorer } from '../books/restore-admin-cluster
import { AdminClusterRestorer } from '../books/restore-admin-cluster';
import { VultrBareAdminClusterCreator } from '../books/bare-admin-cluster-creation/vultr.ts';
import { VultrFullAdminClusterCreator } from '../books/full-admin-cluster-creation/vultr.ts';
import { PhysicalClusterMigrator } from '../books/physical-cluster-migration';
import { PhysicalClusterTransitioner } from '../books/physical-cluster-migration/transition.ts';

function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {
const sulfoxide = SERVICE_TREE.sulfoxide;
Expand Down Expand Up @@ -71,7 +73,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {
CLOUDS.AWS.slug,
),
];
const physicalClusterCreator = new PhysicalClusterCreator(
const phyClusterCreator = new PhysicalClusterCreator(
d.taskRunner,
d.stp,
t.nitrosoWaiter,
Expand Down Expand Up @@ -115,7 +117,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {
),
];

const phyGracefulDestructor = new GracefulPhysicalClusterDestructor(
const phyClusterGracefulDestructor = new GracefulPhysicalClusterDestructor(
d.stp,
d.serviceTreePrinter,
phyGracefulDestructors,
Expand All @@ -134,7 +136,17 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {
sulfoxide.services.argocd,
LANDSCAPE_TREE.v,
);
const phyPurger = new PhysicalClusterPurger(d.stp, d.serviceTreePrinter, genericPhyPurgeCloud);
const phyClusterPurger = new PhysicalClusterPurger(d.stp, d.serviceTreePrinter, genericPhyPurgeCloud);

// migrate physical cluster
const phyTransitioner = new PhysicalClusterTransitioner(d.taskRunner, t.lbDNSSwitcher);
const phyClusterMigrator = new PhysicalClusterMigrator(
d.stp,
d.serviceTreePrinter,
phyClusterCreators,
phyGracefulDestructors,
phyTransitioner,
);

// bare admin cluster creation
const bareAdminCloudCreators: BareAdminClusterCloudCreator[] = [
Expand All @@ -159,7 +171,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {
CLOUDS.Vultr.slug,
),
];
const bareAdminClusterCreator = new BareAdminClusterCreator(d.stp, d.serviceTreePrinter, bareAdminCloudCreators);
const adminClusterBareCreator = new BareAdminClusterCreator(d.stp, d.serviceTreePrinter, bareAdminCloudCreators);

// full admin cluster creation
const fullAdminCloudCreators: FullAdminClusterCloudCreator[] = [
Expand All @@ -183,7 +195,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {
),
];

const fullAdminCloudCreator = new FullAdminClusterCreator(
const adminClusterFullCreator = new FullAdminClusterCreator(
d.stp,
d.serviceTreePrinter,
bareAdminCloudCreators,
Expand Down Expand Up @@ -277,13 +289,14 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] {

return [
// physical cluster
physicalClusterCreator,
phyGracefulDestructor,
phyPurger,
phyClusterCreator,
phyClusterGracefulDestructor,
phyClusterPurger,
phyClusterMigrator,

// admin cluster
bareAdminClusterCreator,
fullAdminCloudCreator,
adminClusterBareCreator,
adminClusterFullCreator,
adminGracefulDestructor,
adminClusterMigrator,
purgeAdminCluster,
Expand Down
5 changes: 4 additions & 1 deletion src/tasks/lb-dns-switcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@ class LoadBalancerDNSSwitcher {
console.log(`✅ DNS now points to ${target.name}`);
await $`sleep 120`;
console.log('🔍 Checking if DNS has propagated...');

const query = async (): Promise<string> => {
console.log(`🖥️ dog pichu.${this.external} -J`);
const result = await $`dog pichu.${this.external} -J`.cwd(path).json();
const answers: { name: string; type: string; data: { domain: string } }[] = result.responses[0].answers;
return answers.find(x => x.type === 'CNAME')?.data.domain ?? '';
};
const t = `${target.slug}.${this.internal}`;
const t = `${target.slug}.${this.internal}.`;
let domain = await query();
while (domain != t) {
console.log(`⏳ Waiting for DNS to propagate...`);
await $`sleep 5`;
domain = await query();
console.log(`✅ Check: ${domain}, Target: ${t}`);
}
console.log(`✅ DNS propagated!`);
},
Expand Down

0 comments on commit 8622ca9

Please sign in to comment.