fix(R-1187): the import's compose runs get the controller's path variables; a compose warning is never read as the container id
gates / gates (push) Successful in 50s

Found live on 9202 (grimmory import timed out on the DB step and left the app down). Red-proved.
This commit is contained in:
2026-10-26 21:38:46 +01:00
parent a1c65b3d81
commit a3ed63ee9a
3 changed files with 81 additions and 5 deletions
+35 -5
View File
@@ -442,6 +442,8 @@ func (e *Exporter) executeImport(req ImportRequest, job *Job) {
e.failJob(job, step, fmt.Sprintf("Konfiguráció visszaállítása sikertelen: %v", err))
return
}
// R-1187 (found live): compose runs below see the controller's path variables, as at every install and start.
env = withImportPathVars(env, e.provider.GetStackNamespaceRoot(manifest.AppName), e.provider.GetImportRoot())
e.debugf("config restored: %d env vars", len(env))
if e.debug {
for k := range env {
@@ -1029,17 +1031,45 @@ func getComposeContainerID(stackDir string, env map[string]string, service strin
if err != nil {
return "", fmt.Errorf("compose ps: %w", err)
}
id := strings.TrimSpace(string(out))
id := containerIDFrom(out)
if id == "" {
return "", fmt.Errorf("no container found for service %s", service)
}
// Take the first line in case of multiple
if idx := strings.Index(id, "\n"); idx > 0 {
id = id[:idx]
}
return id, nil
}
// containerIDFrom picks the container id out of `compose ps -q` output: the first line that IS an id (12–64 hex). The
// output is combined, so a compose WARNING can come first — it was read as the id once (R-1187, found live on 9202).
func containerIDFrom(out []byte) string {
for _, line := range strings.Split(string(out), "\n") {
line = strings.TrimSpace(line)
if n := len(line); n >= 12 && n <= 64 && strings.Trim(line, "0123456789abcdef") == "" {
return line
}
}
return ""
}
// withImportPathVars is the import's env as `compose` must see it: the bundle's values plus the two path variables the
// controller itself adds at every install and start (stacks.policyEnv) — USERDATA_PATH always the controller's own
// derivation from the namespace root (never a bundle's value), IMPORT_PATH the drop-zone root. Without them compose
// printed a warning the DB step read as the container id (R-1187, live on 9202). Returns a new map.
func withImportPathVars(env map[string]string, nsRoot, importRoot string) map[string]string {
out := make(map[string]string, len(env)+2)
for k, v := range env {
out[k] = v
}
delete(out, "USERDATA_PATH")
delete(out, "IMPORT_PATH")
if nsRoot != "" {
out["USERDATA_PATH"] = appbackup.UserdataDir(nsRoot)
}
if importRoot != "" {
out["IMPORT_PATH"] = importRoot
}
return out
}
// waitForDB polls until the database is ready to accept connections.
func waitForDB(containerID, dbType string, env map[string]string) error {
timeout := 60 * time.Second