Skip to content

Commit 645a076

Browse files
Merge pull request #3311 from rabbitmq/rabbitmq-server-3249
Definition import: allow for arbitrary (and pluggable) sources
2 parents 4e91a96 + 5a580c9 commit 645a076

File tree

7 files changed

+596
-61
lines changed

7 files changed

+596
-61
lines changed

deps/rabbit/priv/schema/rabbit.schema

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,139 @@ end}.
121121
%% Definition import
122122
%%
123123

124-
%% Load definitions from a JSON file or directory of files. See
124+
%% Original key for definition loading from a JSON file or directory of files. See
125125
%% https://www.rabbitmq.com/management.html#load-definitions
126-
%%
127-
%% {load_definitions, "/path/to/schema.json"},
128-
%% {load_definitions, "/path/to/schemas"},
129126
{mapping, "load_definitions", "rabbit.load_definitions",
130127
[{datatype, string},
131128
{validators, ["file_accessible"]}]}.
132129

130+
%% Newer syntax for definition loading from a JSON file or directory of files. See
131+
%% https://www.rabbitmq.com/management.html#load-definitions
132+
{mapping, "definitions.local.path", "rabbit.definitions.local_path",
133+
[{datatype, string},
134+
{validators, ["file_accessible"]}]}.
135+
136+
%% Extensive mechanism for loading definitions from a remote source
137+
{mapping, "definitions.import_backend", "rabbit.definitions.import_backend", [
138+
{datatype, atom}
139+
]}.
140+
141+
{translation, "rabbit.definitions.import_backend",
142+
fun(Conf) ->
143+
case cuttlefish:conf_get("definitions.import_backend", Conf, rabbit_definitions_import_local_filesystem) of
144+
%% short aliases for known backends
145+
local_filesystem -> rabbit_definitions_import_local_filesystem;
146+
local -> rabbit_definitions_import_local_filesystem;
147+
https -> rabbit_definitions_import_https;
148+
http -> rabbit_definitions_import_https;
149+
%% accept both rabbitmq_ and rabbit_ (typical core module prefix)
150+
rabbitmq_definitions_import_local_filesystem -> rabbit_definitions_import_local_filesystem;
151+
rabbitmq_definitions_import_local_filesystem -> rabbit_definitions_import_https;
152+
%% any other value is used as is
153+
Module -> Module
154+
end
155+
end}.
156+
157+
%% Load definitions from a remote URL over HTTPS. See
158+
%% https://www.rabbitmq.com/management.html#load-definitions
159+
{mapping, "definitions.https.url", "rabbit.definitions.url",
160+
[{datatype, string}]}.
161+
162+
%% Client-side TLS settings used by e.g. HTTPS definition loading mechanism.
163+
%% These can be reused by other clients.
164+
165+
{mapping, "definitions.tls.verify", "rabbit.definitions.ssl_options.verify", [
166+
{datatype, {enum, [verify_peer, verify_none]}}]}.
167+
168+
{mapping, "definitions.tls.fail_if_no_peer_cert", "rabbit.definitions.ssl_options.fail_if_no_peer_cert", [
169+
{datatype, {enum, [true, false]}}]}.
170+
171+
{mapping, "definitions.tls.cacertfile", "rabbit.definitions.ssl_options.cacertfile",
172+
[{datatype, string}, {validators, ["file_accessible"]}]}.
173+
174+
{mapping, "definitions.tls.certfile", "rabbit.definitions.ssl_options.certfile",
175+
[{datatype, string}, {validators, ["file_accessible"]}]}.
176+
177+
{mapping, "definitions.tls.cacerts.$name", "rabbit.definitions.ssl_options.cacerts",
178+
[{datatype, string}]}.
179+
180+
{translation, "rabbit.definitions.ssl_options.cacerts",
181+
fun(Conf) ->
182+
Settings = cuttlefish_variable:filter_by_prefix("definitions.tls.cacerts", Conf),
183+
[ list_to_binary(V) || {_, V} <- Settings ]
184+
end}.
185+
186+
{mapping, "definitions.tls.cert", "rabbit.definitions.ssl_options.cert",
187+
[{datatype, string}]}.
188+
189+
{translation, "rabbit.definitions.ssl_options.cert",
190+
fun(Conf) ->
191+
list_to_binary(cuttlefish:conf_get("definitions.tls.cert", Conf))
192+
end}.
193+
194+
{mapping, "definitions.tls.reuse_session", "rabbit.definitions.ssl_options.reuse_session",
195+
[{datatype, {enum, [true, false]}}]}.
196+
197+
{mapping, "definitions.tls.crl_check", "rabbit.definitions.ssl_options.crl_check",
198+
[{datatype, [{enum, [true, false, peer, best_effort]}]}]}.
199+
200+
{mapping, "definitions.tls.depth", "rabbit.definitions.ssl_options.depth",
201+
[{datatype, integer}, {validators, ["byte"]}]}.
202+
203+
{mapping, "definitions.tls.dh", "rabbit.definitions.ssl_options.dh",
204+
[{datatype, string}]}.
205+
206+
{translation, "rabbit.definitions.ssl_options.dh",
207+
fun(Conf) ->
208+
list_to_binary(cuttlefish:conf_get("definitions.tls.dh", Conf))
209+
end}.
210+
211+
{translation, "rabbit.definitions.ssl_options.key",
212+
fun(Conf) ->
213+
case cuttlefish_variable:filter_by_prefix("definitions.tls.key", Conf) of
214+
[{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
215+
_ -> cuttlefish:unset()
216+
end
217+
end}.
218+
219+
{mapping, "definitions.tls.keyfile", "rabbit.definitions.ssl_options.keyfile",
220+
[{datatype, string}, {validators, ["file_accessible"]}]}.
221+
222+
{mapping, "definitions.tls.log_alert", "rabbit.definitions.ssl_options.log_alert",
223+
[{datatype, {enum, [true, false]}}]}.
224+
225+
{mapping, "definitions.tls.password", "rabbit.definitions.ssl_options.password",
226+
[{datatype, string}]}.
227+
228+
{mapping, "definitions.tls.secure_renegotiate", "rabbit.definitions.ssl_options.secure_renegotiate",
229+
[{datatype, {enum, [true, false]}}]}.
230+
231+
{mapping, "definitions.tls.reuse_sessions", "rabbit.definitions.ssl_options.reuse_sessions",
232+
[{datatype, {enum, [true, false]}}]}.
233+
234+
{mapping, "definitions.tls.versions.$version", "rabbit.definitions.ssl_options.versions",
235+
[{datatype, atom}]}.
236+
237+
{translation, "rabbit.definitions.ssl_options.versions",
238+
fun(Conf) ->
239+
Settings = cuttlefish_variable:filter_by_prefix("definitions.tls.versions", Conf),
240+
[V || {_, V} <- Settings]
241+
end}.
242+
243+
{mapping, "definitions.tls.ciphers.$cipher", "rabbit.definitions.ssl_options.ciphers",
244+
[{datatype, string}]}.
245+
246+
{translation, "rabbit.definitions.ssl_options.ciphers",
247+
fun(Conf) ->
248+
Settings = cuttlefish_variable:filter_by_prefix("definitions.tls.ciphers", Conf),
249+
lists:reverse([V || {_, V} <- Settings])
250+
end}.
251+
252+
{mapping, "definitions.tls.log_level", "rabbit.definitions.ssl_options.log_level",
253+
[{datatype, {enum, [emergency, alert, critical, error, warning, notice, info, debug]}}]}.
254+
133255
%%
134-
%% Security / AAA
135-
%% ==============
256+
%% Seed User, Authentication, Access Control
136257
%%
137258

138259
%% The default "guest" user is only permitted to access the server
@@ -254,13 +375,16 @@ end}.
254375
fun(Conf) ->
255376
case cuttlefish_variable:filter_by_prefix("ssl_options.key", Conf) of
256377
[{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
257-
_ -> undefined
378+
_ -> cuttlefish:unset()
258379
end
259380
end}.
260381

261382
{mapping, "ssl_options.keyfile", "rabbit.ssl_options.keyfile",
262383
[{datatype, string}, {validators, ["file_accessible"]}]}.
263384

385+
{mapping, "ssl_options.log_level", "rabbit.ssl_options.log_level",
386+
[{datatype, {enum, [emergency, alert, critical, error, warning, notice, info, debug]}}]}.
387+
264388
{mapping, "ssl_options.log_alert", "rabbit.ssl_options.log_alert",
265389
[{datatype, {enum, [true, false]}}]}.
266390

deps/rabbit/src/rabbit_definitions.erl

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@
1010

1111
-export([boot/0]).
1212
%% automatic import on boot
13-
-export([maybe_load_definitions/0, maybe_load_definitions/2, maybe_load_definitions_from/2,
14-
has_configured_definitions_to_load/0]).
13+
-export([
14+
maybe_load_definitions/0,
15+
maybe_load_definitions/2,
16+
maybe_load_definitions_from/2,
17+
18+
has_configured_definitions_to_load/0
19+
]).
1520
%% import
1621
-export([import_raw/1, import_raw/2, import_parsed/1, import_parsed/2,
1722
apply_defs/2, apply_defs/3, apply_defs/4, apply_defs/5]).
@@ -25,7 +30,7 @@
2530
]).
2631
-export([decode/1, decode/2, args/1]).
2732

28-
-import(rabbit_misc, [pget/2]).
33+
-import(rabbit_misc, [pget/2, pget/3]).
2934
-import(rabbit_data_coercion, [to_binary/1]).
3035

3136
%%
@@ -59,9 +64,10 @@ boot() ->
5964
rabbit_sup:start_supervisor_child(definition_import_pool_sup, worker_pool_sup, [PoolSize, ?IMPORT_WORK_POOL]).
6065

6166
maybe_load_definitions() ->
62-
%% Note that management.load_definitions is handled in the plugin for backwards compatibility.
63-
%% This executes the "core" version of load_definitions.
64-
maybe_load_definitions(rabbit, load_definitions).
67+
%% Classic source: local file or data directory
68+
maybe_load_definitions_from_local_filesystem(rabbit, load_definitions),
69+
%% Extensible sources
70+
maybe_load_definitions_from_pluggable_source(rabbit, definitions).
6571

6672
-spec import_raw(Body :: binary() | iolist()) -> ok | {error, term()}.
6773
import_raw(Body) ->
@@ -126,63 +132,76 @@ all_definitions() ->
126132
exchanges => Xs
127133
}.
128134

135+
-spec has_configured_definitions_to_load() -> boolean().
136+
has_configured_definitions_to_load() ->
137+
has_configured_definitions_to_load_via_classic_option() or has_configured_definitions_to_load_via_modern_option().
138+
139+
%% Retained for backwards compatibility, implicitly assumes the local filesystem source
140+
maybe_load_definitions(App, Key) ->
141+
maybe_load_definitions_from_local_filesystem(App, Key).
142+
143+
maybe_load_definitions_from(IsDir, Path) ->
144+
rabbit_definitions_import_local_filesystem:load(IsDir, Path).
145+
129146
%%
130147
%% Implementation
131148
%%
132149

133-
-spec has_configured_definitions_to_load() -> boolean().
134-
has_configured_definitions_to_load() ->
150+
-spec has_configured_definitions_to_load_via_modern_option() -> boolean().
151+
has_configured_definitions_to_load_via_modern_option() ->
152+
case application:get_env(rabbit, definitions) of
153+
undefined -> false;
154+
{ok, none} -> false;
155+
{ok, []} -> false;
156+
{ok, _Options} -> true
157+
end.
158+
159+
has_configured_definitions_to_load_via_classic_option() ->
135160
case application:get_env(rabbit, load_definitions) of
136161
undefined -> false;
137162
{ok, none} -> false;
138163
{ok, _Path} -> true
139164
end.
140165

141-
maybe_load_definitions(App, Key) ->
166+
maybe_load_definitions_from_local_filesystem(App, Key) ->
142167
case application:get_env(App, Key) of
143-
undefined ->
144-
rabbit_log:debug("No definition file configured to import via load_definitions"),
145-
ok;
146-
{ok, none} ->
147-
rabbit_log:debug("No definition file configured to import via load_definitions"),
148-
ok;
149-
{ok, FileOrDir} ->
150-
rabbit_log:debug("Will import definitions file from load_definitions"),
151-
IsDir = filelib:is_dir(FileOrDir),
152-
maybe_load_definitions_from(IsDir, FileOrDir)
168+
undefined -> ok;
169+
{ok, none} -> ok;
170+
{ok, Path} ->
171+
IsDir = filelib:is_dir(Path),
172+
rabbit_definitions_import_local_filesystem:load(IsDir, Path)
153173
end.
154174

155-
maybe_load_definitions_from(true, Dir) ->
156-
rabbit_log:info("Applying definitions from directory ~s", [Dir]),
157-
load_definitions_from_files(file:list_dir(Dir), Dir);
158-
maybe_load_definitions_from(false, File) ->
159-
load_definitions_from_file(File).
160-
161-
load_definitions_from_files({ok, Filenames0}, Dir) ->
162-
Filenames1 = lists:sort(Filenames0),
163-
Filenames2 = [filename:join(Dir, F) || F <- Filenames1],
164-
load_definitions_from_filenames(Filenames2);
165-
load_definitions_from_files({error, E}, Dir) ->
166-
rabbit_log:error("Could not read definitions from directory ~s, Error: ~p", [Dir, E]),
167-
{error, {could_not_read_defs, E}}.
168-
169-
load_definitions_from_filenames([]) ->
170-
ok;
171-
load_definitions_from_filenames([File|Rest]) ->
172-
case load_definitions_from_file(File) of
173-
ok -> load_definitions_from_filenames(Rest);
174-
{error, E} -> {error, {failed_to_import_definitions, File, E}}
175+
maybe_load_definitions_from_pluggable_source(App, Key) ->
176+
case application:get_env(App, Key) of
177+
undefined -> ok;
178+
{ok, none} -> ok;
179+
{ok, []} -> ok;
180+
{ok, Proplist} ->
181+
case pget(import_backend, Proplist, undefined) of
182+
undefined ->
183+
{error, "definition import source is configured but definitions.import_backend is not set"};
184+
ModOrAlias ->
185+
Mod = normalize_backend_module(ModOrAlias),
186+
rabbit_log:debug("Will use module ~s to import definitions", [Mod]),
187+
Mod:load(Proplist)
188+
end
175189
end.
176190

177-
load_definitions_from_file(File) ->
178-
case file:read_file(File) of
179-
{ok, Body} ->
180-
rabbit_log:info("Applying definitions from file at '~s'", [File]),
181-
import_raw(Body);
182-
{error, E} ->
183-
rabbit_log:error("Could not read definitions from file at '~s', error: ~p", [File, E]),
184-
{error, {could_not_read_defs, {File, E}}}
185-
end.
191+
normalize_backend_module(local_filesystem) ->
192+
rabbit_definitions_import_local_filesystem;
193+
normalize_backend_module(local) ->
194+
rabbit_definitions_import_local_filesystem;
195+
normalize_backend_module(https) ->
196+
rabbit_definitions_import_https;
197+
normalize_backend_module(http) ->
198+
rabbit_definitions_import_https;
199+
normalize_backend_module(rabbitmq_definitions_import_local_filesystem) ->
200+
rabbit_definitions_import_local_filesystem;
201+
normalize_backend_module(rabbitmq_definitions_import_https) ->
202+
rabbit_definitions_import_https;
203+
normalize_backend_module(Other) ->
204+
Other.
186205

187206
decode(Keys, Body) ->
188207
case decode(Body) of

0 commit comments

Comments
 (0)