catalog: the lifecycle implementation itself (fixes the previous commit)

The previous commit landed only the new test/badge files: a 'git stash' used
to compare REUSE.md ref-check output silently dropped the staged index, so
every modification to an existing file was left behind and that commit does
not build. This adds the metadata field, the predicates, the fail-closed
deploy gate, the catalog filter, the funcmap entries, the template edits and
the docs that those tests exercise.
This commit is contained in:
2026-07-21 16:20:24 +02:00
parent ea0d3f1764
commit 5fdd2039fd
11 changed files with 213 additions and 5 deletions
+15
View File
@@ -410,6 +410,21 @@ func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name stri
return
}
// Lifecycle gate: an app withdrawn from the catalog (`lifecycle: hidden` / `abandoned`) is not
// installable. FAIL-CLOSED and server-side on purpose — the catalog page already omits these, so
// anything reaching here is a stale link, a bookmarked deploy form, or a direct POST, and a gate
// that only hides the button is not a gate. Deliberately BEFORE every mutation.
//
// This does NOT touch an already-deployed instance: it is on the deploy path only, and the
// manager refuses a redeploy of an existing stack through its own "already deployed" check.
if st, ok := r.stackMgr.GetStack(name); ok && !st.Meta.CanInstall() {
r.logger.Printf("[WARN] [api] Deploy refused for %s: lifecycle=%s (not offered for new installs)",
name, st.Meta.EffectiveLifecycle())
writeJSON(w, http.StatusConflict, apiResponse{OK: false,
Error: "Ez az alkalmazás jelenleg nem telepíthető."})
return
}
// Prevention layer (storage-split): refuse a deploy when the Docker-data volume is at/under its
// reserved buffer, so customer apps can't fill the volume the infra containers (controller,
// traefik, cloudflared, filebrowser) depend on. Fail-OPEN on a measurement error — the buffer is