diff options
| author | Claude <noreply@anthropic.com> | 2026-08-06 23:22:03 -0500 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-08-06 23:22:03 -0500 |
| commit | f34ba20fc566ab861179a8ac2fd55eaabbc6a327 (patch) | |
| tree | f3b421a59cff06d1fa83ae3f75065971dcc750df /internal/health | |
| parent | fc0c6951d7ac36dd8f30ace929991197dc99ae54 (diff) | |
Add skiff config set/unset/list, backed by /app/env in the app's dataset
Config vars are plain env vars, injected into both the running app (via
rc.d) and the release phase, and versioned on the same ZFS snapshot
timeline as deploys - a bad config change gets the same automatic
snapshot/restart/health-check/rollback guarantee a bad deploy already
gets. Extracts deploy.go's health check into internal/health so both
paths share it.
Verified end-to-end against a FreeBSD VM: config set/unset, automatic
rollback on a health-check failure, and explicit `skiff rollback` all
correctly carry the env file along with the rest of the dataset.
Diffstat (limited to 'internal/health')
| -rw-r--r-- | internal/health/health.go | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/internal/health/health.go b/internal/health/health.go new file mode 100644 index 0000000..d1c9df3 --- /dev/null +++ b/internal/health/health.go @@ -0,0 +1,40 @@ +// Package health checks whether a freshly (re)started app is up, by polling +// its declared health endpoint - shared by both internal/deploy and skiff +// config's own restart-and-verify sequence. +package health + +import ( + "context" + "fmt" + "net/http" + "time" + + "skiff/internal/appmeta" + "skiff/internal/layout" +) + +func WaitHealthy(ctx context.Context, meta appmeta.Meta) error { + url := fmt.Sprintf("http://%s:%d%s", meta.IP, meta.Port, meta.HealthPath) + deadline := time.Now().Add(layout.HealthTimeoutSecs * time.Second) + client := &http.Client{Timeout: 2 * time.Second} + + var lastErr error + for time.Now().Before(deadline) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return err + } + resp, err := client.Do(req) + if err == nil { + resp.Body.Close() + if resp.StatusCode < 300 { + return nil + } + lastErr = fmt.Errorf("status %d", resp.StatusCode) + } else { + lastErr = err + } + time.Sleep(500 * time.Millisecond) + } + return fmt.Errorf("never became healthy at %s: %w", url, lastErr) +} |
