Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow AWS runbook deletion #4

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading