summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon MacMullen <simon@rabbitmq.com>2013-03-11 17:23:08 +0000
committerSimon MacMullen <simon@rabbitmq.com>2013-03-11 17:23:08 +0000
commitf6b576d9489b3e00235cf243fc87be63f02cce03 (patch)
treeb89082438d2b43efcb2572cae7f21af5bbe43f87
parentfb4f1957d4560922abc5cabd5531f3cc9f43eb48 (diff)
parentecc5fae0ea796d8b51f4eee75c1045b09fc389d3 (diff)
downloadrabbitmq-server-f6b576d9489b3e00235cf243fc87be63f02cce03.tar.gz
Merge bug 25384
-rw-r--r--ebin/rabbit_app.in1
-rw-r--r--packaging/standalone/Makefile82
-rw-r--r--packaging/standalone/erl.diff5
-rw-r--r--packaging/standalone/src/rabbit_release.erl152
-rw-r--r--scripts/rabbitmq-defaults6
-rwxr-xr-xscripts/rabbitmq-plugins3
-rwxr-xr-xscripts/rabbitmq-server7
-rwxr-xr-xscripts/rabbitmqctl3
-rw-r--r--src/rabbit_error_logger_file_h.erl3
-rw-r--r--src/rabbit_node_monitor.erl50
10 files changed, 303 insertions, 9 deletions
diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in
index ad961a44..339fa69e 100644
--- a/ebin/rabbit_app.in
+++ b/ebin/rabbit_app.in
@@ -44,6 +44,7 @@
{log_levels, [{connection, info}]},
{ssl_cert_login_from, distinguished_name},
{reverse_dns_lookups, false},
+ {cluster_partition_handling, ignore},
{tcp_listen_options, [binary,
{packet, raw},
{reuseaddr, true},
diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile
new file mode 100644
index 00000000..89ccde93
--- /dev/null
+++ b/packaging/standalone/Makefile
@@ -0,0 +1,82 @@
+VERSION=0.0.0
+SOURCE_DIR=rabbitmq-server-$(VERSION)
+TARGET_DIR=rabbitmq_server-$(VERSION)
+TARGET_TARBALL=rabbitmq-server-$(OS)-standalone-$(VERSION)
+RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR)
+
+ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().')
+ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().')
+
+# used to generate the erlang release
+RABBITMQ_HOME=$(TARGET_DIR)
+RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin
+RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins
+RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand
+
+RABBITMQ_DEFAULTS=$(TARGET_DIR)/sbin/rabbitmq-defaults
+fix_defaults = sed -e $(1) $(RABBITMQ_DEFAULTS) > $(RABBITMQ_DEFAULTS).tmp \
+ && mv $(RABBITMQ_DEFAULTS).tmp $(RABBITMQ_DEFAULTS)
+
+dist:
+ tar -zxf ../../dist/$(SOURCE_DIR).tar.gz
+
+ $(MAKE) -C $(SOURCE_DIR) \
+ TARGET_DIR=`pwd`/$(TARGET_DIR) \
+ SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \
+ MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \
+ install
+
+## Here we set the RABBITMQ_HOME variable,
+## then we make ERL_DIR point to our released erl
+## and we add the paths to our released start_clean and start_sasl boot scripts
+ $(call fix_defaults,'s:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:')
+ $(call fix_defaults,'s:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:')
+ $(call fix_defaults,'s:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":')
+ $(call fix_defaults,'s:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":')
+
+ chmod 0755 $(RABBITMQ_DEFAULTS)
+
+ mkdir -p $(TARGET_DIR)/etc/rabbitmq
+
+ $(MAKE) generate_release
+
+ mkdir -p $(RLS_DIR)
+ tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz
+
+# add minimal boot file
+ cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION)
+ cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION)
+
+# move rabbitmq files to top level folder
+ mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR)
+
+# remove empty lib/rabbit-$(VERSION) folder
+ rm -rf $(RLS_DIR)/lib/rabbit-$(VERSION)
+
+# fix Erlang ROOTDIR
+ patch -o $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.src < erl.diff
+
+ tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR)
+ rm -rf $(SOURCE_DIR) $(TARGET_DIR)
+
+clean: clean_partial
+ rm -f rabbitmq-server-$(OS)-standalone-*.tar.gz
+
+clean_partial:
+ rm -rf $(SOURCE_DIR)
+ rm -rf $(TARGET_DIR)
+
+.PHONY : generate_release
+generate_release:
+ erlc \
+ -I $(TARGET_DIR)/include/ -o src -Wall \
+ -v +debug_info -Duse_specs -Duse_proper_qc \
+ -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl
+ erl \
+ -pa "$(RABBITMQ_EBIN_ROOT)" \
+ -pa src \
+ -noinput \
+ -hidden \
+ -s rabbit_release \
+ -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)"
+ rm src/rabbit_release.beam
diff --git a/packaging/standalone/erl.diff b/packaging/standalone/erl.diff
new file mode 100644
index 00000000..c51bfe22
--- /dev/null
+++ b/packaging/standalone/erl.diff
@@ -0,0 +1,5 @@
+20c20,21
+< ROOTDIR="%FINAL_ROOTDIR%"
+---
+> realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" ; }
+> ROOTDIR="$(dirname `realpath $0`)/../.."
diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl
new file mode 100644
index 00000000..26f36d68
--- /dev/null
+++ b/packaging/standalone/src/rabbit_release.erl
@@ -0,0 +1,152 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is VMware, Inc.
+%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
+%%
+-module(rabbit_release).
+
+-export([start/0]).
+
+-include("rabbit.hrl").
+
+-define(BaseApps, [rabbit]).
+-define(ERROR_CODE, 1).
+
+%% We need to calculate all the ERTS apps we need to ship with a
+%% standalone rabbit. To acomplish that we need to unpack and load the plugins
+%% apps that are shiped with rabbit.
+%% Once we get that we generate an erlang release inside a tarball.
+%% Our make file will work with that release to generate our final rabbitmq
+%% package.
+start() ->
+ %% Determine our various directories
+ [PluginsDistDir, UnpackedPluginDir, RabbitHome] =
+ init:get_plain_arguments(),
+ RootName = UnpackedPluginDir ++ "/rabbit",
+
+ %% extract the plugins so we can load their apps later
+ prepare_plugins(PluginsDistDir, UnpackedPluginDir),
+
+ %% add the plugin ebin folder to the code path.
+ add_plugins_to_path(UnpackedPluginDir),
+
+ PluginAppNames = [P#plugin.name ||
+ P <- rabbit_plugins:list(PluginsDistDir)],
+
+ %% Build the entire set of dependencies - this will load the
+ %% applications along the way
+ AllApps = case catch sets:to_list(expand_dependencies(PluginAppNames)) of
+ {failed_to_load_app, App, Err} ->
+ terminate("failed to load application ~s:~n~p",
+ [App, Err]);
+ AppList ->
+ AppList
+ end,
+
+ %% we need a list of ERTS apps we need to ship with rabbit
+ BaseApps = AllApps -- PluginAppNames,
+
+ AppVersions = [determine_version(App) || App <- BaseApps],
+ RabbitVersion = proplists:get_value(rabbit, AppVersions),
+
+ %% Build the overall release descriptor
+ RDesc = {release,
+ {"rabbit", RabbitVersion},
+ {erts, erlang:system_info(version)},
+ AppVersions},
+
+ %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel
+ rabbit_file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])),
+
+ %% Compile the script
+ systools:make_script(RootName),
+ systools:script2boot(RootName),
+ %% Make release tarfile
+ make_tar(RootName, RabbitHome),
+ rabbit_misc:quit(0).
+
+make_tar(Release, RabbitHome) ->
+ systools:make_tar(Release,
+ [
+ {dirs, [docs, etc, include, plugins, sbin, share]},
+ {erts, code:root_dir()},
+ {outdir, RabbitHome}
+ ]).
+
+determine_version(App) ->
+ application:load(App),
+ {ok, Vsn} = application:get_key(App, vsn),
+ {App, Vsn}.
+
+delete_recursively(Fn) ->
+ case rabbit_file:recursive_delete([Fn]) of
+ ok -> ok;
+ {error, {Path, E}} -> {error, {cannot_delete, Path, E}};
+ Error -> Error
+ end.
+
+prepare_plugins(PluginsDistDir, DestDir) ->
+ %% Eliminate the contents of the destination directory
+ case delete_recursively(DestDir) of
+ ok -> ok;
+ {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E])
+ end,
+ case filelib:ensure_dir(DestDir ++ "/") of
+ ok -> ok;
+ {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2])
+ end,
+
+ [prepare_plugin(Plugin, DestDir) ||
+ Plugin <- rabbit_plugins:list(PluginsDistDir)].
+
+prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) ->
+ zip:unzip(Location, [{cwd, PluginDestDir}]);
+prepare_plugin(#plugin{type = dir, name = Name, location = Location},
+ PluginsDestDir) ->
+ rabbit_file:recursive_copy(Location,
+ filename:join([PluginsDestDir, Name])).
+
+expand_dependencies(Pending) ->
+ expand_dependencies(sets:new(), Pending).
+expand_dependencies(Current, []) ->
+ Current;
+expand_dependencies(Current, [Next|Rest]) ->
+ case sets:is_element(Next, Current) of
+ true ->
+ expand_dependencies(Current, Rest);
+ false ->
+ case application:load(Next) of
+ ok ->
+ ok;
+ {error, {already_loaded, _}} ->
+ ok;
+ {error, Reason} ->
+ throw({failed_to_load_app, Next, Reason})
+ end,
+ {ok, Required} = application:get_key(Next, applications),
+ Unique = [A || A <- Required, not(sets:is_element(A, Current))],
+ expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique)
+ end.
+
+add_plugins_to_path(PluginDir) ->
+ [add_plugin_to_path(PluginName) ||
+ PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")].
+
+add_plugin_to_path(PluginAppDescFn) ->
+ %% Add the plugin ebin directory to the load path
+ PluginEBinDirN = filename:dirname(PluginAppDescFn),
+ code:add_path(PluginEBinDirN).
+
+terminate(Fmt, Args) ->
+ io:format("ERROR: " ++ Fmt ++ "~n", Args),
+ rabbit_misc:quit(?ERROR_CODE).
diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults
index db1d4f2b..83c5639d 100644
--- a/scripts/rabbitmq-defaults
+++ b/scripts/rabbitmq-defaults
@@ -18,6 +18,12 @@
### next line potentially updated in package install steps
SYS_PREFIX=
+### next line will be updated when generating a standalone release
+ERL_DIR=
+
+CLEAN_BOOT_FILE=start_clean
+SASL_BOOT_FILE=start_sasl
+
## Set default values
CONFIG_FILE=${SYS_PREFIX}/etc/rabbitmq/rabbitmq
diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins
index 43f450c0..c043c90a 100755
--- a/scripts/rabbitmq-plugins
+++ b/scripts/rabbitmq-plugins
@@ -26,11 +26,12 @@
##--- End of overridden <var_name> variables
-exec erl \
+exec ${ERL_DIR}erl \
-pa "${RABBITMQ_HOME}/ebin" \
-noinput \
-hidden \
-sname rabbitmq-plugins$$ \
+ -boot "${CLEAN_BOOT_FILE}" \
-s rabbit_plugins_main \
-enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \
-plugins_dist_dir "$RABBITMQ_PLUGINS_DIR" \
diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server
index 184ae931..161ec2e6 100755
--- a/scripts/rabbitmq-server
+++ b/scripts/rabbitmq-server
@@ -82,7 +82,8 @@ case "$(uname -s)" in
esac
RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin"
-if ! erl -pa "$RABBITMQ_EBIN_ROOT" \
+if ! ${ERL_DIR}erl -pa "$RABBITMQ_EBIN_ROOT" \
+ -boot "${CLEAN_BOOT_FILE}" \
-noinput \
-hidden \
-s rabbit_prelaunch \
@@ -103,11 +104,11 @@ RABBITMQ_LISTEN_ARG=
# there is no other way of preventing their expansion.
set -f
-exec erl \
+exec ${ERL_DIR}erl \
-pa ${RABBITMQ_EBIN_ROOT} \
${RABBITMQ_START_RABBIT} \
-sname ${RABBITMQ_NODENAME} \
- -boot start_sasl \
+ -boot "${SASL_BOOT_FILE}" \
${RABBITMQ_CONFIG_ARG} \
+W w \
${RABBITMQ_SERVER_ERL_ARGS} \
diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl
index 00fffa9f..0368db3f 100755
--- a/scripts/rabbitmqctl
+++ b/scripts/rabbitmqctl
@@ -26,12 +26,13 @@
##--- End of overridden <var_name> variables
-exec erl \
+exec ${ERL_DIR}erl \
-pa "${RABBITMQ_HOME}/ebin" \
-noinput \
-hidden \
${RABBITMQ_CTL_ERL_ARGS} \
-sname rabbitmqctl$$ \
+ -boot "${CLEAN_BOOT_FILE}" \
-s rabbit_control_main \
-nodename $RABBITMQ_NODENAME \
-extra "$@"
diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl
index 3efc9c0c..eb6247e0 100644
--- a/src/rabbit_error_logger_file_h.erl
+++ b/src/rabbit_error_logger_file_h.erl
@@ -76,6 +76,9 @@ init_file(File, PrevHandler) ->
Error -> Error
end.
+%% filter out "application: foo; exited: stopped; type: temporary"
+handle_event({info_report, _, {_, std_info, _}}, State) ->
+ {ok, State};
handle_event(Event, State) ->
error_logger_file_h:handle_event(Event, State).
diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl
index 7411b3d6..47c753e3 100644
--- a/src/rabbit_node_monitor.erl
+++ b/src/rabbit_node_monitor.erl
@@ -208,9 +208,10 @@ handle_call(_Request, _From, State) ->
%% mnesia information since the message can (and will) overtake the
%% mnesia propagation.
handle_cast({node_up, Node, NodeType},
- State = #state{monitors = Monitors}) ->
+ State = #state{monitors = Monitors, partitions = Partitions}) ->
+ State1 = State#state{partitions = Partitions -- [Node]},
case pmon:is_monitored({rabbit, Node}, Monitors) of
- true -> {noreply, State};
+ true -> {noreply, State1};
false -> rabbit_log:info("rabbit on node ~p up~n", [Node]),
{AllNodes, DiscNodes, RunningNodes} = read_cluster_status(),
write_cluster_status({add_node(Node, AllNodes),
@@ -220,7 +221,7 @@ handle_cast({node_up, Node, NodeType},
end,
add_node(Node, RunningNodes)}),
ok = handle_live_rabbit(Node),
- {noreply, State#state{
+ {noreply, State1#state{
monitors = pmon:monitor({rabbit, Node}, Monitors)}}
end;
handle_cast({joined_cluster, Node, NodeType}, State) ->
@@ -282,7 +283,48 @@ handle_dead_rabbit(Node) ->
ok = rabbit_networking:on_node_down(Node),
ok = rabbit_amqqueue:on_node_down(Node),
ok = rabbit_alarm:on_node_down(Node),
- ok = rabbit_mnesia:on_node_down(Node).
+ ok = rabbit_mnesia:on_node_down(Node),
+ case application:get_env(rabbit, cluster_partition_handling) of
+ {ok, pause_minority} ->
+ case majority() of
+ true -> ok;
+ false -> await_cluster_recovery()
+ end;
+ {ok, ignore} ->
+ ok;
+ {ok, Term} ->
+ rabbit_log:warning("cluster_partition_handling ~p unrecognised, "
+ "assuming 'ignore'~n", [Term]),
+ ok
+ end,
+ ok.
+
+majority() ->
+ Nodes = rabbit_mnesia:cluster_nodes(all),
+ Alive = [N || N <- Nodes, pong =:= net_adm:ping(N)],
+ length(Alive) / length(Nodes) > 0.5.
+
+await_cluster_recovery() ->
+ rabbit_log:warning("Cluster minority status detected - awaiting recovery~n",
+ []),
+ Nodes = rabbit_mnesia:cluster_nodes(all),
+ spawn(fun () ->
+ %% If our group leader is inside an application we are about
+ %% to stop, application:stop/1 does not return.
+ group_leader(whereis(init), self()),
+ %% Ensure only one restarting process at a time, will
+ %% exit(badarg) (harmlessly) if one is already running
+ register(rabbit_restarting_process, self()),
+ rabbit:stop(),
+ wait_for_cluster_recovery(Nodes)
+ end).
+
+wait_for_cluster_recovery(Nodes) ->
+ case majority() of
+ true -> rabbit:start();
+ false -> timer:sleep(1000),
+ wait_for_cluster_recovery(Nodes)
+ end.
handle_live_rabbit(Node) ->
ok = rabbit_alarm:on_node_up(Node),