| 1 | package hostactors |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "github.com/carlosframework/platform/internal/boxid" |
| 6 | "github.com/carlosframework/platform/internal/release" |
| 7 | ) |
| 8 | |
| 9 | type EnrollmentRequest struct { |
| 10 | AttemptID string |
| 11 | ConfirmationID string |
| 12 | Reference string |
| 13 | Namespace string |
| 14 | Label string |
| 15 | Region string |
| 16 | Pool string |
| 17 | AWSRegion string |
| 18 | TofuInstanceID string |
| 19 | ExistingReference bool |
| 20 | Operator string |
| 21 | } |
| 22 | |
| 23 | // Enroll uses operator-approved configuration, not values in the identity |
| 24 | // request. H16/H17's worker confirmation producer belongs to region HA spec 4. |
| 25 | func (d Directory) Enroll(ctx context.Context, sg release.Signer, o Observed, v boxid.Verifier, in EnrollmentRequest, raw []byte) (Observed, error) { |
| 26 | return d.enroll(ctx, sg, o, v, in, raw, false) |
| 27 | } |
| 28 | func (d Directory) enroll(ctx context.Context, sg release.Signer, o Observed, v boxid.Verifier, in EnrollmentRequest, raw []byte, automatic bool) (Observed, error) { |
| 29 | if (!automatic && in.Operator == "") || in.AWSRegion == "" || in.Reference == "" { |
| 30 | return Observed{}, ErrRoster |
| 31 | } |
| 32 | referenced, err := d.Referenced(ctx, in.Reference) |
| 33 | if err != nil { |
| 34 | return Observed{}, err |
| 35 | } |
| 36 | in.ExistingReference = in.ExistingReference || referenced |
| 37 | if automatic && referenced { |
| 38 | return Observed{}, ErrRoster |
| 39 | } |
| 40 | if in.ExistingReference && in.TofuInstanceID == "" { |
| 41 | return Observed{}, ErrRoster |
| 42 | } |
| 43 | expect := boxid.Expect{Box: in.Reference, AWSRegion: in.AWSRegion, TofuInstanceID: in.TofuInstanceID} |
| 44 | for ref, b := range o.Roster.Boxes { |
| 45 | expect.Roster = append(expect.Roster, boxid.Enrolled{Box: ref, Cloud: b.Cloud}) |
| 46 | } |
| 47 | evidence, err := v.Enroll(raw, expect) |
| 48 | if err != nil { |
| 49 | return Observed{}, err |
| 50 | } |
| 51 | cloud := evidence.Cloud |
| 52 | if old, exists := o.Roster.Boxes[in.Reference]; exists { |
| 53 | if old.Cloud == cloud && old.EvidenceSHA256 == evidence.RecordSHA256 && old.Namespace == in.Namespace && old.Region == in.Region && old.Pool == in.Pool { |
| 54 | return o, nil |
| 55 | } |
| 56 | return Observed{}, ErrRoster |
| 57 | } |
| 58 | enrollment, err := d.enrollmentNonce(ctx, in, raw) |
| 59 | if err != nil { |
| 60 | return Observed{}, err |
| 61 | } |
| 62 | r := Clone(o.Roster) |
| 63 | if automatic { |
| 64 | a, ok := r.EnrollmentAttempts[in.Reference] |
| 65 | if !ok || a.Consumed || a.AttemptID != in.AttemptID || a.ConfirmationID != in.ConfirmationID { |
| 66 | return Observed{}, ErrRoster |
| 67 | } |
| 68 | a.Consumed = true |
| 69 | r.EnrollmentAttempts[in.Reference] = a |
| 70 | } |
| 71 | box := Box{Enrollment: enrollment, Namespace: in.Namespace, BoxID: in.Reference, Label: in.Label, Region: in.Region, Pool: in.Pool, Cloud: cloud, EvidenceSHA256: evidence.RecordSHA256, State: "active", LossIDs: []string{}} |
| 72 | if automatic { |
| 73 | // A box the group launched takes the group's role in this same |
| 74 | // write (region HA spec 2 §7, A11-1): a replacement for an |
| 75 | // excluded group is never placeable, even for a moment. |
| 76 | g := r.Regions[in.Region] |
| 77 | box.Group, box.Role = g.GroupName, g.Role |
| 78 | } |
| 79 | r.Boxes[in.Reference] = box |
| 80 | return d.Replace(ctx, sg, o, r) |
| 81 | } |
| 82 | |
| 83 | func (d Directory) EnrollAWS(ctx context.Context, sg release.Signer, o Observed, in EnrollmentRequest, raw []byte) (Observed, error) { |
| 84 | region, ok := o.Roster.Regions[in.Region] |
| 85 | if !ok || region.Account == "" || region.AWSRegion == "" { |
| 86 | return Observed{}, ErrRoster |
| 87 | } |
| 88 | if err := validateScope(o.Roster, Scope{Namespace: in.Namespace, Regions: []string{in.Region}, Pools: []string{in.Pool}}); err != nil { |
| 89 | return Observed{}, err |
| 90 | } |
| 91 | certs, err := boxid.EmbeddedCerts() |
| 92 | if err != nil { |
| 93 | return Observed{}, err |
| 94 | } |
| 95 | in.AWSRegion = region.AWSRegion |
| 96 | return d.Enroll(ctx, sg, o, boxid.Verifier{Account: region.Account, Certs: certs}, in, raw) |
| 97 | } |
| 98 | |