reboot_system_after_config is opt-in if using config/runtime.exs
This commit is contained in:
@@ -109,11 +109,13 @@ defmodule Config.Provider do
|
||||
:providers,
|
||||
:config_path,
|
||||
extra_config: [],
|
||||
prune_after_boot: false,
|
||||
reboot_after_config: true,
|
||||
prune_runtime_sys_config_after_boot: false,
|
||||
reboot_system_after_config: false,
|
||||
validate_compile_env: false
|
||||
]
|
||||
|
||||
@reserved_apps [:kernel, :stdlib]
|
||||
|
||||
@doc """
|
||||
Validates a `t:config_path/0`.
|
||||
"""
|
||||
@@ -194,7 +196,6 @@ defmodule Config.Provider do
|
||||
|
||||
defp boot_providers(path, provider, reboot_config, reboot_fun) do
|
||||
validate_no_cyclic_boot!(path)
|
||||
loaded_applications = :application.loaded_applications()
|
||||
original_config = read_config!(path)
|
||||
|
||||
config =
|
||||
@@ -202,16 +203,16 @@ defmodule Config.Provider do
|
||||
|> Config.__merge__(provider.extra_config)
|
||||
|> run_providers(provider)
|
||||
|
||||
if provider.reboot_after_config do
|
||||
if provider.reboot_system_after_config do
|
||||
config
|
||||
|> Config.__merge__(reboot_config)
|
||||
|> write_config!(path)
|
||||
|
||||
reboot_fun.()
|
||||
else
|
||||
for {app, _, _} <- loaded_applications, config[app] != original_config[app] do
|
||||
for app <- @reserved_apps, config[app] != original_config[app] do
|
||||
abort("""
|
||||
Cannot configure #{inspect(app)} because :reboot_after_config has been set \
|
||||
Cannot configure #{inspect(app)} because :reboot_system_after_config has been set \
|
||||
to false and #{inspect(app)} has already been loaded, meaning any further \
|
||||
configuration won't have an effect.
|
||||
|
||||
@@ -316,8 +317,8 @@ defmodule Config.Provider do
|
||||
Process.sleep(:infinity)
|
||||
end
|
||||
|
||||
defp booted_value(%{prune_after_boot: true}, path), do: {:booted, path}
|
||||
defp booted_value(%{prune_after_boot: false}, _path), do: {:booted, nil}
|
||||
defp booted_value(%{prune_runtime_sys_config_after_boot: true}, path), do: {:booted, path}
|
||||
defp booted_value(%{prune_runtime_sys_config_after_boot: false}, _path), do: {:booted, nil}
|
||||
|
||||
defp validate_no_cyclic_boot!(path) do
|
||||
if System.get_env("ELIXIR_CONFIG_PROVIDER_BOOTED") do
|
||||
|
||||
@@ -106,7 +106,7 @@ defmodule Config.ProviderTest do
|
||||
end
|
||||
|
||||
test "returns :booted if already booted and prunes config file" do
|
||||
init_and_assert_boot(prune_after_boot: true)
|
||||
init_and_assert_boot(prune_runtime_sys_config_after_boot: true)
|
||||
Application.put_all_env(Keyword.take(consult(@sys_config), [:elixir]))
|
||||
assert boot() == :booted
|
||||
refute_received :restart
|
||||
@@ -115,7 +115,7 @@ defmodule Config.ProviderTest do
|
||||
|
||||
test "returns :booted if already booted and runs validate_compile_env" do
|
||||
init_and_assert_boot(
|
||||
prune_after_boot: true,
|
||||
prune_runtime_sys_config_after_boot: true,
|
||||
validate_compile_env: [{:elixir, [:unknown], {:ok, :value}}]
|
||||
)
|
||||
|
||||
@@ -135,14 +135,15 @@ defmodule Config.ProviderTest do
|
||||
|
||||
test "returns without rebooting" do
|
||||
reader = {Config.Reader, fixture_path("configs/kernel.exs")}
|
||||
init = Config.Provider.init([reader], @sys_config, reboot_after_config: false)
|
||||
init = Config.Provider.init([reader], @sys_config, reboot_system_after_config: false)
|
||||
Application.put_all_env(init)
|
||||
|
||||
assert capture_abort(fn ->
|
||||
Provider.boot(fn ->
|
||||
raise "should not be called"
|
||||
end)
|
||||
end) =~ "Cannot configure :kernel because :reboot_after_config has been set to false"
|
||||
end) =~
|
||||
"Cannot configure :kernel because :reboot_system_after_config has been set to false"
|
||||
|
||||
# Make sure values before and after match
|
||||
write_sys_config!(kernel: [elixir_reboot: true])
|
||||
@@ -167,7 +168,7 @@ defmodule Config.ProviderTest do
|
||||
end
|
||||
|
||||
defp init_and_assert_boot(opts \\ []) do
|
||||
init(opts)
|
||||
init(opts ++ [reboot_system_after_config: true])
|
||||
boot()
|
||||
assert_received :restart
|
||||
end
|
||||
|
||||
@@ -429,12 +429,14 @@ defmodule Mix.Release do
|
||||
|
||||
defp merge_provider_config(release, sys_config, config_path) do
|
||||
{reboot?, extra_config, initial_config} = start_distribution(release)
|
||||
prune_after_boot = Keyword.get(release.options, :prune_runtime_sys_config_after_boot, false)
|
||||
|
||||
prune_runtime_sys_config_after_boot =
|
||||
Keyword.get(release.options, :prune_runtime_sys_config_after_boot, false)
|
||||
|
||||
opts = [
|
||||
extra_config: initial_config,
|
||||
prune_after_boot: prune_after_boot,
|
||||
reboot_after_config: reboot?,
|
||||
prune_runtime_sys_config_after_boot: prune_runtime_sys_config_after_boot,
|
||||
reboot_system_after_config: reboot?,
|
||||
validate_compile_env: validate_compile_env(release)
|
||||
]
|
||||
|
||||
@@ -458,7 +460,7 @@ defmodule Mix.Release do
|
||||
end
|
||||
|
||||
defp start_distribution(%{options: opts}) do
|
||||
reboot? = Keyword.get(opts, :reboot_system_after_config, true)
|
||||
reboot? = Keyword.get(opts, :reboot_system_after_config, false)
|
||||
early_distribution? = Keyword.get(opts, :start_distribution_during_config, false)
|
||||
|
||||
if not reboot? or early_distribution? do
|
||||
|
||||
@@ -70,19 +70,26 @@ defmodule Mix.Tasks.Loadconfig do
|
||||
Mix.shell().error("""
|
||||
Cannot configure base applications: #{inspect(reserved_apps)}
|
||||
|
||||
These applications are already started by the time Mix loads and
|
||||
therefore these configurations have no effect.
|
||||
These applications are already started by the time the configuration
|
||||
executes and these configurations have no effect.
|
||||
|
||||
If you want to configure these applications for a release, wrap
|
||||
them in a condition, such as:
|
||||
If you want to configure these applications for a release, you can
|
||||
specify them in your vm.args file:
|
||||
|
||||
-kernel config_key config_value
|
||||
|
||||
Alternatively, if you must configure them dynamically, you can wrap
|
||||
them in a conditional block in your config files:
|
||||
|
||||
if System.get_env("RELEASE_MODE") do
|
||||
config :kernel, ...
|
||||
end
|
||||
|
||||
Alternatively, specify the configuration in your vm.args file:
|
||||
and then configure your releases to reboot after configuration:
|
||||
|
||||
-kernel config_key config_value
|
||||
releases: [
|
||||
my_app: [reboot_system_after_config: true]
|
||||
]
|
||||
|
||||
This happened when loading #{Path.relative_to_cwd(file)} or
|
||||
one of its imports.
|
||||
|
||||
@@ -650,28 +650,29 @@ defmodule Mix.Tasks.Release do
|
||||
The following options can be set inside your releases key in your `mix.exs`
|
||||
to control how config providers work:
|
||||
|
||||
* `:start_distribution_during_config` - on Erlang/OTP 22+, releases
|
||||
only start the Erlang VM distribution features after the config files
|
||||
are evaluated. You can set it to `true` if you need distribution during
|
||||
configuration. Defaults to `false`.
|
||||
|
||||
* `:reboot_system_after_config` - every time your release is configured,
|
||||
the system is rebooted to allow the new configuration to take place.
|
||||
You can set this option to `false` to disable the rebooting for applications
|
||||
that are sensitive to boot time but, in doing so, note you won't be able
|
||||
to configure system applications, such as `:kernel`, `:stdlib` and `:elixir`
|
||||
itself. Defaults to `true`.
|
||||
to configure system applications, such as `:kernel` and `:stdlib`.
|
||||
Defaults to `true` if using the deprecated `config/releases.exs`,
|
||||
`false` otherwise.
|
||||
|
||||
* `:prune_runtime_sys_config_after_boot` - every time your system boots,
|
||||
the release will write a config file to your tmp directory. These
|
||||
configuration files are generally small. But if you are concerned with
|
||||
disk space or if you have other restrictions, you can ask the system to
|
||||
remove said config files after boot. The downside is that you will no
|
||||
longer be able to restart the system internally (neither via
|
||||
`System.restart/0` nor `bin/RELEASE_NAME restart`). If you need a restart,
|
||||
* `:prune_runtime_sys_config_after_boot` - if `:reboot_system_after_config`
|
||||
is set, every time your system boots, the release will write a config file
|
||||
to your tmp directory. These configuration files are generally small.
|
||||
But if you are concerned with disk space or if you have other restrictions,
|
||||
you can ask the system to remove said config files after boot. The downside
|
||||
is that you will no longer be able to restart the system internally (neither
|
||||
via `System.restart/0` nor `bin/RELEASE_NAME restart`). If you need a restart,
|
||||
you will have to terminate the Operating System process and start a new
|
||||
one. Defaults to `false`.
|
||||
|
||||
* `:start_distribution_during_config` - if `:reboot_system_after_config` is
|
||||
set, releases only start the Erlang VM distribution features after the config
|
||||
files are evaluated. You can set it to `true` if you need distribution during
|
||||
configuration. Defaults to `false` from Erlang/OTP 22+.
|
||||
|
||||
* `:config_providers` - a list of tuples with custom config providers.
|
||||
See `Config.Provider` for more information. Defaults to `[]`.
|
||||
|
||||
@@ -1152,6 +1153,7 @@ defmodule Mix.Tasks.Release do
|
||||
version_path = release.version_path
|
||||
File.rm_rf!(version_path)
|
||||
File.mkdir_p!(version_path)
|
||||
release = maybe_add_config_reader_provider(config, release, version_path)
|
||||
|
||||
consolidation_path =
|
||||
if config[:consolidate_protocols] do
|
||||
@@ -1165,7 +1167,6 @@ defmodule Mix.Tasks.Release do
|
||||
[]
|
||||
end
|
||||
|
||||
release = maybe_add_config_reader_provider(config, release, version_path)
|
||||
vm_args_path = Path.join(version_path, "vm.args")
|
||||
cookie_path = Path.join(release.path, "releases/COOKIE")
|
||||
start_erl_path = Path.join(release.path, "releases/start_erl.data")
|
||||
@@ -1188,20 +1189,27 @@ defmodule Mix.Tasks.Release do
|
||||
default_path = config[:config_path] |> Path.dirname() |> Path.join("runtime.exs")
|
||||
deprecated_path = config[:config_path] |> Path.dirname() |> Path.join("releases.exs")
|
||||
|
||||
path =
|
||||
{path, reboot?} =
|
||||
cond do
|
||||
path = opts[:runtime_config_path] ->
|
||||
path
|
||||
{path, false}
|
||||
|
||||
File.exists?(default_path) ->
|
||||
default_path
|
||||
if File.exists?(deprecated_path) do
|
||||
IO.warn(
|
||||
"both #{inspect(default_path)} and #{inspect(deprecated_path)} have been " <>
|
||||
"found, but only #{inspect(default_path)} will be used"
|
||||
)
|
||||
end
|
||||
|
||||
{default_path, false}
|
||||
|
||||
File.exists?(deprecated_path) ->
|
||||
# TODO: Warn from Elixir v1.13 onwards
|
||||
deprecated_path
|
||||
{deprecated_path, true}
|
||||
|
||||
true ->
|
||||
nil
|
||||
{nil, false}
|
||||
end
|
||||
|
||||
cond do
|
||||
@@ -1211,7 +1219,8 @@ defmodule Mix.Tasks.Release do
|
||||
File.cp!(path, Path.join(version_path, "runtime.exs"))
|
||||
init = {:system, "RELEASE_ROOT", "/releases/#{release.version}/runtime.exs"}
|
||||
opts = [path: init, env: Mix.env(), target: Mix.target(), imports: :disabled]
|
||||
update_in(release.config_providers, &[{Config.Reader, opts} | &1])
|
||||
release = update_in(release.config_providers, &[{Config.Reader, opts} | &1])
|
||||
update_in(release.options, &Keyword.put_new(&1, :reboot_system_after_config, reboot?))
|
||||
|
||||
release.config_providers == [] ->
|
||||
skipping("runtime configuration (#{default_path} not found)")
|
||||
@@ -1421,7 +1430,7 @@ defmodule Mix.Tasks.Release do
|
||||
defp release_mode(release, env_var) do
|
||||
# TODO: Remove otp_release check once we require Erlang/OTP 23+
|
||||
otp_gte_23? = :erlang.system_info(:otp_release) >= '23'
|
||||
reboot? = Keyword.get(release.options, :reboot_system_after_config, true)
|
||||
reboot? = Keyword.get(release.options, :reboot_system_after_config, false)
|
||||
|
||||
if otp_gte_23? and reboot? and release.config_providers != [] do
|
||||
"-elixir -config_provider_reboot_mode #{env_var}"
|
||||
|
||||
@@ -542,12 +542,12 @@ defmodule Mix.ReleaseTest do
|
||||
end
|
||||
|
||||
test "writes the given sys_config with config providers" do
|
||||
release = release(config_providers: @providers)
|
||||
release = release(config_providers: @providers, reboot_system_after_config: true)
|
||||
assert make_sys_config(release, [kernel: [key: :value]], "/foo/bar/bat") == :ok
|
||||
assert File.read!(@sys_config) =~ "%% RUNTIME_CONFIG=true"
|
||||
{:ok, [config]} = :file.consult(@sys_config)
|
||||
assert %Config.Provider{} = provider = config[:elixir][:config_provider_init]
|
||||
refute provider.prune_after_boot
|
||||
refute provider.prune_runtime_sys_config_after_boot
|
||||
assert provider.extra_config == [kernel: [start_distribution: true]]
|
||||
assert config[:kernel] == [key: :value, start_distribution: false]
|
||||
end
|
||||
@@ -556,6 +556,7 @@ defmodule Mix.ReleaseTest do
|
||||
release =
|
||||
release(
|
||||
config_providers: @providers,
|
||||
reboot_system_after_config: true,
|
||||
start_distribution_during_config: true,
|
||||
prune_runtime_sys_config_after_boot: true
|
||||
)
|
||||
@@ -564,8 +565,8 @@ defmodule Mix.ReleaseTest do
|
||||
assert File.read!(@sys_config) =~ "%% RUNTIME_CONFIG=true"
|
||||
{:ok, [config]} = :file.consult(@sys_config)
|
||||
assert %Config.Provider{} = provider = config[:elixir][:config_provider_init]
|
||||
assert provider.reboot_after_config
|
||||
assert provider.prune_after_boot
|
||||
assert provider.reboot_system_after_config
|
||||
assert provider.prune_runtime_sys_config_after_boot
|
||||
assert provider.extra_config == []
|
||||
assert config[:kernel] == [key: :value]
|
||||
end
|
||||
@@ -576,7 +577,7 @@ defmodule Mix.ReleaseTest do
|
||||
assert File.read!(@sys_config) =~ "%% RUNTIME_CONFIG=false"
|
||||
{:ok, [config]} = :file.consult(@sys_config)
|
||||
assert %Config.Provider{} = provider = config[:elixir][:config_provider_init]
|
||||
refute provider.reboot_after_config
|
||||
refute provider.reboot_system_after_config
|
||||
assert provider.extra_config == []
|
||||
assert config[:kernel] == [key: :value]
|
||||
end
|
||||
|
||||
@@ -354,9 +354,9 @@ defmodule Mix.Tasks.ReleaseTest do
|
||||
end)
|
||||
end
|
||||
|
||||
test "assembles a bootable release with runtime configuration" do
|
||||
test "assembles a rebootable release with runtime configuration" do
|
||||
in_fixture("release_test", fn ->
|
||||
config = [releases: [runtime_config: []]]
|
||||
config = [releases: [runtime_config: [reboot_system_after_config: true]]]
|
||||
|
||||
Mix.Project.in_project(:release_test, ".", config, fn _ ->
|
||||
File.write!("config/runtime.exs", """
|
||||
|
||||
Reference in New Issue
Block a user