carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "context"
5 "crypto/rand"
6 "encoding/hex"
7 "errors"
8 "github.com/carlosframework/platform/internal/hostclaim"
9 "github.com/carlosframework/platform/internal/hostguard"
10 "github.com/carlosframework/platform/internal/hostwrite"
11 "github.com/carlosframework/platform/internal/registry"
12 "os"
13)
14
15func (w selectedWork) MutateTimer(ctx context.Context, t registry.Timer, expected string, remove bool) (*registry.Timer, error) {
16 if w.source.Timer == nil {
17 return nil, ErrUnsupported
18 }
19 return w.source.Timer(ctx, w, t, expected, remove)
20}
21
22func (a ReconcileAdapter) MutateTimer(ctx context.Context, work hostwrite.Work, t registry.Timer, expected string, remove bool) (*registry.Timer, error) {
23 s, err := Selected(work)
24 if err != nil || s.Host != t.Host || a.Registry == nil || !a.Inventory.Remote() {
25 return nil, ErrUnsupported
26 }
27 in, revision, err := a.Inventory.Snapshot(ctx, s.Host, s.Captured)
28 if err != nil {
29 return nil, err
30 }
31 in.Entries = append([]StopInventoryEntry(nil), in.Entries...)
32 var capture InventoryExpectation
33 var record RecordSelection
34 var old *registry.Timer
35 err = work.With(ctx, hostwrite.Identity{}, func() error {
36 obs, err := hostclaim.Read(ctx, a.Reconciler.Claims, s.Host)
37 if err != nil {
38 return err
39 }
40 if obs.Claim == nil || obs.Claim.Holder == nil {
41 return ErrChanged
42 }
43 h := obs.Claim.Holder
44 _, etag, err := a.Reconciler.Claims.Get(ctx, h.Key)
45 if err != nil {
46 return err
47 }
48 record = RecordSelection{Host: s.Host, Key: h.Key, ETag: etag, Incarnation: s.Captured.Incarnation, Family: h.Family}
49 capture, err = a.CaptureInventory(ctx, record, s.Captured, revision)
50 if err != nil {
51 return err
52 }
53 old, err = a.Registry.GetTimer(s.Host, t.Name)
54 if err != nil && !errors.Is(err, os.ErrNotExist) {
55 return err
56 }
57 if old == nil {
58 if expected != "" || remove {
59 return registry.ErrTimerChanged
60 }
61 } else {
62 if old.Registration != expected {
63 return registry.ErrTimerChanged
64 }
65 if old.Kind != t.Kind {
66 return registry.ErrTimerKind
67 }
68 }
69 rt, err := a.Registry.Get(s.Host)
70 if err != nil {
71 return err
72 }
73 if rt == nil || !rt.ClaimOwnership.Valid() || rt.ClaimIncarnation != s.Captured.Incarnation || rt.ClaimStamp != s.Captured.Stamp {
74 return ErrChanged
75 }
76 if remove {
77 if t.ClaimOwnership != old.ClaimOwnership || t.Registration != old.Registration {
78 return ErrChanged
79 }
80 t = *old
81 } else {
82 if old != nil && t.Registration == "" {
83 t.CreatedAt, t.LastAt, t.LastStatus, t.LastMs = old.CreatedAt, old.LastAt, old.LastStatus, old.LastMs
84 }
85 t.ClaimOwnership = rt.ClaimOwnership
86 if t.Registration == "" {
87 var id [16]byte
88 if _, err := rand.Read(id[:]); err != nil {
89 return err
90 }
91 t.Registration = hex.EncodeToString(id[:])
92 }
93 t.ClaimRowID = t.Registration
94 }
95 return nil
96 })
97 if err != nil {
98 return nil, err
99 }
100 index := -1
101 for i, e := range in.Entries {
102 if e.Kind == "timer" && e.RelativePath == t.Host+"/"+t.Name {
103 if index >= 0 {
104 return nil, ErrTypedInventory
105 }
106 index = i
107 }
108 }
109 if !remove && old != nil && t.Registration == expected {
110 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 {
111 return nil, ErrChanged
112 }
113 err = work.With(ctx, hostwrite.Identity{}, func() error { return a.RevalidateInventory(ctx, record, s.Captured, capture, revision) })
114 return &t, err
115 }
116 entry := StopInventoryEntry{Kind: "timer", ArtifactID: t.Registration, ManagedRoot: "registry", RelativePath: t.Host + "/" + t.Name, Ownership: t.ClaimOwner, RowIdentity: t.Registration, Materialization: "materialized", TimerRetired: remove}
117 if index < 0 {
118 in.Entries = append(in.Entries, entry)
119 } else {
120 entry.ArtifactID = in.Entries[index].ArtifactID
121 in.Entries[index] = entry
122 }
123 revision, err = a.Inventory.WriteTimerExpected(ctx, in, capture, TimerMutation{Timer: t, Expected: expected, Remove: remove})
124 if err != nil {
125 return nil, err
126 }
127 err = work.With(ctx, hostwrite.Identity{}, func() error {
128 return a.RevalidateInventory(ctx, record, hostguard.Captured{Incarnation: s.Captured.Incarnation, Stamp: s.Captured.Stamp}, capture, revision)
129 })
130 if err != nil {
131 return nil, err
132 }
133 return &t, nil
134}
135