kept data: the choice at reinstall, the list, the read-only view, the load (09 decision 36); R-690 fixed
gates / gates (push) Successful in 26s
gates / gates (push) Successful in 26s
An install over an app's kept drive folder (appdata/<app> non-empty) asks the household: "use my kept data" (a load from the newest copy of THIS drive's install, own unit or second-drive mirror, then the template's after_load) or "start fresh" (the folder is renamed into <drive>/kept/<app>/<date>/ with the removed app's unit; nothing deleted). The install API answers 409 kept_data_choice until one is chosen; DeployStack refuses too. New page Megorzott adatok / Kept data (/kept-data): Load / Look / Delete (typed confirmation, the only deletion of kept data). FileBrowser gets a read-only source. The drive-full warning names the kept folders. <drive>/kept is protected and outside every backup leg. R-690: the removed-app restore (R-487) never found a unit on a DATA drive — it asked GetStackComposePath (true for every catalog app) and restored nextcloud with no env. Now isStackDeployed; pinned with a production-shaped provider. Red-proofs: audits/night-2026-09-26/E/redproofs/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -191,8 +191,18 @@ type InstalledImage struct {
|
||||
type DeployRequest struct {
|
||||
StackName string `json:"stack_name"`
|
||||
Values map[string]string `json:"values"` // env_var -> user-provided value
|
||||
// KeptData is the household's answer when the app's drive folder already holds old data
|
||||
// (`09` §3 decision 36): KeptChoiceFresh here; KeptChoiceUse is carried out by the API as a load
|
||||
// from a backup and never reaches DeployStack. "" = no choice was made — refused when old data exists.
|
||||
KeptData string `json:"kept_data,omitempty"`
|
||||
}
|
||||
|
||||
// Kept-data choices at install (`09` §3 decision 36).
|
||||
const (
|
||||
KeptChoiceUse = "use"
|
||||
KeptChoiceFresh = "fresh"
|
||||
)
|
||||
|
||||
// DeployStack handles first-time deployment of an app.
|
||||
// Returns a warning message (empty if none) and an error if deployment is blocked.
|
||||
// 1. Check available memory against app requirements
|
||||
@@ -358,6 +368,34 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// `09` §3 decision 36 (R-657): an install NEVER runs into an app's old data silently. When the app's
|
||||
// private drive folder already holds something, the household chooses: „start fresh" moves it into a
|
||||
// dated kept folder (a rename on the same drive — nothing is deleted); „use my kept data" is a load
|
||||
// from a backup, which the API performs instead of an install. No choice → refused, nothing moved.
|
||||
// Pinned by TestKept_DeployRefusesOverOldDataWithoutAChoice.
|
||||
if old := OldAppDataPaths(stack.ComposePath, env["HDD_PATH"]); len(old) > 0 {
|
||||
switch req.KeptData {
|
||||
case KeptChoiceFresh:
|
||||
unit := ""
|
||||
if m.keptUnitFn != nil {
|
||||
unit = m.keptUnitFn(req.StackName, env["HDD_PATH"])
|
||||
}
|
||||
kept, err := m.KeepAside(req.StackName, env["HDD_PATH"], old, unit, time.Now())
|
||||
if err != nil {
|
||||
clearDeploying()
|
||||
return "", fmt.Errorf("start fresh: %w", err)
|
||||
}
|
||||
m.logger.Printf("[INFO] [stacks] Deploy %s: start fresh — the old data (%v) is kept in %s", req.StackName, old, kept)
|
||||
case "":
|
||||
clearDeploying()
|
||||
m.logger.Printf("[WARN] [stacks] Deploy %s REFUSED: the drive already holds its old data %v and no choice was made", req.StackName, old)
|
||||
return "", util.KindErrorf(ErrKeptDataChoice, "the drive already holds %s's old data (%s): choose use or fresh", req.StackName, strings.Join(old, ", "))
|
||||
default:
|
||||
clearDeploying()
|
||||
return "", util.KindErrorf(ErrKeptDataChoice, "kept_data %q is not an install choice here (use is a load from a backup)", req.KeptData)
|
||||
}
|
||||
}
|
||||
|
||||
// Save app.yaml.
|
||||
// CTRL-T2-1: persist the env now, but mark the ON-DISK state Deployed:false
|
||||
// until `docker compose up -d` actually succeeds (done in runComposeDeploy).
|
||||
|
||||
Reference in New Issue
Block a user