package host import ( "maps" "path/filepath" "reflect" "sync" "github.com/carlosframework/platform/internal/hostclaim" ) type claimRestoreObservation struct { mu sync.Mutex observed hostclaim.Observed box, dataDir, db string } func sameClaimObservation(a, b hostclaim.Observed) bool { return !a.Absent && !b.Absent && a.Host == b.Host && a.ETag != "" && a.ETag == b.ETag && reflect.DeepEqual(a.Claim, b.Claim) } func (in ClaimLaunchInputs) observeClaimWrite(before, after hostclaim.Observed) error { if in.claimWrite == nil || before.Claim == nil && after.Claim == nil { return nil } return in.claimWrite(before, after) } func (r *claimRestoreObservation) snapshot() hostclaim.Observed { r.mu.Lock() defer r.mu.Unlock() return r.observed } func (r *claimRestoreObservation) retag(before, after hostclaim.Observed) error { return r.advance(before, after, func(expected *hostclaim.Claim) error { db, err := filepath.Rel(r.dataDir, r.db) if err != nil || !filepath.IsLocal(db) || expected.Holder == nil { return hostclaim.ErrConflict } db = filepath.ToSlash(db) entry := expected.Inventory[r.box] old, left := entry.Paths[db] marker, right := entry.Paths[db+".gen"] if left != right || old != marker || left && (old == 0 || old >= expected.Holder.Stamp) { return hostclaim.ErrConflict } expected.Inventory = maps.Clone(expected.Inventory) if expected.Inventory == nil { expected.Inventory = make(map[string]hostclaim.InventoryEntry) } entry.Paths = maps.Clone(entry.Paths) if entry.Paths == nil { entry.Paths = make(map[string]uint64) } if expected.Op.Kind == hostclaim.KindServing { entry.Layout = "nested" } entry.Paths[db], entry.Paths[db+".gen"] = expected.Holder.Stamp, expected.Holder.Stamp expected.Inventory[r.box] = entry return nil }) } func (r *claimRestoreObservation) retry(before, after hostclaim.Observed) error { return r.advance(before, after, func(expected *hostclaim.Claim) error { if expected.Op.Kind != hostclaim.KindServing || expected.Holder == nil || after.Claim.Holder == nil { return hostclaim.ErrConflict } holder := *expected.Holder holder.WakeRetry = after.Claim.Holder.WakeRetry expected.Holder = &holder return nil }) } func (r *claimRestoreObservation) advance(before, after hostclaim.Observed, delta func(*hostclaim.Claim) error) error { r.mu.Lock() defer r.mu.Unlock() if !sameClaimObservation(r.observed, before) || before.Claim == nil || after.Claim == nil || after.Absent || after.Host != before.Host || after.ETag == "" || after.ETag == before.ETag || after.Claim.Gen != before.Claim.Gen+1 { return hostclaim.ErrConflict } expected := *before.Claim expected.Gen++ if err := delta(&expected); err != nil { return err } if !reflect.DeepEqual(&expected, after.Claim) { return hostclaim.ErrConflict } r.observed = after return nil }