summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniil Fedotov <dfedotov@pivotal.io>2016-12-20 18:17:10 +0000
committerDaniil Fedotov <dfedotov@pivotal.io>2016-12-20 18:17:10 +0000
commit5f7553c0bd2eac3fc311761bc2b81763f537c1e5 (patch)
tree8949e9f56ce669d90c4b2ac8eb8b9760c7d95aca /src
parent3d568701e733dfb07d9a61390c192364ad4ce68a (diff)
parentcbbcc3e32f97b6047b4f82f0776717dd3e85430c (diff)
downloadrabbitmq-server-git-5f7553c0bd2eac3fc311761bc2b81763f537c1e5.tar.gz
Merge branch 'master' into rabbitmq-server-567
Diffstat (limited to 'src')
-rw-r--r--src/rabbit_plugins.erl215
1 files changed, 151 insertions, 64 deletions
diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl
index b7ba3af732..171448b05c 100644
--- a/src/rabbit_plugins.erl
+++ b/src/rabbit_plugins.erl
@@ -67,15 +67,22 @@ ensure(FileJustChanged0) ->
{error, {enabled_plugins_mismatch, FileJustChanged, OurFile}}
end.
-%% @doc Prepares the file system and installs all enabled plugins.
setup() ->
- {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
-
- %% Eliminate the contents of the destination directory
- case delete_recursively(ExpandDir) of
- ok -> ok;
- {error, E1} -> throw({error, {cannot_delete_plugins_expand_dir,
- [ExpandDir, E1]}})
+ case application:get_env(rabbit, plugins_expand_dir) of
+ {ok, ExpandDir} ->
+ case filelib:is_dir(ExpandDir) of
+ true ->
+ rabbit_log:info(
+ "\"~s\" is no longer used to expand plugins.~n"
+ "RabbitMQ still manages this directory "
+ "but will stop doing so in the future.", [ExpandDir]),
+
+ _ = delete_recursively(ExpandDir);
+ false ->
+ ok
+ end;
+ undefined ->
+ ok
end,
{ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file),
@@ -128,10 +135,56 @@ extract_schema(#plugin{type = dir, location = Location}, SchemaDir) ->
%% @doc Lists the plugins which are currently running.
active() ->
- {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
- InstalledPlugins = plugin_names(list(ExpandDir)),
+ LoadedPluginNames = maybe_keep_required_deps(false, loaded_plugin_names()),
[App || {App, _, _} <- rabbit_misc:which_applications(),
- lists:member(App, InstalledPlugins)].
+ lists:member(App, LoadedPluginNames)].
+
+loaded_plugin_names() ->
+ {ok, PluginsDir} = application:get_env(rabbit, plugins_dir),
+ PluginsDirComponents = filename:split(PluginsDir),
+ loaded_plugin_names(code:get_path(), PluginsDirComponents, []).
+
+loaded_plugin_names([Path | OtherPaths], PluginsDirComponents, PluginNames) ->
+ case lists:sublist(filename:split(Path), length(PluginsDirComponents)) of
+ PluginsDirComponents ->
+ case build_plugin_name_from_code_path(Path) of
+ undefined ->
+ loaded_plugin_names(
+ OtherPaths, PluginsDirComponents, PluginNames);
+ PluginName ->
+ loaded_plugin_names(
+ OtherPaths, PluginsDirComponents,
+ [list_to_atom(PluginName) | PluginNames])
+ end;
+ _ ->
+ loaded_plugin_names(OtherPaths, PluginsDirComponents, PluginNames)
+ end;
+loaded_plugin_names([], _, PluginNames) ->
+ PluginNames.
+
+build_plugin_name_from_code_path(Path) ->
+ AppPath = case filelib:is_dir(Path) of
+ true ->
+ case filelib:wildcard(filename:join(Path, "*.app")) of
+ [AP | _] -> AP;
+ [] -> undefined
+ end;
+ false ->
+ EZ = filename:dirname(filename:dirname(Path)),
+ case filelib:is_regular(EZ) of
+ true ->
+ case find_app_path_in_ez(EZ) of
+ {ok, AP} -> AP;
+ _ -> undefined
+ end;
+ _ ->
+ undefined
+ end
+ end,
+ case AppPath of
+ undefined -> undefined;
+ _ -> filename:basename(AppPath, ".app")
+ end.
%% @doc Get the list of plugins which are ready to be enabled.
list(PluginsPath) ->
@@ -221,25 +274,19 @@ running_plugins() ->
%%----------------------------------------------------------------------------
prepare_plugins(Enabled) ->
- {ok, PluginsDistDir} = application:get_env(rabbit, plugins_dir),
- {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
-
- AllPlugins = list(PluginsDistDir),
+ AllPlugins = installed_plugins(),
Wanted = dependencies(false, Enabled, AllPlugins),
WantedPlugins = lookup_plugins(Wanted, AllPlugins),
{ValidPlugins, Problems} = validate_plugins(WantedPlugins),
maybe_warn_about_invalid_plugins(Problems),
- case filelib:ensure_dir(ExpandDir ++ "/") of
- ok -> ok;
- {error, E2} -> throw({error, {cannot_create_plugins_expand_dir,
- [ExpandDir, E2]}})
- end,
- [prepare_plugin(Plugin, ExpandDir) || Plugin <- ValidPlugins],
- [prepare_dir_plugin(PluginAppDescPath) ||
- PluginAppDescPath <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")],
+ [prepare_dir_plugin(ValidPlugin) || ValidPlugin <- ValidPlugins],
Wanted.
+installed_plugins() ->
+ {ok, PluginsDistDir} = application:get_env(rabbit, plugins_dir),
+ list(PluginsDistDir).
+
maybe_warn_about_invalid_plugins([]) ->
ok;
maybe_warn_about_invalid_plugins(InvalidPlugins) ->
@@ -352,40 +399,60 @@ is_version_supported(Version, ExpectedVersions) ->
end.
clean_plugins(Plugins) ->
- {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
- [clean_plugin(Plugin, ExpandDir) || Plugin <- Plugins].
+ [clean_plugin(Plugin) || Plugin <- Plugins].
-clean_plugin(Plugin, ExpandDir) ->
+clean_plugin(Plugin) ->
{ok, Mods} = application:get_key(Plugin, modules),
+ PluginEbinDir = code:lib_dir(Plugin, ebin),
+
application:unload(Plugin),
[begin
code:soft_purge(Mod),
code:delete(Mod),
false = code:is_loaded(Mod)
end || Mod <- Mods],
- delete_recursively(rabbit_misc:format("~s/~s", [ExpandDir, Plugin])).
-
-prepare_dir_plugin(PluginAppDescPath) ->
- PluginEbinDir = filename:dirname(PluginAppDescPath),
- Plugin = filename:basename(PluginAppDescPath, ".app"),
- code:add_patha(PluginEbinDir),
- case filelib:wildcard(PluginEbinDir++ "/*.beam") of
- [] ->
- ok;
- [BeamPath | _] ->
- Module = list_to_atom(filename:basename(BeamPath, ".beam")),
- case code:ensure_loaded(Module) of
- {module, _} ->
+
+ code:del_path(PluginEbinDir).
+
+plugin_ebin_dir(#plugin{type = ez, location = Location}) ->
+ case find_app_path_in_ez(Location) of
+ {ok, AppPath} ->
+ filename:join(Location, filename:dirname(AppPath));
+ {error, Reason} ->
+ {error, Reason}
+ end;
+plugin_ebin_dir(#plugin{type = dir, location = Location}) ->
+ filename:join(Location, "ebin").
+
+prepare_dir_plugin(#plugin{name = Name} = Plugin) ->
+ PluginEbinDir = case plugin_ebin_dir(Plugin) of
+ {error, Reason} ->
+ throw({plugin_ebin_dir_not_found, Name, Reason});
+ Dir ->
+ Dir
+ end,
+ case code:add_patha(PluginEbinDir) of
+ true ->
+ case filelib:wildcard(PluginEbinDir++ "/*.beam") of
+ [] ->
ok;
- {error, badfile} ->
- rabbit_log:error("Failed to enable plugin \"~s\": "
- "it may have been built with an "
- "incompatible (more recent?) "
- "version of Erlang~n", [Plugin]),
- throw({plugin_built_with_incompatible_erlang, Plugin});
- Error ->
- throw({plugin_module_unloadable, Plugin, Error})
- end
+ [BeamPath | _] ->
+ Module = list_to_atom(filename:basename(BeamPath, ".beam")),
+ case code:ensure_loaded(Module) of
+ {module, _} ->
+ ok;
+ {error, badfile} ->
+ rabbit_log:error("Failed to enable plugin \"~s\": "
+ "it may have been built with an "
+ "incompatible (more recent?) "
+ "version of Erlang~n", [Name]),
+ throw({plugin_built_with_incompatible_erlang, Name});
+ Error ->
+ throw({plugin_module_unloadable, Name, Error})
+ end
+ end;
+ {error, bad_directory} ->
+ throw({plugin_ebin_path_incorrect, Name, PluginEbinDir})
end.
%%----------------------------------------------------------------------------
@@ -396,12 +463,6 @@ delete_recursively(Fn) ->
{error, {Path, E}} -> {error, {cannot_delete, Path, E}}
end.
-prepare_plugin(#plugin{type = ez, location = Location}, ExpandDir) ->
- zip:unzip(Location, [{cwd, ExpandDir}]);
-prepare_plugin(#plugin{type = dir, name = Name, location = Location},
- ExpandDir) ->
- rabbit_file:recursive_copy(Location, filename:join([ExpandDir, Name])).
-
plugin_info({ez, EZ}) ->
case read_app_file(EZ) of
{application, Name, Props} -> mkplugin(Name, Props, ez, EZ);
@@ -428,14 +489,12 @@ mkplugin(Name, Props, Type, Location) ->
broker_version_requirements = BrokerVersions,
dependency_version_requirements = DepsVersions}.
-read_app_file(EZ) ->
+find_app_path_in_ez(EZ) ->
case zip:list_dir(EZ) of
{ok, [_|ZippedFiles]} ->
case find_app_files(ZippedFiles) of
[AppPath|_] ->
- {ok, [{AppPath, AppFile}]} =
- zip:extract(EZ, [{file_list, [AppPath]}, memory]),
- parse_binary(AppFile);
+ {ok, AppPath};
[] ->
{error, no_app_file}
end;
@@ -443,6 +502,16 @@ read_app_file(EZ) ->
{error, {invalid_ez, Reason}}
end.
+read_app_file(EZ) ->
+ case find_app_path_in_ez(EZ) of
+ {ok, AppPath} ->
+ {ok, [{AppPath, AppFile}]} =
+ zip:extract(EZ, [{file_list, [AppPath]}, memory]),
+ parse_binary(AppFile);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
find_app_files(ZippedFiles) ->
{ok, RE} = re:compile("^.*/ebin/.*.app$"),
[Path || {zip_file, Path, _, _, _, _} <- ZippedFiles,
@@ -539,16 +608,34 @@ remove_duplicate_plugins([Plugin|Rest], {Plugins0, Problems0}) ->
maybe_keep_required_deps(true, Plugins) ->
Plugins;
maybe_keep_required_deps(false, Plugins) ->
- %% We load the "rabbit" application to be sure we can get the
- %% "applications" key. This is required for rabbitmq-plugins for
- %% instance.
- application:load(rabbit),
- {ok, RabbitDeps} = application:get_key(rabbit, applications),
- lists:filter(fun(#plugin{name = Name}) ->
+ RabbitDeps = list_all_deps([rabbit]),
+ lists:filter(fun
+ (#plugin{name = Name}) ->
+ not lists:member(Name, RabbitDeps);
+ (Name) when is_atom(Name) ->
not lists:member(Name, RabbitDeps)
end,
Plugins).
+list_all_deps(Applications) ->
+ list_all_deps(Applications, []).
+
+list_all_deps([Application | Applications], Deps) ->
+ %% We load the application to be sure we can get the "applications" key.
+ %% This is required for rabbitmq-plugins for instance.
+ application:load(Application),
+ NewDeps = [Application | Deps],
+ case application:get_key(Application, applications) of
+ {ok, ApplicationDeps} ->
+ RemainingApplications0 = ApplicationDeps ++ Applications,
+ RemainingApplications = RemainingApplications0 -- NewDeps,
+ list_all_deps(RemainingApplications, NewDeps);
+ undefined ->
+ list_all_deps(Applications, NewDeps)
+ end;
+list_all_deps([], Deps) ->
+ Deps.
+
remove_otp_overrideable_plugins(Plugins) ->
lists:filter(fun(P) -> not plugin_provided_by_otp(P) end,
Plugins).