v0.276.0: a restore and a drive move keep the app's records (R-697, R-700)
gates / gates (push) Successful in 26s
gates / gates (push) Successful in 26s
A drive move persisted through the restore's fresh app.yaml write and dropped the pin: the syncer then copied the catalog verbatim and the next start jumped the app past its ladder (R-700). persistDriveFlip now changes HDD_PATH and nothing else. The restore's write carries the life records (conversion copies, desired_state, update history) from the app.yaml it replaces, and a second conversion no longer overwrites the first kept copy's record (R-697). 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:
@@ -742,11 +742,13 @@ func (m *Manager) doFlipRedeploy(name, target string) error {
|
||||
if m.testSeams != nil && m.testSeams.flipRedeploy != nil {
|
||||
return m.testSeams.flipRedeploy(name, target)
|
||||
}
|
||||
cfg := m.LoadAppConfigByName(name)
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("app config not found")
|
||||
// R-700 (v0.276.0): the move changes WHERE the data lives and nothing else — persistDriveFlip keeps
|
||||
// the pin and every record. This used to be RedeployFromEnv, whose fresh app.yaml dropped the pin: the
|
||||
// syncer then copied the catalog verbatim and the next start jumped the app past its ladder.
|
||||
if err := m.persistDriveFlip(name, target); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.RedeployFromEnv(name, flipEnv(cfg.Env, target)); err != nil {
|
||||
if err := m.upFromAppConfig(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if !m.waitHealthy(name) {
|
||||
@@ -755,14 +757,37 @@ func (m *Manager) doFlipRedeploy(name, target string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// flipEnv returns a copy of env with HDD_PATH set to target.
|
||||
func flipEnv(env map[string]string, target string) map[string]string {
|
||||
out := make(map[string]string, len(env)+1)
|
||||
for k, v := range env {
|
||||
out[k] = v
|
||||
// persistDriveFlip points app.yaml's HDD_PATH at target and changes NOTHING else (R-700, v0.276.0):
|
||||
// load-then-save, the SaveAppConfig rule — the pin, the intent, the installed images, the update records
|
||||
// and the kept conversion copies all stay. Starts nothing.
|
||||
func (m *Manager) persistDriveFlip(name, target string) error {
|
||||
stack, ok := m.GetStack(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("stack %q not found", name)
|
||||
}
|
||||
out["HDD_PATH"] = target
|
||||
return out
|
||||
dir := filepath.Dir(stack.ComposePath)
|
||||
cfg := LoadAppConfig(dir)
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("app config not found")
|
||||
}
|
||||
if cfg.Env == nil {
|
||||
cfg.Env = map[string]string{}
|
||||
}
|
||||
from := cfg.Env["HDD_PATH"]
|
||||
cfg.Env["HDD_PATH"] = target
|
||||
cfg.Deployed = true
|
||||
meta := LoadMetadata(dir)
|
||||
if err := SaveAppConfig(dir, cfg, m.encKey, SensitiveEnvVars(&meta)); err != nil {
|
||||
return fmt.Errorf("saving app config: %w", err)
|
||||
}
|
||||
m.mu.Lock()
|
||||
if s, ok := m.stacks[name]; ok {
|
||||
s.Deployed = true
|
||||
s.AppConfig = cfg
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.logger.Printf("[INFO] [stacks] %s: data moved %s -> %s — app.yaml keeps its pin (%d service(s)) and records", name, from, target, len(cfg.PinnedImages))
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitHealthy polls until the stack is up (running/unhealthy) or times out.
|
||||
|
||||
Reference in New Issue
Block a user