Skip to content

Commit

Permalink
Merge pull request #15 from AtomiCloud/ernest/sul-35-switch-dns-runbo…
Browse files Browse the repository at this point in the history
…ok-task

feat: DNS switcher
  • Loading branch information
kirinnee authored Oct 18, 2024
2 parents 2e197f3 + c8c1211 commit 6040b23
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 85 deletions.
1 change: 1 addition & 0 deletions nix/env.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ with packages;
terraform
bun
curl
dogdns
];

lint = [
Expand Down
1 change: 1 addition & 0 deletions nix/packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let
{
inherit
coreutils
dogdns
sd
curl
bash
Expand Down
124 changes: 62 additions & 62 deletions src/books/physical-cluster-creation/digital-ocean.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
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 { 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;
Expand All @@ -18,51 +18,51 @@ class DigitalOceanPhysicalClusterCreator implements PhysicalClusterCloudCreator
private k: KubectlUtil,
private sulfoxide_tofu: ServiceTreeService,
private sulfoxide_helium: ServiceTreeService,
slug: string
slug: string,
) {
this.slug = slug;
}

async Run(
[phyLandscape, phyCluster]: LandscapeCluster,
[adminLandscape, adminCluster]: LandscapeCluster
[adminLandscape, adminCluster]: LandscapeCluster,
): Promise<void> {
// constants
const tofu = this.sulfoxide_tofu;
const He = this.sulfoxide_helium;
const tofuDir = `./platforms/${tofu.platform.slug}/${tofu.principal.slug}`;
const He_Dir = `./platforms/${He.platform.slug}/${He.principal.slug}`;

const He_yamlPath = path.join(He_Dir, "chart", `values.${adminLandscape.slug}.yaml`);
const He_yamlPath = path.join(He_Dir, 'chart', `values.${adminLandscape.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 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 -- -auto-approve`.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 ${phyLandscape.slug}:l0:${phyCluster.principal.slug}:output -- -json`
.cwd(tofuDir)
.json();
Expand All @@ -71,129 +71,129 @@ class DigitalOceanPhysicalClusterCreator implements PhysicalClusterCloudCreator

// 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 -- -auto-approve`.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 -- -auto-approve`.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-sync -- --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
await this.task.Exec([
"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],
],
});
}
},
]);

await this.task.Run([
"Apply Helium Configuration",
'Apply Helium Configuration',
async () => {
await $`pls ${{ raw: HePls }}:install -- --kube-context ${aCtx} -n ${aNs}`.cwd(He_Dir);
}
},
]);
}
}
Expand Down
43 changes: 20 additions & 23 deletions src/books/restore-admin-cluster/index.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
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 { GenericAdminClusterCloudPurger } from "../purge-admin-cluster/cloud.ts";
import type { GenericAdminClusterCloudRestorer } 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 '../bare-admin-cluster-creation/cloud.ts';
import type { GenericAdminClusterCloudPurger } from '../purge-admin-cluster/cloud.ts';
import type { GenericAdminClusterCloudRestorer } from './cloud.ts';

class AdminClusterRestorer implements RunBook {
name: string = "Restore an Admin Cluster";
desc: string = "Purge a lost cluster and restore cluster from backup";
name: string = 'Restore an Admin Cluster';
desc: string = 'Purge a lost cluster and restore cluster from backup';

constructor(
private stp: ServiceTreePrompter,
private printer: ServiceTreePrinter,
private genericCloudPurger: GenericAdminClusterCloudPurger,
private bareClouds: BareAdminClusterCloudCreator[],
private genericCloudRestorer: GenericAdminClusterCloudRestorer
) {
}
private genericCloudRestorer: GenericAdminClusterCloudRestorer,
) {}

async Run(): Promise<void> {
const [fl, fc] = 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 [tl, tc] = 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", [fl, fc]);
this.printer.Print("To ", [tl, tc]);


console.log('🎯 Selected Service Tree to migrate');
this.printer.Print('From', [fl, fc]);
this.printer.Print('To ', [tl, tc]);

// create an empty cluster
const bc = this.bareClouds.find(x => x.slug === tc.cloud.slug);
if (!bc) return console.log("⚠️ Cloud not supported (Missing Bare Cloud Runbook");
if (!bc) return console.log('⚠️ Cloud not supported (Missing Bare Cloud Runbook');
await bc.Run([tl, tc]);

// purge cluster
Expand Down
3 changes: 3 additions & 0 deletions src/init/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ 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 { SulfoxideFluorineCreator } from '../tasks/sulfoxide-fluorine-creator.ts';
import { LoadBalancerDNSSwitcher } from '../tasks/lb-dns-switcher.ts';

interface TaskGenerator {
lbDNSSwitcher: LoadBalancerDNSSwitcher;
nitrosoWaiter: NitrosoWaiter;
sulfoxideHeliumWaiter: SulfoxideHeliumWaiter;
sulfoxideBoronWaiter: SulfoxideBoronWaiter;
Expand All @@ -17,6 +19,7 @@ interface TaskGenerator {
function initTasks(d: Dependencies): TaskGenerator {
const services = SERVICE_TREE.sulfoxide.services;
return {
lbDNSSwitcher: new LoadBalancerDNSSwitcher('cluster.atomi.cloud', 'lb.atomi.cloud'),
nitrosoWaiter: new NitrosoWaiter(d.kubectl, d.httpUtil),
sulfoxideHeliumWaiter: new SulfoxideHeliumWaiter(d.kubectl, services.argocd),
sulfoxideBoronWaiter: new SulfoxideBoronWaiter(d.kubectl, services.internal_ingress),
Expand Down
Loading

0 comments on commit 6040b23

Please sign in to comment.