File: distribution.ex

package info (click to toggle)
rabbitmq-server 3.10.8-1.1%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 32,436 kB
  • sloc: erlang: 200,376; javascript: 18,664; makefile: 2,244; python: 1,934; sh: 1,845; xml: 648; cs: 368; java: 320; ruby: 212; php: 100; perl: 63; awk: 13
file content (138 lines) | stat: -rw-r--r-- 3,609 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
## This Source Code Form is subject to the terms of the Mozilla Public
## License, v. 2.0. If a copy of the MPL was not distributed with this
## file, You can obtain one at https://mozilla.org/MPL/2.0/.
##
## Copyright (c) 2016-2022 VMware, Inc. or its affiliates.  All rights reserved.

defmodule RabbitMQ.CLI.Core.Distribution do
  alias RabbitMQ.CLI.Core.{ANSI, Config, Helpers}

  #
  # API
  #

  def start() do
    start(%{})
  end

  def start(options) do
    node_name_type = Config.get_option(:longnames, options)
    result = start(node_name_type, 10, :undefined)
    ensure_cookie(options)
    result
  end

  def stop, do: Node.stop()

  def start_as(node_name, options) do
    node_name_type = Config.get_option(:longnames, options)
    result = start_with_epmd(node_name, node_name_type)
    ensure_cookie(options)
    result
  end

  ## Optimization. We try to start EPMD only if distribution fails
  def start_with_epmd(node_name, node_name_type) do
    case Node.start(node_name, node_name_type) do
      {:ok, _} = ok ->
        ok

      {:error, {:already_started, _}} = started ->
        started

      {:error, {{:already_started, _}, _}} = started ->
        started

      ## EPMD can be stopped. Retry with EPMD
      {:error, _} ->
        :rabbit_nodes_common.ensure_epmd()
        Node.start(node_name, node_name_type)
    end
  end

  def per_node_timeout(:infinity, _) do
    :infinity
  end

  def per_node_timeout(timeout, node_count) do
    Kernel.trunc(timeout / node_count)
  end

  #
  # Implementation
  #

  def ensure_cookie(options) do
    case Config.get_option(:erlang_cookie, options) do
      nil ->
        :ok

      cookie ->
        Node.set_cookie(cookie)
        maybe_warn_about_deprecated_rabbitmq_erlang_cookie_env_variable(options)
        :ok
    end
  end

  defp start(_opt, 0, last_err) do
    {:error, last_err}
  end

  defp start(node_name_type, attempts, _last_err) do
    candidate = generate_cli_node_name(node_name_type)

    case start_with_epmd(candidate, node_name_type) do
      {:ok, _} ->
        :ok

      {:error, {:already_started, pid}} ->
        {:ok, pid}

      {:error, {{:already_started, pid}, _}} ->
        {:ok, pid}

      {:error, reason} ->
        start(node_name_type, attempts - 1, reason)
    end
  end

  defp generate_cli_node_name(node_name_type) do
    case Helpers.get_rabbit_hostname(node_name_type) do
      {:error, _} = err ->
        throw(err)

      rmq_hostname ->
        # This limits the number of possible unique node names used by CLI tools to avoid
        # the atom table from growing above the node limit. We must use reasonably unique IDs
        # to allow for concurrent CLI tool execution.
        #
        # Enum.random/1 is constant time and space with range arguments https://hexdocs.pm/elixir/Enum.html#random/1.
        id = Enum.random(1..1024)
        String.to_atom("rabbitmqcli-#{id}-#{rmq_hostname}")
    end
  end

  defp maybe_warn_about_deprecated_rabbitmq_erlang_cookie_env_variable(options) do
    case System.get_env("RABBITMQ_ERLANG_COOKIE") do
      nil ->
        :ok

      _ ->
        case Config.output_less?(options) do
          true ->
            :ok

          false ->
            warning =
              ANSI.bright_red(
                "RABBITMQ_ERLANG_COOKIE env variable support is deprecated and will be REMOVED in a future version. "
              ) <>
                ANSI.yellow(
                  "Use the $HOME/.erlang.cookie file or the --erlang-cookie switch instead."
                )

            IO.puts(warning)
        end
    end
  end
end