carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostops
2
3import (
4 "context"
5 "errors"
6 "os"
7 "time"
8
9 "github.com/carlosframework/platform/internal/hostactors"
10 "github.com/carlosframework/platform/internal/hostclaim"
11 "github.com/carlosframework/platform/internal/release"
12)
13
14// BackfillAuthority deliberately grants only the frozen backfill's adoption.
15// Mutation doors use ProductionAuthority once their full construction lands.
16type BackfillAuthority struct{ p ProductionAuthority }
17
18func (a BackfillAuthority) Inventory(ctx context.Context, obs hostclaim.Observed) ([]string, []string, error) {
19 return a.p.Inventory(ctx, obs)
20}
21func (a BackfillAuthority) Adoption(ctx context.Context, r Record) (string, string, []string, []string, error) {
22 return a.p.Adoption(ctx, r)
23}
24func (a BackfillAuthority) ClaimBackfill() *Backfill { return a.p.Backfill }
25
26func (a BackfillAuthority) SelectExecutor(context.Context, hostclaim.Observed, Record) (hostclaim.ExecutorSelection, bool, error) {
27 return hostclaim.ExecutorSelection{}, false, nil // the backfill grants no create (Create refuses)
28}
29func (a BackfillAuthority) Create(context.Context, hostclaim.Observed, Record, string) error {
30 return ErrContract
31}
32func (a BackfillAuthority) Executor(context.Context, hostclaim.Observed, Record) (string, error) {
33 return "", ErrContract
34}
35func (a BackfillAuthority) StartDelete(context.Context, hostclaim.Observed, Record) error {
36 return ErrContract
37}
38func (a BackfillAuthority) FirstPhase(context.Context, hostclaim.Observed) error { return ErrContract }
39func (a BackfillAuthority) PurgeRetirement(context.Context, hostclaim.Observed) error {
40 return ErrContract
41}
42func NewBackfillController(s release.Store, d hostactors.Directory, now func() time.Time, newID func() string) Controller {
43 b := &Backfill{Store: s, Directory: d}
44 return Controller{Store: s, Now: now, NewID: newID, Authority: BackfillAuthority{ProductionAuthority{Store: s, Directory: d, Backfill: b, Now: now}}}
45}
46
47func ReconcileClaims(ctx context.Context, c Controller, adopt func(context.Context, string) (hostclaim.Observed, error)) error {
48 if adopt == nil {
49 return ErrContract
50 }
51 var backfill *Backfill
52 if provider, ok := c.Authority.(interface{ ClaimBackfill() *Backfill }); ok {
53 backfill = provider.ClaimBackfill()
54 }
55 if backfill != nil {
56 plan, _, err := backfill.readPlan(ctx)
57 if errors.Is(err, os.ErrNotExist) {
58 return nil
59 }
60 if err != nil {
61 return err
62 }
63 if plan.Phase != "adopting" {
64 return nil
65 }
66 }
67 apps, err := Apps(ctx, c.Store)
68 if err != nil {
69 return err
70 }
71 var all []error
72 // Collect the entire listing before assigning any hostname. A broken page
73 // cannot authorize an adoption against only the prefix read so far.
74 var records []Record
75 seen := map[string]bool{}
76 for _, app := range apps {
77 rs, err := Records(ctx, c.Store, app.Account, app.Name)
78 if err != nil {
79 return err
80 }
81 for _, r := range rs {
82 if seen[r.Holder.Host] {
83 return ErrIdentity
84 }
85 seen[r.Holder.Host] = true
86 records = append(records, r)
87 }
88 }
89 for _, listed := range records {
90 r, err := ReadRecord(ctx, c.Store, listed.Holder.Key)
91 if errors.Is(err, os.ErrNotExist) {
92 continue
93 }
94 if err != nil {
95 all = append(all, err)
96 continue
97 }
98 if err = CheckHostQuarantine(ctx, c.Store, r.Holder.Host); err != nil {
99 all = append(all, err)
100 continue
101 }
102 obs, err := hostclaim.Read(ctx, c.Store, r.Holder.Host)
103 if err != nil {
104 all = append(all, err)
105 continue
106 }
107 if !obs.Absent && (obs.Claim.Holder == nil || obs.Claim.Holder.Acquisition != "" || r.Holder.Acquisition == "") {
108 continue
109 }
110 if _, err = adopt(ctx, r.Holder.Key); err != nil {
111 all = append(all, err)
112 }
113 }
114 return errors.Join(all...)
115}
116func (c Controller) ReconcileClaims(ctx context.Context) error {
117 return ReconcileClaims(ctx, c, func(ctx context.Context, key string) (hostclaim.Observed, error) {
118 rec, err := ReadRecord(ctx, c.Store, key)
119 if err != nil {
120 return hostclaim.Observed{}, err
121 }
122 return c.Adopt(ctx, rec)
123 })
124}
125
126func (a ProductionAuthority) ClaimBackfill() *Backfill { return a.Backfill }
127