package hostbox import ( "context" "crypto/rand" "encoding/hex" "errors" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostwrite" "github.com/carlosframework/platform/internal/registry" "os" ) func (w selectedWork) MutateTimer(ctx context.Context, t registry.Timer, expected string, remove bool) (*registry.Timer, error) { if w.source.Timer == nil { return nil, ErrUnsupported } return w.source.Timer(ctx, w, t, expected, remove) } func (a ReconcileAdapter) MutateTimer(ctx context.Context, work hostwrite.Work, t registry.Timer, expected string, remove bool) (*registry.Timer, error) { s, err := Selected(work) if err != nil || s.Host != t.Host || a.Registry == nil || !a.Inventory.Remote() { return nil, ErrUnsupported } in, revision, err := a.Inventory.Snapshot(ctx, s.Host, s.Captured) if err != nil { return nil, err } in.Entries = append([]StopInventoryEntry(nil), in.Entries...) var capture InventoryExpectation var record RecordSelection var old *registry.Timer err = work.With(ctx, hostwrite.Identity{}, func() error { obs, err := hostclaim.Read(ctx, a.Reconciler.Claims, s.Host) if err != nil { return err } if obs.Claim == nil || obs.Claim.Holder == nil { return ErrChanged } h := obs.Claim.Holder _, etag, err := a.Reconciler.Claims.Get(ctx, h.Key) if err != nil { return err } record = RecordSelection{Host: s.Host, Key: h.Key, ETag: etag, Incarnation: s.Captured.Incarnation, Family: h.Family} capture, err = a.CaptureInventory(ctx, record, s.Captured, revision) if err != nil { return err } old, err = a.Registry.GetTimer(s.Host, t.Name) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } if old == nil { if expected != "" || remove { return registry.ErrTimerChanged } } else { if old.Registration != expected { return registry.ErrTimerChanged } if old.Kind != t.Kind { return registry.ErrTimerKind } } rt, err := a.Registry.Get(s.Host) if err != nil { return err } if rt == nil || !rt.ClaimOwnership.Valid() || rt.ClaimIncarnation != s.Captured.Incarnation || rt.ClaimStamp != s.Captured.Stamp { return ErrChanged } if remove { if t.ClaimOwnership != old.ClaimOwnership || t.Registration != old.Registration { return ErrChanged } t = *old } else { if old != nil && t.Registration == "" { t.CreatedAt, t.LastAt, t.LastStatus, t.LastMs = old.CreatedAt, old.LastAt, old.LastStatus, old.LastMs } t.ClaimOwnership = rt.ClaimOwnership if t.Registration == "" { var id [16]byte if _, err := rand.Read(id[:]); err != nil { return err } t.Registration = hex.EncodeToString(id[:]) } t.ClaimRowID = t.Registration } return nil }) if err != nil { return nil, err } index := -1 for i, e := range in.Entries { if e.Kind == "timer" && e.RelativePath == t.Host+"/"+t.Name { if index >= 0 { return nil, ErrTypedInventory } index = i } } if !remove && old != nil && t.Registration == expected { if *old != t || index < 0 || in.Entries[index].RowIdentity != expected || in.Entries[index].Materialization != "materialized" || in.Entries[index].TimerRetired || in.Entries[index].Ownership != t.ClaimOwner { return nil, ErrChanged } err = work.With(ctx, hostwrite.Identity{}, func() error { return a.RevalidateInventory(ctx, record, s.Captured, capture, revision) }) return &t, err } entry := StopInventoryEntry{Kind: "timer", ArtifactID: t.Registration, ManagedRoot: "registry", RelativePath: t.Host + "/" + t.Name, Ownership: t.ClaimOwner, RowIdentity: t.Registration, Materialization: "materialized", TimerRetired: remove} if index < 0 { in.Entries = append(in.Entries, entry) } else { entry.ArtifactID = in.Entries[index].ArtifactID in.Entries[index] = entry } revision, err = a.Inventory.WriteTimerExpected(ctx, in, capture, TimerMutation{Timer: t, Expected: expected, Remove: remove}) if err != nil { return nil, err } err = work.With(ctx, hostwrite.Identity{}, func() error { return a.RevalidateInventory(ctx, record, hostguard.Captured{Incarnation: s.Captured.Incarnation, Stamp: s.Captured.Stamp}, capture, revision) }) if err != nil { return nil, err } return &t, nil }