v0.1.13: Strategy Recreate + preStop + liveness fix (ERR-SQS-06 root cause)
- ensureDeployment: Strategy Recreate (not RollingUpdate) — prevents H2 file lock when two pods mount same PVC simultaneously during rollout restart - preStop: sleep 3 — graceful H2 shutdown before SIGTERM - livenessProbe timeoutSeconds: 3 — prevents false positive on GC pause - terminationGracePeriodSeconds: 15 - doc: thinking log, ERR-SQS-06, progress.md, architecture SVG schema
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="700" font-family="Arial, sans-serif" font-size="13">
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="700" fill="#f8f9fa" rx="10"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="32" text-anchor="middle" font-size="18" font-weight="bold" fill="#1a1a2e">SQS Operator — ресурсы на тенанта</text>
|
||||
|
||||
<!-- === QueueService CR === -->
|
||||
<rect x="340" y="55" width="220" height="55" rx="8" fill="#4a90d9" stroke="#2c6fad" stroke-width="1.5"/>
|
||||
<text x="450" y="77" text-anchor="middle" fill="white" font-weight="bold">📋 QueueService CR</text>
|
||||
<text x="450" y="97" text-anchor="middle" fill="#dce9f8" font-size="11">tenant: test001 · enableUI: true</text>
|
||||
|
||||
<!-- Arrow CR -> Operator -->
|
||||
<line x1="450" y1="110" x2="450" y2="145" stroke="#666" stroke-width="1.5" marker-end="url(#arr)"/>
|
||||
<text x="460" y="132" fill="#666" font-size="11">reconcile</text>
|
||||
|
||||
<!-- === Operator === -->
|
||||
<rect x="310" y="145" width="280" height="50" rx="8" fill="#7c3aed" stroke="#5b21b6" stroke-width="1.5"/>
|
||||
<text x="450" y="166" text-anchor="middle" fill="white" font-weight="bold">⚙️ Оператор</text>
|
||||
<text x="450" y="184" text-anchor="middle" fill="#e9d5ff" font-size="11">QueueServiceReconciler (Go)</text>
|
||||
|
||||
<!-- === Namespace box === -->
|
||||
<rect x="30" y="235" width="840" height="430" rx="10" fill="white" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="6,3"/>
|
||||
<text x="50" y="258" fill="#64748b" font-size="12" font-weight="bold">Namespace: sless-fn-test001</text>
|
||||
|
||||
<!-- Arrow Operator -> Namespace -->
|
||||
<line x1="450" y1="195" x2="450" y2="235" stroke="#666" stroke-width="1.5" marker-end="url(#arr)"/>
|
||||
<text x="460" y="220" fill="#666" font-size="11">creates</text>
|
||||
|
||||
<!-- === Row 1: Secret, ConfigMap, PVC === -->
|
||||
<!-- Secret -->
|
||||
<rect x="55" y="270" width="180" height="60" rx="7" fill="#059669" stroke="#047857" stroke-width="1.5"/>
|
||||
<text x="145" y="293" text-anchor="middle" fill="white" font-weight="bold">🔑 Secret</text>
|
||||
<text x="145" y="311" text-anchor="middle" fill="#d1fae5" font-size="11">sqs-creds-test001</text>
|
||||
<text x="145" y="325" text-anchor="middle" fill="#d1fae5" font-size="10">access_key / secret_key</text>
|
||||
|
||||
<!-- ConfigMap -->
|
||||
<rect x="260" y="270" width="180" height="60" rx="7" fill="#d97706" stroke="#b45309" stroke-width="1.5"/>
|
||||
<text x="350" y="293" text-anchor="middle" fill="white" font-weight="bold">📄 ConfigMap</text>
|
||||
<text x="350" y="311" text-anchor="middle" fill="#fef3c7" font-size="11">sqs-cfg-test001</text>
|
||||
<text x="350" y="325" text-anchor="middle" fill="#fef3c7" font-size="10">elasticmq.conf</text>
|
||||
|
||||
<!-- PVC -->
|
||||
<rect x="465" y="270" width="180" height="60" rx="7" fill="#0891b2" stroke="#0e7490" stroke-width="1.5"/>
|
||||
<text x="555" y="293" text-anchor="middle" fill="white" font-weight="bold">💾 PVC</text>
|
||||
<text x="555" y="311" text-anchor="middle" fill="#cffafe" font-size="11">sqs-data-test001</text>
|
||||
<text x="555" y="325" text-anchor="middle" fill="#cffafe" font-size="10">H2 persistence (остаётся при удалении CR)</text>
|
||||
|
||||
<!-- === Deployment === -->
|
||||
<rect x="55" y="365" width="840" height="0" rx="0" fill="none"/>
|
||||
|
||||
<!-- Deployment box -->
|
||||
<rect x="160" y="360" width="380" height="120" rx="9" fill="#f1f5f9" stroke="#475569" stroke-width="2"/>
|
||||
<text x="350" y="380" text-anchor="middle" fill="#334155" font-weight="bold" font-size="12">🚀 Deployment: sqs-test001</text>
|
||||
|
||||
<!-- Container ElasticMQ -->
|
||||
<rect x="175" y="390" width="160" height="75" rx="6" fill="#e2e8f0" stroke="#64748b" stroke-width="1"/>
|
||||
<text x="255" y="410" text-anchor="middle" fill="#1e293b" font-weight="bold" font-size="11">ElasticMQ</text>
|
||||
<text x="255" y="427" text-anchor="middle" fill="#475569" font-size="10">port 9324</text>
|
||||
<text x="255" y="442" text-anchor="middle" fill="#475569" font-size="10">Scala / Akka HTTP</text>
|
||||
<text x="255" y="457" text-anchor="middle" fill="#64748b" font-size="10">elasticmq:1.7.1</text>
|
||||
|
||||
<!-- Container UI -->
|
||||
<rect x="355" y="390" width="170" height="75" rx="6" fill="#e2e8f0" stroke="#64748b" stroke-width="1"/>
|
||||
<text x="440" y="410" text-anchor="middle" fill="#1e293b" font-weight="bold" font-size="11">elasticmq-ui</text>
|
||||
<text x="440" y="427" text-anchor="middle" fill="#475569" font-size="10">port 3000</text>
|
||||
<text x="440" y="442" text-anchor="middle" fill="#475569" font-size="10">Next.js</text>
|
||||
<text x="440" y="457" text-anchor="middle" fill="#64748b" font-size="10">elasticmq-ui:latest</text>
|
||||
|
||||
<!-- Arrows ConfigMap/PVC -> Deployment -->
|
||||
<line x1="350" y1="330" x2="350" y2="360" stroke="#b45309" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#arr)"/>
|
||||
<line x1="555" y1="330" x2="430" y2="360" stroke="#0e7490" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#arr)"/>
|
||||
<text x="470" y="350" fill="#64748b" font-size="10">mount</text>
|
||||
|
||||
<!-- === Service === -->
|
||||
<rect x="600" y="380" width="200" height="65" rx="7" fill="#6366f1" stroke="#4f46e5" stroke-width="1.5"/>
|
||||
<text x="700" y="403" text-anchor="middle" fill="white" font-weight="bold">🔌 Service ClusterIP</text>
|
||||
<text x="700" y="421" text-anchor="middle" fill="#e0e7ff" font-size="11">sqs-svc-test001</text>
|
||||
<text x="700" y="437" text-anchor="middle" fill="#e0e7ff" font-size="11">9324 (SQS) · 3000 (UI)</text>
|
||||
|
||||
<!-- Arrow Deployment -> Service -->
|
||||
<line x1="540" y1="415" x2="600" y2="415" stroke="#666" stroke-width="1.5" marker-end="url(#arr)"/>
|
||||
|
||||
<!-- === Ingresses === -->
|
||||
<!-- ING1 -->
|
||||
<rect x="55" y="520" width="185" height="65" rx="7" fill="#db2777" stroke="#be185d" stroke-width="1.5"/>
|
||||
<text x="147" y="543" text-anchor="middle" fill="white" font-weight="bold">🌐 Ingress SQS API</text>
|
||||
<text x="147" y="560" text-anchor="middle" fill="#fce7f3" font-size="10">sqs-ing-test001</text>
|
||||
<text x="147" y="575" text-anchor="middle" fill="#fce7f3" font-size="10">/sqs/test001/... → :9324</text>
|
||||
|
||||
<!-- ING2 -->
|
||||
<rect x="260" y="520" width="185" height="65" rx="7" fill="#db2777" stroke="#be185d" stroke-width="1.5"/>
|
||||
<text x="352" y="543" text-anchor="middle" fill="white" font-weight="bold">🌐 Ingress UI</text>
|
||||
<text x="352" y="560" text-anchor="middle" fill="#fce7f3" font-size="10">sqs-ing-ui-test001</text>
|
||||
<text x="352" y="575" text-anchor="middle" fill="#fce7f3" font-size="10">/sqs-ui/test001/ → :3000</text>
|
||||
|
||||
<!-- ING3 -->
|
||||
<rect x="465" y="520" width="185" height="65" rx="7" fill="#db2777" stroke="#be185d" stroke-width="1.5"/>
|
||||
<text x="557" y="543" text-anchor="middle" fill="white" font-weight="bold">🌐 Ingress Assets</text>
|
||||
<text x="557" y="560" text-anchor="middle" fill="#fce7f3" font-size="10">sqs-ing-ui-assets-test001</text>
|
||||
<text x="557" y="575" text-anchor="middle" fill="#fce7f3" font-size="10">/_next/ → :3000</text>
|
||||
|
||||
<!-- ING4 -->
|
||||
<rect x="670" y="520" width="185" height="65" rx="7" fill="#db2777" stroke="#be185d" stroke-width="1.5"/>
|
||||
<text x="762" y="543" text-anchor="middle" fill="white" font-weight="bold">🌐 Ingress Routes</text>
|
||||
<text x="762" y="560" text-anchor="middle" fill="#fce7f3" font-size="10">sqs-ing-ui-queues-test001</text>
|
||||
<text x="762" y="575" text-anchor="middle" fill="#fce7f3" font-size="10">/queues/ → :3000</text>
|
||||
|
||||
<!-- Arrows Service -> Ingresses -->
|
||||
<line x1="700" y1="445" x2="700" y2="490" stroke="#4f46e5" stroke-width="1" stroke-dasharray="4,2"/>
|
||||
<line x1="700" y1="490" x2="147" y2="490" stroke="#4f46e5" stroke-width="1" stroke-dasharray="4,2"/>
|
||||
<line x1="147" y1="490" x2="147" y2="520" stroke="#4f46e5" stroke-width="1" marker-end="url(#arr)"/>
|
||||
<line x1="352" y1="490" x2="352" y2="520" stroke="#4f46e5" stroke-width="1" marker-end="url(#arr)"/>
|
||||
<line x1="557" y1="490" x2="557" y2="520" stroke="#4f46e5" stroke-width="1" marker-end="url(#arr)"/>
|
||||
<line x1="700" y1="490" x2="762" y2="490" stroke="#4f46e5" stroke-width="1" stroke-dasharray="4,2"/>
|
||||
<line x1="762" y1="490" x2="762" y2="520" stroke="#4f46e5" stroke-width="1" marker-end="url(#arr)"/>
|
||||
|
||||
<!-- Client -->
|
||||
<rect x="340" y="630" width="220" height="45" rx="8" fill="#1a1a2e" stroke="#334155" stroke-width="1.5"/>
|
||||
<text x="450" y="650" text-anchor="middle" fill="white" font-weight="bold">🖥️ Browser / AWS SDK</text>
|
||||
<text x="450" y="667" text-anchor="middle" fill="#94a3b8" font-size="11">sqs.kube5s.ru (HTTPS)</text>
|
||||
|
||||
<!-- Arrow Client -> Ingresses -->
|
||||
<line x1="380" y1="630" x2="200" y2="588" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arr)"/>
|
||||
<line x1="450" y1="630" x2="420" y2="588" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arr)"/>
|
||||
|
||||
<!-- Arrow marker -->
|
||||
<defs>
|
||||
<marker id="arr" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#666"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
@@ -1662,3 +1662,10 @@ if errors.IsInvalid(err) {
|
||||
**Симптом:** Клик на очередь в UI → браузер переходит на `/queues/1234` → nginx 404.
|
||||
**Причина:** Next.js в образе `elasticmq-ui` собран с `basePath=""`. Внутренние переходы идут по абсолютным путям без prefix `/sqs-ui/tenantID/`. Ingress не знал о маршруте `/queues`.
|
||||
**Решение (v0.1.12):** `ensureIngressUIQueues` — третий ingress `/queues` PathTypePrefix → UI service:3000. Без rewrite-target.
|
||||
|
||||
### ERR-SQS-06: H2 file lock при rollout restart (повторяющийся)
|
||||
|
||||
**Симптом:** После `kubectl rollout restart` ElasticMQ стартует, SQS API отвечает, но SendMessage зависает навсегда. В логах: `MVStoreException: The file is locked: /data/elasticmq.mv.db`.
|
||||
**Ложный фикс (v0.1.10):** `FILE_LOCK=NO` в JDBC URI — не помогает, т.к. lock на уровне `FileChannel.lock()`, не JDBC.
|
||||
**Настоящая причина:** `Deployment strategy: RollingUpdate` + `PVC: ReadWriteOnce`. При rollout новый pod поднимается ДО убийства старого. Оба монтируют один PVC, ElasticMQ-1 держит lock → ElasticMQ-2 не может открыть H2 → persistence actor падает → write-операции зависают (dead letters).
|
||||
**Решение (v0.1.13):** Strategy `Recreate` (старый pod убивается до создания нового), `preStop: sleep 3` (graceful H2 shutdown), `livenessProbe timeoutSeconds: 3` (защита от GC pause false positive).
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| v0.1.10 | FILE_LOCK=NO в H2 JDBC URL (SendMessage зависал после rollout restart) | — |
|
||||
| v0.1.11 | ensureService добавляет port 3000 при enableUI=true (503 после self-healing) | a04d720 |
|
||||
| v0.1.12 | Ingress /queues -> elasticmq-ui:3000 (404 при навигации) | 3adc0d8 |
|
||||
| v0.1.13 | Strategy Recreate + preStop + liveness fix (H2 lock root cause) | pending |
|
||||
|
||||
### Тест-сьют v0.1.10 — финал
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# 2026-04-09 — Thinking Log
|
||||
|
||||
**Агент:** GitHub Copilot (Claude Opus 4)
|
||||
|
||||
---
|
||||
|
||||
## Анализ H2 file lock — корневая причина
|
||||
|
||||
### Симптом
|
||||
При каждом `kubectl rollout restart` ElasticMQ стартует, но `SendMessage` зависает навсегда.
|
||||
В логах: `The file is locked: /data/elasticmq.mv.db [2.2.224/7]`, затем `dead letters` и `AskTimeoutException` на SendMessage.
|
||||
|
||||
### Ошибочная гипотеза (v0.1.10)
|
||||
Предположил что JVM не освобождает JDBC-level lock при crash → добавил `FILE_LOCK=NO` в JDBC URI.
|
||||
**Это было НЕПРАВИЛЬНО.** `FILE_LOCK=NO` отключает только JDBC soft-lock. H2 MVStore использует `java.nio.FileChannel.lock()` — это OS-level file lock, не зависящий от JDBC параметров.
|
||||
|
||||
### Почему "работало" после каждой чистки
|
||||
После `rm /data/elasticmq.mv.db` + restart — файл создаётся заново, lock отсутствует. Но при следующем rollout restart проблема возвращается.
|
||||
|
||||
### Корневая причина (найдена 2026-04-09)
|
||||
|
||||
**Deployment strategy: `RollingUpdate` + PVC: `ReadWriteOnce`**
|
||||
|
||||
Цепочка событий при `kubectl rollout restart`:
|
||||
1. Kubernetes добавляет аннотацию `restartedAt` → меняется template → начинается rollout
|
||||
2. Стратегия `RollingUpdate` (maxSurge=25%, maxUnavailable=25%) → для replicas=1:
|
||||
- maxSurge=1 (ceil 0.25) → Kubernetes поднимает НОВЫЙ pod
|
||||
- maxUnavailable=0 (floor 0.25) → старый pod ЕЩЁ ЖИВА
|
||||
3. PVC `ReadWriteOnce` — допускает mount с нескольких pod на ОДНОЙ НОДЕ (это не ReadWriteOncePod)
|
||||
4. Оба pod монтируют один PVC → оба пытаются открыть `/data/elasticmq.mv.db`
|
||||
5. Старый ElasticMQ держит `FileChannel.lock()` → новый ElasticMQ получает `MVStoreException: The file is locked`
|
||||
6. Persistence actor (SqlQueuePersistenceActor) в новом pod падает → dead letters
|
||||
7. Старый pod убивается (readinessProbe eventual fail) → lock освобождается — но поздно
|
||||
8. SQS REST server работает (port 9324 слушает), но WRITE-операции (SendMessage) зависают — actor мёртв
|
||||
|
||||
### Решение — 3 изменения в ensureDeployment
|
||||
|
||||
1. **Strategy: Recreate** (вместо RollingUpdate)
|
||||
- Kubernetes СНАЧАЛА убивает старый pod, ПОТОМ поднимает новый
|
||||
- Два pod НИКОГДА не работают одновременно → lock невозможен
|
||||
- Downtime ~25-30 секунд (JVM startup) — допустимо для мультитенант SQS
|
||||
|
||||
2. **preStop hook: sleep 3**
|
||||
- При SIGTERM JVM начинает shutdown
|
||||
- `sleep 3` даёт H2 время на `fsync` + `FileChannel.close()`
|
||||
- Без preStop: Kubernetes может убить pod раньше чем H2 закончит flush
|
||||
|
||||
3. **livenessProbe timeoutSeconds: 1 → 3**
|
||||
- JVM стартует за 20-23 секунды
|
||||
- initialDelaySeconds=5 + failureThreshold=5 × period=10 = 55 сек запас — хватает для старта
|
||||
- НО: `timeoutSeconds=1` — если GC pause > 1 сек → liveness fail → unnecessary restart → CrashLoopBackOff
|
||||
- Поднимаем до 3 секунд. GC pause > 3 сек — это уже реальная проблема которую стоит рестартить
|
||||
|
||||
## Дополнительные обнаруженные проблемы
|
||||
|
||||
### /_next/ и /queues/ ingress — глобальные (архитектурная)
|
||||
Пути `/_next/` и `/queues/` на хосте `sqs.kube5s.ru` общие. При двух тенантах с `enableUI=true` — конфликт ingress.
|
||||
**Решение отложено** — пока один тенант с UI. При мультитенант UI → нужен отдельный хост per tenant.
|
||||
|
||||
### imagePullPolicy: Always на UI
|
||||
`softwaremill/elasticmq-ui:latest` + `Always` → upstream может сломать при обновлении.
|
||||
**Пока оставляем** — будем пинить версию когда стабилизируем.
|
||||
|
||||
### memory limit 512Mi vs Xmx 384m
|
||||
`-Xmx384m` + JVM overhead ~150 МБ = ~534 МБ > limit 512 Mi. OOMKill возможен при нагрузке.
|
||||
**Пока оставляем** — в idle не стреляет. Учтём при нагрузочном тестировании.
|
||||
|
||||
## Самоанализ ошибки
|
||||
|
||||
Почему неправильно решил в v0.1.10:
|
||||
- Увидел `The file is locked` → сразу искал H2-настройки → нашёл `FILE_LOCK=NO`
|
||||
- НЕ проверил deployment strategy (RollingUpdate — default в Kubernetes)
|
||||
- НЕ проверил ReadWriteOnce behavior (допускает multi-pod на одной ноде)
|
||||
- НЕ проверил что происходит при rollout (два pod одновременно)
|
||||
- Лечил симптом (lock message) вместо причины (concurrent access)
|
||||
|
||||
**Вывод:** при любой ошибке связанной с persistence/lock/state — ПЕРВЫМ делом проверять: кто ещё имеет доступ к файлу? Сколько pod одновременно работают? Какая стратегия деплоя?
|
||||
@@ -533,6 +533,12 @@ func (r *QueueServiceReconciler) ensureDeployment(ctx context.Context, qs *sqsv1
|
||||
"sqs.kube5s.ru/tenant": qs.Spec.TenantID,
|
||||
},
|
||||
},
|
||||
// Recreate: старый pod убивается ДО создания нового.
|
||||
// H2 MVStore держит FileChannel.lock() на /data/elasticmq.mv.db —
|
||||
// при RollingUpdate два pod лезут в один PVC одновременно = ERR-SQS-06.
|
||||
Strategy: appsv1.DeploymentStrategy{
|
||||
Type: appsv1.RecreateDeploymentStrategyType,
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: sqsLabels(qs.Spec.TenantID),
|
||||
@@ -543,6 +549,8 @@ func (r *QueueServiceReconciler) ensureDeployment(ctx context.Context, qs *sqsv1
|
||||
SecurityContext: &corev1.PodSecurityContext{
|
||||
FSGroup: func() *int64 { v := int64(999); return &v }(),
|
||||
},
|
||||
// 15 сек — достаточно для graceful shutdown JVM + H2 fsync.
|
||||
TerminationGracePeriodSeconds: func() *int64 { v := int64(15); return &v }(),
|
||||
Containers: func() []corev1.Container {
|
||||
ctrs := []corev1.Container{
|
||||
{
|
||||
@@ -605,8 +613,17 @@ func (r *QueueServiceReconciler) ensureDeployment(ctx context.Context, qs *sqsv1
|
||||
},
|
||||
InitialDelaySeconds: 5,
|
||||
PeriodSeconds: 10,
|
||||
TimeoutSeconds: 3,
|
||||
FailureThreshold: 5,
|
||||
},
|
||||
// preStop: 3 сек на graceful shutdown H2 перед SIGTERM.
|
||||
Lifecycle: &corev1.Lifecycle{
|
||||
PreStop: &corev1.LifecycleHandler{
|
||||
Exec: &corev1.ExecAction{
|
||||
Command: []string{"sh", "-c", "sleep 3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if qs.Spec.EnableUI {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
# 2026-04-09 — v0.1.13: Strategy Recreate + preStop hook + liveness timeout fix
|
||||
import sys
|
||||
|
||||
FILE = "/home/naeel/terra/sless/sqs-operator/internal/controller/queueservice_controller.go"
|
||||
|
||||
with open(FILE, "r") as f:
|
||||
code = f.read()
|
||||
|
||||
# === PATCH 1: Strategy Recreate ===
|
||||
old_1 = "\t\t\t},\n\t\t\tTemplate: corev1.PodTemplateSpec{"
|
||||
new_1 = """\t\t\t},
|
||||
\t\t\t// Recreate: старый pod убивается ДО создания нового.
|
||||
\t\t\t// H2 MVStore держит FileChannel.lock() на /data/elasticmq.mv.db —
|
||||
\t\t\t// при RollingUpdate два pod лезут в один PVC одновременно = ERR-SQS-06.
|
||||
\t\t\tStrategy: appsv1.DeploymentStrategy{
|
||||
\t\t\t\tType: appsv1.RecreateDeploymentStrategyType,
|
||||
\t\t\t},
|
||||
\t\t\tTemplate: corev1.PodTemplateSpec{"""
|
||||
|
||||
count = code.count(old_1)
|
||||
if count != 1:
|
||||
print(f"FAIL patch 1: found {count} matches for Selector close + Template")
|
||||
sys.exit(1)
|
||||
code = code.replace(old_1, new_1, 1)
|
||||
print("PATCH 1 OK: Strategy Recreate")
|
||||
|
||||
# === PATCH 2: terminationGracePeriodSeconds ===
|
||||
old_2 = "\t\t\t\t\tSecurityContext: &corev1.PodSecurityContext{\n\t\t\t\t\t\tFSGroup: func() *int64 { v := int64(999); return &v }(),\n\t\t\t\t\t},\n\t\t\t\t\tContainers: func() []corev1.Container {"
|
||||
new_2 = """\t\t\t\t\tSecurityContext: &corev1.PodSecurityContext{
|
||||
\t\t\t\t\t\tFSGroup: func() *int64 { v := int64(999); return &v }(),
|
||||
\t\t\t\t\t},
|
||||
\t\t\t\t\t// 15 сек — достаточно для graceful shutdown JVM + H2 fsync.
|
||||
\t\t\t\t\tTerminationGracePeriodSeconds: func() *int64 { v := int64(15); return &v }(),
|
||||
\t\t\t\t\tContainers: func() []corev1.Container {"""
|
||||
|
||||
count = code.count(old_2)
|
||||
if count != 1:
|
||||
print(f"FAIL patch 2: found {count} matches for SecurityContext + Containers")
|
||||
sys.exit(1)
|
||||
code = code.replace(old_2, new_2, 1)
|
||||
print("PATCH 2 OK: terminationGracePeriodSeconds 15")
|
||||
|
||||
# === PATCH 3: LivenessProbe TimeoutSeconds=3 + preStop hook ===
|
||||
old_3 = "\t\t\t\t\t\t\t\t\tInitialDelaySeconds: 5,\n\t\t\t\t\t\t\t\t\tPeriodSeconds: 10,\n\t\t\t\t\t\t\t\t\tFailureThreshold: 5,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}"
|
||||
new_3 = """\t\t\t\t\t\t\t\t\tInitialDelaySeconds: 5,
|
||||
\t\t\t\t\t\t\t\t\tPeriodSeconds: 10,
|
||||
\t\t\t\t\t\t\t\t\tTimeoutSeconds: 3,
|
||||
\t\t\t\t\t\t\t\t\tFailureThreshold: 5,
|
||||
\t\t\t\t\t\t\t\t},
|
||||
\t\t\t\t\t\t\t\t// preStop: 3 сек на graceful shutdown H2 перед SIGTERM.
|
||||
\t\t\t\t\t\t\t\tLifecycle: &corev1.Lifecycle{
|
||||
\t\t\t\t\t\t\t\t\tPreStop: &corev1.LifecycleHandler{
|
||||
\t\t\t\t\t\t\t\t\t\tExec: &corev1.ExecAction{
|
||||
\t\t\t\t\t\t\t\t\t\t\tCommand: []string{"sh", "-c", "sleep 3"},
|
||||
\t\t\t\t\t\t\t\t\t\t},
|
||||
\t\t\t\t\t\t\t\t\t},
|
||||
\t\t\t\t\t\t\t\t},
|
||||
\t\t\t\t\t\t\t},
|
||||
\t\t\t\t\t\t}"""
|
||||
|
||||
count = code.count(old_3)
|
||||
if count != 1:
|
||||
print(f"FAIL patch 3: found {count} matches for LivenessProbe block")
|
||||
sys.exit(1)
|
||||
code = code.replace(old_3, new_3, 1)
|
||||
print("PATCH 3 OK: timeoutSeconds=3 + preStop sleep 3")
|
||||
|
||||
# === PATCH 4: version ===
|
||||
if "0.1.12" in code:
|
||||
code = code.replace("0.1.12", "0.1.13")
|
||||
print("PATCH 4 OK: version -> 0.1.13")
|
||||
else:
|
||||
print("PATCH 4 SKIP: version string not found")
|
||||
|
||||
with open(FILE, "w") as f:
|
||||
f.write(code)
|
||||
print("\nAll patches applied.")
|
||||
Reference in New Issue
Block a user