| 1 | package hostguard |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "github.com/carlosframework/platform/internal/hostclaim" |
| 7 | ) |
| 8 | |
| 9 | func (g Guard) RebindRootInventory(ctx context.Context, prior Captured, revision uint64, next RootInstallation, intent []byte, check func(*hostclaim.Claim) error, commit func() error) error { |
| 10 | if g.Root == nil || check == nil || commit == nil || len(intent) == 0 || prior.Incarnation != next.Incarnation || prior.Stamp == 0 || next.Stamp <= prior.Stamp || revision == 0 || next.InventoryRevision == 0 { |
| 11 | return ErrInvalidInput |
| 12 | } |
| 13 | return g.locked(ctx, next.Host, func(d *recordDir) error { |
| 14 | c, err := g.fresh(ctx, next.Host) |
| 15 | if err != nil { |
| 16 | return err |
| 17 | } |
| 18 | if err = check(c); err != nil { |
| 19 | return err |
| 20 | } |
| 21 | if c.Holder.Incarnation != next.Incarnation || c.Holder.Stamp != next.Stamp || c.Holder.Account != next.Account || c.Holder.App != next.App { |
| 22 | return ErrClaimIncarnation |
| 23 | } |
| 24 | r, err := d.read(next.Host) |
| 25 | if err != nil { |
| 26 | return err |
| 27 | } |
| 28 | if r.barrier != nil { |
| 29 | return ErrFenced |
| 30 | } |
| 31 | want := Captured{Incarnation: next.Incarnation, Stamp: next.Stamp} |
| 32 | for _, denied := range r.rootRetired { |
| 33 | if denied == want { |
| 34 | return ErrRetiredStamp |
| 35 | } |
| 36 | } |
| 37 | if r.ActiveFence != nil && r.ActiveFence.matchesSession(c, want) { |
| 38 | return ErrFenced |
| 39 | } |
| 40 | for _, receipt := range r.FenceJournal { |
| 41 | if receipt.Target.matchesSession(c, want) { |
| 42 | return ErrFenced |
| 43 | } |
| 44 | } |
| 45 | next.Artifacts = r.installation.Artifacts |
| 46 | if r.installation == next && r.pendingInstallation == nil && bytes.Equal(r.rebindCommit, intent) { |
| 47 | return d.sync(next.Host) |
| 48 | } |
| 49 | old := r.installation |
| 50 | if r.Incarnation != prior.Incarnation || r.ActiveStamp != prior.Stamp || old.InventoryRevision != revision || old.Empty || old.Account != next.Account || old.App != next.App || old.Record != next.Record || old.Box != next.Box || old.Enrollment != next.Enrollment || old.Migration != next.Migration { |
| 51 | return ErrRecordIncarnation |
| 52 | } |
| 53 | if r.pendingInstallation != nil && (*r.pendingInstallation != next || !bytes.Equal(r.pendingInventory, intent)) { |
| 54 | return ErrFenced |
| 55 | } |
| 56 | r.pendingInventory = append([]byte(nil), intent...) |
| 57 | r.pendingInstallation = &next |
| 58 | if err = d.write(r); err != nil { |
| 59 | return err |
| 60 | } |
| 61 | c, err = g.fresh(ctx, next.Host) |
| 62 | if err != nil { |
| 63 | return err |
| 64 | } |
| 65 | if err = check(c); err != nil { |
| 66 | return err |
| 67 | } |
| 68 | if err = commit(); err != nil { |
| 69 | return err |
| 70 | } |
| 71 | r.retire() |
| 72 | r.Incarnation, r.ActiveStamp, r.installation = next.Incarnation, next.Stamp, next |
| 73 | r.pendingInventory, r.pendingInstallation = nil, nil |
| 74 | r.quarantined, r.settled = false, false |
| 75 | r.rebindCommit = append([]byte(nil), intent...) |
| 76 | return d.write(r) |
| 77 | }) |
| 78 | } |
| 79 | |