From 100b54d483f0de1562b43c368eb7c7c6cc4b9140 Mon Sep 17 00:00:00 2001 From: kirinnee Date: Sun, 25 Aug 2024 19:17:24 +0800 Subject: [PATCH 1/5] chore: new branch --- .gitlint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlint b/.gitlint index f87c4d6..57c5506 100644 --- a/.gitlint +++ b/.gitlint @@ -3,4 +3,4 @@ contrib=CT1 ignore=B6 [contrib-title-conventional-commits] -types = amend,build,ci,config,docs,feat,fix,perf,refactor,style,test +types = amend,build,ci,config,docs,feat,fix,perf,refactor,style,test,chore From e13f2c28c5fb8570862e8f011ec936c5c7fda064 Mon Sep 17 00:00:00 2001 From: kirinnee Date: Mon, 26 Aug 2024 18:26:39 +0800 Subject: [PATCH 2/5] 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, From 96312507b0a9870b06143f96320a1962d3c33d44 Mon Sep 17 00:00:00 2001 From: kirinnee Date: Mon, 26 Aug 2024 18:59:16 +0800 Subject: [PATCH 3/5] chore: format --- bun.lockb | Bin 23172 -> 23172 bytes scripts/local/check.sh | 2 +- src/books/physical-cluster-creation/aws.ts | 162 +++--- src/init/index.ts | 24 +- src/init/runbooks.ts | 72 +-- src/lib/service-tree.ts | 564 ++++++++++----------- 6 files changed, 410 insertions(+), 414 deletions(-) diff --git a/bun.lockb b/bun.lockb index dcf997ef1966bb0d5de1b9560528f78c657198a5..386d06a9cee0acbc0859b2c4198d03389462cdd2 100755 GIT binary patch delta 22 ecmZqK%Gk1%al=+ScE&gZJqta9&3o)lNCN;~)(8Rs delta 22 acmZqK%Gk1%al=+Sb|wZe*u2N?gfswCI0mr* diff --git a/scripts/local/check.sh b/scripts/local/check.sh index 9c36965..3e1ef7c 100755 --- a/scripts/local/check.sh +++ b/scripts/local/check.sh @@ -13,4 +13,4 @@ for service in ${SERVICES[@]}; do fi done -echo "🔧 Done!" \ No newline at end of file +echo "🔧 Done!" diff --git a/src/books/physical-cluster-creation/aws.ts b/src/books/physical-cluster-creation/aws.ts index 0ce720a..08c8f5a 100644 --- a/src/books/physical-cluster-creation/aws.ts +++ b/src/books/physical-cluster-creation/aws.ts @@ -1,13 +1,13 @@ -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"; +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; @@ -22,14 +22,14 @@ class AwsPhysicalClusterCreator implements PhysicalClusterCloudCreator { private sulfoxideHelium: ServiceTreeService, private sulfoxideKrypton: ServiceTreeService, private sulfoxideLead: ServiceTreeService, - slug: string + slug: string, ) { this.slug = slug; } async Run( [phyLandscape, phyCluster]: LandscapeCluster, - [adminLandscape, adminCluster]: LandscapeCluster + [adminLandscape, adminCluster]: LandscapeCluster, ): Promise { // constants const tofu = this.sulfoxideTofu; @@ -42,43 +42,43 @@ class AwsPhysicalClusterCreator implements PhysicalClusterCloudCreator { 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 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?"); + 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" }); + 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" }); + 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"); + console.log('✅ AWS Secret Key injected'); } const L0 = `${phyLandscape.slug}:l0:${phyCluster.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); await $`pls ${{ raw: L0 }}: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..."); + 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}`); @@ -86,154 +86,154 @@ class AwsPhysicalClusterCreator implements PhysicalClusterCloudCreator { // build L1 generic infrastructure const L1G = `${phyLandscape.slug}:l1:${phyCluster.set.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); - } + }, ]); // 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('📤 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)", + 'Propagate Tofu outputs to Krypton (Karpenter)', async () => { console.log(`🛣️ Propagating YAML Path: ${Kr_YamlPath}`); await this.y.Mutate(Kr_YamlPath, [ - [["nodeRole"], nodeRole], - [["karpenterRole"], nodeArn], + [['nodeRole'], nodeRole], + [['karpenterRole'], nodeArn], ]); - } + }, ]); await this.task.Run([ - "Commit changes to Krypton", + 'Commit changes to Krypton', async () => { - await this.g.CommitAndPush(Kr_Dir, "action: propagate Tofu outputs to Krypton"); - } + 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('📤 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)", + 'Propagate Tofu outputs to Lead (IRSA Components)', async () => { await this.y.Mutate(Pb_YamlPath, [ - [["role"], irsaRoleArn], - [["vpcId"], vpcId], + [['role'], irsaRoleArn], + [['vpcId'], vpcId], ]); - } + }, ]); await this.task.Run([ - "Commit changes to Lead", + 'Commit changes to Lead', async () => { - await this.g.CommitAndPush(Pb_Dir, "action: propagate Tofu outputs to Lead"); - } + 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", + '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", + '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] + [['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", + '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", + '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: aCtx, namespace: aNS, 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` + name: `${phyLandscape.slug}-${ns}-iodine-etcd`, }); } - } + }, ]); 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` + name: `${phyLandscape.slug}-${ns}-iodine`, }); } - } + }, ]); // retrieve kubectl configurations again await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; - } + }, ]); // last applications to be ready @@ -241,16 +241,16 @@ class AwsPhysicalClusterCreator implements PhysicalClusterCloudCreator { "Wait for vcluster carbon's last sync wave to be ready", async () => { await this.k.WaitForApplications(3, { - kind: "app", + kind: 'app', context: aCtx, namespace: aNS, 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 c9be5fd..fe0785c 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -1,12 +1,12 @@ -import { UtilPrompter } from "../lib/prompts/util-prompter.ts"; -import { HttpUtil } from "../lib/utility/http-util.ts"; -import { KubectlUtil } from "../lib/utility/kubectl-util.ts"; -import { ServiceTreePrinter } from "../lib/utility/service-tree-printer.ts"; -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"; +import { UtilPrompter } from '../lib/prompts/util-prompter.ts'; +import { HttpUtil } from '../lib/utility/http-util.ts'; +import { KubectlUtil } from '../lib/utility/kubectl-util.ts'; +import { ServiceTreePrinter } from '../lib/utility/service-tree-printer.ts'; +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; @@ -25,11 +25,7 @@ const utilPrompter = new UtilPrompter(); const kubectl = new KubectlUtil(utilPrompter); const serviceTreePrinter = new ServiceTreePrinter(); const yamlManipulator = new YamlManipulator(); -const stp = new ServiceTreePrompter( - CLOUD_TREE, - LANDSCAPE_TREE.a, - LANDSCAPE_TREE.p, -); +const stp = new ServiceTreePrompter(CLOUD_TREE, LANDSCAPE_TREE.a, LANDSCAPE_TREE.p); const taskRunner = new TaskRunner(utilPrompter); const git = new Git(); diff --git a/src/init/runbooks.ts b/src/init/runbooks.ts index 3d05bd3..a330897 100644 --- a/src/init/runbooks.ts +++ b/src/init/runbooks.ts @@ -1,23 +1,23 @@ -import type { Dependencies } from "./index.ts"; -import type { TaskGenerator } from "./tasks.ts"; -import type { RunBook } from "../books/run-book.ts"; -import type { PhysicalClusterCloudCreator } from "../books/physical-cluster-creation/cloud.ts"; -import { DigitalOceanPhysicalClusterCreator } from "../books/physical-cluster-creation/digital-ocean.ts"; -import { CLOUDS, LANDSCAPE_TREE, SERVICE_TREE } from "../lib/service-tree.ts"; -import { PhysicalClusterCreator } from "../books/physical-cluster-creation"; -import { GracefulPhysicalClusterDestructor } from "../books/graceful-physical-cluster-destruction"; -import { BareAdminClusterCreator } from "../books/bare-admin-cluster-creation"; -import type { BareAdminClusterCloudCreator } from "../books/bare-admin-cluster-creation/cloud.ts"; -import { DigitalOceanBareAdminClusterCreator } from "../books/bare-admin-cluster-creation/digital-ocean.ts"; -import type { FullAdminClusterCloudCreator } from "../books/full-admin-cluster-creation/cloud.ts"; -import { DigitalOceanFullAdminClusterCreator } from "../books/full-admin-cluster-creation/digital-ocean.ts"; -import { FullAdminClusterCreator } from "../books/full-admin-cluster-creation"; -import { GracefulAdminClusterDestructor } from "../books/graceful-admin-cluster-destruction"; -import { GenericGracefulAdminClusterDestructor } from "../books/graceful-admin-cluster-destruction/generic.ts"; -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"; +import type { Dependencies } from './index.ts'; +import type { TaskGenerator } from './tasks.ts'; +import type { RunBook } from '../books/run-book.ts'; +import type { PhysicalClusterCloudCreator } from '../books/physical-cluster-creation/cloud.ts'; +import { DigitalOceanPhysicalClusterCreator } from '../books/physical-cluster-creation/digital-ocean.ts'; +import { CLOUDS, LANDSCAPE_TREE, SERVICE_TREE } from '../lib/service-tree.ts'; +import { PhysicalClusterCreator } from '../books/physical-cluster-creation'; +import { GracefulPhysicalClusterDestructor } from '../books/graceful-physical-cluster-destruction'; +import { BareAdminClusterCreator } from '../books/bare-admin-cluster-creation'; +import type { BareAdminClusterCloudCreator } from '../books/bare-admin-cluster-creation/cloud.ts'; +import { DigitalOceanBareAdminClusterCreator } from '../books/bare-admin-cluster-creation/digital-ocean.ts'; +import type { FullAdminClusterCloudCreator } from '../books/full-admin-cluster-creation/cloud.ts'; +import { DigitalOceanFullAdminClusterCreator } from '../books/full-admin-cluster-creation/digital-ocean.ts'; +import { FullAdminClusterCreator } from '../books/full-admin-cluster-creation'; +import { GracefulAdminClusterDestructor } from '../books/graceful-admin-cluster-destruction'; +import { GenericGracefulAdminClusterDestructor } from '../books/graceful-admin-cluster-destruction/generic.ts'; +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; @@ -31,7 +31,7 @@ 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, @@ -43,8 +43,8 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { sulfoxide.services.argocd, sulfoxide.services.cluster_scaler, sulfoxide.services.aws_adapter, - CLOUDS.AWS.slug - ) + CLOUDS.AWS.slug, + ), ]; const physicalClusterCreator = new PhysicalClusterCreator( d.taskRunner, @@ -52,7 +52,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { t.nitrosoWaiter, sulfoxide.services.argocd, LANDSCAPE_TREE.v, - phyClusterCreators + phyClusterCreators, ); // graceful physical cluster destruction @@ -63,13 +63,13 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { d.utilPrompter, sulfoxide.services.tofu, sulfoxide.services.argocd, - LANDSCAPE_TREE.v + LANDSCAPE_TREE.v, ); const phyGracefulDestructor = new GracefulPhysicalClusterDestructor( d.stp, d.serviceTreePrinter, - genericPhyGracefulDestructor + genericPhyGracefulDestructor, ); // bare admin cluster creation @@ -82,8 +82,8 @@ 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); @@ -95,15 +95,15 @@ 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 @@ -112,13 +112,13 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { d.kubectl, sulfoxide.services.argocd, sulfoxide.services.internal_ingress, - sulfoxide.services.tofu + sulfoxide.services.tofu, ); const adminGracefulDestructor = new GracefulAdminClusterDestructor( d.stp, d.serviceTreePrinter, - genericAdminGracefulDestructor + genericAdminGracefulDestructor, ); // admin cluster migration @@ -129,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 [ @@ -145,7 +145,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { bareAdminClusterCreator, fullAdminCloudCreator, adminGracefulDestructor, - adminClusterMigrator + adminClusterMigrator, ]; } diff --git a/src/lib/service-tree.ts b/src/lib/service-tree.ts index ad5ab0e..9601e52 100644 --- a/src/lib/service-tree.ts +++ b/src/lib/service-tree.ts @@ -5,215 +5,215 @@ import type { CloudTreeClusterSetPrincipal, ServiceTreeLandscapePrincipal, ServiceTreePlatform, - ServiceTreePlatformPrincipal -} from "./service-tree-def.ts"; + ServiceTreePlatformPrincipal, +} from './service-tree-def.ts'; const LANDSCAPES = { arceus: { - name: "Arceus", - slug: "arceus", - description: "Global meta landscape-agnostic administrative environment" + name: 'Arceus', + slug: 'arceus', + description: 'Global meta landscape-agnostic administrative environment', }, pinsir: { - name: "Pinsir", - slug: "pinsir", - description: "Continuous Integration" + name: 'Pinsir', + slug: 'pinsir', + description: 'Continuous Integration', }, lapras: { - name: "Lapras", - slug: "lapras", - description: "Local development" + name: 'Lapras', + slug: 'lapras', + description: 'Local development', }, tauros: { - name: "Tauros", - slug: "tauros", - description: "Local Development (Production Parity)" + name: 'Tauros', + slug: 'tauros', + description: 'Local Development (Production Parity)', }, absol: { - name: "Absol", - slug: "absol", - description: "Test environment" + name: 'Absol', + slug: 'absol', + description: 'Test environment', }, pichu: { - name: "Pichu", - slug: "pichu", - description: "Singapore Development Environment" + name: 'Pichu', + slug: 'pichu', + description: 'Singapore Development Environment', }, pikachu: { - name: "Pikachu", - slug: "pikachu", - description: "Singapore Staging Environment" + name: 'Pikachu', + slug: 'pikachu', + description: 'Singapore Staging Environment', }, raichu: { - name: "Raichu", - slug: "raichu", - description: "Singapore Production Environment" + name: 'Raichu', + slug: 'raichu', + description: 'Singapore Production Environment', }, suicune: { - name: "Suicune", - slug: "suicune", - description: "Singapore administrative environment" + name: 'Suicune', + slug: 'suicune', + description: 'Singapore administrative environment', }, entei: { - name: "Entei", - slug: "entei", - description: "Singapore physical landscape" - } + name: 'Entei', + slug: 'entei', + description: 'Singapore physical landscape', + }, } satisfies Record; const CLOUDS = { DigitalOcean: { - name: "DigitalOcean", - slug: "digitalocean", - description: "DigitalOcean Cloud" + name: 'DigitalOcean', + slug: 'digitalocean', + description: 'DigitalOcean Cloud', }, Linode: { - name: "Linode", - slug: "linode", - description: "Linode Cloud, Akamai" + name: 'Linode', + slug: 'linode', + description: 'Linode Cloud, Akamai', }, Vultr: { - name: "Vultr", - slug: "vultr", - description: "Vultr Cloud" + name: 'Vultr', + slug: 'vultr', + description: 'Vultr Cloud', }, AWS: { - name: "AWS", - slug: "aws", - description: "AWS Cloud, Amazon Web Services" + name: 'AWS', + slug: 'aws', + description: 'AWS Cloud, Amazon Web Services', }, GCP: { - name: "GCP", - slug: "gcp", - description: "GCP Cloud, Google Cloud Platform" + name: 'GCP', + slug: 'gcp', + description: 'GCP Cloud, Google Cloud Platform', }, Azure: { - name: "Azure", - slug: "azure", - description: "Azure Cloud, Microsoft Azure" - } + name: 'Azure', + slug: 'azure', + description: 'Azure Cloud, Microsoft Azure', + }, } satisfies Record; const CLUSTER_SETS = { OpalRuby: { - name: "OpalRuby", - slug: "opal-ruby", - description: "Opal-Ruby Cluster Set" + name: 'OpalRuby', + slug: 'opal-ruby', + description: 'Opal-Ruby Cluster Set', }, OnyxJade: { - name: "OnyxJade", - slug: "onyx-jade", - description: "Onyx-Jade Cluster Set" + name: 'OnyxJade', + slug: 'onyx-jade', + description: 'Onyx-Jade Cluster Set', }, MicaTalc: { - name: "MicaTalc", - slug: "mica-talc", - description: "Mica-Talc Cluster Set" + name: 'MicaTalc', + slug: 'mica-talc', + description: 'Mica-Talc Cluster Set', }, TopazAmber: { - name: "TopazAmber", - slug: "topaz-amber", - description: "Topaz-Amber Cluster Set" + name: 'TopazAmber', + slug: 'topaz-amber', + description: 'Topaz-Amber Cluster Set', }, AgateLapis: { - name: "AgateLapis", - slug: "agate-lapis", - description: "Agate-Lapis Cluster Set" + name: 'AgateLapis', + slug: 'agate-lapis', + description: 'Agate-Lapis Cluster Set', }, BerylCoral: { - name: "BerylCoral", - slug: "beryl-coral", - description: "Beryl-Coral Cluster Set" - } + name: 'BerylCoral', + slug: 'beryl-coral', + description: 'Beryl-Coral Cluster Set', + }, } satisfies Record; const CLUSTERS = { Opal: { - name: "Opal", - slug: "opal", - description: "Opal Cluster" + name: 'Opal', + slug: 'opal', + description: 'Opal Cluster', }, Ruby: { - name: "Ruby", - slug: "ruby", - description: "Ruby Cluster" + name: 'Ruby', + slug: 'ruby', + description: 'Ruby Cluster', }, Onyx: { - name: "Onyx", - slug: "onyx", - description: "Onyx Cluster" + name: 'Onyx', + slug: 'onyx', + description: 'Onyx Cluster', }, Jade: { - name: "Jade", - slug: "jade", - description: "Jade Cluster" + name: 'Jade', + slug: 'jade', + description: 'Jade Cluster', }, Mica: { - name: "mica", - slug: "mica", - description: "Mica Cluster" + name: 'mica', + slug: 'mica', + description: 'Mica Cluster', }, Talc: { - name: "Talc", - slug: "talc", - description: "Talc Cluster" + name: 'Talc', + slug: 'talc', + description: 'Talc Cluster', }, Topaz: { - name: "Topaz", - slug: "topaz", - description: "Topaz Cluster" + name: 'Topaz', + slug: 'topaz', + description: 'Topaz Cluster', }, Amber: { - name: "Amber", - slug: "amber", - description: "Amber Cluster" + name: 'Amber', + slug: 'amber', + description: 'Amber Cluster', }, Agate: { - name: "Agate", - slug: "agate", - description: "Agate Cluster" + name: 'Agate', + slug: 'agate', + description: 'Agate Cluster', }, Lapis: { - name: "Lapis", - slug: "lapis", - description: "Lapis Cluster" + name: 'Lapis', + slug: 'lapis', + description: 'Lapis Cluster', }, Beryl: { - name: "Beryl", - slug: "beryl", - description: "Beryl Cluster" + name: 'Beryl', + slug: 'beryl', + description: 'Beryl Cluster', }, Coral: { - name: "Coral", - slug: "coral", - description: "Coral Cluster" - } + name: 'Coral', + slug: 'coral', + description: 'Coral Cluster', + }, } satisfies Record; const PLATFORMS = { Sulfoxide: { - name: "Sulfoxide", - slug: "sulfoxide", - description: "System and infrastructure components" + name: 'Sulfoxide', + slug: 'sulfoxide', + description: 'System and infrastructure components', }, Nitroso: { - name: "Nitroso", - slug: "nitroso", - description: "BunnyBooker" + name: 'Nitroso', + slug: 'nitroso', + description: 'BunnyBooker', }, Azo: { - name: "Azo", - slug: "azo", - description: "Romantic Song Composer" - } + name: 'Azo', + slug: 'azo', + description: 'Romantic Song Composer', + }, } satisfies Record; // Associations @@ -223,231 +223,231 @@ const SERVICE_TREE = { services: { aws_adapter: { principal: { - name: "AWS Adapter", - slug: "lead", - description: "AWS Adapters, like CSI and ELB controller", - projectId: "" + name: 'AWS Adapter', + slug: 'lead', + description: 'AWS Adapters, like CSI and ELB controller', + projectId: '', }, - platform: PLATFORMS.Sulfoxide + platform: PLATFORMS.Sulfoxide, }, sos: { principal: { - name: "Secret of secrets", - slug: "sos", - description: "Secrets of other infisical secrets", - projectId: "1cffa31e-7653-4c0d-9a18-9914a2dbc30b" + name: 'Secret of secrets', + slug: 'sos', + description: 'Secrets of other infisical secrets', + 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: "" + name: 'Metrics Server', + slug: 'xenon', + description: 'Metrics Server for Cluster', + projectId: '', }, - platform: PLATFORMS.Sulfoxide + platform: PLATFORMS.Sulfoxide, }, tofu: { principal: { - name: "Tofu", - slug: "tofu", - description: "IaC for AtomiCloud", - projectId: "5c418f54-d211-46dd-a263-e4c07585b47d" + name: 'Tofu', + slug: 'tofu', + description: 'IaC for AtomiCloud', + 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" + name: 'ArgoCD', + slug: 'helium', + description: 'Deployment platform using GitOps', + 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" + name: 'Backup Engine', + slug: 'fluorine', + description: 'Velero as the backup engine for Kubernetes', + 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" + name: 'Secrets Engine', + slug: 'cobalt', + description: 'External Secrets to sync secrets from infisical', + 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" + name: 'Internal Ingress', + slug: 'boron', + description: 'Internal Ingress with cloudflared', + 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" + name: 'External Ingress', + slug: 'gold', + description: 'External Ingress with nginx', + 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" + name: 'Pod Autoscaler', + slug: 'iron', + description: 'KEDA scaler for pods', + 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: "" + name: 'Cluster Scaler', + slug: 'krypton', + description: 'Karpenter scaler for nodes', + 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" + name: 'Cluster Issuer', + slug: 'zinc', + description: 'Cluster Issuer for Certificate', + 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" + name: 'Cert Manager', + slug: 'sulfur', + description: 'Certificate Issuing operator', + projectId: '8a244c1b-f58a-40c0-92dd-585ffa2787d1', }, - platform: PLATFORMS.Sulfoxide + platform: PLATFORMS.Sulfoxide, }, reloader: { principal: { - name: "Reloader", - slug: "chlorine", - description: "Reloader", - projectId: "" + name: 'Reloader', + slug: 'chlorine', + description: 'Reloader', + projectId: '', }, - platform: PLATFORMS.Sulfoxide + platform: PLATFORMS.Sulfoxide, }, policy_engine: { principal: { - name: "Policy Engine", - slug: "argon", - description: "Kyverno operator", - projectId: "3cc63883-9ec6-42c1-9772-8570620de422" + name: 'Policy Engine', + slug: 'argon', + description: 'Kyverno operator', + 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" + name: 'Policies', + slug: 'sodium', + description: 'Kyverno policies', + 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" + name: 'OpenTelemetry Collector', + slug: 'silicon', + description: 'OpenTelemetry Collector', + 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" + name: 'OpenTelemetry Operator', + slug: 'lithium', + description: 'OpenTelemetry Operator', + 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" + name: 'External DNS', + slug: 'tin', + description: 'External DNS', + 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" + name: 'Virtual Cluster', + slug: 'iodine', + description: 'Virtual clusters with vcluster', + projectId: 'e0c572b3-4eb8-40c6-85ea-f9cf54b6e13b', }, - platform: PLATFORMS.Sulfoxide - } - } + platform: PLATFORMS.Sulfoxide, + }, + }, }, nitroso: { principal: PLATFORMS.Nitroso, services: { tin: { principal: { - name: "BunnyBooker Polling System", - slug: "tin", - description: "System of pollers for BunnyBooker", - projectId: "df53bb81-dee0-4479-b515-3cab9af7386f" + name: 'BunnyBooker Polling System', + slug: 'tin', + description: 'System of pollers for BunnyBooker', + 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" + name: 'BunnyBooker API Server', + slug: 'zinc', + description: 'API Server for BunnyBooker', + 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" + name: 'BunnyBooker Frontend', + slug: 'argon', + description: 'Frontend for BunnyBooker', + 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" + name: 'Pollee', + slug: 'helium', + description: 'Pollee for BunnyBooker', + projectId: 'cc897910-0fe7-4784-ac3d-be9847fca2d9', }, - platform: PLATFORMS.Nitroso - } - } + platform: PLATFORMS.Nitroso, + }, + }, }, azo: { principal: PLATFORMS.Azo, - services: {} - } + services: {}, + }, } satisfies Record; const CLOUD_TREE = { @@ -461,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, @@ -482,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: { @@ -504,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: { @@ -526,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: { @@ -548,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: { @@ -570,24 +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] + p: [LANDSCAPES.entei], } satisfies Record; export { LANDSCAPES, CLOUDS, CLUSTERS, CLUSTER_SETS, PLATFORMS, SERVICE_TREE, CLOUD_TREE, LANDSCAPE_TREE }; From f032810df7267257abf02090c6141656c063488b Mon Sep 17 00:00:00 2001 From: kirinnee Date: Tue, 27 Aug 2024 18:47:46 +0800 Subject: [PATCH 4/5] fix: bun install use lock file --- scripts/ci/pre-commit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/pre-commit.sh b/scripts/ci/pre-commit.sh index 2ae2958..171a337 100755 --- a/scripts/ci/pre-commit.sh +++ b/scripts/ci/pre-commit.sh @@ -2,6 +2,6 @@ set -eou pipefail -bun i +bun i --frozen-lockfile pre-commit run --all-files -v From 32bfdba761521d7d3d9b83d8598ce3aaf6909633 Mon Sep 17 00:00:00 2001 From: kirinnee Date: Tue, 27 Aug 2024 18:56:58 +0800 Subject: [PATCH 5/5] chore: format all files --- Taskfile.yaml | 8 +- index.ts | 24 ++-- .../bare-admin-cluster-creation/cloud.ts | 2 +- .../bare-admin-cluster-creation/index.ts | 20 +-- .../full-admin-cluster-creation/cloud.ts | 2 +- .../full-admin-cluster-creation/index.ts | 28 ++-- .../index.ts | 19 ++- .../generic.ts | 132 ++++++------------ .../index.ts | 26 ++-- src/books/physical-cluster-creation/cloud.ts | 2 +- src/books/physical-cluster-creation/index.ts | 27 ++-- src/lib/prompts/landscape.ts | 54 +++---- src/lib/prompts/util-prompter.ts | 18 +-- src/lib/utility/git.ts | 2 +- src/lib/utility/http-util.ts | 8 +- src/lib/utility/service-tree-printer.ts | 10 +- src/lib/utility/yaml-manipulator.ts | 2 +- src/tasks/nitroso-waiter.ts | 20 +-- src/tasks/tasks.ts | 10 +- 19 files changed, 163 insertions(+), 251 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index f7999b1..3b9e4fb 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,4 +1,4 @@ -version: "3" +version: '3' env: SERVICES: >- @@ -29,9 +29,9 @@ tasks: kubectl: desc: Generate kubeconfig from all clusters env: - SLUGS: "pichu pikachu raichu suicune entei" - PCLUSTERS: "entei" - VCLUSTERS: "pichu pikachu raichu" + SLUGS: 'pichu pikachu raichu suicune entei' + PCLUSTERS: 'entei' + VCLUSTERS: 'pichu pikachu raichu' cmds: - ./scripts/local/update-kubectl.sh sync: diff --git a/index.ts b/index.ts index adda69a..0027837 100644 --- a/index.ts +++ b/index.ts @@ -1,8 +1,8 @@ -import { search } from "@inquirer/prompts"; -import { dependencies } from "./src/init"; -import { initTasks } from "./src/init/tasks.ts"; -import { initRunBooks } from "./src/init/runbooks.ts"; -import type { RunBook } from "./src/books/run-book.ts"; +import { search } from '@inquirer/prompts'; +import { dependencies } from './src/init'; +import { initTasks } from './src/init/tasks.ts'; +import { initRunBooks } from './src/init/runbooks.ts'; +import type { RunBook } from './src/books/run-book.ts'; type Choice = { value: Value; @@ -17,13 +17,11 @@ const tasks = initTasks(dependencies); const books = initRunBooks(dependencies, tasks); const book = (await search({ - message: "Which run book do you want to run?", - source: ( - input: string | undefined, - ): Choice[] | Promise[]> => { + message: 'Which run book do you want to run?', + source: (input: string | undefined): Choice[] | Promise[]> => { return books - .filter((b) => b.name.toLowerCase().includes((input ?? "").toLowerCase())) - .map((b) => ({ + .filter(b => b.name.toLowerCase().includes((input ?? '').toLowerCase())) + .map(b => ({ name: b.name, value: b, description: b.desc, @@ -37,9 +35,9 @@ try { if (e != null) { if (e.exitCode) { console.log(`❌ Error running book, exit code: ${e.exitCode}`); - console.log("========= stderr start =========="); + console.log('========= stderr start =========='); console.log(e.stderr.toString()); - console.log("========= stderr end =========="); + console.log('========= stderr end =========='); console.log( Bun.inspect(e, { colors: true, diff --git a/src/books/bare-admin-cluster-creation/cloud.ts b/src/books/bare-admin-cluster-creation/cloud.ts index aef62af..07172af 100644 --- a/src/books/bare-admin-cluster-creation/cloud.ts +++ b/src/books/bare-admin-cluster-creation/cloud.ts @@ -1,4 +1,4 @@ -import type { LandscapeCluster } from "../../lib/service-tree-def.ts"; +import type { LandscapeCluster } from '../../lib/service-tree-def.ts'; interface BareAdminClusterCloudCreator { slug: string; diff --git a/src/books/bare-admin-cluster-creation/index.ts b/src/books/bare-admin-cluster-creation/index.ts index 39ed2a2..fadfaff 100644 --- a/src/books/bare-admin-cluster-creation/index.ts +++ b/src/books/bare-admin-cluster-creation/index.ts @@ -1,11 +1,11 @@ -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 "./cloud.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 './cloud.ts'; class BareAdminClusterCreator implements RunBook { - name: string = "Bare Admin Cluster Creation"; - desc: string = "Create a empty admin Kubernetes cluster"; + name: string = 'Bare Admin Cluster Creation'; + desc: string = 'Create a empty admin Kubernetes cluster'; constructor( private stp: ServiceTreePrompter, @@ -17,11 +17,11 @@ class BareAdminClusterCreator implements RunBook { const [landscape, cluster] = await this.stp.AdminLandscapeCluster(); // output selected service tree for confirmation - console.log("🎯 Selected Service Tree to create"); - this.printer.Print("", [landscape, cluster]); + console.log('🎯 Selected Service Tree to create'); + this.printer.Print('', [landscape, cluster]); - const c = this.clouds.find((x) => x.slug === cluster.cloud.slug); - if (!c) return console.log("⚠️ Cloud not supported"); + const c = this.clouds.find(x => x.slug === cluster.cloud.slug); + if (!c) return console.log('⚠️ Cloud not supported'); await c.Run([landscape, cluster]); } diff --git a/src/books/full-admin-cluster-creation/cloud.ts b/src/books/full-admin-cluster-creation/cloud.ts index 7a64b99..f6df041 100644 --- a/src/books/full-admin-cluster-creation/cloud.ts +++ b/src/books/full-admin-cluster-creation/cloud.ts @@ -1,4 +1,4 @@ -import type { LandscapeCluster } from "../../lib/service-tree-def.ts"; +import type { LandscapeCluster } from '../../lib/service-tree-def.ts'; interface FullAdminClusterCloudCreator { slug: string; diff --git a/src/books/full-admin-cluster-creation/index.ts b/src/books/full-admin-cluster-creation/index.ts index 5a84ded..0465108 100644 --- a/src/books/full-admin-cluster-creation/index.ts +++ b/src/books/full-admin-cluster-creation/index.ts @@ -1,12 +1,12 @@ -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 { FullAdminClusterCloudCreator } from "./cloud.ts"; -import type { BareAdminClusterCloudCreator } from "../bare-admin-cluster-creation/cloud.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 { FullAdminClusterCloudCreator } from './cloud.ts'; +import type { BareAdminClusterCloudCreator } from '../bare-admin-cluster-creation/cloud.ts'; class FullAdminClusterCreator implements RunBook { - name: string = "Full Admin Cluster Creation"; - desc: string = "Create a fully-featured admin Kubernetes cluster"; + name: string = 'Full Admin Cluster Creation'; + desc: string = 'Create a fully-featured admin Kubernetes cluster'; constructor( private stp: ServiceTreePrompter, @@ -19,17 +19,15 @@ class FullAdminClusterCreator implements RunBook { const [landscape, cluster] = await this.stp.AdminLandscapeCluster(); // output selected service tree for confirmation - console.log("🎯 Selected Service Tree to create"); - this.printer.Print("", [landscape, cluster]); + console.log('🎯 Selected Service Tree to create'); + this.printer.Print('', [landscape, cluster]); - const bc = this.bareClouds.find((x) => x.slug === cluster.cloud.slug); - if (!bc) - return console.log("⚠️ Cloud not supported (Missing Bare Cloud Runbook"); + const bc = this.bareClouds.find(x => x.slug === cluster.cloud.slug); + if (!bc) return console.log('⚠️ Cloud not supported (Missing Bare Cloud Runbook'); await bc.Run([landscape, cluster]); - const fc = this.fullClouds.find((x) => x.slug === cluster.cloud.slug); - if (!fc) - return console.log("⚠️ Cloud not supported (Missing Full Cloud Runbook"); + const fc = this.fullClouds.find(x => x.slug === cluster.cloud.slug); + if (!fc) return console.log('⚠️ Cloud not supported (Missing Full Cloud Runbook'); await fc.Run([landscape, cluster]); } } diff --git a/src/books/graceful-admin-cluster-destruction/index.ts b/src/books/graceful-admin-cluster-destruction/index.ts index 6e6befd..8f134c5 100644 --- a/src/books/graceful-admin-cluster-destruction/index.ts +++ b/src/books/graceful-admin-cluster-destruction/index.ts @@ -1,11 +1,11 @@ -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 { GenericGracefulAdminClusterDestructor } from "./generic.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 { GenericGracefulAdminClusterDestructor } from './generic.ts'; class GracefulAdminClusterDestructor implements RunBook { - name: string = "Graceful Admin Cluster Destruction"; - desc: string = "Gracefully Destroy a admin Kubernetes cluster"; + name: string = 'Graceful Admin Cluster Destruction'; + desc: string = 'Gracefully Destroy a admin Kubernetes cluster'; constructor( private stp: ServiceTreePrompter, @@ -15,12 +15,11 @@ class GracefulAdminClusterDestructor implements RunBook { async Run(): Promise { // prompt user for physical landscape and cluster - const [adminLandscape, adminCluster] = - await this.stp.AdminLandscapeCluster(); + const [adminLandscape, adminCluster] = await this.stp.AdminLandscapeCluster(); // output selected service tree for confirmation - console.log("🎯 Selected Service Tree for graceful destruction:"); - this.p.Print("", [adminLandscape, adminCluster]); + console.log('🎯 Selected Service Tree for graceful destruction:'); + this.p.Print('', [adminLandscape, adminCluster]); await this.destructor.Run([adminLandscape, adminCluster]); } diff --git a/src/books/graceful-physical-cluster-destruction/generic.ts b/src/books/graceful-physical-cluster-destruction/generic.ts index d4f2ab3..e9bac88 100644 --- a/src/books/graceful-physical-cluster-destruction/generic.ts +++ b/src/books/graceful-physical-cluster-destruction/generic.ts @@ -2,16 +2,13 @@ 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"; +} 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'; class GenericGracefulPhysicalClusterDestructor { constructor( @@ -39,59 +36,18 @@ class GenericGracefulPhysicalClusterDestructor { 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`, - ); + 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", + '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", - ], - "", - ], + [['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'], ''], ]); }, ]); @@ -99,7 +55,7 @@ class GenericGracefulPhysicalClusterDestructor { // Apply ArgoCD configurations const adminPls = `${admin.landscape.slug}:${admin.cluster.set.slug}`; await this.task.Run([ - "Apply Helium Configuration", + 'Apply Helium Configuration', async () => { await $`pls ${{ raw: adminPls }}:install -- --kube-context ${adminContextSlug} --namespace ${adminNamespaceSlug}`.cwd( heliumDir, @@ -109,35 +65,31 @@ class GenericGracefulPhysicalClusterDestructor { // delete applications from ArgoCD const appsToRemove: ResourceSearch = { - kind: "app", + kind: 'app', context: adminContextSlug, namespace: adminNamespaceSlug, - selector: [["atomi.cloud/cluster", phy.cluster.principal.slug]], + selector: [['atomi.cloud/cluster', phy.cluster.principal.slug]], }; await this.task.Run([ - "Delete Applications", + 'Delete Applications', 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?", - ); + const deleteApp = await this.up.YesNo('Do you want to manually delete the applications?'); if (deleteApp) { - console.log( - `🗑️ Delete Root Application: ${phy.landscape.slug}-${phy.cluster.principal.slug}-carbon`, - ); + console.log(`🗑️ Delete Root Application: ${phy.landscape.slug}-${phy.cluster.principal.slug}-carbon`); await this.k.Delete({ - kind: "app", + kind: 'app', context: adminContextSlug, namespace: adminNamespaceSlug, name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-carbon`, }); - console.log("✅ Root Application deleted"); + console.log('✅ Root Application deleted'); - console.log("🗑️ Deleting applications..."); + console.log('🗑️ Deleting applications...'); await this.k.DeleteRange(appsToRemove); - console.log("✅ Applications deleted"); + console.log('✅ Applications deleted'); } return false; }, @@ -147,7 +99,7 @@ class GenericGracefulPhysicalClusterDestructor { // Delete all validating webhooks await this.task.Run([ - "Delete Validating Webhooks", + 'Delete Validating Webhooks', async () => { await $`kubectl --context ${phyContextSlug} delete validatingwebhookconfigurations --all`.nothrow(); }, @@ -155,9 +107,9 @@ class GenericGracefulPhysicalClusterDestructor { // Delete all namespaces await this.task.Run([ - "Delete Namespaces", + 'Delete Namespaces', async () => { - for (const namespace of ["pichu", "pikachu", "raichu", "sulfoxide"]) { + for (const namespace of ['pichu', 'pikachu', 'raichu', 'sulfoxide']) { console.log(` 🗑️ Removing namespace: ${namespace}`); await this.k.DeleteNamespace({ context: phyContextSlug, @@ -170,7 +122,7 @@ class GenericGracefulPhysicalClusterDestructor { // setup tofu repository correctly await this.task.Run([ - "Setup Tofu", + 'Setup Tofu', async () => { await $`pls setup`.cwd(tofuDir); }, @@ -179,7 +131,7 @@ class GenericGracefulPhysicalClusterDestructor { // destroy generic infrastructure const L1G = `${phy.landscape.slug}:l1:${phy.cluster.set.slug}`; await this.task.Run([ - "Destroy Generic Infrastructure", + 'Destroy Generic Infrastructure', async () => { await $`pls ${{ raw: L1G }}:destroy`.cwd(tofuDir); }, @@ -188,11 +140,9 @@ class GenericGracefulPhysicalClusterDestructor { // destroy L1 infrastructure const L1 = `${phy.landscape.slug}:l1:${phy.cluster.principal.slug}`; await this.task.Run([ - "Destroy L1 Infrastructure", + 'Destroy L1 Infrastructure', async () => { - 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); }, ]); @@ -200,7 +150,7 @@ class GenericGracefulPhysicalClusterDestructor { // destroy L0 infrastructure const L0 = `${phy.landscape.slug}:l0:${phy.cluster.principal.slug}`; await this.task.Run([ - "Destroy L0 Infrastructure", + 'Destroy L0 Infrastructure', async () => { await $`pls ${{ raw: L0 }}:state:rm -- 'module.cluster.module.proxy_secret.kubernetes_namespace.kubernetes-access'` .cwd(tofuDir) @@ -211,37 +161,37 @@ class GenericGracefulPhysicalClusterDestructor { // update kubectl configurations await this.task.Run([ - "Retrieve Kubectl Configurations", + 'Retrieve Kubectl Configurations', async () => { await $`pls kubectl`; }, ]); await this.task.Run([ - "Delete ExternalSecret in admin", + 'Delete ExternalSecret in admin', async () => { - for (const ns of this.virtualLandscapes.map((x) => x.slug)) { + for (const ns of this.virtualLandscapes.map(x => x.slug)) { await this.k.Delete({ - kind: "externalsecret", + kind: 'externalsecret', context: adminContextSlug, namespace: adminNamespaceSlug, name: `phase-6-${ns}-${phy.cluster.principal.slug}-cluster-secret`, }); } await this.k.Delete({ - kind: "externalsecret", + kind: 'externalsecret', context: adminContextSlug, namespace: adminNamespaceSlug, name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret`, }); await this.k.Delete({ - kind: "externalsecret", + kind: 'externalsecret', context: adminContextSlug, namespace: adminNamespaceSlug, name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret-bearer-token`, }); await this.k.Delete({ - kind: "externalsecret", + kind: 'externalsecret', context: adminContextSlug, namespace: adminNamespaceSlug, name: `${phy.landscape.slug}-${phy.cluster.principal.slug}-external-secret-ca-crt`, @@ -251,11 +201,11 @@ class GenericGracefulPhysicalClusterDestructor { // delete pointers to old cluster in admin await this.task.Run([ - "Delete SecretStore in admin", + 'Delete SecretStore in admin', async () => { - for (const ns of this.virtualLandscapes.map((x) => x.slug)) { + for (const ns of this.virtualLandscapes.map(x => x.slug)) { await this.k.Delete({ - kind: "secretstore", + kind: 'secretstore', context: adminContextSlug, namespace: adminNamespaceSlug, name: `phase-6-${ns}-${phy.cluster.principal.slug}`, diff --git a/src/books/graceful-physical-cluster-destruction/index.ts b/src/books/graceful-physical-cluster-destruction/index.ts index d850092..14df6d9 100644 --- a/src/books/graceful-physical-cluster-destruction/index.ts +++ b/src/books/graceful-physical-cluster-destruction/index.ts @@ -1,11 +1,11 @@ -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 { GenericGracefulPhysicalClusterDestructor } from "./generic.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 { GenericGracefulPhysicalClusterDestructor } from './generic.ts'; class GracefulPhysicalClusterDestructor implements RunBook { - name: string = "Graceful Physical Cluster Destruction"; - desc: string = "Gracefully Destroy a physical Kubernetes cluster"; + name: string = 'Graceful Physical Cluster Destruction'; + desc: string = 'Gracefully Destroy a physical Kubernetes cluster'; constructor( private stp: ServiceTreePrompter, @@ -15,18 +15,14 @@ class GracefulPhysicalClusterDestructor implements RunBook { async Run(): Promise { // prompt user for physical landscape and cluster - const [[phyLandscape, phyCluster], [adminLandscape, adminCluster]] = - await this.stp.AdminPhysicalLandscapeCluster(); + const [[phyLandscape, phyCluster], [adminLandscape, adminCluster]] = await this.stp.AdminPhysicalLandscapeCluster(); // output selected service tree for confirmation - console.log("🎯 Selected Service Tree for graceful destruction:"); - this.p.Print("Physical", [phyLandscape, phyCluster]); - this.p.Print("Admin", [adminLandscape, adminCluster]); + console.log('🎯 Selected Service Tree for graceful destruction:'); + this.p.Print('Physical', [phyLandscape, phyCluster]); + this.p.Print('Admin', [adminLandscape, adminCluster]); - await this.destructor.Run( - [phyLandscape, phyCluster], - [adminLandscape, adminCluster], - ); + await this.destructor.Run([phyLandscape, phyCluster], [adminLandscape, adminCluster]); } } diff --git a/src/books/physical-cluster-creation/cloud.ts b/src/books/physical-cluster-creation/cloud.ts index 668de44..60acc00 100644 --- a/src/books/physical-cluster-creation/cloud.ts +++ b/src/books/physical-cluster-creation/cloud.ts @@ -1,4 +1,4 @@ -import type { LandscapeCluster } from "../../lib/service-tree-def.ts"; +import type { LandscapeCluster } from '../../lib/service-tree-def.ts'; interface PhysicalClusterCloudCreator { slug: string; diff --git a/src/books/physical-cluster-creation/index.ts b/src/books/physical-cluster-creation/index.ts index ad1e269..bfbbd7a 100644 --- a/src/books/physical-cluster-creation/index.ts +++ b/src/books/physical-cluster-creation/index.ts @@ -1,16 +1,13 @@ -import type { TaskRunner } from "../../tasks/tasks.ts"; -import type { RunBook } from "../run-book.ts"; -import type { ServiceTreePrompter } from "../../lib/prompts/landscape.ts"; -import type { PhysicalClusterCloudCreator } from "./cloud.ts"; -import type { NitrosoWaiter } from "../../tasks/nitroso-waiter.ts"; -import type { - ServiceTreeLandscapePrincipal, - ServiceTreeService, -} from "../../lib/service-tree-def.ts"; +import type { TaskRunner } from '../../tasks/tasks.ts'; +import type { RunBook } from '../run-book.ts'; +import type { ServiceTreePrompter } from '../../lib/prompts/landscape.ts'; +import type { PhysicalClusterCloudCreator } from './cloud.ts'; +import type { NitrosoWaiter } from '../../tasks/nitroso-waiter.ts'; +import type { ServiceTreeLandscapePrincipal, ServiceTreeService } from '../../lib/service-tree-def.ts'; class PhysicalClusterCreator implements RunBook { - name: string = "Physical Cluster Creation"; - desc: string = "Create a physical Kubernetes cluster from scratch"; + name: string = 'Physical Cluster Creation'; + desc: string = 'Create a physical Kubernetes cluster from scratch'; constructor( private task: TaskRunner, @@ -27,13 +24,13 @@ class PhysicalClusterCreator implements RunBook { const [phy, admin] = await this.stp.AdminPhysicalLandscapeCluster(); // output selected service tree for confirmation - console.log("🎯 Selected Service Tree to create"); + console.log('🎯 Selected Service Tree to create'); const [phyLandscape, phyCluster] = phy; const [adminLandscape, adminCluster] = admin; - const c = this.clouds.find((x) => x.slug === phyCluster.cloud.slug); - if (!c) return console.log("⚠️ Cloud not supported"); + const c = this.clouds.find(x => x.slug === phyCluster.cloud.slug); + if (!c) return console.log('⚠️ Cloud not supported'); await c.Run(phy, admin); const adminContextSlug = `${adminLandscape.slug}-${adminCluster.principal.slug}`; @@ -42,7 +39,7 @@ class PhysicalClusterCreator implements RunBook { // wait for nitroso to be ready const nitrosoTask = this.nitrosoWaiter.task( phyLandscape.slug, - this.virtualLandscapes.map((x) => x.slug), + this.virtualLandscapes.map(x => x.slug), phyCluster.principal.slug, adminContextSlug, adminNamespaceSlug, diff --git a/src/lib/prompts/landscape.ts b/src/lib/prompts/landscape.ts index 2ae486b..f42b2a6 100644 --- a/src/lib/prompts/landscape.ts +++ b/src/lib/prompts/landscape.ts @@ -1,11 +1,11 @@ -import { select } from "@inquirer/prompts"; +import { select } from '@inquirer/prompts'; import type { LandscapeCluster, CloudTreeCloud, CloudTreeCluster, ServiceTreeLandscapePrincipal, CloudTreeClusterSet, -} from "../service-tree-def.ts"; +} from '../service-tree-def.ts'; class ServiceTreePrompter { constructor( @@ -19,20 +19,17 @@ class ServiceTreePrompter { prompt?: string, ): Promise { return (await select({ - message: prompt ?? "Select landscape", - choices: landscapes.map((x) => ({ + message: prompt ?? 'Select landscape', + choices: landscapes.map(x => ({ name: x.name, value: x, })), })) as ServiceTreeLandscapePrincipal; } - async Cluster( - cloudPrompt?: string, - clusterPrompt?: string, - ): Promise { + async Cluster(cloudPrompt?: string, clusterPrompt?: string): Promise { const cloud: CloudTreeCloud = (await select({ - message: cloudPrompt ?? "Select cloud provider", + message: cloudPrompt ?? 'Select cloud provider', choices: Object.entries(this.cloudTree).map(([name, c]) => ({ name, value: c, @@ -40,14 +37,11 @@ class ServiceTreePrompter { })), })) as CloudTreeCloud; - const clusterSets = Object.values( - this.cloudTree[cloud.principal.name].clusterSets, - ); + const clusterSets = Object.values(this.cloudTree[cloud.principal.name].clusterSets); const clusterSet = (await select({ - message: - clusterPrompt ?? `Select cluster set under '${cloud.principal.name}'`, - choices: clusterSets.map((x) => ({ + message: clusterPrompt ?? `Select cluster set under '${cloud.principal.name}'`, + choices: clusterSets.map(x => ({ name: x.principal.name, value: x, description: x.principal.description, @@ -57,10 +51,8 @@ class ServiceTreePrompter { const clusters = Object.values(clusterSet.clusters); return (await select({ - message: - clusterPrompt ?? - `Select cluster under '${cloud.principal.name} {${clusterSet.principal.name}}'`, - choices: clusters.map((x) => ({ + message: clusterPrompt ?? `Select cluster under '${cloud.principal.name} {${clusterSet.principal.name}}'`, + choices: clusters.map(x => ({ name: x.principal.name, value: x, description: x.principal.description, @@ -76,16 +68,8 @@ class ServiceTreePrompter { adminCloudPrompt?: string, adminClusterPrompt?: string, ): Promise<[LandscapeCluster, LandscapeCluster]> { - const phy = await this.PhysicalLandscapeCluster( - phyLandscapePrompt, - phyCloudPrompt, - phyClusterPrompt, - ); - const admin = await this.AdminLandscapeCluster( - adminLandscapePrompt, - adminCloudPrompt, - adminClusterPrompt, - ); + const phy = await this.PhysicalLandscapeCluster(phyLandscapePrompt, phyCloudPrompt, phyClusterPrompt); + const admin = await this.AdminLandscapeCluster(adminLandscapePrompt, adminCloudPrompt, adminClusterPrompt); return [phy, admin]; } @@ -96,9 +80,9 @@ class ServiceTreePrompter { ): Promise { return await this.LandscapeCluster( this.phyLandscapes, - phyLandscapePrompt ?? "Select physical landscape to use", - phyCloudPrompt ?? "Select physical cloud provider to use", - phyClusterPrompt ?? "Select physical cluster key to use", + phyLandscapePrompt ?? 'Select physical landscape to use', + phyCloudPrompt ?? 'Select physical cloud provider to use', + phyClusterPrompt ?? 'Select physical cluster key to use', ); } @@ -109,9 +93,9 @@ class ServiceTreePrompter { ): Promise { return await this.LandscapeCluster( this.adminLandscapes, - landscapePrompt ?? "Select admin landscape to use", - cloudPrompt ?? "Select admin cloud provider to use", - clusterPrompt ?? "Select admin cluster key to use", + landscapePrompt ?? 'Select admin landscape to use', + cloudPrompt ?? 'Select admin cloud provider to use', + clusterPrompt ?? 'Select admin cluster key to use', ); } diff --git a/src/lib/prompts/util-prompter.ts b/src/lib/prompts/util-prompter.ts index ae500cd..f3b1467 100644 --- a/src/lib/prompts/util-prompter.ts +++ b/src/lib/prompts/util-prompter.ts @@ -1,26 +1,26 @@ -import { select } from "@inquirer/prompts"; +import { select } from '@inquirer/prompts'; class UtilPrompter { async YesNo(question: string): Promise { const cont = (await select({ message: question, choices: [ - { name: "Yes", value: true }, - { name: "No", value: false }, + { name: 'Yes', value: true }, + { name: 'No', value: false }, ], })) as boolean; return cont; } - async YesNoExit(question: string): Promise { - const cont = (await select({ + async YesNoExit(question: string): Promise { + const cont = (await select({ message: question, choices: [ - { name: "Yes", value: true }, - { name: "No", value: false }, - { name: "Exit", value: "exit" }, + { name: 'Yes', value: true }, + { name: 'No', value: false }, + { name: 'Exit', value: 'exit' }, ], - })) as boolean | "exit"; + })) as boolean | 'exit'; return cont; } } diff --git a/src/lib/utility/git.ts b/src/lib/utility/git.ts index ab06426..3f697ec 100644 --- a/src/lib/utility/git.ts +++ b/src/lib/utility/git.ts @@ -1,4 +1,4 @@ -import { $ } from "bun"; +import { $ } from 'bun'; class Git { async CommitAndPush(dir: string, message: string): Promise { diff --git a/src/lib/utility/http-util.ts b/src/lib/utility/http-util.ts index 99f7323..690ee88 100644 --- a/src/lib/utility/http-util.ts +++ b/src/lib/utility/http-util.ts @@ -1,10 +1,6 @@ -import { $ } from "bun"; +import { $ } from 'bun'; class HttpUtil { - async WaitFor( - interval: number, - url: string, - method: string = "GET", - ): Promise { + async WaitFor(interval: number, url: string, method: string = 'GET'): Promise { while (true) { const response = await fetch(url, { method }); if (response.ok) return; diff --git a/src/lib/utility/service-tree-printer.ts b/src/lib/utility/service-tree-printer.ts index 937e3c6..e290968 100644 --- a/src/lib/utility/service-tree-printer.ts +++ b/src/lib/utility/service-tree-printer.ts @@ -1,12 +1,8 @@ -import type { LandscapeCluster } from "../service-tree-def.ts"; +import type { LandscapeCluster } from '../service-tree-def.ts'; class ServiceTreePrinter { - Print( - prefix: string, - [landscape, cluster]: LandscapeCluster, - indent: number = 2, - ): void { - const space = " ".repeat(indent); + Print(prefix: string, [landscape, cluster]: LandscapeCluster, indent: number = 2): void { + const space = ' '.repeat(indent); console.log(`${space}⛰️${prefix} Landscape: \t${landscape.name}`); console.log(`${space}☁️${prefix} Cloud: \t${cluster.cloud.name}`); console.log(`${space}🌀${prefix} Cluster: \t${cluster.principal.name}`); diff --git a/src/lib/utility/yaml-manipulator.ts b/src/lib/utility/yaml-manipulator.ts index dbf6695..dc460b9 100644 --- a/src/lib/utility/yaml-manipulator.ts +++ b/src/lib/utility/yaml-manipulator.ts @@ -1,4 +1,4 @@ -import { parseDocument } from "yaml"; +import { parseDocument } from 'yaml'; class YamlManipulator { async Mutate(path: string, mutations: [string[], unknown][]): Promise { diff --git a/src/tasks/nitroso-waiter.ts b/src/tasks/nitroso-waiter.ts index 23f7b06..c975b9a 100644 --- a/src/tasks/nitroso-waiter.ts +++ b/src/tasks/nitroso-waiter.ts @@ -1,7 +1,7 @@ -import type { Task } from "./tasks.ts"; -import { SERVICE_TREE } from "../lib/service-tree.ts"; -import { KubectlUtil } from "../lib/utility/kubectl-util.ts"; -import { HttpUtil } from "../lib/utility/http-util.ts"; +import type { Task } from './tasks.ts'; +import { SERVICE_TREE } from '../lib/service-tree.ts'; +import { KubectlUtil } from '../lib/utility/kubectl-util.ts'; +import { HttpUtil } from '../lib/utility/http-util.ts'; class NitrosoWaiter { constructor( @@ -9,7 +9,7 @@ class NitrosoWaiter { private h: HttpUtil, ) {} - name: string = "Wait for Nitroso to be ready"; + name: string = 'Wait for Nitroso to be ready'; services: string[] = [ SERVICE_TREE.nitroso.services.tin.principal.slug, SERVICE_TREE.nitroso.services.helium.principal.slug, @@ -32,7 +32,7 @@ class NitrosoWaiter { const name = `${phy}-${cluster}-nitroso-${service}`; console.log(`⏱️ Waiting for ${name} to be ready...`); await this.k.WaitForApplication({ - kind: "app", + kind: 'app', context: adminContext, namespace: adminNamespace, name, @@ -44,16 +44,16 @@ class NitrosoWaiter { const sslName = `api-zinc-nitroso-${v}-tls-x-nitroso-x-${phyLandscape}-${v}-iodine`; const context = `${phyLandscape}-${cluster}`; await this.k.Wait(1, 5, { - kind: "certificate", + kind: 'certificate', context, namespace: v, - fieldSelector: [["metadata.name", sslName]], + fieldSelector: [['metadata.name', sslName]], }); await this.k.Wait(1, 5, { - kind: "secret", + kind: 'secret', context, namespace: v, - fieldSelector: [["metadata.name", sslName]], + fieldSelector: [['metadata.name', sslName]], }); } // wait for endpoint to be ready diff --git a/src/tasks/tasks.ts b/src/tasks/tasks.ts index b1c6d93..d385f8b 100644 --- a/src/tasks/tasks.ts +++ b/src/tasks/tasks.ts @@ -1,5 +1,5 @@ -import pc from "picocolors"; -import type { UtilPrompter } from "../lib/prompts/util-prompter.ts"; +import pc from 'picocolors'; +import type { UtilPrompter } from '../lib/prompts/util-prompter.ts'; // name, action interface ComplexTask { @@ -63,10 +63,8 @@ class TaskRunner { * @constructor */ async Run(task: Task): Promise { - const r = await this.up.YesNoExit( - `Do you want to run task '${this.extractName(task)}'?`, - ); - if (r === "exit") process.exit(0); + const r = await this.up.YesNoExit(`Do you want to run task '${this.extractName(task)}'?`); + if (r === 'exit') process.exit(0); if (r) await this.Exec(task); } }