| 1 | package activator |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | |
| 6 | "github.com/carlosframework/platform/internal/registry" |
| 7 | "github.com/carlosframework/platform/internal/seeds" |
| 8 | ) |
| 9 | |
| 10 | // wakeRouteChanged is doWake's check of the row it refreshed against the |
| 11 | // row the wake began from (final review F4). The edge admits a wake on its |
| 12 | // cached row, which can be minutes behind the registry: an import's |
| 13 | // rollback restores the legacy route and lifts its fence before the edge |
| 14 | // reloads, and a verify request admitted on the cached import row would |
| 15 | // then take the host's lease and restore its replica while the rollback |
| 16 | // deletes that same prefix. A refreshed row that the activator no longer |
| 17 | // manages, or that is now a different route (account, app, seed, import |
| 18 | // gate or address), is not woken: the answer is an ordinary failed wake, |
| 19 | // before any lease or restore. nil means the wake goes on. |
| 20 | // |
| 21 | // The shape is judged by the entry's own role (re-review finding 9): an |
| 22 | // instance entry's row must still be ActivatorManaged, and a worker |
| 23 | // entry's (WakeWorker, a standalone sidecar, which binds no socket) must |
| 24 | // still be a sidecar. The identity checks hold for both. |
| 25 | // |
| 26 | // The activator also wakes instance rows that are not ActivatorManaged: |
| 27 | // the edge hands it every hibernating instance row, so a unit-backed one |
| 28 | // (SplitBackend's Unit half, which systemd runs) and a row with no socket |
| 29 | // column (the backend derives its paths) are woken too. A wake that began |
| 30 | // for such a row goes on while the refreshed row has the same shape |
| 31 | // (stillWoken); the identity checks hold for it as well. No seed or import |
| 32 | // is one of these rows: the swap refuses a row with a backing and installs |
| 33 | // an ActivatorManaged one, so a wake that began on an import's row is |
| 34 | // always held to ActivatorManaged, and the row a rollback puts back fails |
| 35 | // it. |
| 36 | func wakeRouteChanged(began, now registry.Route, worker bool) error { |
| 37 | switch { |
| 38 | case worker && !now.IsSidecar(): |
| 39 | return fmt.Errorf("%s: the route changed before the wake: it is no longer a sidecar", now.Host) |
| 40 | case !worker && !stillWoken(began, now): |
| 41 | return fmt.Errorf("%s: the route changed before the wake: the activator no longer manages it", now.Host) |
| 42 | case began.Account != now.Account || began.App != now.App: |
| 43 | return fmt.Errorf("%s: the route changed before the wake: it belongs to %s/%s now, not %s/%s", now.Host, now.Account, now.App, began.Account, began.App) |
| 44 | case began.Addr != now.Addr: |
| 45 | return fmt.Errorf("%s: the route changed before the wake: its address is %q now, not %q", now.Host, now.Addr, began.Addr) |
| 46 | case began.ImportGate != now.ImportGate: |
| 47 | return fmt.Errorf("%s: the route changed before the wake: its import gate changed", now.Host) |
| 48 | case rowSeedID(began.SeedSource) != rowSeedID(now.SeedSource): |
| 49 | return fmt.Errorf("%s: the route changed before the wake: its seed is %q now, not %q", now.Host, rowSeedID(now.SeedSource), rowSeedID(began.SeedSource)) |
| 50 | } |
| 51 | return nil |
| 52 | } |
| 53 | |
| 54 | // stillWoken reports whether now is still a row an instance entry's wake, |
| 55 | // begun for began, may go on with. A wake that began on an ActivatorManaged |
| 56 | // row needs one still. Any other row the activator was handed must be an |
| 57 | // instance row and keep the shape it began with, in the fields |
| 58 | // ActivatorManaged reads: its hibernate flag, whether it has a socket, its |
| 59 | // backing and its tunnel. A sidecar's row is never an instance entry's. |
| 60 | func stillWoken(began, now registry.Route) bool { |
| 61 | if ActivatorManaged(began) { |
| 62 | return ActivatorManaged(now) |
| 63 | } |
| 64 | return now.Kind == "instance" && began.Kind == now.Kind && began.Hibernate == now.Hibernate && |
| 65 | (began.Socket != "") == (now.Socket != "") && |
| 66 | began.Backing == now.Backing && began.Tunnel == now.Tunnel |
| 67 | } |
| 68 | |
| 69 | // rowSeedID is the seed a row's seed column names: its id, "" for none, and |
| 70 | // the column itself when it does not parse. A seed's state moves on while |
| 71 | // it is the same seed (ready, placed, committed), so only the id is the |
| 72 | // row's identity. |
| 73 | func rowSeedID(col string) string { |
| 74 | rs, err := seeds.ParseRowSeed(col) |
| 75 | switch { |
| 76 | case err != nil: |
| 77 | return col |
| 78 | case rs == nil: |
| 79 | return "" |
| 80 | } |
| 81 | return rs.ID |
| 82 | } |
| 83 | |