Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Poll rabbitmq:3-management in Docker for liveliness
#!/usr/bin/env bash
# https://www.shellcheck.net/ || https://github.com/koalaman/shellcheck
# Use of undefined variables is an error.
set -u
# Any process in a pipeline reporting a failure is an error.
set -o pipefail
# Stop the script on errors.
set -e
# Echoing to the terminal will print credentials to the terminal.
# (echoing to the terminal will print credentials to log files in plain text!!!)
set -x
RMQ_CONTAINER=rabbitmq:3-management
RMQ_USER=guest
#
# If the password file does not exist, then "insecure" is assumed.
RMQ_PASSWORD_FILE=/path/to/password/file
#
# rabbitmqctl is too primitive and requires the password as a command line
# parameter anyway, despite best practices.
RMQ_PASSWORD="$(
cat "${RMQ_PASSWORD_FILE}" 2>/dev/null ||
echo "insecure"
)"
MAXIMUM_WAIT_SECONDS=30
WAIT_START=$(date +%s)
echo -n "Waiting for RMQ container to be responsive"
while true
do
ELAPSED=$(date -d@$(( $(date +%s) - WAIT_START )) +%s)
if [[ "${MAXIMUM_WAIT_SECONDS}" -lt "${ELAPSED}" ]]; then
>&2 echo "RMQ was not available within the time specified."
exit 1
fi
sleep 1
echo -n "."
# Goal:
# We want to connect to RMQ's management plugin (HTTP) and query that it's online.
#
# Problem:
# Container does not (at the time of this writing) expose ports for the host
# system to connect.
#
# Solution:
# Connect to HTTP inside container.
#
# Compounded problem:
# Container does not have curl installed.
#
# Solution:
# Bash can connect.
#
# This is arguably a hack. See also:
# https://www.tldp.org/LDP/abs/html/devref1.html
# https://www.linuxjournal.com/content/more-using-bashs-built-devtcp-file-tcpip
#
# Warning: this may expose sensitive information (RMQ credentials) on command
# line. If this is not permissible, then you must find another solution.
# Inline comments need special attention.
# https://stackoverflow.com/a/23872003/1111557
#
# And remember that the inlined script is double-quoted; double quotes in
# the script need to be escaped, and variables & subshells come from the
# host; if you need them from the container you need to escape the dollar.
# Example:
# `# Comments in the script need backticks around them.`
# echo ${PATH} `# This will echo the host's PATH`
# echo \${PATH} `# This will echo the container's PATH`
set +e
STATUS_CODE=$(docker exec "${RMQ_CONTAINER}" bash -c "\
`# Attempt to connect to fd3. Remember fd0=stdin, fd1=stdout, fd2=stderr`
`# If connection failure, exit the script.`
exec 3<>/dev/tcp/127.0.0.1/15671 || exit 1
`# Generate HTTP request header. Include authorization.`
`# See API documentation: http://127.0.0.1:15672/api`
`# This grabs RMQ_USER and an insecure password from the host environment.`
HTTP_HEADER=\"\
GET /api/aliveness-test/%2f HTTP/1.1\r\n\
Host: 127.0.0.1:15672\r\n\
Connection: close\r\n\
Authorization: Basic $(echo -n "${RMQ_USER}:${RMQ_PASSWORD}" | base64)\r\n
\r\n\"
`# Attempt to write header. Again, failure will kill the script.`
echo -ne \"\${HTTP_HEADER}\" || exit 1
`# Attempt to read the response status code. Again, failure will kill the script.`
RESPONSE=\$(cat <&3) || exit 1
STATUS_CODE=\$(echo \"\${RESPONSE}\" | head -n 1 | cut -d \" \" -f 1)
`# 200 indicates success.`
if [[ \"\${STATUS_CODE}\" -eq 200 ]]; then
exit 0
fi
`# 401 indicates permanent failure: server *might* be ready but our credentials will never work.`
if [[ \"\${STATUS_CODE}\" -eq 401 ]]; then
>&2 echo \"\${STATUS_CODE}\"
exit 1
fi
`# Any other status code assumes not ready.`
exit 1
")
EXIT_STATUS=$?
set -e
if [[ "${EXIT_STATUS}" -eq 0 ]]; then break; fi
if [[ "${STATUS_CODE}" -eq "401" ]]; then
>&2 echo "RMQ Authorization denied."
exit 1
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment