Skip to content

Commit

Permalink
Merge pull request #4 from AtomiCloud/ernest/sul-68-split-cluster-del…
Browse files Browse the repository at this point in the history
…etion-for-aws-out

feat: allow AWS runbook deletion
  • Loading branch information
kirinnee authored Aug 27, 2024
2 parents 636d0c5 + 730891d commit 1f08468
Show file tree
Hide file tree
Showing 11 changed files with 404 additions and 71 deletions.
6 changes: 3 additions & 3 deletions src/books/bare-admin-cluster-creation/digital-ocean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class DigitalOceanBareAdminClusterCreator implements BareAdminClusterCloudCreato
async () => {
await $`pls setup`.cwd(tofuDir);
await $`pls ${{ raw: L0 }}:init`.cwd(tofuDir);
await $`pls ${{ raw: L0 }}:apply`.cwd(tofuDir);
await $`pls ${{ raw: L0 }}:apply -- -auto-approve`.cwd(tofuDir);
},
]);

Expand All @@ -72,7 +72,7 @@ class DigitalOceanBareAdminClusterCreator implements BareAdminClusterCloudCreato
'Build L1 Generic Infrastructure',
async () => {
await $`pls ${{ raw: L1G }}:init`.cwd(tofuDir);
await $`pls ${{ raw: L1G }}:apply`.cwd(tofuDir);
await $`pls ${{ raw: L1G }}:apply -- -auto-approve`.cwd(tofuDir);
},
]);

Expand All @@ -81,7 +81,7 @@ class DigitalOceanBareAdminClusterCreator implements BareAdminClusterCloudCreato
'Build L1 Infrastructure',
async () => {
await $`pls ${{ raw: L1 }}:init`.cwd(tofuDir);
await $`pls ${{ raw: L1 }}:apply`.cwd(tofuDir);
await $`pls ${{ raw: L1 }}:apply -- -auto-approve`.cwd(tofuDir);
},
]);

Expand Down
6 changes: 3 additions & 3 deletions src/books/graceful-admin-cluster-destruction/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class GenericGracefulAdminClusterDestructor {
'Destroy Generic Infrastructure',
async () => {
await $`pls ${{ raw: L1G }}:init`.cwd(tofuDir);
await $`pls ${{ raw: L1G }}:destroy`.cwd(tofuDir);
await $`pls ${{ raw: L1G }}:destroy -- -auto-approve`.cwd(tofuDir);
},
]);

Expand All @@ -77,7 +77,7 @@ class GenericGracefulAdminClusterDestructor {
async () => {
await $`pls ${{ raw: L1 }}:init`.cwd(tofuDir);
await $`pls ${{ raw: L1 }}:state:rm -- 'kubernetes_namespace.sulfoxide'`.cwd(tofuDir).nothrow();
await $`pls ${{ raw: L1 }}:destroy`.cwd(tofuDir);
await $`pls ${{ raw: L1 }}:destroy -- -auto-approve`.cwd(tofuDir);
},
]);

Expand All @@ -86,7 +86,7 @@ class GenericGracefulAdminClusterDestructor {
await this.task.Run([
'Destroy L0 Infrastructure',
async () => {
await $`pls ${{ raw: L0 }}:destroy`.cwd(tofuDir);
await $`pls ${{ raw: L0 }}:destroy -- -auto-approve`.cwd(tofuDir);
},
]);

Expand Down
289 changes: 289 additions & 0 deletions src/books/graceful-physical-cluster-destruction/aws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
import type {
LandscapeCluster,
ServiceTreeLandscapePrincipal,
ServiceTreeService,
} from '../../lib/service-tree-def.ts';
import path from 'node:path';
import { $ } from 'bun';
import { KubectlUtil, type ResourceSearch } from '../../lib/utility/kubectl-util.ts';
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';

class AwsGracefulPhysicalClusterDestructor implements GracefulClusterCloudDestructor {
slug: string;

constructor(
private task: TaskRunner,
private y: YamlManipulator,
private k: KubectlUtil,
private up: UtilPrompter,
private sulfoxideTofu: ServiceTreeService,
private sulfoxideHelium: ServiceTreeService,
private sulfoxideGold: ServiceTreeService,
private virtualLandscapes: ServiceTreeLandscapePrincipal[],
slug: string,
) {
this.slug = slug;
}

async Run(
[phyLandscape, phyCluster]: LandscapeCluster,
[adminLandscape, adminCluster]: LandscapeCluster,
): Promise<void> {
const phy = { landscape: phyLandscape, cluster: phyCluster };
const admin = { landscape: adminLandscape, cluster: adminCluster };

// common variables
const tofu = this.sulfoxideTofu;
const He = this.sulfoxideHelium;
const Au = this.sulfoxideGold;

const pCtx = `${phy.landscape.slug}-${phy.cluster.principal.slug}`;
const aCtx = `${admin.landscape.slug}-${admin.cluster.principal.slug}`;
const aNS = `${He.platform.slug}-${He.principal.slug}`;

const tofuDir = `./platforms/${tofu.platform.slug}/${tofu.principal.slug}`;
const He_Dir = `./platforms/${He.platform.slug}/${He.principal.slug}`;

const yamlPath = path.join(He_Dir, 'chart', `values.${admin.landscape.slug}.${admin.cluster.set.slug}.yaml`);

// Update ArgoCD configurations
await this.task.Run([
'Update Helium Configuration',
async () => {
console.log(`🗑️ Removing ArgoCD configurations. Path: ${yamlPath}`);
await this.y.Mutate(yamlPath, [
[['connector', 'clusters', phy.landscape.slug, phy.cluster.principal.slug, 'enable'], false],
[['connector', 'clusters', phy.landscape.slug, phy.cluster.principal.slug, 'deployAppSet'], false],
[['connector', 'clusters', phy.landscape.slug, phy.cluster.principal.slug, 'aoa', 'enable'], false],
[['connector', 'clusters', phy.landscape.slug, phy.cluster.principal.slug, 'destination'], ''],
]);
},
]);

// Apply ArgoCD configurations
const adminPls = `${admin.landscape.slug}:${admin.cluster.set.slug}`;
await this.task.Run([
'Apply Helium Configuration',
async () => {
await $`pls ${{ raw: adminPls }}:install -- --kube-context ${aCtx} --namespace ${aNS}`.cwd(He_Dir);
},
]);

// Must clean up load balancer properly
await this.task.Run([
'Delete Internal Ingress App',
async () => {
// delete root app
await this.k.Delete({
kind: 'app',
context: aCtx,
namespace: aNS,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-carbon`,
});
await this.k.DeleteRange({
kind: 'app',
context: aCtx,
namespace: aNS,
selector: [
['atomi.cloud/cluster', phy.cluster.principal.slug],
['atomi.cloud/landscape', phy.landscape.slug],
['atomi.cloud/element', Au.principal.slug],
],
});
},
]);

await this.task.Run([
'Delete Internal Ingress Service',
async () => {
const name = `${phy.landscape.slug}-${Au.principal.slug}-ingress-nginx-controller`;
await this.k.Delete({
kind: 'service',
context: pCtx,
namespace: Au.platform.slug,
name,
});

await this.k.Wait(0, 5, {
kind: 'service',
context: pCtx,
namespace: Au.platform.slug,
fieldSelector: [['metadata.name', name]],
});
},
]);

// delete applications from ArgoCD
const appsToRemove: ResourceSearch = {
kind: 'app',
context: aCtx,
namespace: aNS,
selector: [['atomi.cloud/cluster', phy.cluster.principal.slug]],
};

const deleteApps = async () => {
console.log(`🗑️ Delete Root Application: ${phy.landscape.slug}-${phy.cluster.principal.slug}-carbon`);
await this.k.Delete({
kind: 'app',
context: aCtx,
namespace: aNS,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-carbon`,
});
console.log('✅ Root Application deleted');

console.log('🗑️ Deleting applications...');
await this.k.DeleteRange(appsToRemove);
console.log('✅ Applications deleted');
};

await this.task.Run(['Delete Applications', deleteApps]);

await this.task.Run([
'Wait for Applications to be deleted',
async () => {
return await this.k.Wait(0, 3, appsToRemove, {
count: 3,
action: async () => {
const deleteApp = await this.up.YesNo('Do you want to manually delete the applications?');
if (deleteApp) await deleteApps();
return false;
},
});
},
]);

// Delete all validating webhooks
await this.task.Run([
'Delete Validating Webhooks',
async () => {
await $`kubectl --context ${pCtx} delete validatingwebhookconfigurations --all`.nothrow();
},
]);

// Delete all namespaces
await this.task.Run([
'Delete Namespaces',
async () => {
for (const namespace of ['pichu', 'pikachu', 'raichu', 'sulfoxide']) {
console.log(` 🗑️ Removing namespace: ${namespace}`);
await this.k.DeleteNamespace({
context: pCtx,
namespace,
});
console.log(` ✅ Namespace removed: ${namespace}`);
}
},
]);

// Wait for Nodes to be decommissioned
await this.task.Exec([
'Wait for Nodes to be decommissioned',
async () => {
await this.k.Wait(0, 5, {
kind: 'node',
context: pCtx,
selector: [['karpenter.sh/nodepool', 'bottlerocket']],
});
},
]);

// setup tofu repository correctly
await this.task.Run([
'Setup Tofu',
async () => {
await $`pls setup`.cwd(tofuDir);
},
]);

// destroy generic infrastructure
const L1G = `${phy.landscape.slug}:l1:${phy.cluster.set.slug}`;
await this.task.Run([
'Destroy Generic Infrastructure',
async () => {
await $`pls ${{ raw: L1G }}:destroy -- -auto-approve`.cwd(tofuDir);
},
]);

// destroy L1 infrastructure
const L1 = `${phy.landscape.slug}:l1:${phy.cluster.principal.slug}`;
await this.task.Run([
'Destroy L1 Infrastructure',
async () => {
await $`pls ${{ raw: L1 }}:state:rm -- 'kubernetes_namespace.sulfoxide'`.cwd(tofuDir).nothrow();
await $`pls ${{ raw: L1 }}:destroy -- -auto-approve`.cwd(tofuDir);
},
]);

// destroy L0 infrastructure
const L0 = `${phy.landscape.slug}:l0:${phy.cluster.principal.slug}`;
await this.task.Run([
'Destroy L0 Infrastructure',
async () => {
await $`pls ${{ raw: L0 }}:state:rm -- 'module.cluster.module.proxy_secret.kubernetes_namespace.kubernetes-access'`
.cwd(tofuDir)
.nothrow();
await $`pls ${{ raw: L0 }}:destroy -- -auto-approve`.cwd(tofuDir);
},
]);

// update kubectl configurations
await this.task.Run([
'Retrieve Kubectl Configurations',
async () => {
await $`pls kubectl`;
},
]);

await this.task.Run([
'Delete ExternalSecret in admin',
async () => {
for (const ns of this.virtualLandscapes.map(x => x.slug)) {
await this.k.Delete({
kind: 'externalsecret',
context: aCtx,
namespace: aNS,
name: `phase-6-${ns}-${phy.cluster.principal.slug}-cluster-secret`,
});
}
await this.k.Delete({
kind: 'externalsecret',
context: aCtx,
namespace: aNS,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret`,
});
await this.k.Delete({
kind: 'externalsecret',
context: aCtx,
namespace: aNS,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret-bearer-token`,
});
await this.k.Delete({
kind: 'externalsecret',
context: aCtx,
namespace: aNS,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret-ca-crt`,
});
},
]);

// delete pointers to old cluster in admin
await this.task.Run([
'Delete SecretStore in admin',
async () => {
for (const ns of this.virtualLandscapes.map(x => x.slug)) {
await this.k.Delete({
kind: 'secretstore',
context: aCtx,
namespace: aNS,
name: `phase-6-${ns}-${phy.cluster.principal.slug}`,
});
}
},
]);
}
}

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

interface GracefulClusterCloudDestructor {
slug: string;

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

export type { GracefulClusterCloudDestructor };
Loading

0 comments on commit 1f08468

Please sign in to comment.