Skip to content

Instantly share code, notes, and snippets.

@hairyhum
Last active December 12, 2016 12:04
Show Gist options
  • Save hairyhum/33506ba041d4e800be2d0ac6282681e0 to your computer and use it in GitHub Desktop.
Save hairyhum/33506ba041d4e800be2d0ac6282681e0 to your computer and use it in GitHub Desktop.

New (3.7.0+) RabbitMQ CLI Tools

Summary

RabbitMQ version 3.7 comes with brave new CLI tool to replace rabbitmqctl.

Some of the issues in the older tool suite we wanted to address:

  • Built-in into RabbitMQ server code
  • Home-grown argument parser
  • Started with erl with a lot of installation-dependent parameters
  • Too many commands in a single tool
  • All commands in resided the same module (function clauses)

All this made it hard to maintain and extend the tools.

The new CLI is different in a number of ways that address the above issues:

  • Standalone repository on GitHub.
  • Implemented in Elixir (using Elixir's standard CLI parser)
  • Each command is its own module
  • Extensible
  • Commands can support more output formats (e.g. JSON and CSV)
  • A single executable file that bundles all dependencies but the Erlang runtime
  • Command scopes associate commands with tools (e.g. rabbitmq-diagnostics reuses relevant commands from rabbitmqctl)

Architecture

Each command is defined in its own module and implements an Elixir (Erlang) behaviour. (See Command behaviour)

Output is processed by a formatter and printer which formats command output and render the output (the default being the standard I/O output). (see Output formatting)

CLI core consists of several modules implementing command execution process:

  • RabbitMQCtl: entry point. Generic execution logic.
  • Parser: responsible for command line argument parsing (drives Elixir's OptionParser)
  • CommandModules: responsible for command module discovery and loading
  • Config: responsible for config unification: merges enviroment variable and command argument values
  • Output: responsible for output formatting
  • Helpers: self-explanatory

Command Execution Process

Arguments parsing

Command line arguments are parsed with OptionParser Parser returns a list of unnamed arguments and a map of options (named arguemtns) First unnamed argument is a command name. Named arguments can be global or command specific. Command specific argument names and types are apecified in the switches/0 callback. Global argument names are described in [Global arguments]

Command discovery

If arguments list is not empty, its first element is considered a command name. Command name is converted to CamelCase and a module with RabbitMQ.CLI.*.Commands.<CommandName>Command name is selected as a command module.

List of available command modules depend on current tool scope (see Command scopes)

Defaults and validation

After the command module is found, effective command arguments are calculated by merging global defaults and command specific defaults for both unnamed and named arguments.

A command specifies defaults using the merge_defaults/2 callback (see Command behaviour)

Arguments are then validated using the validate/2 callback

Command execution

Command is executed with the run/2 callback, which contains main command logic. This callback can return any value.

Output from the run/2 callback is being processed with the output/2 callback, which should format the output to a specific type. (see Output formatting)

Output callback can return an an error, with a specific exit code.

Printing and formatting

The output/2 callback return value is being processed in output.ex module by by the appropriate formatter and printer.

A formatter translates the output value to a sequence of strings or error value. Example formatters are json, csv and erlang

A printer renders formatted strings to an output device (e.g. stdout, file)

Return

Errors during execution (e.g. validation failures, command errors) are being printed to stderr.

If command has failed to execute, a non-zero code is returned.

Usage

CLI tool is an Elixir Mix application. It is compiled into an escript executable file.

This file embeds Elixir, rabbit_common, and rabbitmqcli applications and can be executed anywhere where escript is installed (it comes as a part of erlang).

Although, some commands require rabbitmq broker code and data directory to be known. For example, commands in rabbitmq-plugins tool and those controlling clustering (e.g. forget_cluster_node, rename_cluster_node ...)

Those directories can be defined using environment options or rabbitmq environment variables.

In the broker distribution the escript file is called from a shell/cmd sctipt, which loads broker environment and exports it into the script.

Environment variables also specify the locations of the enabled plugins file and the plugins directory.

All enabled plugins will be searched for commands. Commands from enabled plugins will be shown in usage and available for execution.

Global Arguments

Commonly Used Arguments

  • node (n): atom, broker node name, defaults to rabbit@<current host>
  • quiet (q): boolean, if set to true, command banner is not shown, defaults to false
  • timeout (t): integer, timeout value in seconds (used in some commands), defaults to infinity
  • vhost (p): string, vhost to talk to, defaults to /
  • formatter: string, formatter to use to format command output. (see Output formatting)
  • printer: string, printer to render output (see Output formatting)
  • dry-run: boolean, if specified the command will not run, but print banner only

Environment Arguments

  • script_name: atom, configurable tool name (rabbitmq-plugins, rabbitmqctl) to select command scope (see Command scopes)
  • rabbitmq_home: string, broker install directory
  • mnesia_dir: string, broker mnesia data directory
  • plugins_dir: string, broker plugins directory
  • enabled_plugins_file: string, broker enabled plugins file
  • longnames (l): boolean, use longnames to communicate with broker erlang node. Should be set to true only if broker is started with longnames.

Environment argument defaults are loaded from rabbitmq environment variables (see Environment configuration).

Some named arguments have single-letter aliases (in parenthesis).

Boolean options without a value are parsed as true

For example, parsing command

rabbitmqctl list_queues --vhost my_vhost -t 10 --formatter=json name pid --quiet

Will result with unnamed arguments list ["list_queues", "name", "pid"] and named options map %{vhost: "my_vhost", timeout: 10, quiet: true}

Usage (Listing Commands in help)

Usage is shown when the CLI is called without any arguments, or if there are some problems parsing arguments.

In that cases exit code is 64.

If you want to show usage and return 0 exit code run help command

rabbitmqctl help

Each tool (rabbitmqctl, rabbitmq-plugins) shows its scope of commands (see Command scopes)

Command Behaviour

Each command is implemented as an Elixir (or Erlang) module. Command module should implement RabbitMQ.CLI.CommandBehaviour behaviour.

Behaviour summary:

Following functions MUST be implemented in a command module:

usage() :: String.t | [String.t]

Command usage string, or several strings (one per line) to print in command listing in usage. Typically looks like command_name [arg] --option=opt_value

banner(arguments :: List.t, options :: Map.t) :: String.t

Banner to print before the command execution. Ignored if argument --quiet is specified. If --dry-run argument is specified, th CLI will only print the banner.

merge_defaults(arguments :: List.t, options :: Map.t) :: {List.t, Map.t}

Merge default values for arguments and options (named arguments). Returns a tuple with effective arguments and options, that will be passed to validate/2 and run/2

validate(arguments :: List.t, options :: Map.t) :: :ok | {:validation_failure, Atom.t | {Atom.t, String.t}}

Validate effective arguments and options.

If function returns {:validation_failure, err} CLI will print usage to stderr and exit with non-zero exit code (typically 64).

run(arguments :: List.t, options :: Map.t) :: run_result :: any

Run command. This function usually calls RPC on broker.

output(run_result :: any, options :: Map.t) :: :ok | {:ok, output :: any} | {:stream, Enum.t} | {:error, ExitCodes.exit_code, [String.t]}

Cast the return value of run/2 command to a formattable value and an exit code.

  • :ok - return 0 exit code and won't print anything

  • {:ok, output} - return exit code and print output with format_output/2 callback in formatter

  • {:stream, stream} - format with format_stream/2 callback in formatter, iterating over enumerable elements. Can return non-zero code, if error occurs during stream processing (stream element for error should be {:error, Message}).

  • {:error, exit_code, strings} - print error messages to stderr and return exit_code code

There is a default implementation for this callback in DefaultOutput module

Most of the standard commands use the default implementation via use RabbitMQ.CLI.DefaultOutput

Following functions are optional:

switches() :: Keyword.t

Keyword list of switches (argument names and types) for the command.

For example: def switches(), do: [offline: :boolean, time: :integer] will parse --offline --time=100 arguments to %{offline: true, time: 100} options.

This switches are added to global switches (see Arguments parsing)

aliases() :: Keyword.t

Keyword list of argument names one-letter aliases. For example: [o: :offline, t: :timeout] (see Arguments parsing)

usage_additional() :: String.t | [String.t]

Additional usage strings to print after all commands basic usage. Used to explain additional arguments and not interfere with command listing.

formatter() :: Atom.t

Default formatter for the command. Should be a module name of a module implementing RabbitMQ.CLI.FormatterBehaviour (see Output formatting)

scopes() :: [Atom.t]

List of scopes to include command in. (see Command scopes)

More information about command development can be found in the command tutorial

Command Scopes

Commands can be organized in scopes to be used in different tools like rabbitmq-diagnostics or rabbitmq-plugins.

One command can belong to multiple scopes. Scopes for a command can be defined in the scopes/0 callback in the command module. Each scope is defined as an atom value.

By default a command scope is selected using naming convention. If command module is called RabbitMQ.CLI.MyScope.Commands.DoSomethingCommand, it will belong to my_scope scope. A scope should be defined in snake_case. Namespace for the scope will be translated to CamelCase.

When CLI is run, a scope is selected by a script name, which is the escript file name or the --script-name argument passed.

A script name is associated with a single scope in the application environment:

Script names for scopes:

  • rabbitmqctl - :ctl
  • rabbitmq-plugins - :plugins
  • rabbitmq-diagnostics - :diagnostics

This environment is extended by plugins :scopes environment variables, but cannot be overriden. Plugins scopes can override each other, so should be used with caution.

So all the commands in the RabbitMQ.CLI.Ctl.Commands namespace will be available for rabbitmqctl script. All the commands in the RabbitMQ.CLI.Plugins.Commands namespace will be available for rabbitmq-plugins script.

To add a command to rabbitmqctl, one should either name it with the RabbitMQ.CLI.Ctl.Commands prefix or add the scopes() callback, returning a list with :ctl element

Output Formatting

The CLI supports extensible output formatting. Formatting consists of two stages:

  • formatting - translating command output to a sequence of lines
  • printing - outputting the lines to some device (e.g. stdout or filesystem)

A formatter module performs formatting. Formatter is a module, implementing the RabbitMQ.CLI.FormatterBehaviour behaviour:

format_output(output :: any, options :: Map.t) :: String.t | [String.t]

Format a single value, returned. It accepts output from command and named arguments (options) and returns a list of strings, that should be printed.

format_stream(output_stream :: Enumerable.t, options :: Map.t) :: Enumerable.t

Format a stream of return values. This function uses elixir Stream [http://elixir-lang.org/docs/stable/elixir/Stream.html] abstraction to define processing of continuous data, so the CLI can output data in realtime.

Used in list_* commands, that emit data asynchronously.

DefaultOutput will return all enumerable data as stream, so it will be formatted with this function.

A printer module performs printing. Printer module should implement the RabbitMQ.CLI.PrinterBehaviour behaviour:

init(options :: Map.t) :: {:ok, printer_state :: any} | {:error, error :: any}

Init the internal printer state (e.g. open a file handler).

finish(printer_state :: any) :: :ok

Finalize the internal printer state.

print_output(output :: String.t | [String.t], printer_state :: any) :: :ok

Print the output lines in the printer state context. Is called for {:ok, val} command output after formatting val using formatter, and for each enumerable element of {:stream, enum} enumerable

print_ok(printer_state :: any) :: :ok

Print an output without any values. Is called for :ok command return value.

Output formatting logic is defined in output.ex module.

Following rules apply for a value, returned from output/2 callback:

  • :ok - initializes a printer, calls print_ok and finishes the printer. Exit code is 0.
  • {:ok, value} - calls format_output/2 from formatter, then passes the value to printer. Exit code is 0.
  • {:stream, enum} - calls format_stream/2 to augment the stream with formatting logic, initializes a printer, then calls print_output/2 for each successfully formatted stream element (which is not {:error, msg}). Exit code is 0 if there were no errors. In case of an error element, stream processing stops, error is printed to stderr and the CLI exits with nonzero exit code.
  • {:error, exit_code, msg} - prints msg to stderr and exits with exit_code

Environment Configuration

Some commands require information about the server environment to run correctly. Such information is:

  • rabbitmq code directory
  • rabbitmq mnesia directory
  • enabled plugins file
  • plugins directory

Enabled plugins file and plugins directory are also used to locate plugins commands.

This information can be provided using command line arguments (see Environment arguments)

By default it will be loaded from environment variables, same as used in rabbitmq broker:

Argument name Environment variable
rabbitmq_home RABBITMQ_HOME
mnesia_dir RABBITMQ_MNESIA_DIR
plugins_dir RABBITMQ_PLUGINS_DIR
enabled_plugins_file RABBITMQ_ENABLED_PLUGINS_FILE
longnames RABBITMQ_USE_LONGNAME
node RABBITMQ_NODENAME

Implementing your own rabbitmqctl command

RabbitMQ CLI project allows plugin developers to implement their own commands.

The CLI is written in Elixir language and commands can be implemented in Elixir, Erlang or any other Erlang based language. This tutorial mostly targets Elixir implementation, but also provides an Erlang example. Basic principles are the same.

This tutorial doesn't cover plugin development process. To develop a new plugin you should check existing tutorials:

A CLI command is an elixir/erlang module. It should follow some requirements to be discovered and used in CLI:

  • Follow a naming convention (module name should match RabbitMQ.CLI.(.*).Commands.(.*)Command)
  • Be included in a plugin application modules
  • Implement RabbitMQ.CLI.CommandBehaviour

When implementing a command in erlang, you should add Elixir as a prefix to the module name and behaviour, because CLI is written in elixir. It should match Elixir.RabbitMQ.CLI.(.*).Commands.(.*)Command And implement Elixir.RabbitMQ.CLI.CommandBehaviour

Let's write a command, that does something. Like deleting a queue.

First, we need to declare a module and behaviour

defmodule RabbitMQ.CLI.Ctl.Commands.DeleteQueueCommand do
  @behaviour RabbitMQ.CLI.CommandBehaviour
end

Good. But now we see compilation errors:

warning: undefined behaviour function usage/0 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function switches/0 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function aliases/0 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function banner/2 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function merge_defaults/2 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function validate/2 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function run/2 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

warning: undefined behaviour function output/2 (for behaviour RabbitMQ.CLI.CommandBehaviour)
  lib/delete_queue_command.ex:1

Lets implement the missing functions:

We start with the usage/0 function, to describe how command should be called.

  def usage(), do: "delete_queue queue_name [--if_empty|-e] [--if_unused|-u] [--vhost|-p vhost]"

We want our command to accept a queue_name unnamed argument, and two flag arguments: if_empty and if_unused, and vhost argument with a value.

We also want to specify our named arguments in shorter format.

Then we implement switches/0 and aliases/0 functions to let CLI know how it should parse command line arguments

  def switches(), do: [if_empty: :boolean, if_unused: :boolean]
  def aliases(), do: [e: :if_empty, u: :is_unused]

Switches specify long arguments names and types, aliases specify shorter names. You can see there is no vhost switch there. It's because vhost is a global switch and will be available for any command in the CLI. (see [Global arguments])

We've described how the CLI should parse commands, now lets start describing what the command should do.

We start with the banner/2 function, that tells a user what command is going to do. If you cal the command with --dry-run argument, it will only print the banner, without running the command.

  def banner([qname], %{vhost: vhost,
                        if_empty: if_empty,
                        if_unused: if_unused}) do
    if_empty_str = case if_empty do
      true  -> "if queue is empty"
      false -> ""
    end
    if_unused_str = case if_unused do
      true  -> "if queue is unused"
      false -> ""
    end
    "Deleting queue #{qname} on vhost #{vhost} " <>
      Enum.join([if_empty_str, if_unused_str], " and ")
  end

The function can access arguments and options to tell exactly what command is going to do.

As you can see, the banner/2 function accepts exactly one argument and expects vhost, if_empty and if_unused options. To make sure the command have all correct arguments, you can use merge_defaults/2 and validate/2 functions.

  def merge_defaults(args, options) do
    {
      args,
      Map.merge(%{if_empty: false, if_unused: false, vhost: "/"}, options)
    }
  end

  def validate([], _options) do
    {:validation_failure, :not_enough_args}
  end
  def validate([_,_|_], _options) do
    {:validation_failure, :too_many_args}
  end
  def validate([""], _options) do
    {
      :validation_failure,
      {:bad_argument, "queue name cannot be empty string."}
    }
  end
  def validate([_], _options) do
    :ok
  end

The merge_defaults/2 function accepts arguments and options and returns a tuple with effective arguments and options, that will be passed to validate/2, banner/2 and run/2.

The validate/2 function can return either :ok atom or {:validate, failure} tuple. This function checks that we have exactly one argument and it's not empty.

At least one validate/2 clause should return :ok.

To do the actual thing, the run/2 command is used:

  def run([qname], %{node: node, vhost: vhost,
                     if_empty: if_empty, if_unused: if_unused}) do
    ## Generate the queue resource name from queue name and vhost
    queue_resource = :rabbit_misc.r(vhost, :queue, qname)
    ## Lookup the queue on broker node using resource name
    case :rabbit_misc.rpc_call(node, :rabbit_amqqueue, :lookup,
                                     [queue_resource]) do
      {:ok, queue} ->
        ## Delete the queue
        :rabbit_misc.rpc_call(node, :rabbit_amqqueue, :delete,
                                    [queue, if_empty, if_unused]);
      {:error, _} = error -> error
    end
  end

As you can see, we use :rabbit_misc in the function. You can use any functions from rabbit_common directly, but to do something on a broker node, you should use RPC calls. You can use regular erlang rpc:call.

Rabbit node name is specified in node option, which is a global option and will be available for all commands.

Finally, we would like to print the command execution result. We use output/2 to format run/2 return value to standard formattable output.

  def output({:error, :not_found}, _options) do
    {:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "Queue not found"}
  end
  def output({:error, :not_empty}, _options) do
    {:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "Queue is not empty"}
  end
  def output({:error, :in_use}, _options) do
    {:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "Queue is in use"}
  end
  def output({:ok, queue_length}, _options) do
    {:ok, "Queue was successfully deleted with #{queue_length} messages"}
  end
  ## Use default output for all non-special case outputs
  use RabbitMQ.CLI.DefaultOutput

We have function clauses for all passible outputs of rabbit_amqqueue:delete. For successfull result the output/2 should return {:ok, result}, for errors it should return {:error, exit_code, message}, where exit_code is integer, and message is a string or a list of strings.

Program will exit with exit_code in case of error, or 0 in case of successfull result.

RabbitMQ.CLI.DefaultOutput is a module which handles different error cases (e.g. badrpc). This use statement will import function clauses for output/2 from the DefaultOutput module. For some commands just the use statement can be sufficient to handle any output.

That's it. Now you can add this command to your plugin, enable it and run

rabbitmqctl delete_queue my_queue --vhost my_vhost

to delete the queue.

Full module definition in Elixir:

defmodule RabbitMQ.CLI.Ctl.Commands.DeleteQueueCommand do
  @behaviour RabbitMQ.CLI.CommandBehaviour

  def switches(), do: [if_empty: :boolean, if_unused: :boolean]
  def aliases(), do: [e: :if_empty, u: :is_unused]

  def usage(), do: "delete_queue queue_name [--if_empty|-e] [--if_unused|-u]"

  def banner([qname], %{vhost: vhost,
                        if_empty: if_empty,
                        if_unused: if_unused}) do
    if_empty_str = case if_empty do
      true  -> "if queue is empty"
      false -> ""
    end
    if_unused_str = case if_unused do
      true  -> "if queue is unused"
      false -> ""
    end
    "Deleting queue #{qname} on vhost #{vhost} " <>
      Enum.join([if_empty_str, if_unused_str], " and ")
  end

  def merge_defaults(args, options) do
    {
      args,
      Map.merge(%{if_empty: false, if_unused: false, vhost: "/"}, options)
    }
  end

  def validate([], _options) do
    {:validation_failure, :not_enough_args}
  end
  def validate([_,_|_], _options) do
    {:validation_failure, :too_many_args}
  end
  def validate([""], _options) do
    {
      :validation_failure,
      {:bad_argument, "queue name cannot be empty string."}
    }
  end

  def run([qname], %{node: node, vhost: vhost,
                     if_empty: if_empty, if_unused: if_unused}) do
    ## Generate queue resource name from queue name and vhost
    queue_resource = :rabbit_misc.r(vhost, :queue, qname)
    ## Lookup a queue on broker node using resource name
    case :rabbit_misc.rpc_call(node, :rabbit_amqqueue, :lookup,
                                     [queue_resource]) do
      {:ok, queue} ->
        ## Delete queue
        :rabbit_misc.rpc_call(node, :rabbit_amqqueue, :delete,
                                    [queue, if_empty, if_unused]);
      {:error, _} = error -> error
    end
  end

  def output({:error, :not_found}, _options) do
    {:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "Queue not found"}
  end
  def output({:error, :not_empty}, _options) do
    {:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "Queue is not empty"}
  end
  def output({:error, :in_use}, _options) do
    {:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "Queue is in use"}
  end
  def output({:ok, qlen}, _options) do
    {:ok, "Queue was successfully deleted with #{qlen} messages"}
  end
  ## Use default output for all non-special case outputs
  use RabbitMQ.CLI.DefaultOutput
end

The same module on Erlang.

-module('Elixir.RabbitMQ.CLI.Ctl.Commands.DeleteQueueCommand').

-behaviour('Elixir.RabbitMQ.CLI.CommandBehaviour').

-export([switches/0, aliases/0, usage/0,
         banner/2, merge_defaults/2, validate/2, run/2, output/2]).

switches() -> [{if_empty, boolean}, {if_unused, boolean}].
aliases() -> [{e, if_empty}, {u, is_unused}].

usage() -> <<"delete_queue queue_name [--if_empty|-e] [--if_unused|-u] [--vhost|-p vhost]">>.

banner([Qname], #{vhost := Vhost,
                  if_empty := IfEmpty,
                  if_unused := IfUnused}) ->
    IfEmptyStr = case IfEmpty of
        true  -> ["if queue is empty"];
        false -> []
    end,
    IfUnusedStr = case IfUnused of
        true  -> ["if queue is unused"];
        false -> []
    end,
    iolist_to_binary(
        io_lib:format("Deleting queue ~s on vhost ~s ~s",
                      [Qname, Vhost,
                       string:join(IfEmptyStr ++ IfUnusedStr, " and ")])).

merge_defaults(Args, Options) ->
    {
      Args,
      maps:merge(#{if_empty => false, if_unused => false, vhost => <<"/">>},
                 Options)
    }.

validate([], _Options) ->
    {validation_failure, not_enough_args};
validate([_,_|_], _Options) ->
    {validation_failure, too_many_args};
validate([<<"">>], _Options) ->
    {
        validation_failure,
        {bad_argument, <<"queue name cannot be empty string.">>}
    };
validate([_], _Options) -> ok.

run([Qname], #{node := Node, vhost := Vhost,
               if_empty := IfEmpty, if_unused := IfUnused}) ->
    %% Generate queue resource name from queue name and vhost
    QueueResource = rabbit_misc:r(Vhost, queue, Qname),
    %% Lookup a queue on broker node using resource name
    case rabbit_misc:rpc_call(Node, rabbit_amqqueue, lookup, [QueueResource]) of
        {ok, Queue} ->
        %% Delete queue
            rabbit_misc:rpc_call(Node, rabbit_amqqueue, delete,
                                       [Queue, IfEmpty, IfUnused]);
        {error, _} = Error -> Error
    end.

output({error, not_found}, _Options) ->
    {
        error,
        'Elixir.RabbitMQ.CLI.Core.ExitCodes':exit_usage(),
        <<"Queue not found">>
    };
output({error, not_empty}, _Options) ->
    {
        error,
        'Elixir.RabbitMQ.CLI.Core.ExitCodes':exit_usage(),
        <<"Queue is not empty">>
    };
output({error, in_use}, _Options) ->
    {
        error,
        'Elixir.RabbitMQ.CLI.Core.ExitCodes':exit_usage(),
        <<"Queue is in use">>
    };
output({ok, qlen}, _Options) ->
    {ok, <<"Queue was successfully deleted with #{qlen} messages">>};
output(Other, Options) ->
    'Elixir.RabbitMQ.CLI.DefaultOutput':output(Other, Options, ?MODULE).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment