| 1 | package host |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "strconv" |
| 6 | "time" |
| 7 | |
| 8 | "github.com/carlosframework/platform/internal/hostclaim" |
| 9 | "github.com/carlosframework/platform/internal/hostops" |
| 10 | "github.com/carlosframework/platform/internal/registry" |
| 11 | "github.com/carlosframework/platform/internal/release" |
| 12 | ) |
| 13 | |
| 14 | func verifySidecarWake(ctx context.Context, s release.Store, rt registry.Route, w release.SidecarWake) error { |
| 15 | if w.Schema != 1 || w.AppID == "" || w.Incarnation == "" || w.RequestID == "" { |
| 16 | return hostops.ErrIdentity |
| 17 | } |
| 18 | acct := release.RouteAccount(rt) |
| 19 | a, err := hostops.ReadAdmission(ctx, s, acct, rt.App, w.AppID) |
| 20 | if err != nil { |
| 21 | return err |
| 22 | } |
| 23 | if a.Value.State != "open" || a.Value.Op != nil { |
| 24 | return hostops.ErrPending |
| 25 | } |
| 26 | effect, ok := a.Value.Effects[w.RequestID] |
| 27 | key := release.SidecarWakeKey(acct, rt.App, rt.Slug) |
| 28 | if !ok || effect.State != "complete" || effect.ID != w.RequestID || effect.OperationID != w.RequestID || effect.AppID != w.AppID || effect.Action != "wake" || effect.Resource != "control-object:"+key || effect.ResultReference != w.RequestID || effect.Inputs["key"] != key || effect.Inputs["incarnation"] != w.Incarnation || effect.Inputs["request_id"] != w.RequestID || effect.Inputs["admission_epoch"] != strconv.FormatUint(a.Value.Epoch, 10) { |
| 29 | return hostops.ErrIdentity |
| 30 | } |
| 31 | created, err := time.Parse(time.RFC3339Nano, effect.Inputs["created_at"]) |
| 32 | if err != nil || created.IsZero() { |
| 33 | return hostops.ErrIdentity |
| 34 | } |
| 35 | // Recovery may remove a settled operation's registration. The durable |
| 36 | // completed effect remains the request's binding after that cleanup. |
| 37 | if reg, present := a.Value.Registrations[w.RequestID]; present { |
| 38 | if reg.Kind != "wake" || reg.Owner != effect.Owner || reg.AppID != w.AppID || reg.Epoch != a.Value.Epoch || reg.Host != rt.Host || reg.Incarnation != w.Incarnation || !reg.CreatedAt.Equal(created) { |
| 39 | return hostops.ErrIdentity |
| 40 | } |
| 41 | } |
| 42 | obs, err := hostclaim.Read(ctx, s, rt.Host) |
| 43 | if err != nil { |
| 44 | return err |
| 45 | } |
| 46 | if obs.Absent || obs.Claim.Holder == nil { |
| 47 | return hostops.ErrPending |
| 48 | } |
| 49 | h := obs.Claim.Holder |
| 50 | if obs.Claim.Op.Kind != hostclaim.KindServing || h.Family != "sidecar" || h.Account != acct || h.App != rt.App || h.Name != rt.Slug || h.Incarnation != w.Incarnation || !h.CreatedAt.Equal(created) { |
| 51 | return hostops.ErrIdentity |
| 52 | } |
| 53 | return nil |
| 54 | } |
| 55 | |