urjit.io

16 Jan 2018

Good old bash for startup dependency management

I wanted to share a really simple and clean way to wait for dependencies to be available in Unix*y environments. I often use this script to orchestrate my docker containers.

For example, if I want to bring up a cluster of containers that all work together and then once they are all working, do something else, like run some tests, this script comes in super handy.

Here is a typical setup that this helps with:

Service depends on:

  • Redis
  • Mysql
  • Mongodb

Expected startup sequence:

  • Start Redis, Mysql, Mongodb containers
  • Wait for the containers to start running
  • Wait for each of the processes inside the containers to get ready
    • For example, wait till Mysql is actually ready to accept connections
  • Finally, run the Service
#!/bin/bash

######################################################################
# Waits for Mongodb, Redis and Mysql to be ready
# usage examples:
#    wait-for-dependencies.sh echo "READY TO GO"
#    wait-for-dependencies.sh make build
# Exit Codes: 0 for success, 2 if wait command timed out
######################################################################

TIME_LIMIT=15
INTERVAL=2

set -e
POST_RUN_CMD=( "$@" )

wait_for_command() {
  SECONDS=0
  DEP_NAME="${1}"
  CMD="${2}"
  until ${CMD} &> /dev/null; do
    >&2 echo "${DEP_NAME} is unavailable - sleeping. Time elapsed: ${SECONDS}"
    sleep $INTERVAL
    if [ "${SECONDS}" -gt "${TIME_LIMIT}" ]; then
      >&2 echo "TIMEOUT: ${DEP_NAME} failed to come up"
      exit 2
    fi
  done
  >&2 echo "${DEP_NAME} is ready"
}

# WAIT FOR MONGO
MONGO_NAME='mongodb'
MONGO_WAIT_CMD='mongo --eval "db.stats()" --host mongo'

# WAIT FOR MYSQL
MYSQL_NAME='mysql'
MYSQL_WAIT_CMD='mysqladmin ping  --protocol tcp --host mysql --user root'

# WAIT FOR REDIS
REDIS_NAME='redis'
REDIS_WAIT_CMD='redis-cli ping'

wait_for_command "${MONGO_NAME}" "${MONGO_WAIT_CMD}"
wait_for_command "${MYSQL_NAME}" "${MYSQL_WAIT_CMD}"
wait_for_command "${REDIS_NAME}" "${REDIS_WAIT_CMD}"

# Finally, run the arbitrary command after this wait it done.
exec "${POST_RUN_CMD[@]}"

Handy github script link