wow: init
This is a shitty MMORPG server. Private. Do not touch.
Change-Id: Iddfce069f5895632d305a73fcaa2d963e25dc600
diff --git a/personal/q3k/wow/lib.libsonnet b/personal/q3k/wow/lib.libsonnet
new file mode 100644
index 0000000..4a9517b
--- /dev/null
+++ b/personal/q3k/wow/lib.libsonnet
@@ -0,0 +1,301 @@
+local kube = import "../../../kube/kube.libsonnet";
+
+{
+ local wow = self,
+ local cfg = wow.cfg,
+ local ns = wow.ns,
+ cfg:: {
+ namespace: error "namespace must be set",
+ prefix: "",
+ images: {
+ acore: "registry.k0.hswaw.net/q3k/azerothcore-wowtlk:1606950998",
+ panel: "registry.k0.hswaw.net/q3k/panel:1607033741-f18a531f9b84c5b33653c8db5d64aaa0af337541",
+ },
+ db: {
+ local mkConfig = function(name) {
+ host: error ("db.%s.host must be set" % [name]),
+ port: error ("db.%s.prt must be set" % [name]),
+ user: error ("db.%s.user must be set" % [name]),
+ password: error ("db.%s.password must be set" % [name]),
+ database: "acore_%s" % [name],
+ },
+ auth: mkConfig("auth"),
+ world: mkConfig("world"),
+ characters: mkConfig("characters"),
+ },
+ panel: {
+ domain: error "panel.domain must be set",
+ soap: {
+ username: error "panel.soap.username must be set",
+ password: error "panel.soap.password must be set",
+ },
+ secret: error "panel.secret must be set",
+ oauth: {
+ clientID: error "panel.oauth.clientID must set",
+ clientSecret: error "panel.oauth.clientSecret must set",
+ redirectURL: "https://%s/callback" % [cfg.panel.domain],
+ },
+ motd: "",
+ },
+ overrides: {
+ authserver: {},
+ worldserver: {},
+ ahbot: {},
+ },
+ },
+
+ ns: kube.Namespace(cfg.namespace),
+
+ data: ns.Contain(kube.PersistentVolumeClaim(cfg.prefix + "data")) {
+ spec+: {
+ storageClassName: "waw-hdd-redundant-3",
+ accessModes: ["ReadWriteOnce"],
+ resources: {
+ requests: {
+ storage: "50Gi",
+ },
+ },
+ },
+ },
+
+ // Make a *DatabaseInfo string for use by acore config. These are not any real
+ // standardized DSN format, just some semicolon-delimited proprietary format.
+ local mkDbString = function(config) (
+ "%s;%d;%s;%s;%s" % [
+ config.host,
+ config.port,
+ config.user,
+ config.password,
+ config.database,
+ ]
+ ),
+
+ etc: ns.Contain(kube.Secret(cfg.prefix + "etc")) {
+ data: {
+ "worldserver.conf": std.base64(std.manifestIni({
+ sections: {
+ worldserver: {
+ RealmID: 1,
+ DataDir: "/data/current",
+ LoginDatabaseInfo: mkDbString(cfg.db.auth),
+ WorldDatabaseInfo: mkDbString(cfg.db.world),
+ CharacterDatabaseInfo: mkDbString(cfg.db.characters),
+ LogLevel: 2,
+
+ "Console.Enable": 0,
+ "Ra.Enable": 1,
+ "Ra.IP": "127.0.0.1",
+ "SOAP.Enabled": 1,
+ "SOAP.IP": "0.0.0.0",
+
+ } + cfg.overrides.worldserver,
+
+ },
+ })),
+ "mod_ahbot.conf": std.base64(std.manifestIni({
+ sections: {
+ worldserver: cfg.overrides.ahbot,
+ },
+ })),
+ "authserver.conf": std.base64(std.manifestIni({
+ sections: {
+ authserver: {
+ LoginDatabaseInfo: mkDbString(cfg.db.auth),
+ } + cfg.overrides.authserver,
+ },
+ })),
+ },
+ },
+
+ worldserverDeploy: ns.Contain(kube.Deployment(cfg.prefix + "worldserver")) {
+ spec+: {
+ template+: {
+ spec+: {
+ containers_: {
+ default: kube.Container("default") {
+ image: cfg.images.acore,
+ volumeMounts: [
+ { name: "data", mountPath: "/data" },
+ { name: "etc", mountPath: "/azeroth-server/etc/worldserver.conf", subPath: "worldserver.conf", },
+ { name: "etc", mountPath: "/azeroth-server/etc/mod_ahbot.conf", subPath: "mod_ahbot.conf", },
+ ],
+ command: [
+ "/entrypoint.sh",
+ "/azeroth-server/bin/worldserver",
+ ],
+ },
+ },
+ securityContext: {
+ runAsUser: 999,
+ runAsGroup: 999,
+ fsGroup: 999,
+ },
+ volumes_: {
+ data: kube.PersistentVolumeClaimVolume(wow.data),
+ etc: kube.SecretVolume(wow.etc),
+ },
+ },
+ },
+ },
+ },
+
+ authserverDeploy: ns.Contain(kube.Deployment(cfg.prefix + "authserver")) {
+ spec+: {
+ template+: {
+ spec+: {
+ containers_: {
+ default: kube.Container("default") {
+ image: cfg.images.acore,
+ volumeMounts_: {
+ etc: { mountPath: "/azeroth-server/etc/authserver.conf", subPath: "authserver.conf", },
+ },
+ command: [
+ "/azeroth-server/bin/authserver",
+ ],
+ },
+ },
+ securityContext: {
+ runAsUser: 999,
+ runAsGroup: 999,
+ },
+ volumes_: {
+ etc: kube.SecretVolume(wow.etc),
+ },
+ },
+ },
+ },
+ },
+
+ soapSvc: ns.Contain(kube.Service(cfg.prefix + "worldserver-soap")) {
+ target_pod:: wow.worldserverDeploy.spec.template,
+ spec+: {
+ ports: [
+ { name: "soap", port: 7878, targetPort: 7878, protocol: "TCP" },
+ ],
+ },
+ },
+ worldserverSvc: ns.Contain(kube.Service(cfg.prefix + "worldserver")) {
+ target_pod:: wow.worldserverDeploy.spec.template,
+ metadata+: {
+ annotations+: {
+ "metallb.universe.tf/allow-shared-ip": "%s/%ssvc" % [cfg.namespace, cfg.prefix],
+ },
+ },
+ spec+: {
+ ports: [
+ { name: "worldserver", port: 8085, targetPort: 8085, protocol: "TCP" },
+ ],
+ type: "LoadBalancer",
+ externalTrafficPolicy: "Cluster",
+ loadBalancerIP: cfg.address,
+ },
+ },
+ authserverSvc: ns.Contain(kube.Service(cfg.prefix + "authserver")) {
+ target_pod:: wow.authserverDeploy.spec.template,
+ metadata+: {
+ annotations+: {
+ "metallb.universe.tf/allow-shared-ip": "%s/%ssvc" % [cfg.namespace, cfg.prefix],
+ },
+ },
+ spec+: {
+ ports: [
+ { name: "authserver", port: 3724, targetPort: 3724, protocol: "TCP" },
+ ],
+ type: "LoadBalancer",
+ externalTrafficPolicy: "Cluster",
+ loadBalancerIP: cfg.address,
+ },
+ },
+
+ panelSecret: ns.Contain(kube.Secret(cfg.prefix + "panel-secret")) {
+ data+: {
+ soapPassword: std.base64(cfg.panel.soap.password),
+ secret: std.base64(cfg.panel.secret),
+ oauthSecret: std.base64(cfg.panel.oauth.clientSecret),
+ "motd.txt": std.base64(cfg.panel.motd),
+ },
+ },
+ panelData: ns.Contain(kube.PersistentVolumeClaim(cfg.prefix + "panel-data")) {
+ spec+: {
+ storageClassName: "waw-hdd-redundant-3",
+ accessModes: ["ReadWriteOnce"],
+ resources: {
+ requests: {
+ storage: "128Mi",
+ },
+ },
+ },
+ },
+ panelDeploy: ns.Contain(kube.Deployment(cfg.prefix + "panel")) {
+ spec+: {
+ template+: {
+ spec+: {
+ containers_: {
+ default: kube.Container("default") {
+ image: cfg.images.panel,
+ env_: {
+ SOAP_PASSWORD: kube.SecretKeyRef(wow.panelSecret, "soapPassword"),
+ SECRET: kube.SecretKeyRef(wow.panelSecret, "secret"),
+ OAUTH_SECRET: kube.SecretKeyRef(wow.panelSecret, "oauthSecret"),
+ },
+ command: [
+ "/personal/q3k/wow/panel/panel",
+ "-listen", "0.0.0.0:8080",
+ "-db", "/data/panel.db",
+ "-soap_address", "http://%s" % [wow.soapSvc.host_colon_port],
+ "-soap_password", "$(SOAP_PASSWORD)",
+ "-secret", "$(SECRET)",
+ "-oauth_client_id", cfg.panel.oauth.clientID,
+ "-oauth_client_secret", "$(OAUTH_SECRET)",
+ "-oauth_redirect_url", cfg.panel.oauth.redirectURL,
+ "-motd", "/secret/motd.txt",
+ ],
+ volumeMounts_: {
+ data: { mountPath: "/data" },
+ secret: { mountPath: "/secret" },
+ },
+ },
+ },
+ volumes_: {
+ data: kube.PersistentVolumeClaimVolume(wow.panelData),
+ secret: kube.SecretVolume(wow.panelSecret),
+ },
+ },
+ },
+ },
+ },
+ panelSvc: ns.Contain(kube.Service(cfg.prefix + "panel")) {
+ target_pod:: wow.panelDeploy.spec.template,
+ spec+: {
+ ports: [
+ { name: "web", port: 8080, targetPort: 8080, protocol: "TCP" },
+ ],
+ },
+ },
+ panelIngress: ns.Contain(kube.Ingress(cfg.prefix + "panel")) {
+ metadata+: {
+ annotations+: {
+ "kubernetes.io/tls-acme": "true",
+ "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+ },
+ },
+ spec+: {
+ tls: [
+ {
+ hosts: [cfg.panel.domain],
+ secretName: cfg.prefix + "panel-tls",
+ },
+ ],
+ rules: [
+ {
+ host: cfg.panel.domain,
+ http: {
+ paths: [
+ { path: "/", backend: wow.panelSvc.name_port },
+ ],
+ },
+ }
+ ],
+ },
+ },
+}