package hostguard import ( "context" "errors" "github.com/carlosframework/platform/internal/hostclaim" ) func (g Guard) RetireInstallationWith(ctx context.Context, host string, captured Captured, check func(*hostclaim.Claim) error, fn func() error) error { return g.retireInstallationWith(ctx, host, captured, check, fn, nil) } // persist is injected by package tests to interrupt the record's durable write. func (g Guard) retireInstallationWith(ctx context.Context, host string, captured Captured, check func(*hostclaim.Claim) error, fn func() error, persist func(*recordDir, record) error) error { if captured.Stamp == 0 || check == nil || fn == nil { return ErrInvalidInput } return g.locked(ctx, host, func(d *recordDir) error { c, err := g.fresh(ctx, host) if err != nil { return err } if c.Holder.Incarnation != captured.Incarnation { return ErrClaimIncarnation } if err := check(c); err != nil { return err } r, err := d.read(host) if err != nil { return err } if r.Incarnation != captured.Incarnation { return ErrRecordIncarnation } if r.ActiveStamp != 0 { if r.ActiveStamp != captured.Stamp { return ErrStampMismatch } r.retire() if persist == nil { persist = (*recordDir).write } } else { n := len(r.RetiredStamps) if n == 0 || r.RetiredStamps[n-1] != captured.Stamp { return ErrStampMismatch } if persist == nil { persist = func(d *recordDir, _ record) error { return d.sync(host) } } } if err := ctx.Err(); err != nil { return err } // An inactive record can be visible after rename without being durable. // Sync it on retries before the callback may move any artifact. if err := persist(d, r); err != nil { return err } if err := ctx.Err(); err != nil { return err } return fn() }) } func (g Guard) WithNoInstallation(ctx context.Context, host, incarnation string, check func(*hostclaim.Claim) error, fn func() error) error { if incarnation == "" || check == nil || fn == nil { return ErrInvalidInput } return g.locked(ctx, host, func(d *recordDir) error { c, err := g.fresh(ctx, host) if err != nil { return err } if c.Holder.Incarnation != incarnation { return ErrClaimIncarnation } if err := check(c); err != nil { return err } if g.Root != nil { captured := Captured{Incarnation: incarnation, Stamp: c.Holder.Stamp} view, err := g.Root.viewLocked(host, captured) if err != nil { return err } if err = view.admit(host, captured, c.Holder.Session); err != nil { return err } if !view.Installation.Empty || view.Installation.Account != c.Holder.Account || view.Installation.App != c.Holder.App { return ErrStampMismatch } return fn() } _, err = d.read(host) if !errors.Is(err, ErrNoRecord) { if err != nil { return err } return ErrStampMismatch } if err := ctx.Err(); err != nil { return err } return fn() }) }