abbd9488c6
New internal/infra package renders traefik/cloudflared/filebrowser from config (pinned images, single source of truth; web filebrowser path delegates here). stacks.EnsureBaseStack deploys the traefik-public network + the three stacks, single-flight + idempotent + non-fatal; wired to first boot and every health tick. monitor.EffectiveProtected drops cloudflared when no tunnel token. Section-G fix lives in felhom-agent build-golden.sh (same-path stacks bind). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
37 lines
1.1 KiB
Go
37 lines
1.1 KiB
Go
package stacks
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
|
)
|
|
|
|
// TestEnsureBaseStackSingleFlight proves the single-flight guard short-circuits: when infraMu is
|
|
// already held, EnsureBaseStack returns immediately (nil) WITHOUT touching docker. We hold the lock
|
|
// in this goroutine and call EnsureBaseStack in the same goroutine — Go mutexes are non-reentrant, so
|
|
// TryLock fails and the function returns before any docker network/inspect call. If the guard were
|
|
// missing, the call would shell out to docker (unavailable in unit tests) and not return nil cleanly.
|
|
func TestEnsureBaseStackSingleFlight(t *testing.T) {
|
|
m := &Manager{
|
|
cfg: &config.Config{},
|
|
logger: log.New(io.Discard, "", 0),
|
|
}
|
|
m.infraMu.Lock()
|
|
defer m.infraMu.Unlock()
|
|
|
|
done := make(chan error, 1)
|
|
go func() { done <- m.EnsureBaseStack() }()
|
|
|
|
select {
|
|
case err := <-done:
|
|
if err != nil {
|
|
t.Fatalf("expected nil (single-flight no-op) while lock held, got %v", err)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("EnsureBaseStack did not short-circuit while infraMu was held (single-flight guard missing?)")
|
|
}
|
|
}
|