From e13f2c28c5fb8570862e8f011ec936c5c7fda064 Mon Sep 17 00:00:00 2001 From: kirinnee Date: Mon, 26 Aug 2024 18:26:39 +0800 Subject: [PATCH] feat: aws creation runbook --- .prettierrc.yaml | 8 + src/books/admin-cluster-migration/index.ts | 43 ++- .../admin-cluster-migration/transition.ts | 61 ++-- .../digital-ocean.ts | 63 ++-- .../digital-ocean.ts | 35 +-- .../generic.ts | 29 +- src/books/physical-cluster-creation/aws.ts | 258 +++++++++++++++ .../digital-ocean.ts | 167 ++++------ src/init/index.ts | 4 + src/init/runbooks.ts | 77 ++--- src/init/tasks.ts | 27 +- src/lib/service-tree.ts | 293 +++++++++--------- src/lib/utility/kubectl-util.ts | 106 +++---- src/tasks/sulfoxide-boron-waiter.ts | 14 +- src/tasks/sulfoxide-helium-waiter.ts | 12 +- src/tasks/sulfoxide-xenon-waiter.ts | 10 +- 16 files changed, 675 insertions(+), 532 deletions(-) create mode 100644 .prettierrc.yaml create mode 100644 src/books/physical-cluster-creation/aws.ts diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..a455290 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,8 @@ +tabWidth: 2 +semi: true +singleQuote: true +bracketSpacing: true +trailingComma: all +arrowParens: avoid +printWidth: 120 +singleAttributePerLine: false diff --git a/src/books/admin-cluster-migration/index.ts b/src/books/admin-cluster-migration/index.ts index 2b9732d..86baa29 100644 --- a/src/books/admin-cluster-migration/index.ts +++ b/src/books/admin-cluster-migration/index.ts @@ -1,13 +1,13 @@ -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 { BareAdminClusterCloudCreator } from "../bare-admin-cluster-creation/cloud.ts"; -import type { GenericGracefulAdminClusterDestructor } from "../graceful-admin-cluster-destruction/generic.ts"; -import type { AdminClusterTransitioner } from "./transition.ts"; +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 { BareAdminClusterCloudCreator } from '../bare-admin-cluster-creation/cloud.ts'; +import type { GenericGracefulAdminClusterDestructor } from '../graceful-admin-cluster-destruction/generic.ts'; +import type { AdminClusterTransitioner } from './transition.ts'; class AdminClusterMigrator implements RunBook { - name: string = "Admin Cluster Migration"; - desc: string = "Migrate an admin cluster to a new cluster"; + name: string = 'Admin Cluster Migration'; + desc: string = 'Migrate an admin cluster to a new cluster'; constructor( private stp: ServiceTreePrompter, @@ -19,31 +19,28 @@ class AdminClusterMigrator implements RunBook { async Run(): Promise { const [fromLandscape, fromCluster] = await this.stp.AdminLandscapeCluster( - "Select the admin landscape to migrate from", - "Select the admin cloud to migrate from", - "Select the admin cluster to migrate from", + 'Select the admin landscape to migrate from', + 'Select the admin cloud to migrate from', + 'Select the admin cluster to migrate from', ); const [toLandscape, toCluster] = await this.stp.AdminLandscapeCluster( - "Select the admin landscape to migrate to", - "Select the admin cloud to migrate to", - "Select the admin cluster to migrate to", + 'Select the admin landscape to migrate to', + 'Select the admin cloud to migrate to', + 'Select the admin cluster to migrate to', ); - console.log("🎯 Selected Service Tree to migrate"); - this.printer.Print("From", [fromLandscape, fromCluster]); - this.printer.Print("To ", [toLandscape, toCluster]); + console.log('🎯 Selected Service Tree to migrate'); + this.printer.Print('From', [fromLandscape, fromCluster]); + this.printer.Print('To ', [toLandscape, toCluster]); // select the cloud to create the new cluster in - const c = this.clouds.find((x) => x.slug === toCluster.cloud.slug); - if (!c) return console.log("⚠️ Cloud not supported"); + const c = this.clouds.find(x => x.slug === toCluster.cloud.slug); + if (!c) return console.log('⚠️ Cloud not supported'); await c.Run([toLandscape, toCluster]); // perform transition - await this.transition.Run( - [fromLandscape, fromCluster], - [toLandscape, toCluster], - ); + await this.transition.Run([fromLandscape, fromCluster], [toLandscape, toCluster]); // destroy old cluster await this.destructors.Run([fromLandscape, fromCluster]); diff --git a/src/books/admin-cluster-migration/transition.ts b/src/books/admin-cluster-migration/transition.ts index 4b4ddaf..9d6a867 100644 --- a/src/books/admin-cluster-migration/transition.ts +++ b/src/books/admin-cluster-migration/transition.ts @@ -1,12 +1,9 @@ -import type { - LandscapeCluster, - ServiceTreeService, -} from "../../lib/service-tree-def.ts"; -import type { TaskRunner } from "../../tasks/tasks.ts"; -import type { KubectlUtil, Resource } from "../../lib/utility/kubectl-util.ts"; -import { $ } from "bun"; -import type { SulfoxideHeliumWaiter } from "../../tasks/sulfoxide-helium-waiter.ts"; -import type { SulfoxideBoronWaiter } from "../../tasks/sulfoxide-boron-waiter.ts"; +import type { LandscapeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts'; +import type { TaskRunner } from '../../tasks/tasks.ts'; +import type { KubectlUtil, Resource } from '../../lib/utility/kubectl-util.ts'; +import { $ } from 'bun'; +import type { SulfoxideHeliumWaiter } from '../../tasks/sulfoxide-helium-waiter.ts'; +import type { SulfoxideBoronWaiter } from '../../tasks/sulfoxide-boron-waiter.ts'; class AdminClusterTransitioner { constructor( @@ -19,10 +16,7 @@ class AdminClusterTransitioner { private sulfoxideBoronWaiter: SulfoxideBoronWaiter, ) {} - async Run( - [fL, fC]: LandscapeCluster, - [tL, tC]: LandscapeCluster, - ): Promise { + async Run([fL, fC]: LandscapeCluster, [tL, tC]: LandscapeCluster): Promise { // common variables // services @@ -41,7 +35,7 @@ class AdminClusterTransitioner { // dir const F_Dir = `./platforms/${F.platform.slug}/${F.principal.slug}`; - console.log("🎯 Both systems are operational, performing transition..."); + console.log('🎯 Both systems are operational, performing transition...'); const prefix = `${He_NS}-argocd`; const resources: Resource[] = [ ...[ @@ -50,14 +44,14 @@ class AdminClusterTransitioner { `${prefix}-redis`, `${prefix}-server`, `${prefix}-repo-server`, - ].map((name) => ({ - kind: "deployment", + ].map(name => ({ + kind: 'deployment', context: fCtx, namespace: He_NS, name, })), { - kind: "statefulset", + kind: 'statefulset', context: fCtx, namespace: He_NS, name: `${prefix}-application-controller`, @@ -66,17 +60,16 @@ class AdminClusterTransitioner { const replicas: Record = {}; await this.t.Run([ - "Get Replicas", + 'Get Replicas', async () => { - for (const resource of resources) - replicas[resource.name] = await this.k.GetReplica(resource); + for (const resource of resources) replicas[resource.name] = await this.k.GetReplica(resource); }, ]); - console.log("🔀 Replicas before scaling down:", replicas); + console.log('🔀 Replicas before scaling down:', replicas); await this.t.Run([ - "Scale Down Old Cluster", + 'Scale Down Old Cluster', async () => { for (const resource of resources) await this.k.Scale(resource, 0); }, @@ -84,47 +77,45 @@ class AdminClusterTransitioner { // perform migration via velero await this.t.Run([ - "Velero Migration", + 'Velero Migration', async () => { await $`nix develop -c pls migrate -- ${fCtx} ${tCtx}`.cwd(F_Dir); }, ]); await this.t.Exec([ - "Wait for Helium to be migrated", + 'Wait for Helium to be migrated', async () => { await this.k.Wait(5, 5, { - kind: "deployment", + kind: 'deployment', context: tCtx, namespace: He_NS, - selector: [["app.kubernetes.io/part-of", `argocd`]], + selector: [['app.kubernetes.io/part-of', `argocd`]], }); await this.k.Wait(1, 5, { - kind: "statefulset", + kind: 'statefulset', context: tCtx, namespace: He_NS, - selector: [["app.kubernetes.io/part-of", `argocd`]], + selector: [['app.kubernetes.io/part-of', `argocd`]], }); }, ]); await this.t.Exec([ - "Wait for Boron to be migrated", + 'Wait for Boron to be migrated', async () => { await this.k.Wait(1, 5, { - kind: "deployment", + kind: 'deployment', context: tCtx, namespace: B_NS, - fieldSelector: [ - ["metadata.name", `${B.platform.slug}-${B.principal.slug}`], - ], + fieldSelector: [['metadata.name', `${B.platform.slug}-${B.principal.slug}`]], }); }, ]); // Scale back up the new cluster await this.t.Run([ - "Scale Up New Cluster", + 'Scale Up New Cluster', async () => { for (const resource of resources) { resource.context = tCtx; @@ -140,7 +131,7 @@ class AdminClusterTransitioner { const waitForBoron = this.sulfoxideBoronWaiter.task(tCtx, B_NS); await this.t.Run(waitForBoron); - console.log("🎉 Transition completed!"); + console.log('🎉 Transition completed!'); } } diff --git a/src/books/bare-admin-cluster-creation/digital-ocean.ts b/src/books/bare-admin-cluster-creation/digital-ocean.ts index 6b2d652..4a1c4e3 100644 --- a/src/books/bare-admin-cluster-creation/digital-ocean.ts +++ b/src/books/bare-admin-cluster-creation/digital-ocean.ts @@ -1,18 +1,13 @@ -import type { TaskRunner } from "../../tasks/tasks.ts"; -import type { UtilPrompter } from "../../lib/prompts/util-prompter.ts"; -import type { - LandscapeCluster, - ServiceTreeService, -} from "../../lib/service-tree-def.ts"; -import { input } from "@inquirer/prompts"; -import { $ } from "bun"; -import type { BareAdminClusterCloudCreator } from "./cloud.ts"; -import type { KubectlUtil } from "../../lib/utility/kubectl-util.ts"; -import type {SulfoxideXenonWaiter} from "../../tasks/sulfoxide-xenon-waiter.ts"; +import type { TaskRunner } from '../../tasks/tasks.ts'; +import type { UtilPrompter } from '../../lib/prompts/util-prompter.ts'; +import type { LandscapeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts'; +import { input } from '@inquirer/prompts'; +import { $ } from 'bun'; +import type { BareAdminClusterCloudCreator } from './cloud.ts'; +import type { KubectlUtil } from '../../lib/utility/kubectl-util.ts'; +import type { SulfoxideXenonWaiter } from '../../tasks/sulfoxide-xenon-waiter.ts'; -class DigitalOceanBareAdminClusterCreator - implements BareAdminClusterCloudCreator -{ +class DigitalOceanBareAdminClusterCreator implements BareAdminClusterCloudCreator { slug: string; constructor( @@ -40,20 +35,16 @@ class DigitalOceanBareAdminClusterCreator const context = `${admin.landscape.slug}-${admin.cluster.principal.slug}`; // Check if we want to inject the DO secrets - const doSecrets = await this.up.YesNo( - "Do you want to inject Digital Ocean secrets?", - ); + const doSecrets = await this.up.YesNo('Do you want to inject Digital Ocean secrets?'); if (doSecrets) { - const token = await input({ message: "Enter your Digital Ocean token" }); + const token = await input({ message: 'Enter your Digital Ocean token' }); await $`infisical secrets set --projectId=${tofu.principal.projectId} --env=${admin.landscape.slug} ${admin.cluster.principal.slug.toUpperCase()}_DIGITALOCEAN_TOKEN=${token}`; - console.log( - `✅ Digital Ocean secrets for '${admin.landscape.name}' '${admin.cluster.principal.name} injected`, - ); + console.log(`✅ Digital Ocean secrets for '${admin.landscape.name}' '${admin.cluster.principal.name} injected`); } const L0 = `${admin.landscape.slug}:l0:${admin.cluster.principal.slug}`; await this.task.Run([ - "Build L0 Infrastructure", + 'Build L0 Infrastructure', async () => { await $`pls setup`.cwd(tofuDir); await $`pls ${{ raw: L0 }}:init`.cwd(tofuDir); @@ -62,17 +53,15 @@ class DigitalOceanBareAdminClusterCreator ]); await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; }, ]); // extract endpoint to use - console.log("📤 Extract endpoint to use..."); - const output = await $`pls ${{ raw: L0 }}:output -- -json` - .cwd(tofuDir) - .json(); + console.log('📤 Extract endpoint to use...'); + const output = await $`pls ${{ raw: L0 }}:output -- -json`.cwd(tofuDir).json(); const endpoint = output.cluster_endpoint.value; console.log(`✅ Extracted endpoint: ${endpoint}`); @@ -80,7 +69,7 @@ class DigitalOceanBareAdminClusterCreator const L1G = `${admin.landscape.slug}:l1:${admin.cluster.set.slug}`; const L1 = `${admin.landscape.slug}:l1:${admin.cluster.principal.slug}`; await this.task.Run([ - "Build L1 Generic Infrastructure", + 'Build L1 Generic Infrastructure', async () => { await $`pls ${{ raw: L1G }}:init`.cwd(tofuDir); await $`pls ${{ raw: L1G }}:apply`.cwd(tofuDir); @@ -89,7 +78,7 @@ class DigitalOceanBareAdminClusterCreator // build L1 infrastructure await this.task.Run([ - "Build L1 Infrastructure", + 'Build L1 Infrastructure', async () => { await $`pls ${{ raw: L1 }}:init`.cwd(tofuDir); await $`pls ${{ raw: L1 }}:apply`.cwd(tofuDir); @@ -98,7 +87,7 @@ class DigitalOceanBareAdminClusterCreator // setup velero backup engine await this.task.Run([ - "Setup Velero Backup Engine", + 'Setup Velero Backup Engine', async () => { await $`nix develop -c pls setup`.cwd(F_Dir); }, @@ -106,7 +95,7 @@ class DigitalOceanBareAdminClusterCreator // install velero backup engine await this.task.Run([ - "Install Velero Backup Engine", + 'Install Velero Backup Engine', async () => { await $`nix develop -c pls velero:${{ raw: admin.landscape.slug }}:install -- --kubecontext ${{ raw: context }}`.cwd( F_Dir, @@ -115,27 +104,27 @@ class DigitalOceanBareAdminClusterCreator ]); await this.task.Exec([ - "Wait for Velero to be ready", + 'Wait for Velero to be ready', async () => { await this.k.WaitForReplica({ - namespace: "velero", - name: "velero", + namespace: 'velero', + name: 'velero', context: context, - kind: "deployment", + kind: 'deployment', }); await $`kubectl --context ${{ raw: context }} -n velero wait --for=jsonpath=.status.phase=Available --timeout=6000s BackupStorageLocation default`; }, ]); await this.task.Run([ - "Deploy Metrics Server", + 'Deploy Metrics Server', async () => { const plsXe = `${landscape.slug}:${cluster.set.slug}`; await $`nix develop -c pls ${{ raw: plsXe }}:install -- --kube-context ${context} -n kube-system`.cwd(Xe_Dir); }, ]); - const xenonWaiter = this.sulfoxideXenonWaiter.task(context, "kube-system"); + const xenonWaiter = this.sulfoxideXenonWaiter.task(context, 'kube-system'); await this.task.Exec(xenonWaiter); } } diff --git a/src/books/full-admin-cluster-creation/digital-ocean.ts b/src/books/full-admin-cluster-creation/digital-ocean.ts index 633f5af..0011382 100644 --- a/src/books/full-admin-cluster-creation/digital-ocean.ts +++ b/src/books/full-admin-cluster-creation/digital-ocean.ts @@ -1,16 +1,11 @@ -import { $ } from "bun"; -import type { - LandscapeCluster, - ServiceTreeService, -} from "../../lib/service-tree-def.ts"; -import type { FullAdminClusterCloudCreator } from "./cloud.ts"; -import type { TaskRunner } from "../../tasks/tasks.ts"; -import type { SulfoxideHeliumWaiter } from "../../tasks/sulfoxide-helium-waiter.ts"; -import type { SulfoxideBoronWaiter } from "../../tasks/sulfoxide-boron-waiter.ts"; +import { $ } from 'bun'; +import type { LandscapeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts'; +import type { FullAdminClusterCloudCreator } from './cloud.ts'; +import type { TaskRunner } from '../../tasks/tasks.ts'; +import type { SulfoxideHeliumWaiter } from '../../tasks/sulfoxide-helium-waiter.ts'; +import type { SulfoxideBoronWaiter } from '../../tasks/sulfoxide-boron-waiter.ts'; -class DigitalOceanFullAdminClusterCreator - implements FullAdminClusterCloudCreator -{ +class DigitalOceanFullAdminClusterCreator implements FullAdminClusterCloudCreator { slug: string; constructor( @@ -37,19 +32,17 @@ class DigitalOceanFullAdminClusterCreator const boronNS = `${boron.platform.slug}-${boron.principal.slug}`; await this.task.Run([ - "Create Helium Namespace", + 'Create Helium Namespace', async () => { await $`kubectl create --context ${context} ns ${namespace}`; }, ]); await this.task.Run([ - "Create Helium Helm Release", + 'Create Helium Helm Release', async () => { const heliumPls = `${admin.landscape.slug}:${admin.cluster.set.slug}`; - await $`pls ${{ raw: heliumPls }}:install -- --kube-context ${context} -n ${namespace}`.cwd( - heliumDir, - ); + await $`pls ${{ raw: heliumPls }}:install -- --kube-context ${context} -n ${namespace}`.cwd(heliumDir); }, ]); @@ -57,19 +50,17 @@ class DigitalOceanFullAdminClusterCreator await this.task.Run(waitForHelium); await this.task.Run([ - "Create Boron Namespace", + 'Create Boron Namespace', async () => { await $`kubectl create --context ${context} ns ${boronNS}`; }, ]); await this.task.Run([ - "Create Boron Helm Release", + 'Create Boron Helm Release', async () => { const boronPls = `${admin.landscape.slug}:${admin.cluster.set.slug}`; - await $`pls ${{ raw: boronPls }}:install -- --kube-context ${context} -n ${boronNS}`.cwd( - boronDir, - ); + await $`pls ${{ raw: boronPls }}:install -- --kube-context ${context} -n ${boronNS}`.cwd(boronDir); }, ]); diff --git a/src/books/graceful-admin-cluster-destruction/generic.ts b/src/books/graceful-admin-cluster-destruction/generic.ts index a71ec52..40bea99 100644 --- a/src/books/graceful-admin-cluster-destruction/generic.ts +++ b/src/books/graceful-admin-cluster-destruction/generic.ts @@ -1,10 +1,7 @@ -import type { - LandscapeCluster, - ServiceTreeService, -} from "../../lib/service-tree-def.ts"; -import { $ } from "bun"; -import type { TaskRunner } from "../../tasks/tasks.ts"; -import type { KubectlUtil } from "../../lib/utility/kubectl-util.ts"; +import type { LandscapeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts'; +import { $ } from 'bun'; +import type { TaskRunner } from '../../tasks/tasks.ts'; +import type { KubectlUtil } from '../../lib/utility/kubectl-util.ts'; class GenericGracefulAdminClusterDestructor { constructor( @@ -34,7 +31,7 @@ class GenericGracefulAdminClusterDestructor { // Delete all validating webhooks await this.task.Run([ - "Delete Validating Webhooks", + 'Delete Validating Webhooks', async () => { await $`kubectl --context ${context} delete validatingwebhookconfigurations --all`.nothrow(); }, @@ -42,7 +39,7 @@ class GenericGracefulAdminClusterDestructor { // Delete all namespaces await this.task.Run([ - "Delete Namespaces", + 'Delete Namespaces', async () => { for (const namespace of [heliumNS, boronNS, fluorineNS, mainNS]) { console.log(` 🗑️ Removing namespace: ${namespace}`); @@ -57,7 +54,7 @@ class GenericGracefulAdminClusterDestructor { // setup tofu repository correctly await this.task.Run([ - "Setup Tofu", + 'Setup Tofu', async () => { await $`pls setup`.cwd(tofuDir); }, @@ -66,7 +63,7 @@ class GenericGracefulAdminClusterDestructor { // destroy generic infrastructure const L1G = `${admin.landscape.slug}:l1:${admin.cluster.set.slug}`; await this.task.Run([ - "Destroy Generic Infrastructure", + 'Destroy Generic Infrastructure', async () => { await $`pls ${{ raw: L1G }}:init`.cwd(tofuDir); await $`pls ${{ raw: L1G }}:destroy`.cwd(tofuDir); @@ -76,12 +73,10 @@ class GenericGracefulAdminClusterDestructor { // destroy L1 infrastructure const L1 = `${admin.landscape.slug}:l1:${admin.cluster.principal.slug}`; await this.task.Run([ - "Destroy L1 Infrastructure", + 'Destroy L1 Infrastructure', async () => { await $`pls ${{ raw: L1 }}:init`.cwd(tofuDir); - await $`pls ${{ raw: L1 }}:state:rm -- 'kubernetes_namespace.sulfoxide'` - .cwd(tofuDir) - .nothrow(); + await $`pls ${{ raw: L1 }}:state:rm -- 'kubernetes_namespace.sulfoxide'`.cwd(tofuDir).nothrow(); await $`pls ${{ raw: L1 }}:destroy`.cwd(tofuDir); }, ]); @@ -89,7 +84,7 @@ class GenericGracefulAdminClusterDestructor { // destroy L0 infrastructure const L0 = `${admin.landscape.slug}:l0:${admin.cluster.principal.slug}`; await this.task.Run([ - "Destroy L0 Infrastructure", + 'Destroy L0 Infrastructure', async () => { await $`pls ${{ raw: L0 }}:destroy`.cwd(tofuDir); }, @@ -97,7 +92,7 @@ class GenericGracefulAdminClusterDestructor { // update kubectl configurations await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; }, diff --git a/src/books/physical-cluster-creation/aws.ts b/src/books/physical-cluster-creation/aws.ts new file mode 100644 index 0000000..0ce720a --- /dev/null +++ b/src/books/physical-cluster-creation/aws.ts @@ -0,0 +1,258 @@ +import type { PhysicalClusterCloudCreator } from "./cloud.ts"; +import { $ } from "bun"; +import * as path from "node:path"; +import type { UtilPrompter } from "../../lib/prompts/util-prompter.ts"; +import { input } from "@inquirer/prompts"; +import type { YamlManipulator } from "../../lib/utility/yaml-manipulator.ts"; +import type { KubectlUtil } from "../../lib/utility/kubectl-util.ts"; +import type { LandscapeCluster, ServiceTreeService } from "../../lib/service-tree-def.ts"; +import type { TaskRunner } from "../../tasks/tasks.ts"; +import type { Git } from "../../lib/utility/git.ts"; + +class AwsPhysicalClusterCreator implements PhysicalClusterCloudCreator { + slug: string; + + constructor( + private task: TaskRunner, + private y: YamlManipulator, + private up: UtilPrompter, + private k: KubectlUtil, + private g: Git, + private sulfoxideTofu: ServiceTreeService, + private sulfoxideHelium: ServiceTreeService, + private sulfoxideKrypton: ServiceTreeService, + private sulfoxideLead: ServiceTreeService, + slug: string + ) { + this.slug = slug; + } + + async Run( + [phyLandscape, phyCluster]: LandscapeCluster, + [adminLandscape, adminCluster]: LandscapeCluster + ): Promise { + // constants + const tofu = this.sulfoxideTofu; + const He = this.sulfoxideHelium; + const Kr = this.sulfoxideKrypton; + const Pb = this.sulfoxideLead; + + const tofuDir = `./platforms/${tofu.platform.slug}/${tofu.principal.slug}`; + const He_Dir = `./platforms/${He.platform.slug}/${He.principal.slug}`; + const Kr_Dir = `./platforms/${Kr.platform.slug}/${Kr.principal.slug}`; + const Pb_Dir = `./platforms/${Pb.platform.slug}/${Pb.principal.slug}`; + + const He_YamlPath = path.join(He_Dir, "chart", `values.${adminLandscape.slug}.${adminCluster.set.slug}.yaml`); + const Kr_YamlPath = path.join(Kr_Dir, "chart", `values.${phyLandscape.slug}.${phyCluster.principal.slug}.yaml`); + const Pb_YamlPath = path.join(Pb_Dir, "chart", `values.${phyLandscape.slug}.${phyCluster.principal.slug}.yaml`); + + const aCtx = `${adminLandscape.slug}-${adminCluster.principal.slug}`; + const aNS = `${He.platform.slug}-${He.principal.slug}`; + + // Check if we want to inject the DO secrets + const awsSecrets = await this.up.YesNo("Do you want to inject AWS secrets?"); + if (awsSecrets) { + const access = await input({ message: "Enter your AWS Access Key" }); + await $`infisical secrets set --projectId=${tofu.principal.projectId} --env=${phyLandscape.slug} ${phyCluster.principal.slug.toUpperCase()}_AWS_ACCESS_KEY=${access}`; + console.log("✅ AWS Access Key injected"); + const secret = await input({ message: "Enter your AWS Secret Key" }); + await $`infisical secrets set --projectId=${tofu.principal.projectId} --env=${phyLandscape.slug} ${phyCluster.principal.slug.toUpperCase()}_AWS_SECRET_KEY=${secret}`; + console.log("✅ AWS Secret Key injected"); + } + + const L0 = `${phyLandscape.slug}:l0:${phyCluster.principal.slug}`; + await this.task.Run([ + "Build L0 Infrastructure", + async () => { + await $`pls setup`.cwd(tofuDir); + await $`pls ${{ raw: L0 }}:init`.cwd(tofuDir); + await $`pls ${{ raw: L0 }}:apply`.cwd(tofuDir); + } + ]); + + await this.task.Run([ + "Retrieve Kubectl Configurations", + async () => { + await $`pls kubectl`; + } + ]); + + // extract endpoint to use + console.log("📤 Extract endpoint to use..."); + const output = await $`pls ${{ raw: L0 }}:output -- -json`.cwd(tofuDir).json(); + const endpoint = output.cluster_endpoint.value; + console.log(`✅ Extracted endpoint: ${endpoint}`); + + // build L1 generic infrastructure + const L1G = `${phyLandscape.slug}:l1:${phyCluster.set.slug}`; + await this.task.Run([ + "Build L1 Generic Infrastructure", + async () => { + await $`pls ${{ raw: L1G }}:init`.cwd(tofuDir); + await $`pls ${{ raw: L1G }}:apply`.cwd(tofuDir); + } + ]); + + // Propagate Tofu outputs for Karpenter + const nodeRole = output.karpenter_node_role_name.value; + const nodeArn = output.karpenter_role_arn.value; + console.log("📤 Extract node role and ARN to use..."); + console.log(`✅ Extracted node role: ${nodeRole}`); + console.log(`✅ Extracted node ARN: ${nodeArn}`); + + await this.task.Run([ + "Propagate Tofu outputs to Krypton (Karpenter)", + async () => { + console.log(`🛣️ Propagating YAML Path: ${Kr_YamlPath}`); + await this.y.Mutate(Kr_YamlPath, [ + [["nodeRole"], nodeRole], + [["karpenterRole"], nodeArn], + ]); + } + ]); + + await this.task.Run([ + "Commit changes to Krypton", + async () => { + await this.g.CommitAndPush(Kr_Dir, "action: propagate Tofu outputs to Krypton"); + } + ]); + + // propagate Tofu outputs for Lead + const irsaRoleArn = output.irsa_role_arn.value; + const vpcId = output.vpc_id.value; + console.log("📤 Extract IRSA Role ARN and VPC ID to use..."); + console.log(`✅ Extracted IRSA Role ARN: ${irsaRoleArn}`); + console.log(`✅ Extracted VPC ID: ${vpcId}`); + await this.task.Run([ + "Propagate Tofu outputs to Lead (IRSA Components)", + async () => { + await this.y.Mutate(Pb_YamlPath, [ + [["role"], irsaRoleArn], + [["vpcId"], vpcId], + ]); + } + ]); + + await this.task.Run([ + "Commit changes to Lead", + async () => { + await this.g.CommitAndPush(Pb_Dir, "action: propagate Tofu outputs to Lead"); + } + ]); + + // build L1 infrastructure + const L1 = `${phyLandscape.slug}:l1:${phyCluster.principal.slug}`; + await this.task.Run([ + "Build L1 Infrastructure", + async () => { + await $`pls ${{ raw: L1 }}:init`.cwd(tofuDir); + await $`pls ${{ raw: L1 }}:apply`.cwd(tofuDir); + } + ]); + + // retrieve yaml in helium folder and replace + await this.task.Run([ + "Update Helium Configuration", + async () => { + await this.y.Mutate(He_YamlPath, [ + [["connector", "clusters", phyLandscape.slug, phyCluster.principal.slug, "enable"], true], + [["connector", "clusters", phyLandscape.slug, phyCluster.principal.slug, "deployAppSet"], true], + [["connector", "clusters", phyLandscape.slug, phyCluster.principal.slug, "aoa", "enable"], true], + [["connector", "clusters", phyLandscape.slug, phyCluster.principal.slug, "destination"], endpoint] + ]); + } + ]); + + // apply ArgoCD configurations + const HePls = `${adminLandscape.slug}:${adminCluster.set.slug}`; + await this.task.Run([ + "Apply Helium Configuration", + async () => { + await $`pls ${{ raw: HePls }}:install -- --kube-context ${aCtx} -n ${aNS}`.cwd(He_Dir); + } + ]); + + // retrieve kubectl configurations again + await this.task.Run([ + "Retrieve Kubectl Configurations", + async () => { + await $`pls kubectl`; + } + ]); + + // wait for iodine to be ready + console.log("🕙 Waiting for iodine to be ready..."); + + await this.task.Exec([ + "Wait for iodine applications to be ready", + async () => { + await this.k.WaitForApplications(3, { + kind: "app", + context: aCtx, + namespace: aNS, + selector: [ + ["atomi.cloud/sync-wave", "wave-5"], + ["atomi.cloud/landscape", phyLandscape.slug], + ["atomi.cloud/cluster", phyCluster.principal.slug] + ] + }); + } + ]); + + await this.task.Exec([ + "Wait for statefulset (etcd) to be ready", + async () => { + for (const ns of ["pichu", "pikachu", "raichu"]) { + await this.k.WaitForReplica({ + kind: "statefulset", + context: `${phyLandscape.slug}-${phyCluster.principal.slug}`, + namespace: ns, + name: `${phyLandscape.slug}-${ns}-iodine-etcd` + }); + } + } + ]); + + await this.task.Exec([ + "Wait for deployment (iodine) to be ready", + async () => { + for (const ns of ["pichu", "pikachu", "raichu"]) { + await this.k.WaitForReplica({ + kind: "deployment", + context: `${phyLandscape.slug}-${phyCluster.principal.slug}`, + namespace: ns, + name: `${phyLandscape.slug}-${ns}-iodine` + }); + } + } + ]); + + // retrieve kubectl configurations again + await this.task.Run([ + "Retrieve Kubectl Configurations", + async () => { + await $`pls kubectl`; + } + ]); + + // last applications to be ready + await this.task.Exec([ + "Wait for vcluster carbon's last sync wave to be ready", + async () => { + await this.k.WaitForApplications(3, { + kind: "app", + context: aCtx, + namespace: aNS, + selector: [ + ["atomi.cloud/sync-wave", "wave-5"], + ["atomi.cloud/element", "silicon"], + ["atomi.cloud/cluster", phyCluster.principal.slug] + ] + }); + } + ]); + } +} + +export { AwsPhysicalClusterCreator }; diff --git a/src/books/physical-cluster-creation/digital-ocean.ts b/src/books/physical-cluster-creation/digital-ocean.ts index 1585472..d12fcf4 100644 --- a/src/books/physical-cluster-creation/digital-ocean.ts +++ b/src/books/physical-cluster-creation/digital-ocean.ts @@ -1,19 +1,14 @@ -import type { PhysicalClusterCloudCreator } from "./cloud.ts"; -import { $ } from "bun"; -import * as path from "node:path"; -import type { UtilPrompter } from "../../lib/prompts/util-prompter.ts"; -import { input } from "@inquirer/prompts"; -import type { YamlManipulator } from "../../lib/utility/yaml-manipulator.ts"; -import type { KubectlUtil } from "../../lib/utility/kubectl-util.ts"; -import type { - LandscapeCluster, - ServiceTreeService, -} from "../../lib/service-tree-def.ts"; -import type { TaskRunner } from "../../tasks/tasks.ts"; - -class DigitalOceanPhysicalClusterCreator - implements PhysicalClusterCloudCreator -{ +import type { PhysicalClusterCloudCreator } from './cloud.ts'; +import { $ } from 'bun'; +import * as path from 'node:path'; +import type { UtilPrompter } from '../../lib/prompts/util-prompter.ts'; +import { input } from '@inquirer/prompts'; +import type { YamlManipulator } from '../../lib/utility/yaml-manipulator.ts'; +import type { KubectlUtil } from '../../lib/utility/kubectl-util.ts'; +import type { LandscapeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts'; +import type { TaskRunner } from '../../tasks/tasks.ts'; + +class DigitalOceanPhysicalClusterCreator implements PhysicalClusterCloudCreator { slug: string; constructor( @@ -38,132 +33,76 @@ class DigitalOceanPhysicalClusterCreator const tofuDir = `./platforms/${tofu.platform.slug}/${tofu.principal.slug}`; const heliumDir = `./platforms/${helium.platform.slug}/${helium.principal.slug}`; - const yamlPath = path.join( - heliumDir, - "chart", - `values.${adminLandscape.slug}.${adminCluster.set.slug}.yaml`, - ); + const yamlPath = path.join(heliumDir, 'chart', `values.${adminLandscape.slug}.${adminCluster.set.slug}.yaml`); const adminContextSlug = `${adminLandscape.slug}-${adminCluster.principal.slug}`; const adminNamespaceSlug = `${helium.platform.slug}-${helium.principal.slug}`; // Check if we want to inject the DO secrets - const doSecrets = await this.up.YesNo( - "Do you want to inject Digital Ocean secrets?", - ); + const doSecrets = await this.up.YesNo('Do you want to inject Digital Ocean secrets?'); if (doSecrets) { - const token = await input({ message: "Enter your Digital Ocean token" }); + const token = await input({ message: 'Enter your Digital Ocean token' }); await $`infisical secrets set --projectId=${tofu.principal.projectId} --env=${phyLandscape.slug} ${phyCluster.principal.slug.toUpperCase()}_DIGITALOCEAN_TOKEN=${token}`; - console.log("✅ Digital Ocean secrets injected"); + console.log('✅ Digital Ocean secrets injected'); } await this.task.Run([ - "Build L0 Infrastructure", + 'Build L0 Infrastructure', async () => { await $`pls setup`.cwd(tofuDir); - await $`pls ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:init`.cwd( - tofuDir, - ); - await $`pls ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:apply`.cwd( - tofuDir, - ); + await $`pls ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:init`.cwd(tofuDir); + await $`pls ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:apply`.cwd(tofuDir); }, ]); await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; }, ]); // extract endpoint to use - console.log("📤 Extract endpoint to use..."); - const output = - await $`pls ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:output -- -json` - .cwd(tofuDir) - .json(); + console.log('📤 Extract endpoint to use...'); + const output = await $`pls ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:output -- -json` + .cwd(tofuDir) + .json(); const endpoint = output.cluster_endpoint.value; console.log(`✅ Extracted endpoint: ${endpoint}`); // build L1 generic infrastructure await this.task.Run([ - "Build L1 Generic Infrastructure", + 'Build L1 Generic Infrastructure', async () => { - await $`pls ${phyLandscape.slug}:l1:${phyCluster.set.slug}:init`.cwd( - tofuDir, - ); - await $`pls ${phyLandscape.slug}:l1:${phyCluster.set.slug}:apply`.cwd( - tofuDir, - ); + await $`pls ${phyLandscape.slug}:l1:${phyCluster.set.slug}:init`.cwd(tofuDir); + await $`pls ${phyLandscape.slug}:l1:${phyCluster.set.slug}:apply`.cwd(tofuDir); }, ]); // build L1 infrastructure await this.task.Run([ - "Build L1 Infrastructure", + 'Build L1 Infrastructure', async () => { - await $`pls ${phyLandscape.slug}:l1:${phyCluster.principal.slug}:init`.cwd( - tofuDir, - ); - await $`pls ${phyLandscape.slug}:l1:${phyCluster.principal.slug}:apply`.cwd( - tofuDir, - ); + await $`pls ${phyLandscape.slug}:l1:${phyCluster.principal.slug}:init`.cwd(tofuDir); + await $`pls ${phyLandscape.slug}:l1:${phyCluster.principal.slug}:apply`.cwd(tofuDir); }, ]); // retrieve yaml in helium folder and replace await this.task.Run([ - "Update Helium Configuration", + 'Update Helium Configuration', async () => { await this.y.Mutate(yamlPath, [ - [ - [ - "connector", - "clusters", - phyLandscape.slug, - phyCluster.principal.slug, - "enable", - ], - true, - ], - [ - [ - "connector", - "clusters", - phyLandscape.slug, - phyCluster.principal.slug, - "deployAppSet", - ], - true, - ], - [ - [ - "connector", - "clusters", - phyLandscape.slug, - phyCluster.principal.slug, - "aoa", - "enable", - ], - true, - ], - [ - [ - "connector", - "clusters", - phyLandscape.slug, - phyCluster.principal.slug, - "destination", - ], - endpoint, - ], + [['connector', 'clusters', phyLandscape.slug, phyCluster.principal.slug, 'enable'], true], + [['connector', 'clusters', phyLandscape.slug, phyCluster.principal.slug, 'deployAppSet'], true], + [['connector', 'clusters', phyLandscape.slug, phyCluster.principal.slug, 'aoa', 'enable'], true], + [['connector', 'clusters', phyLandscape.slug, phyCluster.principal.slug, 'destination'], endpoint], ]); }, ]); // apply ArgoCD configurations await this.task.Run([ - "Apply Helium Configuration", + 'Apply Helium Configuration', async () => { await $`pls ${adminLandscape.slug}:${adminCluster.set.slug}:install -- --kube-context ${adminContextSlug} -n ${adminNamespaceSlug}`.cwd( heliumDir, @@ -173,37 +112,37 @@ class DigitalOceanPhysicalClusterCreator // retrieve kubectl configurations again await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; }, ]); // wait for iodine to be ready - console.log("🕙 Waiting for iodine to be ready..."); + console.log('🕙 Waiting for iodine to be ready...'); await this.task.Exec([ - "Wait for iodine applications to be ready", + 'Wait for iodine applications to be ready', async () => { await this.k.WaitForApplications(3, { - kind: "app", + kind: 'app', context: adminContextSlug, namespace: adminNamespaceSlug, selector: [ - ["atomi.cloud/sync-wave", "wave-5"], - ["atomi.cloud/landscape", phyLandscape.slug], - ["atomi.cloud/cluster", phyCluster.principal.slug], + ['atomi.cloud/sync-wave', 'wave-5'], + ['atomi.cloud/landscape', phyLandscape.slug], + ['atomi.cloud/cluster', phyCluster.principal.slug], ], }); }, ]); await this.task.Exec([ - "Wait for statefulset (etcd) to be ready", + 'Wait for statefulset (etcd) to be ready', async () => { - for (const ns of ["pichu", "pikachu", "raichu"]) { + for (const ns of ['pichu', 'pikachu', 'raichu']) { await this.k.WaitForReplica({ - kind: "statefulset", + kind: 'statefulset', context: `${phyLandscape.slug}-${phyCluster.principal.slug}`, namespace: ns, name: `${phyLandscape.slug}-${ns}-iodine-etcd`, @@ -213,11 +152,11 @@ class DigitalOceanPhysicalClusterCreator ]); await this.task.Exec([ - "Wait for deployment (iodine) to be ready", + 'Wait for deployment (iodine) to be ready', async () => { - for (const ns of ["pichu", "pikachu", "raichu"]) { + for (const ns of ['pichu', 'pikachu', 'raichu']) { await this.k.WaitForReplica({ - kind: "deployment", + kind: 'deployment', context: `${phyLandscape.slug}-${phyCluster.principal.slug}`, namespace: ns, name: `${phyLandscape.slug}-${ns}-iodine`, @@ -228,7 +167,7 @@ class DigitalOceanPhysicalClusterCreator // retrieve kubectl configurations again await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; }, @@ -239,13 +178,13 @@ class DigitalOceanPhysicalClusterCreator "Wait for vcluster carbon's last sync wave to be ready", async () => { await this.k.WaitForApplications(3, { - kind: "app", + kind: 'app', context: adminContextSlug, namespace: adminNamespaceSlug, selector: [ - ["atomi.cloud/sync-wave", "wave-5"], - ["atomi.cloud/element", "silicon"], - ["atomi.cloud/cluster", phyCluster.principal.slug], + ['atomi.cloud/sync-wave', 'wave-5'], + ['atomi.cloud/element', 'silicon'], + ['atomi.cloud/cluster', phyCluster.principal.slug], ], }); }, diff --git a/src/init/index.ts b/src/init/index.ts index 954ac2e..c9be5fd 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -6,6 +6,7 @@ import { YamlManipulator } from "../lib/utility/yaml-manipulator.ts"; import { ServiceTreePrompter } from "../lib/prompts/landscape.ts"; import { CLOUD_TREE, LANDSCAPE_TREE } from "../lib/service-tree.ts"; import { TaskRunner } from "../tasks/tasks.ts"; +import { Git } from "../lib/utility/git.ts"; interface Dependencies { httpUtil: HttpUtil; @@ -15,6 +16,7 @@ interface Dependencies { yamlManipulator: YamlManipulator; stp: ServiceTreePrompter; taskRunner: TaskRunner; + git: Git; } // helpers @@ -29,6 +31,7 @@ const stp = new ServiceTreePrompter( LANDSCAPE_TREE.p, ); const taskRunner = new TaskRunner(utilPrompter); +const git = new Git(); const dependencies: Dependencies = { httpUtil, @@ -38,6 +41,7 @@ const dependencies: Dependencies = { yamlManipulator, stp, taskRunner, + git, }; export { dependencies, type Dependencies }; diff --git a/src/init/runbooks.ts b/src/init/runbooks.ts index f7aabfb..3d05bd3 100644 --- a/src/init/runbooks.ts +++ b/src/init/runbooks.ts @@ -17,6 +17,7 @@ import { GenericGracefulAdminClusterDestructor } from "../books/graceful-admin-c import { GenericGracefulPhysicalClusterDestructor } from "../books/graceful-physical-cluster-destruction/generic.ts"; import { AdminClusterMigrator } from "../books/admin-cluster-migration"; import { AdminClusterTransitioner } from "../books/admin-cluster-migration/transition.ts"; +import { AwsPhysicalClusterCreator } from "../books/physical-cluster-creation/aws.ts"; function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { const sulfoxide = SERVICE_TREE.sulfoxide; @@ -30,8 +31,20 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { d.kubectl, sulfoxide.services.tofu, sulfoxide.services.argocd, - CLOUDS.DigitalOcean.slug, + CLOUDS.DigitalOcean.slug ), + new AwsPhysicalClusterCreator( + d.taskRunner, + d.yamlManipulator, + d.utilPrompter, + d.kubectl, + d.git, + sulfoxide.services.tofu, + sulfoxide.services.argocd, + sulfoxide.services.cluster_scaler, + sulfoxide.services.aws_adapter, + CLOUDS.AWS.slug + ) ]; const physicalClusterCreator = new PhysicalClusterCreator( d.taskRunner, @@ -39,25 +52,24 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { t.nitrosoWaiter, sulfoxide.services.argocd, LANDSCAPE_TREE.v, - phyClusterCreators, + phyClusterCreators ); // graceful physical cluster destruction - const genericPhyGracefulDestructor = - new GenericGracefulPhysicalClusterDestructor( - d.taskRunner, - d.yamlManipulator, - d.kubectl, - d.utilPrompter, - sulfoxide.services.tofu, - sulfoxide.services.argocd, - LANDSCAPE_TREE.v, - ); + const genericPhyGracefulDestructor = new GenericGracefulPhysicalClusterDestructor( + d.taskRunner, + d.yamlManipulator, + d.kubectl, + d.utilPrompter, + sulfoxide.services.tofu, + sulfoxide.services.argocd, + LANDSCAPE_TREE.v + ); const phyGracefulDestructor = new GracefulPhysicalClusterDestructor( d.stp, d.serviceTreePrinter, - genericPhyGracefulDestructor, + genericPhyGracefulDestructor ); // bare admin cluster creation @@ -70,14 +82,10 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { sulfoxide.services.backup_engine, sulfoxide.services.metricsServer, t.sulfoxideXenonWaiter, - CLOUDS.DigitalOcean.slug, - ), + CLOUDS.DigitalOcean.slug + ) ]; - const bareAdminClusterCreator = new BareAdminClusterCreator( - d.stp, - d.serviceTreePrinter, - bareAdminCloudCreators, - ); + const bareAdminClusterCreator = new BareAdminClusterCreator(d.stp, d.serviceTreePrinter, bareAdminCloudCreators); // full admin cluster creation const fullAdminCloudCreators: FullAdminClusterCloudCreator[] = [ @@ -87,31 +95,30 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { sulfoxide.services.internal_ingress, t.sulfoxideHeliumWaiter, t.sulfoxideBoronWaiter, - CLOUDS.DigitalOcean.slug, - ), + CLOUDS.DigitalOcean.slug + ) ]; const fullAdminCloudCreator = new FullAdminClusterCreator( d.stp, d.serviceTreePrinter, bareAdminCloudCreators, - fullAdminCloudCreators, + fullAdminCloudCreators ); // graceful admin cluster destruction - const genericAdminGracefulDestructor = - new GenericGracefulAdminClusterDestructor( - d.taskRunner, - d.kubectl, - sulfoxide.services.argocd, - sulfoxide.services.internal_ingress, - sulfoxide.services.tofu, - ); + const genericAdminGracefulDestructor = new GenericGracefulAdminClusterDestructor( + d.taskRunner, + d.kubectl, + sulfoxide.services.argocd, + sulfoxide.services.internal_ingress, + sulfoxide.services.tofu + ); const adminGracefulDestructor = new GracefulAdminClusterDestructor( d.stp, d.serviceTreePrinter, - genericAdminGracefulDestructor, + genericAdminGracefulDestructor ); // admin cluster migration @@ -122,14 +129,14 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { sulfoxide.services.backup_engine, sulfoxide.services.internal_ingress, t.sulfoxideHeliumWaiter, - t.sulfoxideBoronWaiter, + t.sulfoxideBoronWaiter ); const adminClusterMigrator = new AdminClusterMigrator( d.stp, d.serviceTreePrinter, bareAdminCloudCreators, genericAdminGracefulDestructor, - adminClusterTransitioner, + adminClusterTransitioner ); return [ @@ -138,7 +145,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { bareAdminClusterCreator, fullAdminCloudCreator, adminGracefulDestructor, - adminClusterMigrator, + adminClusterMigrator ]; } diff --git a/src/init/tasks.ts b/src/init/tasks.ts index 910e709..5ff373e 100644 --- a/src/init/tasks.ts +++ b/src/init/tasks.ts @@ -1,9 +1,9 @@ -import { NitrosoWaiter } from "../tasks/nitroso-waiter.ts"; -import type { Dependencies } from "./index.ts"; -import { SulfoxideHeliumWaiter } from "../tasks/sulfoxide-helium-waiter.ts"; -import { SulfoxideBoronWaiter } from "../tasks/sulfoxide-boron-waiter.ts"; -import { SERVICE_TREE } from "../lib/service-tree.ts"; -import {SulfoxideXenonWaiter} from "../tasks/sulfoxide-xenon-waiter.ts"; +import { NitrosoWaiter } from '../tasks/nitroso-waiter.ts'; +import type { Dependencies } from './index.ts'; +import { SulfoxideHeliumWaiter } from '../tasks/sulfoxide-helium-waiter.ts'; +import { SulfoxideBoronWaiter } from '../tasks/sulfoxide-boron-waiter.ts'; +import { SERVICE_TREE } from '../lib/service-tree.ts'; +import { SulfoxideXenonWaiter } from '../tasks/sulfoxide-xenon-waiter.ts'; interface TaskGenerator { nitrosoWaiter: NitrosoWaiter; @@ -16,18 +16,9 @@ function initTasks(d: Dependencies): TaskGenerator { const services = SERVICE_TREE.sulfoxide.services; return { nitrosoWaiter: new NitrosoWaiter(d.kubectl, d.httpUtil), - sulfoxideHeliumWaiter: new SulfoxideHeliumWaiter( - d.kubectl, - services.argocd, - ), - sulfoxideBoronWaiter: new SulfoxideBoronWaiter( - d.kubectl, - services.internal_ingress, - ), - sulfoxideXenonWaiter: new SulfoxideXenonWaiter( - d.kubectl, - services.metricsServer, - ), + sulfoxideHeliumWaiter: new SulfoxideHeliumWaiter(d.kubectl, services.argocd), + sulfoxideBoronWaiter: new SulfoxideBoronWaiter(d.kubectl, services.internal_ingress), + sulfoxideXenonWaiter: new SulfoxideXenonWaiter(d.kubectl, services.metricsServer), }; } diff --git a/src/lib/service-tree.ts b/src/lib/service-tree.ts index d0f3d0a..ad5ab0e 100644 --- a/src/lib/service-tree.ts +++ b/src/lib/service-tree.ts @@ -5,215 +5,215 @@ import type { CloudTreeClusterSetPrincipal, ServiceTreeLandscapePrincipal, ServiceTreePlatform, - ServiceTreePlatformPrincipal, + ServiceTreePlatformPrincipal } from "./service-tree-def.ts"; const LANDSCAPES = { arceus: { name: "Arceus", slug: "arceus", - description: "Global meta landscape-agnostic administrative environment", + description: "Global meta landscape-agnostic administrative environment" }, pinsir: { name: "Pinsir", slug: "pinsir", - description: "Continuous Integration", + description: "Continuous Integration" }, lapras: { name: "Lapras", slug: "lapras", - description: "Local development", + description: "Local development" }, tauros: { name: "Tauros", slug: "tauros", - description: "Local Development (Production Parity)", + description: "Local Development (Production Parity)" }, absol: { name: "Absol", slug: "absol", - description: "Test environment", + description: "Test environment" }, pichu: { name: "Pichu", slug: "pichu", - description: "Singapore Development Environment", + description: "Singapore Development Environment" }, pikachu: { name: "Pikachu", slug: "pikachu", - description: "Singapore Staging Environment", + description: "Singapore Staging Environment" }, raichu: { name: "Raichu", slug: "raichu", - description: "Singapore Production Environment", + description: "Singapore Production Environment" }, suicune: { name: "Suicune", slug: "suicune", - description: "Singapore administrative environment", + description: "Singapore administrative environment" }, entei: { name: "Entei", slug: "entei", - description: "Singapore physical landscape", - }, + description: "Singapore physical landscape" + } } satisfies Record; const CLOUDS = { DigitalOcean: { name: "DigitalOcean", slug: "digitalocean", - description: "DigitalOcean Cloud", + description: "DigitalOcean Cloud" }, Linode: { name: "Linode", slug: "linode", - description: "Linode Cloud, Akamai", + description: "Linode Cloud, Akamai" }, Vultr: { name: "Vultr", slug: "vultr", - description: "Vultr Cloud", + description: "Vultr Cloud" }, AWS: { name: "AWS", slug: "aws", - description: "AWS Cloud, Amazon Web Services", + description: "AWS Cloud, Amazon Web Services" }, GCP: { name: "GCP", slug: "gcp", - description: "GCP Cloud, Google Cloud Platform", + description: "GCP Cloud, Google Cloud Platform" }, Azure: { name: "Azure", slug: "azure", - description: "Azure Cloud, Microsoft Azure", - }, + description: "Azure Cloud, Microsoft Azure" + } } satisfies Record; const CLUSTER_SETS = { OpalRuby: { name: "OpalRuby", slug: "opal-ruby", - description: "Opal-Ruby Cluster Set", + description: "Opal-Ruby Cluster Set" }, OnyxJade: { name: "OnyxJade", slug: "onyx-jade", - description: "Onyx-Jade Cluster Set", + description: "Onyx-Jade Cluster Set" }, MicaTalc: { name: "MicaTalc", slug: "mica-talc", - description: "Mica-Talc Cluster Set", + description: "Mica-Talc Cluster Set" }, TopazAmber: { name: "TopazAmber", slug: "topaz-amber", - description: "Topaz-Amber Cluster Set", + description: "Topaz-Amber Cluster Set" }, AgateLapis: { name: "AgateLapis", slug: "agate-lapis", - description: "Agate-Lapis Cluster Set", + description: "Agate-Lapis Cluster Set" }, BerylCoral: { name: "BerylCoral", slug: "beryl-coral", - description: "Beryl-Coral Cluster Set", - }, + description: "Beryl-Coral Cluster Set" + } } satisfies Record; const CLUSTERS = { Opal: { name: "Opal", slug: "opal", - description: "Opal Cluster", + description: "Opal Cluster" }, Ruby: { name: "Ruby", slug: "ruby", - description: "Ruby Cluster", + description: "Ruby Cluster" }, Onyx: { name: "Onyx", slug: "onyx", - description: "Onyx Cluster", + description: "Onyx Cluster" }, Jade: { name: "Jade", slug: "jade", - description: "Jade Cluster", + description: "Jade Cluster" }, Mica: { name: "mica", slug: "mica", - description: "Mica Cluster", + description: "Mica Cluster" }, Talc: { name: "Talc", slug: "talc", - description: "Talc Cluster", + description: "Talc Cluster" }, Topaz: { name: "Topaz", slug: "topaz", - description: "Topaz Cluster", + description: "Topaz Cluster" }, Amber: { name: "Amber", slug: "amber", - description: "Amber Cluster", + description: "Amber Cluster" }, Agate: { name: "Agate", slug: "agate", - description: "Agate Cluster", + description: "Agate Cluster" }, Lapis: { name: "Lapis", slug: "lapis", - description: "Lapis Cluster", + description: "Lapis Cluster" }, Beryl: { name: "Beryl", slug: "beryl", - description: "Beryl Cluster", + description: "Beryl Cluster" }, Coral: { name: "Coral", slug: "coral", - description: "Coral Cluster", - }, + description: "Coral Cluster" + } } satisfies Record; const PLATFORMS = { Sulfoxide: { name: "Sulfoxide", slug: "sulfoxide", - description: "System and infrastructure components", + description: "System and infrastructure components" }, Nitroso: { name: "Nitroso", slug: "nitroso", - description: "BunnyBooker", + description: "BunnyBooker" }, Azo: { name: "Azo", slug: "azo", - description: "Romantic Song Composer", - }, + description: "Romantic Song Composer" + } } satisfies Record; // Associations @@ -221,178 +221,187 @@ const SERVICE_TREE = { sulfoxide: { principal: PLATFORMS.Sulfoxide, services: { + aws_adapter: { + principal: { + name: "AWS Adapter", + slug: "lead", + description: "AWS Adapters, like CSI and ELB controller", + projectId: "" + }, + platform: PLATFORMS.Sulfoxide + }, sos: { principal: { name: "Secret of secrets", slug: "sos", description: "Secrets of other infisical secrets", - projectId: "1cffa31e-7653-4c0d-9a18-9914a2dbc30b", + projectId: "1cffa31e-7653-4c0d-9a18-9914a2dbc30b" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, metricsServer: { principal: { name: "Metrics Server", slug: "xenon", description: "Metrics Server for Cluster", - projectId: "", + projectId: "" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, tofu: { principal: { name: "Tofu", slug: "tofu", description: "IaC for AtomiCloud", - projectId: "5c418f54-d211-46dd-a263-e4c07585b47d", + projectId: "5c418f54-d211-46dd-a263-e4c07585b47d" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, argocd: { principal: { name: "ArgoCD", slug: "helium", description: "Deployment platform using GitOps", - projectId: "7b458fa1-1225-40ce-8e48-3f4a67031bc0", + projectId: "7b458fa1-1225-40ce-8e48-3f4a67031bc0" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, backup_engine: { principal: { name: "Backup Engine", slug: "fluorine", description: "Velero as the backup engine for Kubernetes", - projectId: "d712436d-2022-4970-838b-f0b854a83c9c", + projectId: "d712436d-2022-4970-838b-f0b854a83c9c" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, secrets_engine: { principal: { name: "Secrets Engine", slug: "cobalt", description: "External Secrets to sync secrets from infisical", - projectId: "1c2bc52e-c49b-4307-ad41-ff69a755beed", + projectId: "1c2bc52e-c49b-4307-ad41-ff69a755beed" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, internal_ingress: { principal: { name: "Internal Ingress", slug: "boron", description: "Internal Ingress with cloudflared", - projectId: "9ab0fcd2-ee24-4dd4-8701-c530b888b805", + projectId: "9ab0fcd2-ee24-4dd4-8701-c530b888b805" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, external_ingress: { principal: { name: "External Ingress", slug: "gold", description: "External Ingress with nginx", - projectId: "47c29693-3255-4a5b-b0a7-09e0281e1910", + projectId: "47c29693-3255-4a5b-b0a7-09e0281e1910" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, scaler: { principal: { name: "Pod Autoscaler", slug: "iron", description: "KEDA scaler for pods", - projectId: "ca687032-485f-4d09-9d7c-5d0f50bf3ab3", + projectId: "ca687032-485f-4d09-9d7c-5d0f50bf3ab3" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, cluster_scaler: { principal: { name: "Cluster Scaler", slug: "krypton", description: "Karpenter scaler for nodes", - projectId: "", + projectId: "" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, cluster_issuer: { principal: { name: "Cluster Issuer", slug: "zinc", description: "Cluster Issuer for Certificate", - projectId: "eb3db9f2-3b49-493c-81df-8528121c0ccc", + projectId: "eb3db9f2-3b49-493c-81df-8528121c0ccc" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, cert_manager: { principal: { name: "Cert Manager", slug: "sulfur", description: "Certificate Issuing operator", - projectId: "8a244c1b-f58a-40c0-92dd-585ffa2787d1", + projectId: "8a244c1b-f58a-40c0-92dd-585ffa2787d1" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, reloader: { principal: { name: "Reloader", slug: "chlorine", description: "Reloader", - projectId: "", + projectId: "" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, policy_engine: { principal: { name: "Policy Engine", slug: "argon", description: "Kyverno operator", - projectId: "3cc63883-9ec6-42c1-9772-8570620de422", + projectId: "3cc63883-9ec6-42c1-9772-8570620de422" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, policies: { principal: { name: "Policies", slug: "sodium", description: "Kyverno policies", - projectId: "cd55ee78-bf70-4853-9bb1-fcc9f3d445cd", + projectId: "cd55ee78-bf70-4853-9bb1-fcc9f3d445cd" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, otel_collector: { principal: { name: "OpenTelemetry Collector", slug: "silicon", description: "OpenTelemetry Collector", - projectId: "3e90c2ef-7007-47a4-bd6a-bac6cdae7396", + projectId: "3e90c2ef-7007-47a4-bd6a-bac6cdae7396" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, otel_operator: { principal: { name: "OpenTelemetry Operator", slug: "lithium", description: "OpenTelemetry Operator", - projectId: "ef96dd70-dea3-4548-81b7-0a22231b54ad", + projectId: "ef96dd70-dea3-4548-81b7-0a22231b54ad" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, external_dns: { principal: { name: "External DNS", slug: "tin", description: "External DNS", - projectId: "4c3d88b5-977d-449f-8f4c-2bf9e09e97c8", + projectId: "4c3d88b5-977d-449f-8f4c-2bf9e09e97c8" }, - platform: PLATFORMS.Sulfoxide, + platform: PLATFORMS.Sulfoxide }, vcluster: { principal: { name: "Virtual Cluster", slug: "iodine", description: "Virtual clusters with vcluster", - projectId: "e0c572b3-4eb8-40c6-85ea-f9cf54b6e13b", + projectId: "e0c572b3-4eb8-40c6-85ea-f9cf54b6e13b" }, - platform: PLATFORMS.Sulfoxide, - }, - }, + platform: PLATFORMS.Sulfoxide + } + } }, nitroso: { principal: PLATFORMS.Nitroso, @@ -402,43 +411,43 @@ const SERVICE_TREE = { name: "BunnyBooker Polling System", slug: "tin", description: "System of pollers for BunnyBooker", - projectId: "df53bb81-dee0-4479-b515-3cab9af7386f", + projectId: "df53bb81-dee0-4479-b515-3cab9af7386f" }, - platform: PLATFORMS.Nitroso, + platform: PLATFORMS.Nitroso }, zinc: { principal: { name: "BunnyBooker API Server", slug: "zinc", description: "API Server for BunnyBooker", - projectId: "3b4f93e1-aab6-4b4a-a883-55de7eb401ea", + projectId: "3b4f93e1-aab6-4b4a-a883-55de7eb401ea" }, - platform: PLATFORMS.Nitroso, + platform: PLATFORMS.Nitroso }, argon: { principal: { name: "BunnyBooker Frontend", slug: "argon", description: "Frontend for BunnyBooker", - projectId: "65f937ec-50a5-4551-9338-eb146df712af", + projectId: "65f937ec-50a5-4551-9338-eb146df712af" }, - platform: PLATFORMS.Nitroso, + platform: PLATFORMS.Nitroso }, helium: { principal: { name: "Pollee", slug: "helium", description: "Pollee for BunnyBooker", - projectId: "cc897910-0fe7-4784-ac3d-be9847fca2d9", + projectId: "cc897910-0fe7-4784-ac3d-be9847fca2d9" }, - platform: PLATFORMS.Nitroso, - }, - }, + platform: PLATFORMS.Nitroso + } + } }, azo: { principal: PLATFORMS.Azo, - services: {}, - }, + services: {} + } } satisfies Record; const CLOUD_TREE = { @@ -452,16 +461,16 @@ const CLOUD_TREE = { { principal: CLUSTERS.Opal, cloud: CLOUDS.DigitalOcean, - set: CLUSTER_SETS.OpalRuby, + set: CLUSTER_SETS.OpalRuby }, { principal: CLUSTERS.Ruby, cloud: CLOUDS.DigitalOcean, - set: CLUSTER_SETS.OpalRuby, - }, - ], - }, - }, + set: CLUSTER_SETS.OpalRuby + } + ] + } + } }, Linode: { principal: CLOUDS.Linode, @@ -473,16 +482,16 @@ const CLOUD_TREE = { { principal: CLUSTERS.Onyx, cloud: CLOUDS.Linode, - set: CLUSTER_SETS.OnyxJade, + set: CLUSTER_SETS.OnyxJade }, { principal: CLUSTERS.Jade, cloud: CLOUDS.Linode, - set: CLUSTER_SETS.OnyxJade, - }, - ], - }, - }, + set: CLUSTER_SETS.OnyxJade + } + ] + } + } }, // vultr - mica talc Vultr: { @@ -495,16 +504,16 @@ const CLOUD_TREE = { { principal: CLUSTERS.Mica, cloud: CLOUDS.Vultr, - set: CLUSTER_SETS.MicaTalc, + set: CLUSTER_SETS.MicaTalc }, { principal: CLUSTERS.Talc, cloud: CLOUDS.Vultr, - set: CLUSTER_SETS.MicaTalc, - }, - ], - }, - }, + set: CLUSTER_SETS.MicaTalc + } + ] + } + } }, // aws - topaz amber AWS: { @@ -517,16 +526,16 @@ const CLOUD_TREE = { { principal: CLUSTERS.Topaz, cloud: CLOUDS.AWS, - set: CLUSTER_SETS.TopazAmber, + set: CLUSTER_SETS.TopazAmber }, { principal: CLUSTERS.Amber, cloud: CLOUDS.AWS, - set: CLUSTER_SETS.TopazAmber, - }, - ], - }, - }, + set: CLUSTER_SETS.TopazAmber + } + ] + } + } }, // gcp - agate lapis GCP: { @@ -539,16 +548,16 @@ const CLOUD_TREE = { { principal: CLUSTERS.Agate, cloud: CLOUDS.GCP, - set: CLUSTER_SETS.AgateLapis, + set: CLUSTER_SETS.AgateLapis }, { principal: CLUSTERS.Lapis, cloud: CLOUDS.GCP, - set: CLUSTER_SETS.AgateLapis, - }, - ], - }, - }, + set: CLUSTER_SETS.AgateLapis + } + ] + } + } }, // azure - beryl coral Azure: { @@ -561,38 +570,24 @@ const CLOUD_TREE = { { principal: CLUSTERS.Beryl, cloud: CLOUDS.Azure, - set: CLUSTER_SETS.BerylCoral, + set: CLUSTER_SETS.BerylCoral }, { principal: CLUSTERS.Coral, cloud: CLOUDS.Azure, - set: CLUSTER_SETS.BerylCoral, - }, - ], - }, - }, - }, + set: CLUSTER_SETS.BerylCoral + } + ] + } + } + } } satisfies Record; const LANDSCAPE_TREE = { a: [LANDSCAPES.suicune], v: [LANDSCAPES.pichu, LANDSCAPES.pikachu, LANDSCAPES.raichu], - l: [ - LANDSCAPES.lapras, - LANDSCAPES.tauros, - LANDSCAPES.absol, - LANDSCAPES.pinsir, - ], - p: [LANDSCAPES.entei], + l: [LANDSCAPES.lapras, LANDSCAPES.tauros, LANDSCAPES.absol, LANDSCAPES.pinsir], + p: [LANDSCAPES.entei] } satisfies Record; -export { - LANDSCAPES, - CLOUDS, - CLUSTERS, - CLUSTER_SETS, - PLATFORMS, - SERVICE_TREE, - CLOUD_TREE, - LANDSCAPE_TREE, -}; +export { LANDSCAPES, CLOUDS, CLUSTERS, CLUSTER_SETS, PLATFORMS, SERVICE_TREE, CLOUD_TREE, LANDSCAPE_TREE }; diff --git a/src/lib/utility/kubectl-util.ts b/src/lib/utility/kubectl-util.ts index 72caad6..ff3cac4 100644 --- a/src/lib/utility/kubectl-util.ts +++ b/src/lib/utility/kubectl-util.ts @@ -1,5 +1,5 @@ -import { $ } from "bun"; -import type { UtilPrompter } from "../prompts/util-prompter.ts"; +import { $ } from 'bun'; +import type { UtilPrompter } from '../prompts/util-prompter.ts'; interface ResourceSearch { kind: string; @@ -30,12 +30,10 @@ class KubectlUtil { constructor(private up: UtilPrompter) {} private async generateFlags(search: ResourceSearch): Promise { - const selectorFlag = - (search.selector ?? []).length > 0 ? "--selector=" : ""; - const selector = `${selectorFlag}${(search.selector ?? []).map(([k, v]) => `${k}=${v}`).join(",")}`; - const findFlag = - (search.fieldSelector ?? []).length > 0 ? "--field-selector=" : ""; - const fieldSelector = `${findFlag}${(search.fieldSelector ?? []).map(([k, v]) => `${k}=${v}`).join(",")}`; + const selectorFlag = (search.selector ?? []).length > 0 ? '--selector=' : ''; + const selector = `${selectorFlag}${(search.selector ?? []).map(([k, v]) => `${k}=${v}`).join(',')}`; + const findFlag = (search.fieldSelector ?? []).length > 0 ? '--field-selector=' : ''; + const fieldSelector = `${findFlag}${(search.fieldSelector ?? []).map(([k, v]) => `${k}=${v}`).join(',')}`; return `${selector} ${fieldSelector}`; } @@ -68,7 +66,7 @@ class KubectlUtil { kind: r.kind, context: r.context, namespace: r.namespace, - fieldSelector: [["metadata.name", r.name]], + fieldSelector: [['metadata.name', r.name]], }); const cmds = $.escape( `kubectl --context ${r.context} -n ${r.namespace} wait --for=jsonpath=.status.health.status=Healthy --timeout=6000s ${r.kind} ${r.name}`, @@ -83,16 +81,11 @@ class KubectlUtil { await $`kubectl --context ${r.context} -n ${r.namespace} wait --for=jsonpath=.status.sync.status=Synced --timeout=6000s ${r.kind} ${r.name}`; } - async WaitForApplications( - target: number, - search: ResourceSearch, - ): Promise { + async WaitForApplications(target: number, search: ResourceSearch): Promise { await this.Wait(target, 5, search); const apps = await this.GetRange(search); - const waits = apps.map((x) => this.WaitForApplication(x)); - return Promise.all(waits).then(() => - console.log("✅ All applications are healthy"), - ); + const waits = apps.map(x => this.WaitForApplication(x)); + return Promise.all(waits).then(() => console.log('✅ All applications are healthy')); } async Count(search: ResourceSearch): Promise { @@ -118,12 +111,7 @@ class KubectlUtil { })); } - async Wait( - target: number, - interval: number, - search: ResourceSearch, - intervention?: Intervention, - ): Promise { + async Wait(target: number, interval: number, search: ResourceSearch, intervention?: Intervention): Promise { // number of iterations let count = 0; @@ -131,18 +119,14 @@ class KubectlUtil { let ret = await this.Count(search); // iterate until target is reached while (ret != target) { - console.log( - `🚧 Waiting for all ${search.kind} to reach ${target}, current = ${ret}, index = ${count}...`, - ); + console.log(`🚧 Waiting for all ${search.kind} to reach ${target}, current = ${ret}, index = ${count}...`); await $`sleep ${interval}`; // if intervention is configured if (intervention) { // if intervention threshold is reached if (count % intervention.count === 0 && count != 0) { // check if user wants to exit - const shouldExit = await this.up.YesNo( - "Seems to be taking a long time. Do you want to exit?", - ); + const shouldExit = await this.up.YesNo('Seems to be taking a long time. Do you want to exit?'); if (shouldExit) return true; // provide alternative intervention action if (intervention.action) { @@ -176,55 +160,54 @@ class KubectlUtil { kind: r.kind, context: r.context, namespace: r.namespace, - fieldSelector: [["metadata.name", r.name]], + fieldSelector: [['metadata.name', r.name]], }); } async DeleteNamespace(ns: NamespaceSearch): Promise { const delNS = async () => - $`kubectl delete namespace --context ${ns.context} ${ns.namespace}`.then( - () => console.log("✅ Namespace deleted"), + $`kubectl delete namespace --context ${ns.context} ${ns.namespace}`.then(() => + console.log('✅ Namespace deleted'), ); const delDeployment = async () => - $`kubectl delete deployment --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ Deployments deleted"), + $`kubectl delete deployment --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ Deployments deleted'), ); const delStatefulSet = async () => - $`kubectl delete statefulset --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ StatefulSets deleted"), + $`kubectl delete statefulset --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ StatefulSets deleted'), ); const delJob = async () => - $`kubectl delete job --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ Jobs deleted"), + $`kubectl delete job --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ Jobs deleted'), ); const delDaemonSet = async () => - $`kubectl delete daemonset --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ DaemonSets deleted"), + $`kubectl delete daemonset --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ DaemonSets deleted'), ); const delCronJob = async () => - $`kubectl delete cronjob --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ CronJobs deleted"), + $`kubectl delete cronjob --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ CronJobs deleted'), ); const delPod = async () => - $`kubectl delete pod --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ Pods deleted"), + $`kubectl delete pod --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ Pods deleted'), ); const delReplicaSet = async () => - $`kubectl delete replicaset --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ ReplicaSets deleted"), + $`kubectl delete replicaset --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ ReplicaSets deleted'), ); const delService = async () => - $`kubectl delete service --context ${ns.context} --namespace ${ns.namespace} --all`.then( - () => console.log("✅ Services deleted"), + $`kubectl delete service --context ${ns.context} --namespace ${ns.namespace} --all`.then(() => + console.log('✅ Services deleted'), ); const finalizeNS = async () => { // generate random port: const port = Math.floor(Math.random() * 10000 + 10000); - const timeout = - $`timeout 10 kubectl --context ${ns.context} proxy -p ${port}`.then( - () => console.log("✅ Proxy closed"), - ); + const timeout = $`timeout 10 kubectl --context ${ns.context} proxy -p ${port}`.then(() => + console.log('✅ Proxy closed'), + ); const wait5 = async () => await $`sleep 5`; const finalize = () => wait5() @@ -233,7 +216,7 @@ class KubectlUtil { jq '.spec.finalizers=[]' | \ curl -X PUT http://localhost:${{ raw: port.toString(10) }}/api/v1/namespaces/${{ raw: ns.namespace }}/finalize -H "Content-Type: application/json" --data @-`.nothrow(), ) - .then(() => console.log("💪 Namespace finalizers removal attempted")); + .then(() => console.log('💪 Namespace finalizers removal attempted')); await Promise.all([timeout, finalize()]); }; @@ -244,23 +227,18 @@ class KubectlUtil { while (true) { const ns = await countNS(); console.log(`🔢 NS Count: ${ns.items.length}`); - if (ns.items.length == 0) - return console.log("✅ Namespace finalizers removed"); - console.log( - "😔 Namespace finalizers removal failed, sleep 5 then retry...", - ); + if (ns.items.length == 0) return console.log('✅ Namespace finalizers removed'); + console.log('😔 Namespace finalizers removal failed, sleep 5 then retry...'); await $`sleep 5`; - console.log("💪 Namespace finalizers removal attempted"); + console.log('💪 Namespace finalizers removal attempted'); await finalizeNS(); } }; const count = await countNS(); - if (count.items.length == 0) - return console.log("✅ Namespace already deleted"); + if (count.items.length == 0) return console.log('✅ Namespace already deleted'); - const wait = async () => - await $`sleep 10`.then(() => console.log("✅ Buffer time over")); + const wait = async () => await $`sleep 10`.then(() => console.log('✅ Buffer time over')); const resources: Promise = Promise.all([ delDeployment(), @@ -272,7 +250,7 @@ class KubectlUtil { delReplicaSet(), delService(), ]) - .then(() => console.log("✅ Resources deleted")) + .then(() => console.log('✅ Resources deleted')) .then(wait) .then(finalizeLoop); await Promise.all([delNS(), resources]); diff --git a/src/tasks/sulfoxide-boron-waiter.ts b/src/tasks/sulfoxide-boron-waiter.ts index a3422c4..8751a31 100644 --- a/src/tasks/sulfoxide-boron-waiter.ts +++ b/src/tasks/sulfoxide-boron-waiter.ts @@ -1,6 +1,6 @@ -import type { Task } from "./tasks.ts"; -import type { KubectlUtil } from "../lib/utility/kubectl-util.ts"; -import type { ServiceTreeService } from "../lib/service-tree-def.ts"; +import type { Task } from './tasks.ts'; +import type { KubectlUtil } from '../lib/utility/kubectl-util.ts'; +import type { ServiceTreeService } from '../lib/service-tree-def.ts'; class SulfoxideBoronWaiter { constructor( @@ -8,21 +8,21 @@ class SulfoxideBoronWaiter { private sulfoxideBoron: ServiceTreeService, ) {} - name: string = "Wait for Sulfoxide Helium to be ready"; + name: string = 'Wait for Sulfoxide Helium to be ready'; task(context: string, namespace: string): Task { return [ this.name, async () => { const boron = this.sulfoxideBoron; - console.log("⏱️ Waiting for Boron to be ready..."); + console.log('⏱️ Waiting for Boron to be ready...'); await this.k.WaitForReplica({ - kind: "deployment", + kind: 'deployment', context, namespace, name: `${boron.platform.slug}-${boron.principal.slug}`, }); - console.log("✅ Boron is ready"); + console.log('✅ Boron is ready'); }, ]; } diff --git a/src/tasks/sulfoxide-helium-waiter.ts b/src/tasks/sulfoxide-helium-waiter.ts index 343a5f5..6a7c73c 100644 --- a/src/tasks/sulfoxide-helium-waiter.ts +++ b/src/tasks/sulfoxide-helium-waiter.ts @@ -1,6 +1,6 @@ -import type { Task } from "./tasks.ts"; -import type { KubectlUtil } from "../lib/utility/kubectl-util.ts"; -import type { ServiceTreeService } from "../lib/service-tree-def.ts"; +import type { Task } from './tasks.ts'; +import type { KubectlUtil } from '../lib/utility/kubectl-util.ts'; +import type { ServiceTreeService } from '../lib/service-tree-def.ts'; class SulfoxideHeliumWaiter { constructor( @@ -8,7 +8,7 @@ class SulfoxideHeliumWaiter { private sulfoxideHelium: ServiceTreeService, ) {} - name: string = "Wait for Sulfoxide Helium to be ready"; + name: string = 'Wait for Sulfoxide Helium to be ready'; task(context: string, namespace: string): Task { return [ @@ -26,7 +26,7 @@ class SulfoxideHeliumWaiter { for (const name of deployments) { console.log(`🚧 Waiting for ${name} to be ready...`); await this.k.WaitForReplica({ - kind: "deployment", + kind: 'deployment', context, namespace, name, @@ -35,7 +35,7 @@ class SulfoxideHeliumWaiter { } console.log(`🚧 Waiting for statefulset to be ready...`); await this.k.WaitForReplica({ - kind: "statefulset", + kind: 'statefulset', context, namespace, name: `${prefix}-application-controller`, diff --git a/src/tasks/sulfoxide-xenon-waiter.ts b/src/tasks/sulfoxide-xenon-waiter.ts index bba1732..0c8ed43 100644 --- a/src/tasks/sulfoxide-xenon-waiter.ts +++ b/src/tasks/sulfoxide-xenon-waiter.ts @@ -1,6 +1,6 @@ -import type { Task } from "./tasks.ts"; -import type { KubectlUtil } from "../lib/utility/kubectl-util.ts"; -import type { ServiceTreeService } from "../lib/service-tree-def.ts"; +import type { Task } from './tasks.ts'; +import type { KubectlUtil } from '../lib/utility/kubectl-util.ts'; +import type { ServiceTreeService } from '../lib/service-tree-def.ts'; class SulfoxideXenonWaiter { constructor( @@ -8,7 +8,7 @@ class SulfoxideXenonWaiter { private sulfoxideXenon: ServiceTreeService, ) {} - name: string = "Wait for Sulfoxide Xenon to be ready"; + name: string = 'Wait for Sulfoxide Xenon to be ready'; task(context: string, namespace: string): Task { return [ @@ -18,7 +18,7 @@ class SulfoxideXenonWaiter { const name = `${Xe.platform.slug}-${Xe.principal.slug}-metrics-server`; console.log(`🚧 Waiting for deployment ${name} to be ready...`); await this.k.WaitForReplica({ - kind: "deployment", + kind: 'deployment', context, namespace, name,