diff --git a/gaming-services/minecraft/index.ts b/gaming-services/minecraft/index.ts new file mode 100644 index 0000000..e83947b --- /dev/null +++ b/gaming-services/minecraft/index.ts @@ -0,0 +1,33 @@ +import { Construct } from "constructs"; +import { TerraformStack } from "cdktf"; +import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider"; +import { NamespaceV1 } from "@cdktf/provider-kubernetes/lib/namespace-v1"; +import { OnePasswordSecret } from "../../utils"; +import { TerraFirmaGreg } from "./tfg"; + +export class GamingServices extends TerraformStack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const provider = new KubernetesProvider(this, "kubernetes", { + configPath: "~/.kube/config", + }); + + const namespace = "minecraft"; + + new NamespaceV1(this, "namespace", { + metadata: { + name: namespace, + }, + }); + + new OnePasswordSecret(this, "curseforge", { + provider, + namespace, + name: "curseforge", + itemPath: "vaults/Lab/items/curseforge", + }); + + new TerraFirmaGreg(this, "tfg", provider, namespace); + } +} diff --git a/gaming-services/minecraft/tfg.ts b/gaming-services/minecraft/tfg.ts new file mode 100644 index 0000000..098a813 --- /dev/null +++ b/gaming-services/minecraft/tfg.ts @@ -0,0 +1,78 @@ +import { Construct } from "constructs"; +import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider"; + +import { MinecraftServer } from "./utils"; + +export class TerraFirmaGreg extends Construct { + constructor( + scope: Construct, + id: string, + provider: KubernetesProvider, + namespace: string, + ) { + super(scope, id); + + new MinecraftServer(this, "tfg", { + provider, + namespace, + image: "itzg/minecraft-server:java17", + name: "tfg", + env: [ + { + name: "EULA", + value: "TRUE", + }, + { + name: "MODE", + value: "survival", + }, + { + name: "MODPACK_PLATFORM", + value: "AUTO_CURSEFORGE", + }, + { + name: "CF_API_KEY", + valueFrom: { + secretKeyRef: { + name: "curseforge", + key: "credential", + }, + }, + }, + { + name: "CF_PAGE_URL", + value: + "https://www.curseforge.com/minecraft/modpacks/terrafirmagreg-modern/", + }, + { + name: "CF_FILENAME_MATCHER", + value: "0.10.17", + }, + { + name: "VERSION", + value: "1.20.1", + }, + { + name: "INIT_MEMORY", + value: "2G", + }, + { + name: "MAX_MEMORY", + value: "12G", + }, + { + name: "ALLOW_FLIGHT", + value: "TRUE", + }, + { + name: "ENABLE_ROLLING_LOGS", + value: "TRUE", + }, + { + name: "USE_MEOWICE_FLAGS", + value: "TRUE", + }, + ], + }); + } +} diff --git a/gaming-services/minecraft/utils/index.ts b/gaming-services/minecraft/utils/index.ts new file mode 100644 index 0000000..db0afd5 --- /dev/null +++ b/gaming-services/minecraft/utils/index.ts @@ -0,0 +1,129 @@ +import { Construct } from "constructs"; +import { + StatefulSetV1, + StatefulSetV1SpecTemplateSpecContainerEnv, +} from "@cdktf/provider-kubernetes/lib/stateful-set-v1"; +import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider"; +import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1"; + +import { IngressRouteTcp, LonghornPvc } from "../../../utils"; + +export type MinecraftServerOptions = { + provider: KubernetesProvider; + namespace: string; + name: string; + env: StatefulSetV1SpecTemplateSpecContainerEnv[]; + image: string; + size?: string; +}; + +export class MinecraftServer extends Construct { + constructor(scope: Construct, id: string, opts: MinecraftServerOptions) { + super(scope, id); + + const { provider, namespace, name, image, env, size = "10Gi" } = opts; + + const pvc = new LonghornPvc(this, "pvc", { + provider, + namespace, + name, + size, + }); + + new ServiceV1(this, "service", { + provider, + metadata: { + name, + namespace, + }, + spec: { + selector: { + app: name, + }, + port: [ + { + port: 25565, + targetPort: name, + }, + ], + type: "ClusterIP", + }, + }); + + new StatefulSetV1(this, "stateful-set", { + provider, + metadata: { + name, + namespace, + }, + spec: { + replicas: "1", + serviceName: name, + selector: { + matchLabels: { + app: name, + }, + }, + template: { + metadata: { + labels: { + app: name, + }, + }, + spec: { + nodeSelector: { + nodepool: "worker", + }, + volume: [ + { + name: `${name}-data`, + persistentVolumeClaim: { + claimName: pvc.name, + }, + }, + ], + container: [ + { + name, + image, + env, + port: [ + { + name: "minecraft", + containerPort: 25565, + }, + ], + volumeMount: [ + { + name: `${name}-data`, + mountPath: "/data", + }, + ], + resources: { + requests: { + cpu: "2", + memory: "4Gi", + }, + limits: { + cpu: "6", + memory: "12Gi", + }, + }, + }, + ], + }, + }, + }, + }); + + new IngressRouteTcp(this, "ingress", { + provider, + namespace, + name, + serviceName: name, + servicePort: 25565, + entryPoint: `minecraft-${name}`, + match: "HostSNI(`*`)", + }); + } +} diff --git a/main.ts b/main.ts index 365f24e..c1b88f1 100644 --- a/main.ts +++ b/main.ts @@ -6,6 +6,7 @@ import { UtilityServices } from "./utility-services"; import { K8SOperators } from "./k8s-operators"; import { CoreServices } from "./core-services"; import { NetworkSecurity } from "./network-security"; +import { GamingServices } from "./gaming-services/minecraft"; dotenv.config(); @@ -34,6 +35,9 @@ utilityServices.node.addDependency(networkSecurity); const caches = new CacheInfrastructure(app, "cache-infrastructure"); caches.node.addDependency(utilityServices); +const gamingServices = new GamingServices(app, "gaming-services"); +gamingServices.node.addDependency(networkSecurity); + const deploy: (stack: TerraformStack, key: string) => S3Backend = ( stack, key, @@ -60,5 +64,6 @@ deploy(k8sOperators, "k8s-operators"); deploy(networkSecurity, "network-security"); deploy(utilityServices, "utility-services"); deploy(caches, "cache-infrastructure"); +deploy(gamingServices, "gaming-services"); app.synth();