Repository navigation
Conversation
…nd keep deploys from needing it While the landing page had no ready pod, visitors got Envoy's plain-text "no healthy upstream". A BackendTrafficPolicy on landing-route now answers 502/503/504 with files/maintenance.html, served by Envoy from a ConfigMap. The page must not contain "%": Envoy reads the body as a format string and otherwise drops the override while the policy still reports Accepted. Deploys now keep a ready pod: maxUnavailable 0 and a 10s preStop drain for the landing page and REST service, and a tcpSocket readiness probe for the service, which had none. landingPage.replicas and service.replicas above 1 add a PodDisruptionBudget each (podDisruptionBudget.enabled) and spread over nodes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 📝 WalkthroughWalkthroughThe chart adds an Envoy Gateway maintenance response for landing-route errors 502, 503, and 504. It also adds configurable replicas and rollout protections for the landing-page and REST Deployments, with conditional PodDisruptionBudgets for multi-replica deployments. ChangesMaintenance handling and workload availability
Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant Client
participant EnvoyGateway
participant LandingRoute
participant BackendTrafficPolicy
participant MaintenanceConfigMap
Client->>EnvoyGateway: Send request to landing route
EnvoyGateway->>LandingRoute: Forward request
LandingRoute-->>EnvoyGateway: Return 502, 503, or 504
EnvoyGateway->>BackendTrafficPolicy: Apply configured response override
BackendTrafficPolicy->>MaintenanceConfigMap: Reference maintenance HTML body
EnvoyGateway-->>Client: Return HTML body with 5xx status
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 1 files. (12 skipped: 12 unsupported.)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Rendered diff across all environments1368 lines changeddiff -ru out-base/bonn.eduide.aet.cit.tum.de.yaml out-head/bonn.eduide.aet.cit.tum.de.yaml
--- out-base/bonn.eduide.aet.cit.tum.de.yaml 2026-09-29 08:10:36.956967541 +0000
+++ out-head/bonn.eduide.aet.cit.tum.de.yaml 2026-09-29 08:10:39.565966735 +0000
@@ -106,6 +106,52 @@
sentryEnvironment: "eduide-bonn",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-bonn
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -439,6 +485,11 @@
namespace: eduide-bonn
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -451,6 +502,13 @@
checksum/config: 25978d9c243b6ba9ea937030ae36d822203f11582eca5d8b6f39dc35a9caf724
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -468,6 +526,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -555,6 +617,11 @@
namespace: eduide-bonn
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -569,6 +636,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -577,6 +651,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -630,6 +713,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-bonn
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/e2e.eduide.student.k8s.aet.cit.tum.de.yaml out-head/e2e.eduide.student.k8s.aet.cit.tum.de.yaml
--- out-base/e2e.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:37.196951888 +0000
+++ out-head/e2e.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:39.814968290 +0000
@@ -298,6 +298,52 @@
sentryEnvironment: "eduide-e2e-test",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-e2e-test
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -1088,6 +1134,11 @@
namespace: eduide-e2e-test
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -1100,6 +1151,13 @@
checksum/config: 5e4ecb4fa3e445e157447556fc69675a3df3b4f576a2c0b94ab6183741b48726
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -1117,6 +1175,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -1211,6 +1273,11 @@
namespace: eduide-e2e-test
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -1225,6 +1292,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -1233,6 +1307,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -1573,6 +1656,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-e2e-test
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/eduide.artemis.cit.tum.de.yaml out-head/eduide.artemis.cit.tum.de.yaml
--- out-base/eduide.artemis.cit.tum.de.yaml 2026-09-29 08:10:37.407953219 +0000
+++ out-head/eduide.artemis.cit.tum.de.yaml 2026-09-29 08:10:40.029969632 +0000
@@ -144,6 +144,52 @@
sentryEnvironment: "eduide-tum-production",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-tum-production
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -523,6 +569,11 @@
namespace: eduide-tum-production
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -535,6 +586,13 @@
checksum/config: 2fe3aabc4f560bc33677b38cc681f0cb15b161aef5d14bb5e7a5eb4ac09ab8d0
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -552,6 +610,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -646,6 +708,11 @@
namespace: eduide-tum-production
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -660,6 +727,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -668,6 +742,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -1008,6 +1091,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-tum-production
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/mannheim.eduide.aet.cit.tum.de.yaml out-head/mannheim.eduide.aet.cit.tum.de.yaml
--- out-base/mannheim.eduide.aet.cit.tum.de.yaml 2026-09-29 08:10:37.609954493 +0000
+++ out-head/mannheim.eduide.aet.cit.tum.de.yaml 2026-09-29 08:10:40.231970894 +0000
@@ -107,6 +107,52 @@
sentryEnvironment: "eduide-mannheim",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-mannheim
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -444,6 +490,11 @@
namespace: eduide-mannheim
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -456,6 +507,13 @@
checksum/config: c6796bb9738bafd7d486eb374d61da1e3e76bd1d2a0755424eeb75ebfd7ac735
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -473,6 +531,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -567,6 +629,11 @@
namespace: eduide-mannheim
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -581,6 +648,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -589,6 +663,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -642,6 +725,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-mannheim
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/staging.eduide.student.k8s.aet.cit.tum.de.yaml out-head/staging.eduide.student.k8s.aet.cit.tum.de.yaml
--- out-base/staging.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:37.845955982 +0000
+++ out-head/staging.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:40.476972423 +0000
@@ -298,6 +298,52 @@
sentryEnvironment: "eduide-staging",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-staging
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -1088,6 +1134,11 @@
namespace: eduide-staging
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -1100,6 +1151,13 @@
checksum/config: ba74a0ec9e8d86587ba80a17aa671547d7341ef2eefab0417b86eaa9a808efcd
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -1117,6 +1175,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -1211,6 +1273,11 @@
namespace: eduide-staging
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -1225,6 +1292,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -1233,6 +1307,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -1573,6 +1656,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-staging
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/test1.eduide.student.k8s.aet.cit.tum.de.yaml out-head/test1.eduide.student.k8s.aet.cit.tum.de.yaml
--- out-base/test1.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:38.082957475 +0000
+++ out-head/test1.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:40.716973922 +0000
@@ -298,6 +298,52 @@
sentryEnvironment: "eduide-test1",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-test1
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -1088,6 +1134,11 @@
namespace: eduide-test1
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -1100,6 +1151,13 @@
checksum/config: bac9bcef6af4cfc81417882b92a5db9366528fff9276b88169d620c4cc2e127a
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -1117,6 +1175,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -1211,6 +1273,11 @@
namespace: eduide-test1
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -1225,6 +1292,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -1233,6 +1307,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -1573,6 +1656,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-test1
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/test2.eduide.student.k8s.aet.cit.tum.de.yaml out-head/test2.eduide.student.k8s.aet.cit.tum.de.yaml
--- out-base/test2.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:38.323958980 +0000
+++ out-head/test2.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:40.990975633 +0000
@@ -298,6 +298,52 @@
sentryEnvironment: "eduide-test2",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-test2
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -1088,6 +1134,11 @@
namespace: eduide-test2
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -1100,6 +1151,13 @@
checksum/config: f7580e72dce962a2ebec2547fc826a8646a449ff791a7a43c8be8d9bbb8918fe
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -1117,6 +1175,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -1211,6 +1273,11 @@
namespace: eduide-test2
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -1225,6 +1292,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -1233,6 +1307,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -1573,6 +1656,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-test2
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
diff -ru out-base/test3.eduide.student.k8s.aet.cit.tum.de.yaml out-head/test3.eduide.student.k8s.aet.cit.tum.de.yaml
--- out-base/test3.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:38.564960485 +0000
+++ out-head/test3.eduide.student.k8s.aet.cit.tum.de.yaml 2026-09-29 08:10:41.353977899 +0000
@@ -298,6 +298,52 @@
sentryEnvironment: "eduide-test3",
};
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: maintenance-page
+ namespace: eduide-test3
+data:
+ response.body: |
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="refresh" content="30">
+ <meta name="robots" content="noindex">
+ <title>EduIDE - currently unavailable</title>
+ <style>
+ :root { color-scheme: light dark; --bg: #f5f5f5; --fg: #1a1a1a; --muted: #5c5c5c; }
+ @media (prefers-color-scheme: dark) {
+ :root { --bg: #121212; --fg: #f0f0f0; --muted: #a8a8a8; }
+ }
+ body { min-height: 100vh; margin: 0; }
+ body {
+ display: flex; align-items: center; justify-content: center;
+ padding: 0 16px; box-sizing: border-box;
+ background: var(--bg); color: var(--fg);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ text-align: center;
+ }
+ main { max-width: 32rem; }
+ .logo { width: 8rem; height: auto; margin-bottom: 2rem; }
+ h1 { font-size: 1.6rem; margin: 0 0 1rem; }
+ p { line-height: 1.5; margin: 0 0 0.75rem; }
+ .de { color: var(--muted); font-size: 0.9rem; }
+ </style>
+ </head>
+ <body>
+ <main>
+ <svg class="logo" role="img" aria-label="EduIDE" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" preserveAspectRatio="xMinYMin meet" viewBox="0, 0, 1150, 540.6"> <g id="Layer_1" fill="#0d82ff"> <path d="M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z" /> <path d="M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z" /> <path d="M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z" /> <path d="M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z" /> </g> </svg>
+ <h1>EduIDE is currently unavailable</h1>
+ <p>We're updating EduIDE and will be back in a few minutes. This page reloads automatically.</p>
+ <p class="de" lang="de">EduIDE wird gerade aktualisiert und ist in wenigen Minuten wieder da.</p>
+ </main>
+ </body>
+ </html>
+---
# Source: eduide/templates/oauth2-configmap-htmlpage.yaml
apiVersion: v1
kind: ConfigMap
@@ -1088,6 +1134,11 @@
namespace: eduide-test3
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: landing-page
@@ -1100,6 +1151,13 @@
checksum/config: ba3967c9f9d5aee392d1678ea55f7e0863722c6013d9e48b1183263e1d19f68e
spec:
automountServiceAccountToken: false
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: landing-page
containers:
- name: landing-page-container
image: ghcr.io/eduide/eduide-landing-page:1.2.2
@@ -1117,6 +1175,10 @@
port: 80
initialDelaySeconds: 5
periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
volumeMounts:
- name: landing-page-config
mountPath: /usr/share/nginx/html/config.js
@@ -1211,6 +1273,11 @@
namespace: eduide-test3
spec:
replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 0
+ maxSurge: 1
selector:
matchLabels:
app: service
@@ -1225,6 +1292,13 @@
# we need to be able to create and read sessions
automountServiceAccountToken: true
serviceAccountName: service-api-service-account
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ labelSelector:
+ matchLabels:
+ app: service
containers:
- name: service-container
image: ghcr.io/eduide/eduide-cloud/service:1.2.0
@@ -1233,6 +1307,15 @@
- name: http
containerPort: 8081
protocol: TCP
+ readinessProbe:
+ tcpSocket:
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "10"]
envFrom:
- configMapRef:
name: service-config
@@ -1573,6 +1656,35 @@
dataBridgeEnabled: "true"
dataBridgePort: "16281"
---
+# Source: eduide/templates/maintenance-page.yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: landing-maintenance-page
+ namespace: eduide-test3
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: landing-route
+ responseOverride:
+ - match:
+ statusCodes:
+ - type: Value
+ value: 502
+ - type: Value
+ value: 503
+ - type: Value
+ value: 504
+ response:
+ contentType: text/html; charset=utf-8
+ body:
+ type: ValueRef
+ valueRef:
+ group: ""
+ kind: ConfigMap
+ name: maintenance-page
+---
# Source: eduide/templates/httproute-instances.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute |
Why
During deploys the landing page sometimes had no ready pod, and visitors got Envoy's raw
no healthy upstream/ 503. This adds a static maintenance page and removes most of the downtime at its source.What
Maintenance page
files/maintenance.html: self-contained page ("EduIDE is currently unavailable", short German line, inline logo, light/dark, reloads every 30s, no external assets).templates/maintenance-page.yaml: ConfigMapmaintenance-page+ Envoy GatewayBackendTrafficPolicyonlanding-routethat answers 502/503/504 with the page. The status code stays 5xx.maintenancePage.enabled(defaulttrue).landing-page-deploymentto 0. EduIDE-deployment gets a workflow for that.Rolling updates keep a ready pod
maxUnavailable: 0/maxSurge: 1,preStopsleep 10s.tcpSocketreadiness probe. It had none, so the old pod was killed before Quarkus listened.landingPage.replicas,service.replicas(default 1). Above 1: a PodDisruptionBudget each (podDisruptionBudget.enabled, turn off on single-node clusters) and soft spreading across nodes.Gotcha found on test1
Envoy treats the override body as a format string. A
%in the page (height: 100%) makes Envoy drop the override silently: the policy still reportsAccepted, and visitors get an empty 503. The page has no%now, and the test guards against it.Tests
scripts/test-maintenance-page.sh(bash + yq, same style astest-app-consistency.sh), wired into thelintjob: policy, ConfigMap, toggles, rollout settings, PDB conditions, page size / no external assets / no%.helm lint,test-app-consistency.sh, kubeconform (BTP + PDB validate against CRDs-catalog), andrender-envs.shdiff: only the intended objects/fields per environment.Verified on test1 (tum-student, Envoy Gateway v1.8.3)
Acceptedon all test1 listeners, 2 PDBs, replicas on different nodes.503,text/html, 3222-byte page on/and deep links. Service unaffected. Scaled back:200.rollout restart: 0 errors. The first upgrade from the old template still had 2 single 503s, because those pods lacked the probe and preStop.responseOverride.valueRef.Follow-up: EduIDE-deployment PR (maintenance workflow, replicas 2, pins) once 2.4.0 is released.
🤖 Generated with Claude Code
Summary by CodeRabbit