diff --git a/src/books/admin-cluster-migration/index.ts b/src/books/admin-cluster-migration/index.ts new file mode 100644 index 0000000..2b9732d --- /dev/null +++ b/src/books/admin-cluster-migration/index.ts @@ -0,0 +1,53 @@ +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"; + + constructor( + private stp: ServiceTreePrompter, + private printer: ServiceTreePrinter, + private clouds: BareAdminClusterCloudCreator[], + private destructors: GenericGracefulAdminClusterDestructor, + private transition: AdminClusterTransitioner, + ) {} + + 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", + ); + 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", + ); + + 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"); + + await c.Run([toLandscape, toCluster]); + + // perform transition + await this.transition.Run( + [fromLandscape, fromCluster], + [toLandscape, toCluster], + ); + + // destroy old cluster + await this.destructors.Run([fromLandscape, fromCluster]); + } +} + +export { AdminClusterMigrator }; diff --git a/src/books/admin-cluster-migration/transition.ts b/src/books/admin-cluster-migration/transition.ts new file mode 100644 index 0000000..4b4ddaf --- /dev/null +++ b/src/books/admin-cluster-migration/transition.ts @@ -0,0 +1,147 @@ +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( + private t: TaskRunner, + private k: KubectlUtil, + private sulfoxideHelium: ServiceTreeService, + private sulfoxideFluorine: ServiceTreeService, + private sulfoxideBoron: ServiceTreeService, + private sulfoxideHeliumWaiter: SulfoxideHeliumWaiter, + private sulfoxideBoronWaiter: SulfoxideBoronWaiter, + ) {} + + async Run( + [fL, fC]: LandscapeCluster, + [tL, tC]: LandscapeCluster, + ): Promise { + // common variables + + // services + const He = this.sulfoxideHelium; + const F = this.sulfoxideFluorine; + const B = this.sulfoxideBoron; + + // contexts + const fCtx = `${fL.slug}-${fC.principal.slug}`; + const tCtx = `${tL.slug}-${tC.principal.slug}`; + + // namespaces + const He_NS = `${He.platform.slug}-${He.principal.slug}`; + const B_NS = `${B.platform.slug}-${B.principal.slug}`; + + // dir + const F_Dir = `./platforms/${F.platform.slug}/${F.principal.slug}`; + + console.log("🎯 Both systems are operational, performing transition..."); + const prefix = `${He_NS}-argocd`; + const resources: Resource[] = [ + ...[ + `${prefix}-applicationset-controller`, + `${prefix}-notifications-controller`, + `${prefix}-redis`, + `${prefix}-server`, + `${prefix}-repo-server`, + ].map((name) => ({ + kind: "deployment", + context: fCtx, + namespace: He_NS, + name, + })), + { + kind: "statefulset", + context: fCtx, + namespace: He_NS, + name: `${prefix}-application-controller`, + }, + ]; + const replicas: Record = {}; + + await this.t.Run([ + "Get Replicas", + async () => { + for (const resource of resources) + replicas[resource.name] = await this.k.GetReplica(resource); + }, + ]); + + console.log("🔀 Replicas before scaling down:", replicas); + + await this.t.Run([ + "Scale Down Old Cluster", + async () => { + for (const resource of resources) await this.k.Scale(resource, 0); + }, + ]); + + // perform migration via velero + await this.t.Run([ + "Velero Migration", + async () => { + await $`nix develop -c pls migrate -- ${fCtx} ${tCtx}`.cwd(F_Dir); + }, + ]); + + await this.t.Exec([ + "Wait for Helium to be migrated", + async () => { + await this.k.Wait(5, 5, { + kind: "deployment", + context: tCtx, + namespace: He_NS, + selector: [["app.kubernetes.io/part-of", `argocd`]], + }); + await this.k.Wait(1, 5, { + kind: "statefulset", + context: tCtx, + namespace: He_NS, + selector: [["app.kubernetes.io/part-of", `argocd`]], + }); + }, + ]); + + await this.t.Exec([ + "Wait for Boron to be migrated", + async () => { + await this.k.Wait(1, 5, { + kind: "deployment", + context: tCtx, + namespace: B_NS, + fieldSelector: [ + ["metadata.name", `${B.platform.slug}-${B.principal.slug}`], + ], + }); + }, + ]); + + // Scale back up the new cluster + await this.t.Run([ + "Scale Up New Cluster", + async () => { + for (const resource of resources) { + resource.context = tCtx; + await this.k.Scale(resource, replicas[resource.name]); + } + }, + ]); + + // wait for new cluster to be ready + const waitForHelium = this.sulfoxideHeliumWaiter.task(tCtx, He_NS); + await this.t.Exec(waitForHelium); + + const waitForBoron = this.sulfoxideBoronWaiter.task(tCtx, B_NS); + await this.t.Run(waitForBoron); + + console.log("🎉 Transition completed!"); + } +} + +export { AdminClusterTransitioner }; diff --git a/src/books/bare-admin-cluster-creation/digital-ocean.ts b/src/books/bare-admin-cluster-creation/digital-ocean.ts index 761e071..6b2d652 100644 --- a/src/books/bare-admin-cluster-creation/digital-ocean.ts +++ b/src/books/bare-admin-cluster-creation/digital-ocean.ts @@ -8,6 +8,7 @@ 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 @@ -20,6 +21,8 @@ class DigitalOceanBareAdminClusterCreator private k: KubectlUtil, private sulfoxideTofu: ServiceTreeService, private sulfoxideFluorine: ServiceTreeService, + private sulfoxideXenon: ServiceTreeService, + private sulfoxideXenonWaiter: SulfoxideXenonWaiter, slug: string, ) { this.slug = slug; @@ -29,9 +32,11 @@ class DigitalOceanBareAdminClusterCreator // constants const admin = { landscape, cluster }; const tofu = this.sulfoxideTofu; - const fluorine = this.sulfoxideFluorine; + const F = this.sulfoxideFluorine; + const Xe = this.sulfoxideXenon; const tofuDir = `./platforms/${tofu.platform.slug}/${tofu.principal.slug}`; - const fluorineDir = `./platforms/${fluorine.platform.slug}/${fluorine.principal.slug}`; + const F_Dir = `./platforms/${F.platform.slug}/${F.principal.slug}`; + const Xe_Dir = `./platforms/${Xe.platform.slug}/${Xe.principal.slug}`; const context = `${admin.landscape.slug}-${admin.cluster.principal.slug}`; // Check if we want to inject the DO secrets @@ -95,7 +100,7 @@ class DigitalOceanBareAdminClusterCreator await this.task.Run([ "Setup Velero Backup Engine", async () => { - await $`nix develop -c pls setup`.cwd(fluorineDir); + await $`nix develop -c pls setup`.cwd(F_Dir); }, ]); @@ -104,15 +109,15 @@ class DigitalOceanBareAdminClusterCreator "Install Velero Backup Engine", async () => { await $`nix develop -c pls velero:${{ raw: admin.landscape.slug }}:install -- --kubecontext ${{ raw: context }}`.cwd( - fluorineDir, + F_Dir, ); }, ]); - await this.task.Run([ + await this.task.Exec([ "Wait for Velero to be ready", async () => { - await this.k.WaitForReplicas({ + await this.k.WaitForReplica({ namespace: "velero", name: "velero", context: context, @@ -121,6 +126,17 @@ class DigitalOceanBareAdminClusterCreator await $`kubectl --context ${{ raw: context }} -n velero wait --for=jsonpath=.status.phase=Available --timeout=6000s BackupStorageLocation default`; }, ]); + + await this.task.Run([ + "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"); + 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 034f144..633f5af 100644 --- a/src/books/full-admin-cluster-creation/digital-ocean.ts +++ b/src/books/full-admin-cluster-creation/digital-ocean.ts @@ -5,7 +5,8 @@ import type { } from "../../lib/service-tree-def.ts"; import type { FullAdminClusterCloudCreator } from "./cloud.ts"; import type { TaskRunner } from "../../tasks/tasks.ts"; -import type { KubectlUtil } from "../../lib/utility/kubectl-util.ts"; +import type { SulfoxideHeliumWaiter } from "../../tasks/sulfoxide-helium-waiter.ts"; +import type { SulfoxideBoronWaiter } from "../../tasks/sulfoxide-boron-waiter.ts"; class DigitalOceanFullAdminClusterCreator implements FullAdminClusterCloudCreator @@ -14,9 +15,10 @@ class DigitalOceanFullAdminClusterCreator constructor( private task: TaskRunner, - private k: KubectlUtil, private sulfoxideHelium: ServiceTreeService, private sulfoxideBoron: ServiceTreeService, + private sulfoxideHeliumWaiter: SulfoxideHeliumWaiter, + private sulfoxideBoronWaiter: SulfoxideBoronWaiter, slug: string, ) { this.slug = slug; @@ -45,45 +47,14 @@ class DigitalOceanFullAdminClusterCreator "Create Helium Helm Release", async () => { const heliumPls = `${admin.landscape.slug}:${admin.cluster.set.slug}`; - await $`pls ${{ raw: heliumPls }}:install -- --kube-context ${{ raw: context }} -n ${{ raw: namespace }}`.cwd( + await $`pls ${{ raw: heliumPls }}:install -- --kube-context ${context} -n ${namespace}`.cwd( heliumDir, ); }, ]); - console.log("⏱️ Waiting for Helium to be ready..."); - const prefix = `${helium.platform.slug}-${helium.principal.slug}-argocd`; - await this.k.WaitForReplicas({ - kind: "deployment", - context, - namespace, - name: `${prefix}-server`, - }); - await this.k.WaitForReplicas({ - kind: "deployment", - context, - namespace, - name: `${prefix}-repo-server`, - }); - await this.k.WaitForReplicas({ - kind: "deployment", - context, - namespace, - name: `${prefix}-redis`, - }); - await this.k.WaitForReplicas({ - kind: "deployment", - context, - namespace, - name: `${prefix}-notifications-controller`, - }); - await this.k.WaitForReplicas({ - kind: "deployment", - context, - namespace, - name: `${prefix}-applicationset-controller`, - }); - console.log("✅ Helium is ready"); + const waitForHelium = this.sulfoxideHeliumWaiter.task(context, namespace); + await this.task.Run(waitForHelium); await this.task.Run([ "Create Boron Namespace", @@ -96,20 +67,14 @@ class DigitalOceanFullAdminClusterCreator "Create Boron Helm Release", async () => { const boronPls = `${admin.landscape.slug}:${admin.cluster.set.slug}`; - await $`pls ${{ raw: boronPls }}:install -- --kube-context ${{ raw: context }} -n ${{ raw: boronNS }}`.cwd( + await $`pls ${{ raw: boronPls }}:install -- --kube-context ${context} -n ${boronNS}`.cwd( boronDir, ); }, ]); - console.log("⏱️ Waiting for Boron to be ready..."); - await this.k.WaitForReplicas({ - kind: "deployment", - context, - namespace, - name: `${boron.platform.slug}-${boron.principal.slug}`, - }); - console.log("✅ Boron is ready"); + const waitForBoron = this.sulfoxideBoronWaiter.task(context, namespace); + await this.task.Run(waitForBoron); } } diff --git a/src/books/graceful-admin-cluster-destruction/generic.ts b/src/books/graceful-admin-cluster-destruction/generic.ts index 4cb8a27..a71ec52 100644 --- a/src/books/graceful-admin-cluster-destruction/generic.ts +++ b/src/books/graceful-admin-cluster-destruction/generic.ts @@ -78,6 +78,7 @@ class GenericGracefulAdminClusterDestructor { await this.task.Run([ "Destroy L1 Infrastructure", async () => { + await $`pls ${{ raw: L1 }}:init`.cwd(tofuDir); await $`pls ${{ raw: L1 }}:state:rm -- 'kubernetes_namespace.sulfoxide'` .cwd(tofuDir) .nothrow(); diff --git a/src/books/physical-cluster-creation/digital-ocean.ts b/src/books/physical-cluster-creation/digital-ocean.ts index 8849848..1585472 100644 --- a/src/books/physical-cluster-creation/digital-ocean.ts +++ b/src/books/physical-cluster-creation/digital-ocean.ts @@ -202,7 +202,7 @@ class DigitalOceanPhysicalClusterCreator "Wait for statefulset (etcd) to be ready", async () => { for (const ns of ["pichu", "pikachu", "raichu"]) { - await this.k.WaitForReplicas({ + await this.k.WaitForReplica({ kind: "statefulset", context: `${phyLandscape.slug}-${phyCluster.principal.slug}`, namespace: ns, @@ -216,7 +216,7 @@ class DigitalOceanPhysicalClusterCreator "Wait for deployment (iodine) to be ready", async () => { for (const ns of ["pichu", "pikachu", "raichu"]) { - await this.k.WaitForReplicas({ + await this.k.WaitForReplica({ kind: "deployment", context: `${phyLandscape.slug}-${phyCluster.principal.slug}`, namespace: ns, diff --git a/src/init/runbooks.ts b/src/init/runbooks.ts index eb4e9a9..f7aabfb 100644 --- a/src/init/runbooks.ts +++ b/src/init/runbooks.ts @@ -15,6 +15,8 @@ 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"; function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { const sulfoxide = SERVICE_TREE.sulfoxide; @@ -66,6 +68,8 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { d.kubectl, sulfoxide.services.tofu, sulfoxide.services.backup_engine, + sulfoxide.services.metricsServer, + t.sulfoxideXenonWaiter, CLOUDS.DigitalOcean.slug, ), ]; @@ -79,9 +83,10 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { const fullAdminCloudCreators: FullAdminClusterCloudCreator[] = [ new DigitalOceanFullAdminClusterCreator( d.taskRunner, - d.kubectl, sulfoxide.services.argocd, sulfoxide.services.internal_ingress, + t.sulfoxideHeliumWaiter, + t.sulfoxideBoronWaiter, CLOUDS.DigitalOcean.slug, ), ]; @@ -109,12 +114,31 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { genericAdminGracefulDestructor, ); + // admin cluster migration + const adminClusterTransitioner = new AdminClusterTransitioner( + d.taskRunner, + d.kubectl, + sulfoxide.services.argocd, + sulfoxide.services.backup_engine, + sulfoxide.services.internal_ingress, + t.sulfoxideHeliumWaiter, + t.sulfoxideBoronWaiter, + ); + const adminClusterMigrator = new AdminClusterMigrator( + d.stp, + d.serviceTreePrinter, + bareAdminCloudCreators, + genericAdminGracefulDestructor, + adminClusterTransitioner, + ); + return [ physicalClusterCreator, phyGracefulDestructor, bareAdminClusterCreator, fullAdminCloudCreator, adminGracefulDestructor, + adminClusterMigrator, ]; } diff --git a/src/init/tasks.ts b/src/init/tasks.ts index e902b8e..910e709 100644 --- a/src/init/tasks.ts +++ b/src/init/tasks.ts @@ -1,13 +1,33 @@ 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; + sulfoxideHeliumWaiter: SulfoxideHeliumWaiter; + sulfoxideBoronWaiter: SulfoxideBoronWaiter; + sulfoxideXenonWaiter: SulfoxideXenonWaiter; } 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, + ), }; } diff --git a/src/lib/service-tree.ts b/src/lib/service-tree.ts index eb4de8d..d0f3d0a 100644 --- a/src/lib/service-tree.ts +++ b/src/lib/service-tree.ts @@ -230,6 +230,15 @@ const SERVICE_TREE = { }, platform: PLATFORMS.Sulfoxide, }, + metricsServer: { + principal: { + name: "Metrics Server", + slug: "xenon", + description: "Metrics Server for Cluster", + projectId: "", + }, + platform: PLATFORMS.Sulfoxide, + }, tofu: { principal: { name: "Tofu", diff --git a/src/lib/utility/kubectl-util.ts b/src/lib/utility/kubectl-util.ts index cc4bd34..72caad6 100644 --- a/src/lib/utility/kubectl-util.ts +++ b/src/lib/utility/kubectl-util.ts @@ -39,10 +39,22 @@ class KubectlUtil { return `${selector} ${fieldSelector}`; } - async WaitForReplicas(r: Resource): Promise { + async Scale(r: Resource, replicas: number): Promise { + const cmds = $.escape( + `kubectl scale --context ${r.context} -n ${r.namespace} ${r.kind} ${r.name} --replicas=${replicas}`, + ); + console.log(`🖥️ Scale Execute Command: ${cmds}`); + await $`kubectl scale --context ${r.context} -n ${r.namespace} ${r.kind} ${r.name} --replicas=${{ raw: replicas.toString(10) }}`; + } + + async GetReplica(r: Resource): Promise { const { stdout } = await $`kubectl get --context ${r.context} --namespace ${r.namespace} ${r.kind} ${r.name} -o jsonpath="{.status.replicas}"`.quiet(); - const replicas = parseInt(stdout.toString().trim(), 10); + return parseInt(stdout.toString().trim(), 10); + } + + async WaitForReplica(r: Resource): Promise { + const replicas = await this.GetReplica(r); const cmds = $.escape( `kubectl --context ${r.context} -n ${r.namespace} wait --for=jsonpath=.status.readyReplicas=${replicas} --timeout=600s ${r.kind} ${r.name}`, ); @@ -267,4 +279,5 @@ class KubectlUtil { } } -export { KubectlUtil, type ResourceSearch, type Intervention }; +export { KubectlUtil }; +export type { Resource, ResourceSearch, Intervention, NamespaceSearch }; diff --git a/src/tasks/sulfoxide-boron-waiter.ts b/src/tasks/sulfoxide-boron-waiter.ts new file mode 100644 index 0000000..a3422c4 --- /dev/null +++ b/src/tasks/sulfoxide-boron-waiter.ts @@ -0,0 +1,31 @@ +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( + private k: KubectlUtil, + private sulfoxideBoron: ServiceTreeService, + ) {} + + 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..."); + await this.k.WaitForReplica({ + kind: "deployment", + context, + namespace, + name: `${boron.platform.slug}-${boron.principal.slug}`, + }); + console.log("✅ Boron is ready"); + }, + ]; + } +} + +export { SulfoxideBoronWaiter }; diff --git a/src/tasks/sulfoxide-helium-waiter.ts b/src/tasks/sulfoxide-helium-waiter.ts new file mode 100644 index 0000000..343a5f5 --- /dev/null +++ b/src/tasks/sulfoxide-helium-waiter.ts @@ -0,0 +1,49 @@ +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( + private k: KubectlUtil, + private sulfoxideHelium: ServiceTreeService, + ) {} + + name: string = "Wait for Sulfoxide Helium to be ready"; + + task(context: string, namespace: string): Task { + return [ + this.name, + async () => { + const helium = this.sulfoxideHelium; + const prefix = `${helium.platform.slug}-${helium.principal.slug}-argocd`; + const deployments = [ + `${prefix}-server`, + `${prefix}-repo-server`, + `${prefix}-redis`, + `${prefix}-notifications-controller`, + `${prefix}-applicationset-controller`, + ]; + for (const name of deployments) { + console.log(`🚧 Waiting for ${name} to be ready...`); + await this.k.WaitForReplica({ + kind: "deployment", + context, + namespace, + name, + }); + console.log(`✅ ${name} is ready`); + } + console.log(`🚧 Waiting for statefulset to be ready...`); + await this.k.WaitForReplica({ + kind: "statefulset", + context, + namespace, + name: `${prefix}-application-controller`, + }); + console.log(`✅ Statefulset is ready`); + }, + ]; + } +} + +export { SulfoxideHeliumWaiter }; diff --git a/src/tasks/sulfoxide-xenon-waiter.ts b/src/tasks/sulfoxide-xenon-waiter.ts new file mode 100644 index 0000000..bba1732 --- /dev/null +++ b/src/tasks/sulfoxide-xenon-waiter.ts @@ -0,0 +1,32 @@ +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( + private k: KubectlUtil, + private sulfoxideXenon: ServiceTreeService, + ) {} + + name: string = "Wait for Sulfoxide Xenon to be ready"; + + task(context: string, namespace: string): Task { + return [ + this.name, + async () => { + const Xe = this.sulfoxideXenon; + 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", + context, + namespace, + name, + }); + console.log(`✅ Deployment is ${name} ready`); + }, + ]; + } +} + +export { SulfoxideXenonWaiter };