summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJean-Sébastien Pédron <jean-sebastien@rabbitmq.com>2019-12-19 14:02:24 +0100
committerGitHub <noreply@github.com>2019-12-19 14:02:24 +0100
commit87951c47bb335104be044a02adc9fb045c5a0b72 (patch)
tree5e83c9858f95f36684cbbdd83af8b6eee9432c3f /src
parent6ca895a30105c0e0553e56cead0d72cb3148a15c (diff)
parent027f68648648dbd25e8a329de5eaec574af4c529 (diff)
downloadrabbitmq-server-git-87951c47bb335104be044a02adc9fb045c5a0b72.tar.gz
Merge pull request #2187 from rabbitmq/lrb-apply-config-earlier
Run rabbit_prelaunch_conf:setup/1 earlier in startup
Diffstat (limited to 'src')
-rw-r--r--src/rabbit.erl13
-rw-r--r--src/rabbit_prelaunch_conf.erl534
-rw-r--r--src/rabbit_prelaunch_enabled_plugins_file.erl46
3 files changed, 54 insertions, 539 deletions
diff --git a/src/rabbit.erl b/src/rabbit.erl
index 41d4d89e17..992afadbf8 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -331,24 +331,27 @@ run_prelaunch_second_phase() ->
ok
end,
- %% 1. Feature flags registry.
+ %% 1. Enabled plugins file.
+ ok = rabbit_prelaunch_enabled_plugins_file:setup(Context),
+
+ %% 2. Feature flags registry.
ok = rabbit_prelaunch_feature_flags:setup(Context),
- %% 2. Configuration check + loading.
+ %% 3. Configuration check + loading.
ok = rabbit_prelaunch_conf:setup(Context),
- %% 3. Logging.
+ %% 4. Logging.
ok = rabbit_prelaunch_logging:setup(Context),
case IsInitialPass of
true ->
- %% 4. HiPE compilation.
+ %% 5. HiPE compilation.
ok = rabbit_prelaunch_hipe:setup(Context);
false ->
ok
end,
- %% 5. Clustering.
+ %% 6. Clustering.
ok = rabbit_prelaunch_cluster:setup(Context),
%% Start Mnesia now that everything is ready.
diff --git a/src/rabbit_prelaunch_conf.erl b/src/rabbit_prelaunch_conf.erl
deleted file mode 100644
index 23d0f68f82..0000000000
--- a/src/rabbit_prelaunch_conf.erl
+++ /dev/null
@@ -1,534 +0,0 @@
--module(rabbit_prelaunch_conf).
-
--include_lib("kernel/include/file.hrl").
--include_lib("stdlib/include/zip.hrl").
-
--include_lib("rabbit_common/include/rabbit.hrl").
-
--export([setup/1,
- get_config_state/0,
- generate_config_from_cuttlefish_files/3,
- decrypt_config/1]).
-
--ifdef(TEST).
--export([decrypt_config/2]).
--endif.
-
-setup(Context) ->
- rabbit_log_prelaunch:debug(""),
- rabbit_log_prelaunch:debug("== Configuration =="),
-
- %% TODO: Check if directories/files are inside Mnesia dir.
-
- %% TODO: Support glob patterns & directories in RABBITMQ_CONFIG_FILE.
- %% TODO: Always try parsing of both erlang and cuttlefish formats.
-
- update_enabled_plugins_file(Context),
-
- set_default_config(),
-
- AdvancedConfigFile = find_actual_advanced_config_file(Context),
- State = case find_actual_main_config_file(Context) of
- {MainConfigFile, erlang} ->
- Config = load_erlang_term_based_config_file(
- MainConfigFile),
- Apps = [App || {App, _} <- Config],
- decrypt_config(Apps),
- #{config_type => erlang,
- config_files => [MainConfigFile],
- config_advanced_file => undefined};
- {MainConfigFile, cuttlefish} ->
- ConfigFiles = [MainConfigFile],
- Config = load_cuttlefish_config_file(Context,
- ConfigFiles,
- AdvancedConfigFile),
- Apps = [App || {App, _} <- Config],
- decrypt_config(Apps),
- #{config_type => cuttlefish,
- config_files => ConfigFiles,
- config_advanced_file => AdvancedConfigFile};
- undefined when AdvancedConfigFile =/= undefined ->
- rabbit_log_prelaunch:warning(
- "Using RABBITMQ_ADVANCED_CONFIG_FILE: ~s",
- [AdvancedConfigFile]),
- Config = load_erlang_term_based_config_file(
- AdvancedConfigFile),
- Apps = [App || {App, _} <- Config],
- decrypt_config(Apps),
- #{config_type => erlang,
- config_files => [AdvancedConfigFile],
- config_advanced_file => AdvancedConfigFile};
- undefined ->
- #{config_type => undefined,
- config_files => [],
- config_advanced_file => undefined}
- end,
- override_with_hard_coded_critical_config(),
- rabbit_log_prelaunch:debug(
- "Saving config state to application env: ~p", [State]),
- store_config_state(State).
-
-store_config_state(ConfigState) ->
- persistent_term:put({rabbitmq_prelaunch, config_state}, ConfigState).
-
-get_config_state() ->
- persistent_term:get({rabbitmq_prelaunch, config_state}, undefined).
-
-%% -------------------------------------------------------------------
-%% `enabled_plugins` file content initialization.
-%% -------------------------------------------------------------------
-
-update_enabled_plugins_file(Context) ->
- %% We only do this on startup, not when the configuration is
- %% reloaded.
- case get_config_state() of
- undefined -> update_enabled_plugins_file1(Context);
- _ -> ok
- end.
-
-update_enabled_plugins_file1(#{enabled_plugins := undefined}) ->
- ok;
-update_enabled_plugins_file1(#{enabled_plugins := all,
- plugins_path := Path} = Context) ->
- List = [P#plugin.name || P <- rabbit_plugins:list(Path)],
- do_update_enabled_plugins_file(Context, List);
-update_enabled_plugins_file1(#{enabled_plugins := List} = Context) ->
- do_update_enabled_plugins_file(Context, List).
-
-do_update_enabled_plugins_file(#{enabled_plugins_file := File}, List) ->
- SortedList = lists:usort(List),
- case SortedList of
- [] ->
- rabbit_log_prelaunch:debug("Marking all plugins as disabled");
- _ ->
- rabbit_log_prelaunch:debug(
- "Marking the following plugins as enabled:"),
- [rabbit_log_prelaunch:debug(" - ~s", [P]) || P <- SortedList]
- end,
- Content = io_lib:format("~p.~n", [SortedList]),
- case file:write_file(File, Content) of
- ok ->
- ok;
- {error, Reason} ->
- rabbit_log_prelaunch:error(
- "Failed to update enabled plugins file \"~ts\" "
- "from $RABBITMQ_ENABLED_PLUGINS: ~ts",
- [File, file:format_error(Reason)]),
- throw({error, failed_to_update_enabled_plugins_file})
- end.
-
-%% -------------------------------------------------------------------
-%% Configuration loading.
-%% -------------------------------------------------------------------
-
-set_default_config() ->
- rabbit_log_prelaunch:debug("Setting default config"),
- Config = [
- {ra,
- [
- %% Use a larger segments size for queues.
- {segment_max_entries, 32768},
- {wal_max_size_bytes, 536870912} %% 5 * 2 ^ 20
- ]},
- {sysmon_handler,
- [{process_limit, 100},
- {port_limit, 100},
- {gc_ms_limit, 0},
- {schedule_ms_limit, 0},
- {heap_word_limit, 0},
- {busy_port, false},
- {busy_dist_port, true}]}
- ],
- apply_erlang_term_based_config(Config).
-
-find_actual_main_config_file(#{main_config_file := File}) ->
- case filelib:is_regular(File) of
- true ->
- Format = case filename:extension(File) of
- ".conf" -> cuttlefish;
- ".config" -> erlang;
- _ -> determine_config_format(File)
- end,
- {File, Format};
- false ->
- OldFormatFile = File ++ ".config",
- NewFormatFile = File ++ ".conf",
- case filelib:is_regular(OldFormatFile) of
- true ->
- case filelib:is_regular(NewFormatFile) of
- true ->
- rabbit_log_prelaunch:warning(
- "Both old (.config) and new (.conf) format config "
- "files exist."),
- rabbit_log_prelaunch:warning(
- "Using the old format config file: ~s",
- [OldFormatFile]),
- rabbit_log_prelaunch:warning(
- "Please update your config files to the new format "
- "and remove the old file."),
- ok;
- false ->
- ok
- end,
- {OldFormatFile, erlang};
- false ->
- case filelib:is_regular(NewFormatFile) of
- true -> {NewFormatFile, cuttlefish};
- false -> undefined
- end
- end
- end.
-
-find_actual_advanced_config_file(#{advanced_config_file := File}) ->
- case filelib:is_regular(File) of
- true -> File;
- false -> undefined
- end.
-
-determine_config_format(File) ->
- case filelib:file_size(File) of
- 0 ->
- cuttlefish;
- _ ->
- case file:consult(File) of
- {ok, _} -> erlang;
- _ -> cuttlefish
- end
- end.
-
-load_erlang_term_based_config_file(ConfigFile) ->
- rabbit_log_prelaunch:debug(
- "Loading configuration file \"~ts\" (Erlang terms based)", [ConfigFile]),
- case file:consult(ConfigFile) of
- {ok, [Config]} when is_list(Config) ->
- apply_erlang_term_based_config(Config),
- Config;
- {ok, OtherTerms} ->
- rabbit_log_prelaunch:error(
- "Failed to load configuration file \"~ts\", "
- "incorrect format: ~p",
- [ConfigFile, OtherTerms]),
- throw({error, failed_to_parse_configuration_file});
- {error, Reason} ->
- rabbit_log_prelaunch:error(
- "Failed to load configuration file \"~ts\": ~ts",
- [ConfigFile, file:format_error(Reason)]),
- throw({error, failed_to_read_configuration_file})
- end.
-
-load_cuttlefish_config_file(Context,
- ConfigFiles,
- AdvancedConfigFile) ->
- Config = generate_config_from_cuttlefish_files(
- Context, ConfigFiles, AdvancedConfigFile),
- apply_erlang_term_based_config(Config),
- Config.
-
-generate_config_from_cuttlefish_files(Context,
- ConfigFiles,
- AdvancedConfigFile) ->
- %% Load schemas.
- SchemaFiles = find_cuttlefish_schemas(Context),
- case SchemaFiles of
- [] ->
- rabbit_log_prelaunch:error(
- "No configuration schema found~n", []),
- throw({error, no_configuration_schema_found});
- _ ->
- rabbit_log_prelaunch:debug(
- "Configuration schemas found:~n", []),
- [rabbit_log_prelaunch:debug(" - ~ts", [SchemaFile])
- || SchemaFile <- SchemaFiles],
- ok
- end,
- Schema = cuttlefish_schema:files(SchemaFiles),
-
- %% Load configuration.
- rabbit_log_prelaunch:debug(
- "Loading configuration files (Cuttlefish based):"),
- [rabbit_log_prelaunch:debug(
- " - ~ts", [ConfigFile]) || ConfigFile <- ConfigFiles],
- case cuttlefish_conf:files(ConfigFiles) of
- {errorlist, Errors} ->
- rabbit_log_prelaunch:error("Error generating configuration:", []),
- [rabbit_log_prelaunch:error(
- " - ~ts",
- [cuttlefish_error:xlate(Error)])
- || Error <- Errors],
- throw({error, failed_to_generate_configuration_file});
- Config0 ->
- %% Finalize configuration, based on the schema.
- Config = case cuttlefish_generator:map(Schema, Config0) of
- {error, Phase, {errorlist, Errors}} ->
- %% TODO
- rabbit_log_prelaunch:error(
- "Error generating configuration in phase ~ts:",
- [Phase]),
- [rabbit_log_prelaunch:error(
- " - ~ts",
- [cuttlefish_error:xlate(Error)])
- || Error <- Errors],
- throw(
- {error, failed_to_generate_configuration_file});
- ValidConfig ->
- proplists:delete(vm_args, ValidConfig)
- end,
-
- %% Apply advanced configuration overrides, if any.
- override_with_advanced_config(Config, AdvancedConfigFile)
- end.
-
-find_cuttlefish_schemas(Context) ->
- Apps = list_apps(Context),
- rabbit_log_prelaunch:debug(
- "Looking up configuration schemas in the following applications:"),
- find_cuttlefish_schemas(Apps, []).
-
-find_cuttlefish_schemas([App | Rest], AllSchemas) ->
- Schemas = list_schemas_in_app(App),
- find_cuttlefish_schemas(Rest, AllSchemas ++ Schemas);
-find_cuttlefish_schemas([], AllSchemas) ->
- lists:sort(fun(A,B) -> A < B end, AllSchemas).
-
-list_apps(#{os_type := {win32, _}, plugins_path := PluginsPath}) ->
- PluginsDirs = string:lexemes(PluginsPath, ";"),
- list_apps1(PluginsDirs, []);
-list_apps(#{plugins_path := PluginsPath}) ->
- PluginsDirs = string:lexemes(PluginsPath, ":"),
- list_apps1(PluginsDirs, []).
-
-
-list_apps1([Dir | Rest], Apps) ->
- case file:list_dir(Dir) of
- {ok, Filenames} ->
- NewApps = [list_to_atom(
- hd(
- string:split(filename:basename(F, ".ex"), "-")))
- || F <- Filenames],
- Apps1 = lists:umerge(Apps, lists:sort(NewApps)),
- list_apps1(Rest, Apps1);
- {error, Reason} ->
- rabbit_log_prelaunch:debug(
- "Failed to list directory \"~ts\" content: ~ts",
- [Dir, file:format_error(Reason)]),
- list_apps1(Rest, Apps)
- end;
-list_apps1([], AppInfos) ->
- AppInfos.
-
-list_schemas_in_app(App) ->
- {Loaded, Unload} = case application:load(App) of
- ok -> {true, true};
- {error, {already_loaded, _}} -> {true, false};
- {error, _} -> {false, false}
- end,
- List = case Loaded of
- true ->
- case code:priv_dir(App) of
- {error, bad_name} ->
- rabbit_log_prelaunch:debug(
- " [ ] ~s (no readable priv dir)", [App]),
- [];
- PrivDir ->
- SchemaDir = filename:join([PrivDir, "schema"]),
- do_list_schemas_in_app(App, SchemaDir)
- end;
- false ->
- rabbit_log_prelaunch:debug(
- " [ ] ~s (failed to load application)", [App]),
- []
- end,
- case Unload of
- true -> application:unload(App);
- false -> ok
- end,
- List.
-
-do_list_schemas_in_app(App, SchemaDir) ->
- case erl_prim_loader:list_dir(SchemaDir) of
- {ok, Files} ->
- rabbit_log_prelaunch:debug(" [x] ~s", [App]),
- [filename:join(SchemaDir, File)
- || [C | _] = File <- Files,
- C =/= $.];
- error ->
- rabbit_log_prelaunch:debug(
- " [ ] ~s (no readable schema dir)", [App]),
- []
- end.
-
-override_with_advanced_config(Config, undefined) ->
- Config;
-override_with_advanced_config(Config, AdvancedConfigFile) ->
- rabbit_log_prelaunch:debug(
- "Override with advanced configuration file \"~ts\"",
- [AdvancedConfigFile]),
- case file:consult(AdvancedConfigFile) of
- {ok, [AdvancedConfig]} ->
- cuttlefish_advanced:overlay(Config, AdvancedConfig);
- {ok, OtherTerms} ->
- rabbit_log_prelaunch:error(
- "Failed to load advanced configuration file \"~ts\", "
- "incorrect format: ~p",
- [AdvancedConfigFile, OtherTerms]),
- throw({error, failed_to_parse_advanced_configuration_file});
- {error, Reason} ->
- rabbit_log_prelaunch:error(
- "Failed to load advanced configuration file \"~ts\": ~ts",
- [AdvancedConfigFile, file:format_error(Reason)]),
- throw({error, failed_to_read_advanced_configuration_file})
- end.
-
-override_with_hard_coded_critical_config() ->
- rabbit_log_prelaunch:debug("Override with hard-coded critical config"),
- Config = [
- {ra,
- %% Make Ra use a custom logger that dispatches to lager
- %% instead of the default OTP logger
- [{logger_module, rabbit_log_ra_shim}]}
- ],
- apply_erlang_term_based_config(Config).
-
-apply_erlang_term_based_config([{_, []} | Rest]) ->
- apply_erlang_term_based_config(Rest);
-apply_erlang_term_based_config([{App, Vars} | Rest]) ->
- rabbit_log_prelaunch:debug(" Applying configuration for '~s':", [App]),
- apply_app_env_vars(App, Vars),
- apply_erlang_term_based_config(Rest);
-apply_erlang_term_based_config([]) ->
- ok.
-
-apply_app_env_vars(App, [{Var, Value} | Rest]) ->
- rabbit_log_prelaunch:debug(
- " - ~s = ~p",
- [Var, Value]),
- ok = application:set_env(App, Var, Value, [{persistent, true}]),
- apply_app_env_vars(App, Rest);
-apply_app_env_vars(_, []) ->
- ok.
-
-%% -------------------------------------------------------------------
-%% Config decryption.
-%% -------------------------------------------------------------------
-
-decrypt_config(Apps) ->
- rabbit_log_prelaunch:debug("Decoding encrypted config values (if any)"),
- ConfigEntryDecoder = application:get_env(rabbit, config_entry_decoder, []),
- decrypt_config(Apps, ConfigEntryDecoder).
-
-decrypt_config([], _) ->
- ok;
-decrypt_config([App | Apps], Algo) ->
- Algo1 = decrypt_app(App, application:get_all_env(App), Algo),
- decrypt_config(Apps, Algo1).
-
-decrypt_app(_, [], Algo) ->
- Algo;
-decrypt_app(App, [{Key, Value} | Tail], Algo) ->
- Algo2 = try
- case decrypt(Value, Algo) of
- {Value, Algo1} ->
- Algo1;
- {NewValue, Algo1} ->
- rabbit_log_prelaunch:debug(
- "Value of `~s` decrypted", [Key]),
- ok = application:set_env(App, Key, NewValue,
- [{persistent, true}]),
- Algo1
- end
- catch
- throw:{bad_config_entry_decoder, _} = Error ->
- throw(Error);
- _:Msg ->
- throw({config_decryption_error, {key, Key}, Msg})
- end,
- decrypt_app(App, Tail, Algo2).
-
-decrypt({encrypted, EncValue},
- {Cipher, Hash, Iterations, PassPhrase} = Algo) ->
- {rabbit_pbe:decrypt_term(Cipher, Hash, Iterations, PassPhrase, EncValue),
- Algo};
-decrypt({encrypted, _} = Value,
- ConfigEntryDecoder)
- when is_list(ConfigEntryDecoder) ->
- Algo = config_entry_decoder_to_algo(ConfigEntryDecoder),
- decrypt(Value, Algo);
-decrypt(List, Algo) when is_list(List) ->
- decrypt_list(List, Algo, []);
-decrypt(Value, Algo) ->
- {Value, Algo}.
-
-%% We make no distinction between strings and other lists.
-%% When we receive a string, we loop through each element
-%% and ultimately return the string unmodified, as intended.
-decrypt_list([], Algo, Acc) ->
- {lists:reverse(Acc), Algo};
-decrypt_list([{Key, Value} | Tail], Algo, Acc)
- when Key =/= encrypted ->
- {Value1, Algo1} = decrypt(Value, Algo),
- decrypt_list(Tail, Algo1, [{Key, Value1} | Acc]);
-decrypt_list([Value | Tail], Algo, Acc) ->
- {Value1, Algo1} = decrypt(Value, Algo),
- decrypt_list(Tail, Algo1, [Value1 | Acc]).
-
-config_entry_decoder_to_algo(ConfigEntryDecoder) ->
- case get_passphrase(ConfigEntryDecoder) of
- undefined ->
- throw({bad_config_entry_decoder, missing_passphrase});
- PassPhrase ->
- {
- proplists:get_value(
- cipher, ConfigEntryDecoder, rabbit_pbe:default_cipher()),
- proplists:get_value(
- hash, ConfigEntryDecoder, rabbit_pbe:default_hash()),
- proplists:get_value(
- iterations, ConfigEntryDecoder, rabbit_pbe:default_iterations()),
- PassPhrase
- }
- end.
-
-get_passphrase(ConfigEntryDecoder) ->
- rabbit_log_prelaunch:debug("Getting encrypted config passphrase"),
- case proplists:get_value(passphrase, ConfigEntryDecoder) of
- prompt ->
- IoDevice = get_input_iodevice(),
- io:setopts(IoDevice, [{echo, false}]),
- PP = lists:droplast(io:get_line(IoDevice,
- "\nPlease enter the passphrase to unlock encrypted "
- "configuration entries.\n\nPassphrase: ")),
- io:setopts(IoDevice, [{echo, true}]),
- io:format(IoDevice, "~n", []),
- PP;
- {file, Filename} ->
- {ok, File} = file:read_file(Filename),
- [PP|_] = binary:split(File, [<<"\r\n">>, <<"\n">>]),
- PP;
- PP ->
- PP
- end.
-
-%% This function retrieves the correct IoDevice for requesting
-%% input. The problem with using the default IoDevice is that
-%% the Erlang shell prevents us from getting the input.
-%%
-%% Instead we therefore look for the io process used by the
-%% shell and if it can't be found (because the shell is not
-%% started e.g with -noshell) we use the 'user' process.
-%%
-%% This function will not work when either -oldshell or -noinput
-%% options are passed to erl.
-get_input_iodevice() ->
- case whereis(user) of
- undefined ->
- user;
- User ->
- case group:interfaces(User) of
- [] ->
- user;
- [{user_drv, Drv}] ->
- case user_drv:interfaces(Drv) of
- [] -> user;
- [{current_group, IoDevice}] -> IoDevice
- end
- end
- end.
diff --git a/src/rabbit_prelaunch_enabled_plugins_file.erl b/src/rabbit_prelaunch_enabled_plugins_file.erl
new file mode 100644
index 0000000000..e201b52403
--- /dev/null
+++ b/src/rabbit_prelaunch_enabled_plugins_file.erl
@@ -0,0 +1,46 @@
+-module(rabbit_prelaunch_enabled_plugins_file).
+
+-include_lib("rabbit_common/include/rabbit.hrl").
+
+-export([setup/1]).
+
+setup(Context) ->
+ rabbit_log_prelaunch:debug(""),
+ rabbit_log_prelaunch:debug("== Enabled plugins file =="),
+ update_enabled_plugins_file(Context).
+
+%% -------------------------------------------------------------------
+%% `enabled_plugins` file content initialization.
+%% -------------------------------------------------------------------
+
+update_enabled_plugins_file(#{enabled_plugins := undefined}) ->
+ ok;
+update_enabled_plugins_file(#{enabled_plugins := all,
+ plugins_path := Path} = Context) ->
+ List = [P#plugin.name || P <- rabbit_plugins:list(Path)],
+ do_update_enabled_plugins_file(Context, List);
+update_enabled_plugins_file(#{enabled_plugins := List} = Context) ->
+ do_update_enabled_plugins_file(Context, List).
+
+do_update_enabled_plugins_file(#{enabled_plugins_file := File}, List) ->
+ SortedList = lists:usort(List),
+ case SortedList of
+ [] ->
+ rabbit_log_prelaunch:debug("Marking all plugins as disabled");
+ _ ->
+ rabbit_log_prelaunch:debug(
+ "Marking the following plugins as enabled:"),
+ [rabbit_log_prelaunch:debug(" - ~s", [P]) || P <- SortedList]
+ end,
+ Content = io_lib:format("~p.~n", [SortedList]),
+ case file:write_file(File, Content) of
+ ok ->
+ rabbit_log_prelaunch:debug("Wrote plugins file: ~ts", [File]),
+ ok;
+ {error, Reason} ->
+ rabbit_log_prelaunch:error(
+ "Failed to update enabled plugins file \"~ts\" "
+ "from $RABBITMQ_ENABLED_PLUGINS: ~ts",
+ [File, file:format_error(Reason)]),
+ throw({error, failed_to_update_enabled_plugins_file})
+ end.