1## This Source Code Form is subject to the terms of the Mozilla Public 2## License, v. 2.0. If a copy of the MPL was not distributed with this 3## file, You can obtain one at https://mozilla.org/MPL/2.0/. 4## 5## Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. 6 7defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckProtocolListenerCommand do 8 @moduledoc """ 9 Exits with a non-zero code if there is no active listener 10 for the given protocol on the target node. 11 12 This command is meant to be used in health checks. 13 """ 14 15 import RabbitMQ.CLI.Core.Listeners, 16 only: [listeners_on: 2, listener_maps: 1, normalize_protocol: 1] 17 18 @behaviour RabbitMQ.CLI.CommandBehaviour 19 20 use RabbitMQ.CLI.Core.AcceptsDefaultSwitchesAndTimeout 21 use RabbitMQ.CLI.Core.MergesNoDefaults 22 use RabbitMQ.CLI.Core.AcceptsOnePositionalArgument 23 use RabbitMQ.CLI.Core.RequiresRabbitAppRunning 24 25 def run([proto], %{node: node_name, timeout: timeout}) do 26 proto = normalize_protocol(proto) 27 28 case :rabbit_misc.rpc_call(node_name, :rabbit_networking, :active_listeners, [], timeout) do 29 {:error, _} = err -> 30 err 31 32 {:error, _, _} = err -> 33 err 34 35 xs when is_list(xs) -> 36 locals = listeners_on(xs, node_name) |> listener_maps 37 38 found = 39 Enum.any?(locals, fn %{protocol: p} -> 40 to_string(proto) == to_string(p) 41 end) 42 43 case found do 44 true -> {true, proto} 45 false -> {false, proto, locals} 46 end 47 48 other -> 49 other 50 end 51 end 52 53 def output({true, proto}, %{node: node_name, formatter: "json"}) do 54 {:ok, %{"result" => "ok", "node" => node_name, "protocol" => proto}} 55 end 56 57 def output({true, proto}, %{node: node_name}) do 58 {:ok, "A listener for protocol #{proto} is running on node #{node_name}."} 59 end 60 61 def output({false, proto, listeners}, %{formatter: "json"}) do 62 protocols = Enum.map(listeners, fn %{protocol: p} -> p end) 63 64 {:error, 65 %{ 66 "result" => "error", 67 "missing" => proto, 68 "protocols" => protocols, 69 "listeners" => listeners 70 }} 71 end 72 73 def output({false, proto, listeners}, %{node: node_name}) do 74 protocols = Enum.map(listeners, fn %{protocol: p} -> p end) |> Enum.sort() |> Enum.join(", ") 75 76 {:error, 77 "No listener for protocol #{proto} is active on node #{node_name}. " <> 78 "Found listeners for the following protocols: #{protocols}"} 79 end 80 81 def help_section(), do: :observability_and_health_checks 82 83 def description(), do: "Health check that exits with a non-zero code if target node does not have an active listener for given protocol" 84 85 def usage, do: "check_protocol_listener <protocol>" 86 87 def banner([proto], %{node: node_name}) do 88 "Asking node #{node_name} if there's an active listener for protocol #{proto} ..." 89 end 90end 91