bitnami-containers/bitnami/rabbitmq/3.8/debian-10/rootfs/librabbitmq.sh

486 lines
14 KiB
Bash

#!/bin/bash
#
# Bitnami RabbitMQ library
# shellcheck disable=SC1090
# shellcheck disable=SC1091
# Load Generic Libraries
. /libfile.sh
. /libfs.sh
. /liblog.sh
. /libos.sh
. /libservice.sh
. /libvalidations.sh
# Functions
########################
# Load global variables used on RabbitMQ configuration.
# Globals:
# RABBITMQ_*
# Arguments:
# None
# Returns:
# Series of exports to be used as 'eval' arguments
#########################
rabbitmq_env() {
cat <<"EOF"
# Paths
export RABBITMQ_VOLUME_DIR="/bitnami/rabbitmq"
export RABBITMQ_BASE_DIR="/opt/bitnami/rabbitmq"
export RABBITMQ_BIN_DIR="${RABBITMQ_BASE_DIR}/sbin"
export RABBITMQ_CONF_DIR="${RABBITMQ_BASE_DIR}/etc/rabbitmq"
export RABBITMQ_DATA_DIR="${RABBITMQ_VOLUME_DIR}/mnesia"
export RABBITMQ_HOME_DIR="${RABBITMQ_BASE_DIR}/.rabbitmq"
export RABBITMQ_LIB_DIR="${RABBITMQ_BASE_DIR}/var/lib/rabbitmq"
export RABBITMQ_LOG_DIR="${RABBITMQ_BASE_DIR}/var/log/rabbitmq"
export RABBITMQ_PLUGINS_DIR="${RABBITMQ_BASE_DIR}/plugins"
export PATH="${RABBITMQ_BIN_DIR}:${PATH}"
# OS
export RABBITMQ_DAEMON_USER="rabbitmq"
export RABBITMQ_DAEMON_GROUP="rabbitmq"
# RabbitMQ locations
export RABBITMQ_MNESIA_BASE="${RABBITMQ_DATA_DIR}"
# Settings
export RABBITMQ_CLUSTER_NODE_NAME="${RABBITMQ_CLUSTER_NODE_NAME:-}"
export RABBITMQ_CLUSTER_PARTITION_HANDLING="${RABBITMQ_CLUSTER_PARTITION_HANDLING:-ignore}"
export RABBITMQ_DISK_FREE_LIMIT="${RABBITMQ_DISK_FREE_LIMIT:-{mem_relative, 1.0\}}"
export RABBITMQ_ERL_COOKIE="${RABBITMQ_ERL_COOKIE:-}"
export RABBITMQ_HASHED_PASSWORD="${RABBITMQ_HASHED_PASSWORD:-}"
export RABBITMQ_MANAGER_BIND_IP="${RABBITMQ_MANAGER_BIND_IP:-0.0.0.0}"
export RABBITMQ_MANAGER_PORT_NUMBER="${RABBITMQ_MANAGER_PORT_NUMBER:-15672}"
export RABBITMQ_NODE_NAME="${RABBITMQ_NODE_NAME:-rabbit@localhost}"
export RABBITMQ_NODE_PORT_NUMBER="${RABBITMQ_NODE_PORT_NUMBER:-5672}"
export RABBITMQ_NODE_TYPE="${RABBITMQ_NODE_TYPE:-stats}"
export RABBITMQ_PASSWORD="${RABBITMQ_PASSWORD:-bitnami}"
export RABBITMQ_USERNAME="${RABBITMQ_USERNAME:-user}"
export RABBITMQ_VHOST="${RABBITMQ_VHOST:-/}"
# Print all log messages to standard output
export RABBITMQ_LOGS="${RABBITMQ_LOGS:--}"
export RABBITMQ_ENABLE_LDAP="${RABBITMQ_ENABLE_LDAP:-no}"
export RABBITMQ_LDAP_TLS="${RABBITMQ_LDAP_TLS:-no}"
export RABBITMQ_LDAP_SERVER="${RABBITMQ_LDAP_SERVER:-}"
export RABBITMQ_LDAP_SERVER_PORT="${RABBITMQ_LDAP_SERVER_PORT:-389}"
export RABBITMQ_LDAP_USER_DN_PATTERN="${RABBITMQ_LDAP_USER_DN_PATTERN:-}"
EOF
}
########################
# Validate settings in RABBITMQ_* environment variables
# Globals:
# RABBITMQ_*
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_validate() {
info "Validating settings in RABBITMQ_* env vars.."
local error_code=0
# Auxiliary functions
print_validation_error() {
error "$1"
error_code=1
}
if [[ -z "$RABBITMQ_PASSWORD" && -z "$RABBITMQ_HASHED_PASSWORD" ]]; then
print_validation_error "You must indicate a password or a hashed password."
fi
if [[ -n "$RABBITMQ_PASSWORD" && -n "$RABBITMQ_HASHED_PASSWORD" ]]; then
warn "You initialized RabbitMQ indicating both a password and a hashed password. Please note only the hashed password will be considered."
fi
if ! is_yes_no_value "$RABBITMQ_ENABLE_LDAP"; then
print_validation_error "An invalid value was specified in the environment variable RABBITMQ_ENABLE_LDAP. Valid values are: yes or no"
fi
if is_boolean_yes "$RABBITMQ_ENABLE_LDAP" && ( [[ -z "${RABBITMQ_LDAP_SERVER}" ]] || [[ -z "${RABBITMQ_LDAP_USER_DN_PATTERN}" ]] ); then
print_validation_error "The LDAP configuration is required when LDAP authentication is enabled. Set the environment variables RABBITMQ_LDAP_SERVER and RABBITMQ_LDAP_USER_DN_PATTERN."
if ! is_yes_no_value "$RABBITMQ_LDAP_TLS"; then
print_validation_error "An invalid value was specified in the environment variable RABBITMQ_LDAP_TLS. Valid values are: yes or no"
fi
fi
if [[ "$RABBITMQ_NODE_TYPE" = "stats" ]]; then
if ! validate_ipv4 "$RABBITMQ_MANAGER_BIND_IP"; then
print_validation_error "An invalid IP was specified in the environment variable RABBITMQ_MANAGER_BIND_IP."
fi
local validate_port_args=()
! am_i_root && validate_port_args+=("-unprivileged")
if ! err=$(validate_port "${validate_port_args[@]}" "$RABBITMQ_MANAGER_PORT_NUMBER"); then
print_validation_error "An invalid port was specified in the environment variable RABBITMQ_MANAGER_PORT_NUMBER: ${err}."
fi
if [[ -n "$RABBITMQ_CLUSTER_NODE_NAME" ]]; then
warn "This node will not be clustered. Use type queue-* instead."
fi
elif [[ "$RABBITMQ_NODE_TYPE" = "queue-disc" ]] || [[ "$RABBITMQ_NODE_TYPE" = "queue-ram" ]]; then
if [[ -z "$RABBITMQ_CLUSTER_NODE_NAME" ]]; then
warn "You did not define any node to cluster with."
fi
else
print_validation_error "${RABBITMQ_NODE_TYPE} is not a valid type. You can use 'stats', 'queue-disc' or 'queue-ram'."
fi
[[ "$error_code" -eq 0 ]] || exit "$error_code"
}
########################
# Creates RabbitMQ configuration file
# Globals:
# RABBITMQ_CONF_DIR
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_create_config_file() {
debug "Creating configuration file..."
local auth_backend=""
local separator=""
is_boolean_yes "$RABBITMQ_ENABLE_LDAP" && auth_backend="{auth_backends, [rabbit_auth_backend_ldap]},"
is_boolean_yes "$RABBITMQ_LDAP_TLS" && separator=","
cat > "${RABBITMQ_CONF_DIR}/rabbitmq.config" <<EOF
[
{rabbit,
[
$auth_backend
{tcp_listeners, [$RABBITMQ_NODE_PORT_NUMBER]},
{disk_free_limit, $RABBITMQ_DISK_FREE_LIMIT},
{cluster_partition_handling, $RABBITMQ_CLUSTER_PARTITION_HANDLING},
{default_vhost, <<"$RABBITMQ_VHOST">>},
{default_user, <<"$RABBITMQ_USERNAME">>},
{default_permissions, [<<".*">>, <<".*">>, <<".*">>]}
EOF
if is_boolean_yes "$RABBITMQ_ENABLE_LDAP"; then
cat >> "${RABBITMQ_CONF_DIR}/rabbitmq.config" <<EOF
]
},
{rabbitmq_auth_backend_ldap,
[
{servers, ["$RABBITMQ_LDAP_SERVER"]},
{user_dn_pattern, "$RABBITMQ_LDAP_USER_DN_PATTERN"},
{port, $RABBITMQ_LDAP_SERVER_PORT}$separator
EOF
if is_boolean_yes "$RABBITMQ_LDAP_TLS"; then
cat >> "${RABBITMQ_CONF_DIR}/rabbitmq.config" <<EOF
{use_ssl, true}
EOF
fi
fi
cat >> "${RABBITMQ_CONF_DIR}/rabbitmq.config" <<EOF
]
},
{rabbitmq_management,
[
{listener, [{port, $RABBITMQ_MANAGER_PORT_NUMBER}, {ip, "$RABBITMQ_MANAGER_BIND_IP"}]},
{strict_transport_security, "max-age=0;"}
]
}
].
EOF
}
########################
# Creates RabbitMQ environment file
# Globals:
# RABBITMQ_CONF_DIR
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_create_environment_file() {
debug "Creating environment file..."
cat > "${RABBITMQ_CONF_DIR}/rabbitmq-env.conf" <<EOF
HOME=$RABBITMQ_HOME_DIR
NODE_PORT=$RABBITMQ_NODE_PORT_NUMBER
NODENAME=$RABBITMQ_NODE_NAME
EOF
}
########################
# Creates RabbitMQ Erlang cookie
# Globals:
# RABBITMQ_ERL_COOKIE
# RABBITMQ_HOME_DIR
# RABBITMQ_LIB_DIR
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_create_erlang_cookie() {
debug "Creating Erlang cookie..."
if [[ -z $RABBITMQ_ERL_COOKIE ]]; then
info "Generating random cookie"
RABBITMQ_ERL_COOKIE=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c32)
fi
echo "$RABBITMQ_ERL_COOKIE" > "${RABBITMQ_HOME_DIR}/.erlang.cookie"
}
########################
# Enables a RabbitMQ plugin
# Globals:
# RABBITMQ_BIN_DIR
# BITNAMI_DEBUG
# Arguments:
# $1 - Plugin to enable
# Returns:
# None
#########################
rabbitmq_enable_plugin() {
local plugin="${1:?plugin is required}"
debug "Enabling plugin '${plugin}'..."
if ! debug_execute "${RABBITMQ_BIN_DIR}/rabbitmq-plugins" "enable" "--offline" "$plugin"; then
warn "Couldn't enable plugin '${plugin}'."
fi
}
########################
# Checks if RabbitMQ is running
# Globals:
# RABBITMQ_PID
# RABBITMQ_BIN_DIR
# Arguments:
# None
# Returns:
# Boolean
#########################
is_rabbitmq_running() {
if [[ -z "${RABBITMQ_PID:-}" ]]; then
false
else
is_service_running "$RABBITMQ_PID"
fi
}
########################
# Checks if a RabbitMQ node is running
# Globals:
# RABBITMQ_BIN_DIR
# Arguments:
# $1 - Node to check
# Returns:
# Boolean
#########################
node_is_running() {
local node="${1:?node is required}"
info "Checking node ${node}"
if debug_execute "${RABBITMQ_BIN_DIR}/rabbitmqctl" await_startup -n "$node"; then
true
else
false
fi
}
########################
# Starts RabbitMQ in background and waits until it's ready
# Globals:
# BITNAMI_DEBUG
# RABBITMQ_BIN_DIR
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_start_bg() {
is_rabbitmq_running && return
info "Starting RabbitMQ in background..."
if [[ "${BITNAMI_DEBUG:-false}" = true ]]; then
"${RABBITMQ_BIN_DIR}/rabbitmq-server" &
else
"${RABBITMQ_BIN_DIR}/rabbitmq-server" >/dev/null 2>&1 &
fi
export RABBITMQ_PID="$!"
local counter=0
while ! "${RABBITMQ_BIN_DIR}/rabbitmqctl" wait --pid "$RABBITMQ_PID" --timeout 5; do
debug "Waiting for RabbitMQ to start..."
counter=$((counter + 1))
if [[ $counter -eq 10 ]]; then
error "Couldn't start RabbitMQ in background."
exit 1
fi
done
}
########################
# Stop RabbitMQ
# Globals:
# BITNAMI_DEBUG
# RABBITMQ_BIN_DIR
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_stop() {
! is_rabbitmq_running && return
info "Stopping RabbitMQ..."
debug_execute "${RABBITMQ_BIN_DIR}/rabbitmqctl" stop
local counter=10
while [[ "$counter" -ne 0 ]] && is_rabbitmq_running; do
debug "Waiting for RabbitMQ to stop..."
sleep 1
counter=$((counter - 1))
done
}
########################
# Change the password of a user
# Globals:
# BITNAMI_DEBUG
# RABBITMQ_BIN_DIR
# Arguments:
# $1 - User
# $2 - Password
# Returns:
# None
#########################
rabbitmq_change_password() {
local user="${1:?user is required}"
local password="${2:?password is required}"
debug "Changing password for user '${user}'..."
if ! debug_execute "${RABBITMQ_BIN_DIR}/rabbitmqctl" change_password "$user" "$password"; then
error "Couldn't change password for user '${user}'."
exit 1
fi
}
########################
# Migrate old custom configuration files
# Globals:
# RABBITMQ_CONF_DIR
# RABBITMQ_VOLUME_DIR
# Arguments:
# None
# Returns:
# None
#########################
migrate_old_configuration() {
debug "Persisted configuration detected. Migrating any existing configuration files..."
warn "Configuration files won't be persisted anymore!"
cp -Lr "${RABBITMQ_VOLUME_DIR}/conf/." "$RABBITMQ_CONF_DIR"
cp -Lr "${RABBITMQ_VOLUME_DIR}/var/lib/rabbitmq/mnesia" "$RABBITMQ_VOLUME_DIR"
if am_i_root; then
[[ -e "${RABBITMQ_VOLUME_DIR}/.initialized" ]] && rm "${RABBITMQ_VOLUME_DIR}/.initialized"
rm -rf "${RABBITMQ_VOLUME_DIR}/conf" "${RABBITMQ_VOLUME_DIR:?}/var" "${RABBITMQ_VOLUME_DIR}/.rabbitmq"
else
warn "Old configuration migrated, please manually remove the 'conf', 'var' and '.rabbitmq' directories from the volume."
fi
}
########################
# Make a node join a cluster
# Globals:
# BITNAMI_DEBUG
# RABBITMQ_BIN_DIR
# Arguments:
# $1 - Node to cluster with
# $2 - Type of node
# Returns:
# None
#########################
rabbitmq_join_cluster() {
local clusternode="${1:?node is required}"
local type="${2:?type is required}"
local join_cluster_args=("$clusternode")
[[ "$type" = "queue-ram" ]] && join_cluster_args+=("--ram")
debug_execute "${RABBITMQ_BIN_DIR}/rabbitmqctl" stop_app
local counter=0
while ! debug_execute "${RABBITMQ_BIN_DIR}/rabbitmq-plugins" --node "$clusternode" is_enabled rabbitmq_management; do
debug "Waiting for ${clusternode} to be ready..."
counter=$((counter + 1))
sleep 1
if [[ $counter -eq 120 ]]; then
error "Node ${clusternode} is not running."
exit 1
fi
done
info "Clustering with ${clusternode}"
if ! debug_execute "${RABBITMQ_BIN_DIR}/rabbitmqctl" join_cluster "${join_cluster_args[@]}"; then
error "Couldn't cluster with node '${clusternode}'."
exit 1
fi
debug_execute "${RABBITMQ_BIN_DIR}/rabbitmqctl" start_app
}
########################
# Ensure RabbitMQ is initialized
# Globals:
# RABBITMQ_*
# Arguments:
# None
# Returns:
# None
#########################
rabbitmq_initialize() {
info "Initializing RabbitMQ..."
local skip_setup=false
! is_dir_empty "$RABBITMQ_DATA_DIR" && skip_setup=true
# Persisted configuration files from old versions
! is_dir_empty "$RABBITMQ_VOLUME_DIR" && [[ -d "${RABBITMQ_VOLUME_DIR}/conf" ]] && migrate_old_configuration && skip_setup=true
[[ ! -f "${RABBITMQ_CONF_DIR}/rabbitmq.config" ]] && rabbitmq_create_config_file
[[ ! -f "${RABBITMQ_CONF_DIR}/rabbit-env.conf" ]] && rabbitmq_create_environment_file
[[ ! -f "${RABBITMQ_HOME_DIR}/.erlang.cookie" ]] && rabbitmq_create_erlang_cookie
chmod 400 "${RABBITMQ_HOME_DIR}/.erlang.cookie"
ln -sf "${RABBITMQ_HOME_DIR}/.erlang.cookie" "${RABBITMQ_LIB_DIR}/.erlang.cookie"
debug "Ensuring expected directories/files exist..."
for dir in "$RABBITMQ_DATA_DIR" "$RABBITMQ_LIB_DIR" "$RABBITMQ_HOME_DIR"; do
ensure_dir_exists "$dir"
am_i_root && chown -R "$RABBITMQ_DAEMON_USER:$RABBITMQ_DAEMON_GROUP" "$dir"
done
if "$skip_setup"; then
info "Persisted data detected. Restoring..."
else
! is_rabbitmq_running && rabbitmq_start_bg
rabbitmq_change_password "$RABBITMQ_USERNAME" "$RABBITMQ_PASSWORD"
if [[ "$RABBITMQ_NODE_TYPE" != "stats" ]] && [[ -n "$RABBITMQ_CLUSTER_NODE_NAME" ]]; then
rabbitmq_join_cluster "$RABBITMQ_CLUSTER_NODE_NAME" "$RABBITMQ_NODE_TYPE"
fi
fi
if [[ "$RABBITMQ_NODE_TYPE" = "stats" ]]; then
rabbitmq_enable_plugin "rabbitmq_management"
else
rabbitmq_enable_plugin "rabbitmq_management_agent"
fi
if is_boolean_yes "$RABBITMQ_ENABLE_LDAP"; then
rabbitmq_enable_plugin "rabbitmq_auth_backend_ldap"
fi
}