From 42874ce315feb81053c6f32f3ecf11630ca399d0 Mon Sep 17 00:00:00 2001 From: SeraphimNoob01 Date: Sun, 4 Oct 2026 23:09:28 +0100 Subject: [PATCH 1/2] Add faf-tournaments The tournament site (FAForever/faf-tournaments), until now hosted privately. A Node.js service with no runtime dependencies that keeps its data in one db.json plus image folders, so it gets its own small volume and nothing in the shared databases. - Released by pushing to main in its repository: the image is republished as faforever/faf-tournaments:latest and Keel rolls it out, polled every two minutes because tournaments run live. - Recreate strategy: one db.json on one volume, so the old pod must be gone before the new one starts. - The image runs as uid 1000; an init container hands it the volume, since a local volume keeps the owner its directory was created with. - /healthz for liveness and readiness. - FAF login stays dormant until FAF_CLIENT_ID and FAF_CLIENT_SECRET are set in the secret. --- Tiltfile | 4 ++ apps/faf-tournaments/Chart.yaml | 8 +++ apps/faf-tournaments/templates/config.yaml | 14 ++++ .../faf-tournaments/templates/deployment.yaml | 69 +++++++++++++++++++ apps/faf-tournaments/templates/ingress.yaml | 13 ++++ .../templates/local-secret.yaml | 11 +++ apps/faf-tournaments/templates/service.yaml | 12 ++++ apps/faf-tournaments/values-prod.yaml | 0 apps/faf-tournaments/values-test.yaml | 0 apps/faf-tournaments/values.yaml | 2 + cluster/storage/values.yaml | 5 ++ 11 files changed, 138 insertions(+) create mode 100644 apps/faf-tournaments/Chart.yaml create mode 100644 apps/faf-tournaments/templates/config.yaml create mode 100644 apps/faf-tournaments/templates/deployment.yaml create mode 100644 apps/faf-tournaments/templates/ingress.yaml create mode 100644 apps/faf-tournaments/templates/local-secret.yaml create mode 100644 apps/faf-tournaments/templates/service.yaml create mode 100644 apps/faf-tournaments/values-prod.yaml create mode 100644 apps/faf-tournaments/values-test.yaml create mode 100644 apps/faf-tournaments/values.yaml diff --git a/Tiltfile b/Tiltfile index 88f760a8..d3ff30a9 100644 --- a/Tiltfile +++ b/Tiltfile @@ -411,6 +411,10 @@ unitdb_labels = ["unitdb"] unitdb_links = [link("http://unitdb.{}".format(base_domain), "Rackover UnitDB")] proxy_local_service_if_set(service_name="faf-unitdb", service_chart="apps/faf-unitdb", service_namespace="faf-apps", service_labels=unitdb_labels, service_links=unitdb_links) +tournaments_labels = ["tournaments"] +tournaments_links = [link("http://tournaments.{}".format(base_domain), "FAF Tournaments")] +proxy_local_service_if_set(service_name="faf-tournaments", service_chart="apps/faf-tournaments", service_namespace="faf-apps", service_deps=["volumes"], service_labels=tournaments_labels, service_links=tournaments_links) + icebreaker_deps = ["faf-db-migrations", "ory-hydra"] + rabbitmq_setup_resources icebreaker_labels = ["api"] icebreaker_patch = {"HYDRA_URL": "http://ory-hydra:4444", "XIRSYS_ENABLED": "false", "XIRSYS_TURN_ENABLED": "false", "CLOUDFLARE_ENABLED": "false"} diff --git a/apps/faf-tournaments/Chart.yaml b/apps/faf-tournaments/Chart.yaml new file mode 100644 index 00000000..85f4bdc3 --- /dev/null +++ b/apps/faf-tournaments/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: faf-tournaments +version: 1.0.0 + +dependencies: + - name: infisical-secret + version: 1.0.0 + repository: file://../../common/infisical-secret diff --git a/apps/faf-tournaments/templates/config.yaml b/apps/faf-tournaments/templates/config.yaml new file mode 100644 index 00000000..79899845 --- /dev/null +++ b/apps/faf-tournaments/templates/config.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: faf-tournaments + labels: + app: faf-tournaments +data: + PORT: "8090" + DATA_DIR: "/data" + # FAF login stays dormant until FAF_CLIENT_ID and FAF_CLIENT_SECRET are set + # in the secret as well; the site then falls back to name-only login. + FAF_HYDRA_HOST: "hydra.{{.Values.baseDomain}}" + FAF_API_HOST: "api.{{.Values.baseDomain}}" + FAF_REDIRECT_URI: "https://tournaments.{{.Values.baseDomain}}/auth/faf/callback" diff --git a/apps/faf-tournaments/templates/deployment.yaml b/apps/faf-tournaments/templates/deployment.yaml new file mode 100644 index 00000000..db7cc01e --- /dev/null +++ b/apps/faf-tournaments/templates/deployment.yaml @@ -0,0 +1,69 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: faf-tournaments + labels: + app: faf-tournaments + annotations: + reloader.stakater.com/auto: "true" + # Pushing to main in FAForever/faf-tournaments is the release: the image is + # republished as `latest`, and Keel rolls it out. Polled every two minutes + # rather than hourly, because tournaments run live and a fix cannot wait. + keel.sh/policy: force + keel.sh/matchTag: "true" + keel.sh/trigger: poll + keel.sh/pollSchedule: "@every 2m" +spec: + replicas: 1 + revisionHistoryLimit: 10 + # One db.json on one volume: the old pod has to be gone before the new one + # starts, or both would write the same file. + strategy: + type: Recreate + selector: + matchLabels: + app: faf-tournaments + template: + metadata: + labels: + app: faf-tournaments + spec: + # The image runs as the unprivileged `node` user (uid 1000), and a local + # volume keeps whatever owner its directory was created with. + initContainers: + - name: data-owner + image: busybox:1.37 + command: ["sh", "-c", "chown -R 1000:1000 /data"] + volumeMounts: + - name: data + mountPath: /data + containers: + - image: faforever/faf-tournaments:latest + imagePullPolicy: Always + name: faf-tournaments + envFrom: + - configMapRef: + name: faf-tournaments + - secretRef: + name: faf-tournaments + ports: + - containerPort: 8090 + livenessProbe: + httpGet: + path: /healthz + port: 8090 + initialDelaySeconds: 5 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /healthz + port: 8090 + periodSeconds: 10 + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: faf-tournaments-pvc + restartPolicy: Always diff --git a/apps/faf-tournaments/templates/ingress.yaml b/apps/faf-tournaments/templates/ingress.yaml new file mode 100644 index 00000000..de0a1c01 --- /dev/null +++ b/apps/faf-tournaments/templates/ingress.yaml @@ -0,0 +1,13 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: faf-tournaments +spec: + entryPoints: + - websecure + routes: + - match: Host(`tournaments.{{.Values.baseDomain}}`) + kind: Rule + services: + - name: faf-tournaments + port: 8090 diff --git a/apps/faf-tournaments/templates/local-secret.yaml b/apps/faf-tournaments/templates/local-secret.yaml new file mode 100644 index 00000000..40995cb2 --- /dev/null +++ b/apps/faf-tournaments/templates/local-secret.yaml @@ -0,0 +1,11 @@ +{{- if not (index .Values "infisical-secret" "enabled") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Chart.Name }} +type: Opaque +stringData: + ADMIN_PASSWORD: "banana" + FAF_CLIENT_ID: "" + FAF_CLIENT_SECRET: "" +{{- end}} diff --git a/apps/faf-tournaments/templates/service.yaml b/apps/faf-tournaments/templates/service.yaml new file mode 100644 index 00000000..9bd16921 --- /dev/null +++ b/apps/faf-tournaments/templates/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: faf-tournaments + labels: + app: faf-tournaments +spec: + selector: + app: faf-tournaments + ports: + - port: 8090 + targetPort: 8090 diff --git a/apps/faf-tournaments/values-prod.yaml b/apps/faf-tournaments/values-prod.yaml new file mode 100644 index 00000000..e69de29b diff --git a/apps/faf-tournaments/values-test.yaml b/apps/faf-tournaments/values-test.yaml new file mode 100644 index 00000000..e69de29b diff --git a/apps/faf-tournaments/values.yaml b/apps/faf-tournaments/values.yaml new file mode 100644 index 00000000..c1356f2a --- /dev/null +++ b/apps/faf-tournaments/values.yaml @@ -0,0 +1,2 @@ +infisical-secret: + name: faf-tournaments diff --git a/cluster/storage/values.yaml b/cluster/storage/values.yaml index 374e8dd4..029cad57 100644 --- a/cluster/storage/values.yaml +++ b/cluster/storage/values.yaml @@ -93,3 +93,8 @@ managedStorages: size: 1Gi pvc: namespace: faf-ops + - pv: + name: faf-tournaments + size: 5Gi + pvc: + namespace: faf-apps From 6351a566647f7ed668c9cffc73691e8c9630e0f1 Mon Sep 17 00:00:00 2001 From: SeraphimNoob01 Date: Sun, 4 Oct 2026 23:45:37 +0100 Subject: [PATCH 2/2] Reuse the existing FAF Tournaments OAuth client Hydra already has a client for the site, registered for its current host. Add the cluster host as a second redirect URI, so both work while the site moves, and give the app that client id. The secret is Hydra's FAFTOURNEY_SECRET; until it is in the app's secret, FAF login stays off and the site uses name-only login. --- apps/faf-tournaments/templates/config.yaml | 6 ++++-- apps/faf-tournaments/templates/local-secret.yaml | 1 - apps/ory-hydra/values.yaml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/faf-tournaments/templates/config.yaml b/apps/faf-tournaments/templates/config.yaml index 79899845..bae52d49 100644 --- a/apps/faf-tournaments/templates/config.yaml +++ b/apps/faf-tournaments/templates/config.yaml @@ -7,8 +7,10 @@ metadata: data: PORT: "8090" DATA_DIR: "/data" - # FAF login stays dormant until FAF_CLIENT_ID and FAF_CLIENT_SECRET are set - # in the secret as well; the site then falls back to name-only login. + # The "FAF Tournaments" client in apps/ory-hydra. FAF login stays dormant + # until FAF_CLIENT_SECRET (Hydra's FAFTOURNEY_SECRET) is in the secret too; + # the site then falls back to name-only login. + FAF_CLIENT_ID: "f4d9a7e2-1c3b-4a8e-9f26-8b5e0d47c1a9" FAF_HYDRA_HOST: "hydra.{{.Values.baseDomain}}" FAF_API_HOST: "api.{{.Values.baseDomain}}" FAF_REDIRECT_URI: "https://tournaments.{{.Values.baseDomain}}/auth/faf/callback" diff --git a/apps/faf-tournaments/templates/local-secret.yaml b/apps/faf-tournaments/templates/local-secret.yaml index 40995cb2..753306fe 100644 --- a/apps/faf-tournaments/templates/local-secret.yaml +++ b/apps/faf-tournaments/templates/local-secret.yaml @@ -6,6 +6,5 @@ metadata: type: Opaque stringData: ADMIN_PASSWORD: "banana" - FAF_CLIENT_ID: "" FAF_CLIENT_SECRET: "" {{- end}} diff --git a/apps/ory-hydra/values.yaml b/apps/ory-hydra/values.yaml index 95250e03..bffe2573 100644 --- a/apps/ory-hydra/values.yaml +++ b/apps/ory-hydra/values.yaml @@ -162,7 +162,7 @@ clients: key: FAFTOURNEY_SECRET grantType: "authorization_code,refresh_token" scope: "openid,offline,public_profile" - redirectUri: "https://tournaments.doodlepros.com/auth/faf/callback" + redirectUri: "https://tournaments.doodlepros.com/auth/faf/callback,https://tournaments.$BASE_DOMAIN/auth/faf/callback" logoUri: "https://tournaments.doodlepros.com/favicon.svg" clientUri: "https://tournaments.doodlepros.com" tokenEndpointAuthMethod: "client_secret_post"