carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package hostguard
2
3import "context"
4
5type FenceView struct {
6 MigrationDenies []MigrationDeny
7 Schema int
8 Host string
9 Captured Captured
10 Installation RootInstallation
11 Retired []Captured
12 Pending *FenceTarget
13 Receipts []FenceReceipt
14 Barrier *FenceTarget
15 // Settled is set only in the migration census's views: the
16 // installation is quarantined by an obsolete-intent settlement.
17 Settled bool `json:",omitempty"`
18}
19type FenceViewProvider func(context.Context, string, Captured) (FenceView, error)
20
21// Installation reads one atomic root record without acquiring the host lock.
22// A caller using it to authorize an effect must hold Guard.With's exclusion.
23// It does not confer admission or replace the guard's fresh claim checks.
24func (a *RootAuthority) Installation(ctx context.Context, host string, captured Captured) (RootInstallation, error) {
25 if err := ctx.Err(); err != nil {
26 return RootInstallation{}, err
27 }
28 v, err := a.viewLocked(host, captured)
29 return v.Installation, err
30}
31
32func (a *RootAuthority) View(ctx context.Context, host string, captured Captured) (FenceView, error) {
33 if err := ctx.Err(); err != nil {
34 return FenceView{}, err
35 }
36 if a == nil || a.store == nil || validateFileHost(host) != nil {
37 return FenceView{}, ErrInvalidInput
38 }
39 unlock, err := a.store.Lock(ctx, host+".lock")
40 if err != nil {
41 return FenceView{}, err
42 }
43 defer unlock()
44 return a.viewLocked(host, captured)
45}
46
47func (a *RootAuthority) viewLocked(host string, captured Captured) (FenceView, error) {
48 return a.readView(host, captured, false)
49}
50
51// readView is viewLocked's read. A quarantined record is refused, except
52// that the refusal-only migration census (census) inspects a settled one
53// (SettleObsoleteRootInventory): it still names its installation and
54// inventory revision for cleanup and migration startup, and its view says
55// Settled. A pending intent is refused either way.
56func (a *RootAuthority) readView(host string, captured Captured, census bool) (FenceView, error) {
57 r, err := a.read(host)
58 if err != nil {
59 return FenceView{}, err
60 }
61 if r.pendingInstallation != nil || (r.quarantined && !(census && r.settled)) {
62 return FenceView{}, ErrFenced
63 }
64 known := r.Incarnation == captured.Incarnation && r.ActiveStamp == captured.Stamp && captured.Stamp != 0
65 for _, old := range r.rootRetired {
66 if old == captured {
67 known = true
68 }
69 }
70 if !known {
71 return FenceView{}, ErrStampMismatch
72 }
73 return FenceView{MigrationDenies: r.migrationDenies, Schema: 1, Host: host, Captured: captured, Installation: r.installation, Retired: r.rootRetired, Pending: r.ActiveFence, Receipts: r.FenceJournal, Barrier: r.barrier, Settled: r.quarantined && r.settled}, nil
74}
75func (v FenceView) validate(host string, c Captured) error {
76 if v.Schema != 1 || v.Host != host || v.Installation.Host != host || v.Installation.Migration == 0 || v.Captured != c {
77 return ErrMalformedRecord
78 }
79 if v.Pending != nil && v.Pending.validate(host) != nil {
80 return ErrMalformedRecord
81 }
82 if v.Barrier != nil && v.Barrier.validate(host) != nil {
83 return ErrMalformedRecord
84 }
85 for _, r := range v.Receipts {
86 if r.validate(host) != nil {
87 return ErrMalformedRecord
88 }
89 }
90 return nil
91}
92func (v FenceView) admit(host string, c Captured, claimSession uint64) error {
93 if err := v.validate(host, c); err != nil {
94 return err
95 }
96 if v.Installation.Incarnation != c.Incarnation || v.Installation.Stamp != c.Stamp || c.Stamp == 0 {
97 return ErrInactive
98 }
99 for _, retired := range v.Retired {
100 if retired == c {
101 return ErrRetiredStamp
102 }
103 }
104 if migrationAdmissionDenied(v.MigrationDenies, v.Receipts, c, claimSession) {
105 return ErrFenced
106 }
107 if v.Barrier != nil {
108 return ErrFenced
109 }
110 denied := func(t FenceTarget) bool {
111 return t.Host == host && t.Incarnation == c.Incarnation && t.Stamp == c.Stamp && t.Session == claimSession
112 }
113 if v.Pending != nil && denied(*v.Pending) {
114 return ErrFenced
115 }
116 for _, r := range v.Receipts {
117 if denied(r.Target) {
118 return ErrFenced
119 }
120 }
121 return nil
122}
123func (g Guard) fenceLocked(ctx context.Context, host string, c Captured, fn func(*recordDir) error) error {
124 if g.FenceView == nil {
125 return g.readLocked(ctx, host, fn)
126 }
127 view, err := g.FenceView(ctx, host, c)
128 if err != nil {
129 return err
130 }
131 if err := view.validate(host, c); err != nil {
132 return err
133 }
134 r := record{migrationDenies: view.MigrationDenies, V: 1, Host: host, Incarnation: view.Installation.Incarnation, ActiveStamp: view.Installation.Stamp, ActiveFence: view.Pending, FenceJournal: view.Receipts, root: true, installation: view.Installation, rootRetired: view.Retired, barrier: view.Barrier}
135 return fn(&recordDir{snapshot: &r})
136}
137