//go:build linux package host import ( "context" "log" "reflect" "github.com/carlosframework/platform/internal/hostbox" ) func (p *servingParkRoot) commitServingReboot(ctx context.Context, id string) error { return p.prepareOrCommitServingReboot(ctx, id, true) } // The namespace binding is durable first. An unbound record still carries its // old boot and every filesystem effect refuses it, including reused mount IDs. // A lost reply at either write is replayed against fresh independent evidence. func (l *servingParkLedger) commitRebootLocked(ctx context.Context, saved servingParkRecord, active *servingParkRebootAuthority) error { fresh, err := l.observeRebootPreparation(ctx, saved, active) if err != nil { return err } body, err := l.state.Read(servingNamespacePreparationName(saved.Authorization.ID, fresh.Observation.Topology)) if err != nil { return err } var prepared servingParkRebootPreparation if servingParkDecode(body, &prepared) != nil || !reflect.DeepEqual(prepared, fresh) { return hostbox.ErrChanged } t := fresh.Observation.Topology namespace := servingParkBootBinding{Version: 1, ID: "namespace", BeforeBoot: t.Previous.BootID, BootID: t.BootID, BeforeParent: t.Previous.Parent, Parent: t.CurrentParent} if err := ctx.Err(); err != nil { return err } if err := l.write("current-namespace.json", namespace); err != nil { return err } again, err := l.observeRebootPreparation(ctx, saved, active) if err != nil { return err } if !reflect.DeepEqual(fresh, again) { return hostbox.ErrChanged } record := servingParkBootBinding{Version: 1, ID: "retirement-" + saved.Authorization.ID, BeforeBoot: saved.Source.BootID, BootID: t.BootID, BeforeParent: saved.Source.Parent, Parent: t.CurrentParent} if err := ctx.Err(); err != nil { return err } prior, err := servingParkReadBinding(l.state, record.ID) if err != nil { return err } if saved.Source.Volume != "" && saved.Source.Volume == t.Volume && (prior == nil || prior.Volume != "") { record.Volume = t.Volume } else { log.Printf("Host %q: serving database retirement %q retained: %s.", saved.Authorization.Successor.Host, saved.Authorization.ID, hostbox.ErrChanged) } return l.write("current-"+record.ID+".json", record) } func (f *servingParkFS) readNamespace() (servingParkNamespace, error) { var n servingParkNamespace body, err := f.state.Read("namespace.json") if err != nil { return n, err } if servingParkDecode(body, &n) != nil { return n, hostbox.ErrChanged } b, err := servingParkReadBinding(f.state, "namespace") if err != nil || b == nil { return n, err } if n.BootID != b.BeforeBoot || n.Parent != b.BeforeParent { return n, hostbox.ErrChanged } n.BootID = b.BootID if err = b.directory(&n.Parent, false); err != nil { return n, err } err = b.directory(&n.Root, false) return n, err }