|
1 | 1 | ### Added |
2 | 2 |
|
| 3 | +- The configuration of the version being installed is now expanded by running |
| 4 | + *that version's* config providers, in a temporary VM booted from that |
| 5 | + version's own boot script on its own emulator, rather than by running provider |
| 6 | + state stashed at build time in the version that happens to be running. A |
| 7 | + provider module can differ between the two — which is precisely what an |
| 8 | + upgrade may change — and only the target's own answer is the right one. It |
| 9 | + also leaves Elixir's `Config.Provider` as the single implementation of the |
| 10 | + provider pipeline: Castle drives it and no longer keeps a copy of it. |
| 11 | + |
| 12 | + The temporary VM needs no epmd, no cookie, no node name and no distributed |
| 13 | + Erlang: it talks to the running node over a socket on the loopback interface, |
| 14 | + and whatever it prints — a provider explaining what it could not find, say — |
| 15 | + arrives on the terminal that asked for the install. It is stopped on every way |
| 16 | + out, including every failing one, and it cannot hold an install open: both its |
| 17 | + boot and the work it is asked to do have deadlines. Everything that can refuse |
| 18 | + to go on refuses before the upgrade is applied, so configuration that cannot |
| 19 | + be expanded leaves an install that did not happen rather than one that |
| 20 | + half did. |
| 21 | + |
| 22 | + Each expansion starts from the configuration the release was built with, which |
| 23 | + the first one copies aside as `sys.config.pristine` and none of them |
| 24 | + overwrites. Config providers are not obliged to be idempotent, and the |
| 25 | + familiar ones are not: a `runtime.exs` that sets a key only when an |
| 26 | + environment variable is present says nothing about that key when it is absent, |
| 27 | + so expanding over the previous result would leave a value behind after the |
| 28 | + provider had stopped supplying it — and the version made permanent would be |
| 29 | + configured differently from the way it goes on to boot. Expanding from the |
| 30 | + original instead means installing and then committing produce the same answer |
| 31 | + a boot would, which is the point of expanding at either. That copy is made |
| 32 | + atomically and with the permissions `sys.config` has, so a partly written one |
| 33 | + can never be found and read, and restricting `sys.config` — as an operator |
| 34 | + might, since it holds credentials — restricts this too. |
| 35 | + |
| 36 | + Among the things that refuse is the check Elixir makes on a configuration |
| 37 | + before booting into it: that what `Application.compile_env/3` read when the |
| 38 | + release was compiled is what the resolved configuration says now. A version |
| 39 | + whose runtime configuration contradicts what it was compiled against is |
| 40 | + refused here, where refusing costs nothing, rather than accepted and then |
| 41 | + found to be unbootable — which, for an upgrade that restarts, is found on the |
| 42 | + way back up with a rollback as the only way out. |
| 43 | + |
| 44 | + Which way a release is configured is settled by the release itself. One whose |
| 45 | + configuration was intercepted at build time — every release assembled by the |
| 46 | + Forecastle this is released alongside, recognisable by the `build.config` in |
| 47 | + its version directory — is expanded exactly as it was before, so nothing about |
| 48 | + installing or committing such a release changes. The new path is taken by a |
| 49 | + release whose ordinary Mix provider pipeline is intact, which is the shape |
| 50 | + Forecastle stops interfering with in its own next release. |
3 | 51 | - `Castle.Error`, the exception raised by a release-management command that did |
4 | 52 | not succeed. |
5 | 53 | - `Castle.running/1`, which succeeds when the version it is given is the |
|
0 commit comments