| 1 | package hostexec |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "errors" |
| 7 | "os" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/rootparticipation" |
| 10 | "github.com/carlosframework/platform/internal/rootstate" |
| 11 | ) |
| 12 | |
| 13 | func ReadRoot(ctx context.Context, store rootstate.Store, clock Clock, q ExecutionRequest, serving bool) (ExecutionRecord, error) { |
| 14 | var r ExecutionRecord |
| 15 | if store == nil || clock == nil { |
| 16 | return r, ErrState |
| 17 | } |
| 18 | if _, err := store.Read("update.json"); !errors.Is(err, os.ErrNotExist) { |
| 19 | return r, ErrLost |
| 20 | } |
| 21 | ledger := rootparticipation.Ledger{State: store} |
| 22 | census, err := ledger.Census() |
| 23 | if err != nil || census.NeverEnrolled { |
| 24 | return r, errors.Join(err, ErrState) |
| 25 | } |
| 26 | raw, err := store.Read(q.Host + ".json") |
| 27 | if serving { |
| 28 | if errors.Is(err, os.ErrNotExist) { |
| 29 | return r, nil |
| 30 | } |
| 31 | if err != nil { |
| 32 | return r, err |
| 33 | } |
| 34 | if rootparticipation.Decode(raw, &r) != nil || r.Schema != 1 || !r.Request.Valid() || r.Token == "" || r.Replay == nil || r.Request.Host != q.Host || r.Status != "settled" || r.Authority.Migration != census.Migration || r.Authority.Box != census.Box || r.Authority.Enrollment != census.Enrollment { |
| 35 | return r, ErrLost |
| 36 | } |
| 37 | return r, nil |
| 38 | } |
| 39 | if err != nil { |
| 40 | return r, err |
| 41 | } |
| 42 | if rootparticipation.Decode(raw, &r) != nil || r.Schema != 1 || !r.Request.Valid() || !sameExecution(q, r) || r.Status != "active" || r.Authority.ParticipationOnly || r.Authority.Installation == "" || r.Authority.Migration != census.Migration || r.Authority.Box != census.Box || r.Authority.Enrollment != census.Enrollment || r.Token == "" { |
| 43 | return r, ErrLost |
| 44 | } |
| 45 | boot, err := BootID() |
| 46 | if err != nil || r.Peer.KernelBootID != boot { |
| 47 | return r, ErrLost |
| 48 | } |
| 49 | _, now, err := clock.Now() |
| 50 | if err != nil || now >= r.SafeUntil { |
| 51 | return r, ErrLost |
| 52 | } |
| 53 | if err = ControllerAlive(ctx, r.Peer); err != nil { |
| 54 | return r, err |
| 55 | } |
| 56 | if err := ctx.Err(); err != nil { |
| 57 | return r, err |
| 58 | } |
| 59 | alive, err := rootstate.LiveHeld(store, q.Host+".live") |
| 60 | if err != nil || !alive { |
| 61 | return r, errors.Join(ErrLost, err) |
| 62 | } |
| 63 | again, err := store.Read(q.Host + ".json") |
| 64 | if err != nil || !bytes.Equal(raw, again) { |
| 65 | return r, ErrLost |
| 66 | } |
| 67 | _, now, err = clock.Now() |
| 68 | if err != nil || now >= r.SafeUntil { |
| 69 | return r, ErrLost |
| 70 | } |
| 71 | return r, nil |
| 72 | } |
| 73 | |