Skip to content

Commit

Permalink
feat: runbook to create/destroy phy vultr clusters
Browse files Browse the repository at this point in the history
  • Loading branch information
kirinnee committed Sep 2, 2024
1 parent 1f08468 commit 3bb23da
Show file tree
Hide file tree
Showing 5 changed files with 452 additions and 1 deletion.
230 changes: 230 additions & 0 deletions src/books/graceful-physical-cluster-destruction/vultr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
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 VultrGracefulPhysicalClusterDestructor implements GracefulClusterCloudDestructor {
slug: string;

constructor(
private task: TaskRunner,
private y: YamlManipulator,
private k: KubectlUtil,
private up: UtilPrompter,
private sulfoxideTofu: ServiceTreeService,
private sulfoxideHelium: 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 argo = this.sulfoxideHelium;
const tofu = this.sulfoxideTofu;
const phyContextSlug = `${phy.landscape.slug}-${phy.cluster.principal.slug}`;
const adminContextSlug = `${admin.landscape.slug}-${admin.cluster.principal.slug}`;
const adminNamespaceSlug = `${argo.platform.slug}-${argo.principal.slug}`;
const tofuDir = `./platforms/${tofu.platform.slug}/${tofu.principal.slug}`;
const heliumDir = `./platforms/${argo.platform.slug}/${argo.principal.slug}`;
const yamlPath = path.join(heliumDir, '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 ${adminContextSlug} --namespace ${adminNamespaceSlug}`.cwd(
heliumDir,
);
},
]);

// delete applications from ArgoCD
const appsToRemove: ResourceSearch = {
kind: 'app',
context: adminContextSlug,
namespace: adminNamespaceSlug,
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: adminContextSlug,
namespace: adminNamespaceSlug,
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 ${phyContextSlug} 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: phyContextSlug,
namespace,
});
console.log(` ✅ Namespace removed: ${namespace}`);
}
},
]);

// 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 ${L1G}`,
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 ${L1}`,
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 ${L0}`,
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: adminContextSlug,
namespace: adminNamespaceSlug,
name: `phase-5-${ns}-${phy.cluster.principal.slug}-cluster-secret`,
});
}
await this.k.Delete({
kind: 'externalsecret',
context: adminContextSlug,
namespace: adminNamespaceSlug,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret`,
});
await this.k.Delete({
kind: 'externalsecret',
context: adminContextSlug,
namespace: adminNamespaceSlug,
name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret-bearer-token`,
});
await this.k.Delete({
kind: 'externalsecret',
context: adminContextSlug,
namespace: adminNamespaceSlug,
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: adminContextSlug,
namespace: adminNamespaceSlug,
name: `phase-5-${ns}-${phy.cluster.principal.slug}`,
});
}
},
]);
}
}

export { VultrGracefulPhysicalClusterDestructor };
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 @@ -129,7 +129,7 @@ class DigitalOceanPhysicalClusterCreator implements PhysicalClusterCloudCreator
context: adminContextSlug,
namespace: adminNamespaceSlug,
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
Loading

0 comments on commit 3bb23da

Please sign in to comment.