From e48571e96353ba6a07a4d96cd9c92a2700f46d47 Mon Sep 17 00:00:00 2001 From: kirinnee Date: Sun, 8 Sep 2024 21:37:02 +0800 Subject: [PATCH] feat: secerets operator creation playbook --- src/books/secrets-operator-creation/index.ts | 122 ++++ src/init/runbooks.ts | 12 + src/lib/service-tree.ts | 571 ++++++++++--------- 3 files changed, 424 insertions(+), 281 deletions(-) create mode 100644 src/books/secrets-operator-creation/index.ts diff --git a/src/books/secrets-operator-creation/index.ts b/src/books/secrets-operator-creation/index.ts new file mode 100644 index 0000000..7e9dbf5 --- /dev/null +++ b/src/books/secrets-operator-creation/index.ts @@ -0,0 +1,122 @@ +import type { CloudTreeCluster, ServiceTreeService } from '../../lib/service-tree-def.ts'; +import type { TaskRunner } from '../../tasks/tasks.ts'; +import type { ServiceTreePrompter } from '../../lib/prompts/landscape.ts'; +import { $ } from 'bun'; +import type { UtilPrompter } from '../../lib/prompts/util-prompter.ts'; +import { input, password } from '@inquirer/prompts'; +import type { YamlManipulator } from '../../lib/utility/yaml-manipulator.ts'; +import path from 'node:path'; +import type { RunBook } from '../run-book.ts'; + +class SecretsOperatorCreator implements RunBook { + constructor( + private task: TaskRunner, + private stp: ServiceTreePrompter, + private up: UtilPrompter, + private y: YamlManipulator, + private sulfoxide_infisical: ServiceTreeService, + ) {} + + name: string = 'Create Secrets Operator'; + desc: string = 'Deploy the secrets operator to a selected cloud-cluster'; + + async Run(): Promise { + const cluster: CloudTreeCluster = await this.stp.Cluster( + 'Which cloud do you want to create infisical in?', + 'Which cluster do you want to create infisical in?', + ); + + const infisical = this.sulfoxide_infisical; + + const i_path = `./platforms/${infisical.platform.slug}/${infisical.principal.slug}`; + + await this.task.Run([ + 'Setup infisical', + async () => { + const pw = await password({ message: 'Enter your Bitwarden password' }); + + await $`echo ${pw} | nix develop -c pls setup`.cwd(i_path); + }, + ]); + + // prompt to check if we want new secrets + const newSecrets = await this.up.YesNo(`Do you want to inject new secrets for ${cluster.principal.name}?`); + if (newSecrets) { + await this.task.Run([ + 'Inject new secrets', + async () => { + const token = await input({ message: `Enter your ${cluster.cloud.name} token` }); + + const yamlPath = path.join(i_path, 'bw.secrets.yaml'); + await this.y.Mutate(yamlPath, [[['Tokens', cluster.cloud.name, cluster.principal.name], token]]); + + console.log('✅ Secrets modified. Remember to update Bitwarden the new secrets'); + + let updated = false; + + while (!updated) { + updated = await this.up.YesNo('Have you updated Bitwarden with the new secrets?'); + } + }, + ]); + } + + // synchronize secrets + await this.task.Run([ + 'Synchronize secrets', + async () => { + await $`nix develop -c pls sync`.cwd(i_path); + }, + ]); + + await this.task.Run([ + 'Initialize general (database and ingress) Tofu', + async () => { + await $`pls general:init`.cwd(i_path); + }, + ]); + + await this.task.Run([ + 'Apply general (database and ingress) Tofu', + async () => { + await $`pls general:apply -- -auto-approve`.cwd(i_path); + }, + ]); + + await this.task.Run([ + 'Generate .env', + async () => { + await $`nix develop -c pls generate:env`.cwd(i_path); + }, + ]); + + // provision compute + const compute = cluster.principal.slug; + await this.task.Run([ + 'Provision compute', + async () => { + await $`pls ${{ raw: compute }}:init`.cwd(i_path); + }, + ]); + + await this.task.Run([ + 'Apply compute', + async () => { + await $`pls ${{ raw: compute }}:apply -- -auto-approve`.cwd(i_path); + }, + ]); + + // deploy secrets operator + const c = cluster.principal.slug; + await this.task.Run([ + 'Deploy secrets operator', + async () => { + await $`nix develop -c pls deploy -- ${{ raw: c }}`.cwd(i_path).env({ + ANSIBLE_HOST_KEY_CHECKING: 'False', + }); + }, + ]); + } +} + +export { SecretsOperatorCreator }; diff --git a/src/init/runbooks.ts b/src/init/runbooks.ts index d5436f7..a66c5b1 100644 --- a/src/init/runbooks.ts +++ b/src/init/runbooks.ts @@ -21,6 +21,7 @@ import { AwsPhysicalClusterCreator } from '../books/physical-cluster-creation/aw import { AwsGracefulPhysicalClusterDestructor } from '../books/graceful-physical-cluster-destruction/aws.ts'; import { VultrPhysicalClusterCreator } from "../books/physical-cluster-creation/vultr.ts"; import { VultrGracefulPhysicalClusterDestructor } from "../books/graceful-physical-cluster-destruction/vultr.ts"; +import { SecretsOperatorCreator } from "../books/secrets-operator-creation"; function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { const sulfoxide = SERVICE_TREE.sulfoxide; @@ -175,6 +176,16 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { adminClusterTransitioner, ); + // create secrets operator + const secretsOperatorCreator = new SecretsOperatorCreator( + d.taskRunner, + d.stp, + d.utilPrompter, + d.yamlManipulator, + sulfoxide.services.infisical, + ); + + return [ physicalClusterCreator, phyGracefulDestructor, @@ -182,6 +193,7 @@ function initRunBooks(d: Dependencies, t: TaskGenerator): RunBook[] { fullAdminCloudCreator, adminGracefulDestructor, adminClusterMigrator, + secretsOperatorCreator, ]; } diff --git a/src/lib/service-tree.ts b/src/lib/service-tree.ts index 9601e52..743615a 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,240 @@ 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 }, - }, + infisical: { + principal: { + name: "Infisical", + slug: "infisical", + description: "SecretOps platform", + projectId: "" + }, + 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 +470,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 +491,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 +513,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 +535,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 +557,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 +579,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 };