summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Klishin <mklishin@pivotal.io>2019-03-06 20:19:52 +0300
committerMichael Klishin <mklishin@pivotal.io>2019-03-06 20:19:52 +0300
commitc339b6a10cc36696135ddf5c5dc2ca2b648e013a (patch)
tree7a7f9971fff17d25800aff22137e2add99cbb7ad
parent89d5eb80bb97c5881505f01c294cabdd7e2ebc65 (diff)
downloadrabbitmq-server-git-c339b6a10cc36696135ddf5c5dc2ca2b648e013a.tar.gz
ctl shutdown: infer hostnames from node names
inet_db is not a very reliable source as it doesn't take node name CLI arguments and ERL_INETRC file settings. That can lead to false positives in environments where inet_db returns the same value (e.g. `localhost`) for every cluster member. Per discussion with @gerhard. Closes #327. References #309.
-rw-r--r--deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex56
-rw-r--r--deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/shutdown_command.ex25
-rw-r--r--deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs8
3 files changed, 50 insertions, 39 deletions
diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex
index ec200161e5..df5d0e97b2 100644
--- a/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex
+++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex
@@ -34,6 +34,40 @@ defmodule RabbitMQ.CLI.Core.NodeName do
def hostname, do: :inet_db.gethostname() |> List.to_string()
@doc """
+ Get hostname part of current node name
+ """
+ def hostname_from_node do
+ [_, hostname] = split_node(Node.self())
+ hostname
+ end
+
+ @doc """
+ Get hostname part of given node name
+ """
+ def hostname_from_node(name) do
+ [_, hostname] = split_node(name)
+ hostname
+ end
+
+ def split_node(name) when is_atom(name) do
+ split_node(to_string(name))
+ end
+
+ def split_node(name) do
+ case String.split(name, "@", parts: 2) do
+ ["", host] ->
+ default_name = to_string(Config.get_option(:node))
+ [default_name, host]
+
+ [_head, _host] = rslt ->
+ rslt
+
+ [head] ->
+ [head, ""]
+ end
+ end
+
+ @doc """
Get local domain. If unavailable, makes a good guess. We're using
:inet_db here because that's what Erlang/OTP uses when it creates a node
name:
@@ -43,6 +77,10 @@ defmodule RabbitMQ.CLI.Core.NodeName do
domain(1)
end
+ #
+ # Implementation
+ #
+
defp domain(attempt) do
case {attempt, :inet_db.res_option(:domain), :os.type()} do
{1, [], _} ->
@@ -162,24 +200,6 @@ defmodule RabbitMQ.CLI.Core.NodeName do
Regex.match?(rx, head)
end
- defp split_node(name) when is_atom(name) do
- split_node(to_string(name))
- end
-
- defp split_node(name) do
- case String.split(name, "@", parts: 2) do
- ["", host] ->
- default_name = to_string(Config.get_option(:node))
- [default_name, host]
-
- [_head, _host] = rslt ->
- rslt
-
- [head] ->
- [head, ""]
- end
- end
-
defp do_load_resolv do
# It could be we haven't read domain name from resolv file yet
:ok = :inet_config.do_load_resolv(:os.type(), :longnames)
diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/shutdown_command.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/shutdown_command.ex
index 051bc89e4d..74a9b53333 100644
--- a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/shutdown_command.ex
+++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/shutdown_command.ex
@@ -15,7 +15,7 @@
defmodule RabbitMQ.CLI.Ctl.Commands.ShutdownCommand do
@behaviour RabbitMQ.CLI.CommandBehaviour
- alias RabbitMQ.CLI.Core.OsPid
+ alias RabbitMQ.CLI.Core.{OsPid, NodeName}
def switches() do
[timeout: :integer,
@@ -32,18 +32,17 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ShutdownCommand do
:ok
end
- def validate([], %{node: node_name, wait: true, timeout: timeout}) do
- hostname = :inet_db.gethostname()
- case :rabbit_misc.rpc_call(node_name, :inet_db, :gethostname, [], timeout) do
- {:badrpc, _} = err -> {:error, err}
- remote_hostname ->
- case hostname == remote_hostname do
- true -> :ok;
- false ->
- msg = "\nThis command can only --wait for shutdown of local nodes but node #{node_name} is remote (local hostname: #{hostname}, remote: #{remote_hostname}).\n" <>
- "Pass --no-wait to shut node #{node_name} down without waiting.\n"
- {:validation_failure, {:unsupported_target, msg}}
- end
+ def validate([], %{node: node_name, wait: true}) do
+ local_hostname = NodeName.hostname_from_node(Node.self())
+ remote_hostname = NodeName.hostname_from_node(node_name)
+ case local_hostname == remote_hostname do
+ true -> :ok;
+ false ->
+ msg = "\nThis command can only --wait for shutdown of local nodes " <>
+ "but node #{node_name} seems to be remote " <>
+ "(local hostname: #{local_hostname}, remote: #{remote_hostname}).\n" <>
+ "Pass --no-wait to shut node #{node_name} down without waiting.\n"
+ {:validation_failure, {:unsupported_target, msg}}
end
end
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
diff --git a/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs b/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs
index b427c967dd..c4f658687b 100644
--- a/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs
+++ b/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs
@@ -42,14 +42,6 @@ defmodule ShutdownCommandTest do
assert @command.validate([], Map.merge(%{wait: false}, context[:opts])) == :ok
end
- # this command performs rpc calls in validate/2
- test "validate: request to a non-existent node returns nodedown" do
- target = :jake@thedog
-
- opts = %{node: target, wait: true, timeout: 10}
- assert match?({:error, {:badrpc, _}}, @command.validate([], opts))
- end
-
test "run: request to a non-existent node returns nodedown" do
target = :jake@thedog