fix: go.work replace + test destroy targets
This commit is contained in:
Executable
+119
@@ -0,0 +1,119 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# test_cache_matrix.sh — 2026-03-23 (v3)
|
||||||
|
# Комплексный тест кэша registry:
|
||||||
|
# Phase 1 — полный деплой всех 24 ресурсов (kaniko builds, т.к. нет образов)
|
||||||
|
# Phase 2 — destroy sless_* + re-apply (все образы из кэша)
|
||||||
|
# Phase 3 — одновременно: удаление 2, смена кода 2, смена параметров 2
|
||||||
|
# ВАЖНО: nubes_postgres.npg и nubes_postgres_database НЕ уничтожаются.
|
||||||
|
# Причина: пересоздание БД ротирует пароль user0 в k8s secret;
|
||||||
|
# vault_secrets на это не реагирует сразу → FunctionJob получает старый пароль.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
LOG="$DIR/test_cache_matrix_$(date +%Y%m%d_%H%M%S).log"
|
||||||
|
TIMINGS="$LOG.timings"
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG"; }
|
||||||
|
sep() { log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; }
|
||||||
|
|
||||||
|
timed_op() {
|
||||||
|
local label="$1"; shift
|
||||||
|
log "▶ START: $label"
|
||||||
|
local t0; t0=$(date +%s%3N)
|
||||||
|
"$@" 2>&1 | tee -a "$LOG"
|
||||||
|
local rc=${PIPESTATUS[0]}
|
||||||
|
local t1; t1=$(date +%s%3N)
|
||||||
|
local elapsed=$(( (t1 - t0) / 1000 ))
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
|
log "✓ DONE: $label — ${elapsed}s"
|
||||||
|
PASS=$((PASS+1))
|
||||||
|
else
|
||||||
|
log "✗ FAIL: $label — ${elapsed}s (exit $rc)"
|
||||||
|
FAIL=$((FAIL+1))
|
||||||
|
fi
|
||||||
|
echo "$label: ${elapsed}s" >> "$TIMINGS"
|
||||||
|
return $rc
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_sless_only() {
|
||||||
|
local label="$1"
|
||||||
|
local targets
|
||||||
|
targets=$(terraform state list 2>/dev/null | grep -E '^(sless_service|sless_job)' | sed 's/^/-target=/' | tr '
|
||||||
|
' ' ' || true)
|
||||||
|
if [[ -z "$targets" ]]; then
|
||||||
|
log " (nothing to destroy for $label — state empty)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local n; n=$(terraform state list 2>/dev/null | grep -cE '^(sless_service|sless_job)' || true)
|
||||||
|
log " Targets: $n resources"
|
||||||
|
timed_op "$label" terraform destroy $targets -auto-approve
|
||||||
|
}
|
||||||
|
|
||||||
|
cd "$DIR"
|
||||||
|
|
||||||
|
sep
|
||||||
|
log "PHASE 1: Полный начальный деплой"
|
||||||
|
sep
|
||||||
|
destroy_sless_only "phase1-pre-clean"
|
||||||
|
timed_op "phase1-apply-all" terraform apply -auto-approve
|
||||||
|
|
||||||
|
log "--- Образы в registry после Phase 1 ---"
|
||||||
|
kubectl exec -n sless deployment/sless-registry -- sh -c 'find /var/lib/registry -name "*.json" -path "*/tags/*" 2>/dev/null | sed "s|.*repository/||;s|/_manifests.*||" | sort | uniq -c | sort -rn' 2>/dev/null | head -30 | tee -a "$LOG" || log "(registry inspect failed)"
|
||||||
|
|
||||||
|
sep
|
||||||
|
log "PHASE 2: Destroy sless_* → Re-apply (ожидаем cache hits)"
|
||||||
|
sep
|
||||||
|
destroy_sless_only "phase2-destroy"
|
||||||
|
timed_op "phase2-apply-cached" terraform apply -auto-approve
|
||||||
|
|
||||||
|
sep
|
||||||
|
log "PHASE 3: Mixed ops (delete+code+params)"
|
||||||
|
sep
|
||||||
|
|
||||||
|
log "--- 3a: destroy targets: stress_divzero, chaos_echo ---"
|
||||||
|
timed_op "phase3a-destroy-targets" terraform destroy -target=sless_service.stress_divzero -target=sless_service.chaos_echo -auto-approve
|
||||||
|
|
||||||
|
log "--- 3b: code changes (new sha256 → kaniko) ---"
|
||||||
|
echo "" >> "$DIR/code/pg-counter/pg_counter.py"
|
||||||
|
echo "# cache-test-$(date +%s)" >> "$DIR/code/pg-counter/pg_counter.py"
|
||||||
|
echo "" >> "$DIR/code/stress-js-async/stress_js_async.js"
|
||||||
|
echo "// cache-test-$(date +%s)" >> "$DIR/code/stress-js-async/stress_js_async.js"
|
||||||
|
log " changed: pg_counter.py, stress_js_async.js"
|
||||||
|
|
||||||
|
log "--- 3c: param changes (same sha256 → no kaniko) ---"
|
||||||
|
python3 - <<'PYEOF'
|
||||||
|
import re, sys
|
||||||
|
with open("stress.tf") as f:
|
||||||
|
content = f.read()
|
||||||
|
orig = content
|
||||||
|
content = re.sub(
|
||||||
|
r'(resource "sless_service" "stress_slow" \{[^}]*?)memory_mb\s*=\s*\d+',
|
||||||
|
lambda m: m.group(1) + 'memory_mb = 192',
|
||||||
|
content, flags=re.DOTALL
|
||||||
|
)
|
||||||
|
content = re.sub(
|
||||||
|
r'(resource "sless_service" "pg_stats" \{[^}]*?)timeout_sec\s*=\s*\d+',
|
||||||
|
lambda m: m.group(1) + 'timeout_sec = 20',
|
||||||
|
content, flags=re.DOTALL
|
||||||
|
)
|
||||||
|
if content == orig:
|
||||||
|
print(" stress.tf: no changes (already patched?)", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
with open("stress.tf", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print(" stress.tf: stress_slow→memory_mb=192, pg_stats→timeout_sec=20")
|
||||||
|
PYEOF
|
||||||
|
|
||||||
|
log "--- 3d: apply всех mixed изменений ---"
|
||||||
|
log " Expected: stress_divzero+chaos_echo=cache_hit, pg_counter+stress_js_async=kaniko, stress_slow+pg_stats=k8s_only"
|
||||||
|
timed_op "phase3d-mixed-apply" terraform apply -auto-approve
|
||||||
|
|
||||||
|
sep
|
||||||
|
log "ИТОГ"
|
||||||
|
sep
|
||||||
|
log "Timings:"
|
||||||
|
cat "$TIMINGS" 2>/dev/null | tee -a "$LOG"
|
||||||
|
log "Pass: $PASS | Fail: $FAIL"
|
||||||
|
log "Лог: $LOG"
|
||||||
@@ -93,23 +93,30 @@ func generateDockerfile(runtime string, hasRequirements bool, hasPackageJSON boo
|
|||||||
// Go: multi-stage build с go.work (v0.1.3+).
|
// Go: multi-stage build с go.work (v0.1.3+).
|
||||||
// base-образ содержит: /app/server/ (module sless/fn/server, package main).
|
// base-образ содержит: /app/server/ (module sless/fn/server, package main).
|
||||||
// kaniko копирует пользовательский код в /app/handler/.
|
// kaniko копирует пользовательский код в /app/handler/.
|
||||||
// Генерируем go.mod для handler если его нет (module sless/fn/handler).
|
// Модуль handler всегда переименовывается в sless/fn/handler — это позволяет server.go
|
||||||
// Генерируем go.work: use ./server + use ./handler + replace sless/fn/handler => ./handler.
|
// импортировать его по фиксированному пути без replace-директивы.
|
||||||
// replace позволяет server.go импортировать sless/fn/handler независимо от имени модуля пользователя.
|
// Почему нет replace: Go 1.23 запрещает replace для модуля, который одновременно в workspace
|
||||||
// GOFLAGS=-mod=mod: позволяет go build подтягивать зависимости из handler/go.mod через GOPROXY.
|
// (use ./handler). Ошибка: "workspace module sless/fn/handler is replaced at all versions".
|
||||||
|
// Решение: rename module → use workspace → require без replace.
|
||||||
goModStep := ""
|
goModStep := ""
|
||||||
if !hasGoMod {
|
if !hasGoMod {
|
||||||
// Пользователь не предоставил go.mod — генерируем минимальный
|
// Пользователь не предоставил go.mod — генерируем минимальный с правильным именем.
|
||||||
goModStep = "RUN printf 'module sless/fn/handler\\n\\ngo 1.23\\n' > /app/handler/go.mod\n"
|
goModStep = "RUN printf 'module sless/fn/handler\\n\\ngo 1.23\\n' > /app/handler/go.mod\n"
|
||||||
|
} else {
|
||||||
|
// Пользователь предоставил go.mod с произвольным именем модуля — переименовываем.
|
||||||
|
goModStep = "RUN sed -i 's/^module .*/module sless\\/fn\\/handler/' /app/handler/go.mod\n"
|
||||||
}
|
}
|
||||||
content := fmt.Sprintf(
|
content := fmt.Sprintf(
|
||||||
"FROM %s AS builder\n"+
|
"FROM %s AS builder\n"+
|
||||||
"WORKDIR /app\n"+
|
"WORKDIR /app\n"+
|
||||||
"COPY . /app/handler/\n"+
|
"COPY . /app/handler/\n"+
|
||||||
goModStep+
|
goModStep+
|
||||||
"RUN printf 'go 1.23\\n\\nuse ./server\\nuse ./handler\\n\\nreplace sless/fn/handler => ./handler\\n' > /app/go.work\n"+ // server/go.mod не содержит require sless/fn/handler — Go требует явного require даже при replace в go.work.
|
// go.work: workspace из двух модулей без replace-директив.
|
||||||
// Патчим server/go.mod в момент kaniko-сборки (не меняет базовый образ).
|
"RUN printf 'go 1.23\\n\\nuse ./server\\nuse ./handler\\n' > /app/go.work\n"+
|
||||||
"RUN printf '\\nrequire sless/fn/handler v0.0.0\\n' >> /app/server/go.mod\n"+"RUN CGO_ENABLED=0 go build -o /server ./server\n"+
|
// Патчим server/go.mod в момент kaniko-сборки — добавляем require на handler.
|
||||||
|
// Не меняет базовый образ (выполняется на этапе сборки пользовательского образа).
|
||||||
|
"RUN printf '\\nrequire sless/fn/handler v0.0.0\\n' >> /app/server/go.mod\n"+
|
||||||
|
"RUN CGO_ENABLED=0 go build -o /server ./server\n"+
|
||||||
"FROM alpine:3.20\n"+
|
"FROM alpine:3.20\n"+
|
||||||
"COPY --from=builder /server /server\n"+
|
"COPY --from=builder /server /server\n"+
|
||||||
"EXPOSE 8080\n"+
|
"EXPOSE 8080\n"+
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Изменено: 2026-03-22 — v0.1.3: server/ субдиректория + go.work для поддержки пользовательских go.mod.
|
# Изменено: 2026-03-23 — v0.1.4: fix go.work (убран replace, добавлен sed rename модуля).
|
||||||
# Base builder image для Go 1.23 serverless функций.
|
# Base builder image для Go 1.23 serverless функций.
|
||||||
# Это BUILDER-образ: golang:1.23-alpine + server/ + pre-cached зависимости.
|
# Это BUILDER-образ: golang:1.23-alpine + server/ + pre-cached зависимости.
|
||||||
# go mod download кеширует pgx/v5 в /root/go/pkg/mod — kaniko не скачивает их при каждой сборке.
|
# go mod download кеширует pgx/v5 в /root/go/pkg/mod — kaniko не скачивает их при каждой сборке.
|
||||||
@@ -7,14 +7,16 @@
|
|||||||
# FROM naeel/sless-runtime-go1.23:v0.1.3 AS builder
|
# FROM naeel/sless-runtime-go1.23:v0.1.3 AS builder
|
||||||
# WORKDIR /app
|
# WORKDIR /app
|
||||||
# COPY . /app/handler/
|
# COPY . /app/handler/
|
||||||
# RUN [ -f /app/handler/go.mod ] || printf 'module sless/fn/handler\n\ngo 1.23\n' > /app/handler/go.mod
|
# RUN [ -f /app/handler/go.mod ] && sed -i 's/^module .*/module sless\/fn\/handler/' || printf '...' > /app/handler/go.mod
|
||||||
# RUN printf 'go 1.23\n\nuse ./server\nuse ./handler\n\nreplace sless/fn/handler => ./handler\n' > /app/go.work
|
# RUN printf 'go 1.23\n\nuse ./server\nuse ./handler\n' > /app/go.work # БЕЗ replace!
|
||||||
|
# RUN printf '\nrequire sless/fn/handler v0.0.0\n' >> /app/server/go.mod
|
||||||
# RUN CGO_ENABLED=0 go build -o /server ./server
|
# RUN CGO_ENABLED=0 go build -o /server ./server
|
||||||
# FROM alpine:3.20
|
# FROM alpine:3.20
|
||||||
# COPY --from=builder /server /server
|
# COPY --from=builder /server /server
|
||||||
#
|
#
|
||||||
# Почему server/ субдиректория: go.work + replace позволяет пользователю иметь любой go.mod
|
# Почему replace убран: Go 1.23 запрещает replace для модуля, который одновременно в workspace.
|
||||||
# с любыми зависимостями. Вложенные модули (nested) поддерживаются через workspace.
|
# Ошибка: "workspace module sless/fn/handler is replaced at all versions in the go.work file".
|
||||||
|
# Решение: handler всегда переименовывается в sless/fn/handler через sed — replace не нужен.
|
||||||
|
|
||||||
FROM golang:1.23-alpine
|
FROM golang:1.23-alpine
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user