#!/bin/bash # General lib for devops scripts # @author Craig McDaniel # # Source config data source ${INCLUDE_DIR}/config.sh # Save all arguments to the script here. BASH_ARGS=$@ # Ensure dir exists mkdir -p /opt/busnet/data # Make sure there is an ssh key pair for the current user. This will be distributed to servers and # used for Ansible SSH connections. It's not fancy at all. This is a simple solution for the BusNet # system. generate_busnet_key() { local KEY_PATH="${HOME}/.ssh/${CONFIG_ANSIBLE_KEY_NAME}" # Only run if the key isn't there. if [[ ! -f "$KEY_PATH" ]]; then echo "No SSH private key detected for ansible-busnet. Generating one. DO NOT REMOVE THIS!" ssh-keygen -t ed25519 -f "${KEY_PATH}" -N "" -C "ansible-busnet@$(hostname)" fi } generate_busnet_key # Make sure the docker image specified by $1 is built and available for use. buildx_image() { IMAGE_NAME=$1 DOCKERFILE=$2 DOCKERDIR=$3 #DOCKERARGS="" # If --no-cache was specified, pass that on and ALSO add --pull to get the latest images. if [[ "${BASH_ARGS}" =~ "--no-cache" ]]; then DOCKERARGS="${DOCKERARGS} --pull --no-cache" # If --pull was specified, pass just that on. elif [[ "${BASH_ARGS}" =~ "--pull" ]]; then DOCKERARGS="${DOCKERARGS} --pull" fi echo "Building docker image ${IMAGE_NAME} using ${DOCKERFILE} in directory ${DOCKERDIR}" echo "Docker args: ${DOCKERARGS}" # This uses the new docker builder. It can build multi-platform images. The default-load=true # will load the image into the local machine so that docker image ls shows it. docker buildx create --name busnet-builder --driver-opt=default-load=true docker buildx build --builder busnet-builder --tag $IMAGE_NAME $DOCKERARGS --platform $CONFIG_DOCKER_BUILD_PLATFORM --file $DOCKERFILE $DOCKERDIR } # Execute Ansible inside the specified container. In order for this to work, the container image # must have Ansible and required Ansible modules already installed. # $1 playbook to execute # $2 extra vars to pass to Ansible in --extra-vars # # Set the VARIABLE $DOCKER_OPTIONS with any custom options you want, and we'll pass this to the docker # program verbatim. ansible_playbook() { ANSIBLE_IMAGE_NAME="ansible-busnet" PLAYBOOK=$1 EXTRA_VARS=$2 echo "Running Ansible playbook ${PLAYBOOK} in a container using ansible image ${ANSIBLE_IMAGE_NAME} ..." docker inspect $ANSIBLE_IMAGE_NAME &>/dev/null if [ $? != 0 ]; then if [ "${ANSIBLE_IMAGE_NAME}" == "ansible-busnet" ]; then echo "You need to build the ansible docker image first with --ansible-build" exit 1 else echo "I just attempted to run the container $ANSIBLE_IMAGE_NAME, but I could not because it doesn't exist on your system." exit 1 fi; fi # SSH remote user read -e -p "Enter username for remote SSH: " ansible_user # SSH key file ansible_ssh_private_key_file=${CONFIG_ANSIBLE_KEY_DIR_INSIDE}/${CONFIG_ANSIBLE_KEY_NAME} EXTRA_VARS="${EXTRA_VARS} ansible_user=${ansible_user} ansible_ssh_private_key_file=${ansible_ssh_private_key_file}" echo "EXTRA VARS (if any): ${EXTRA_VARS}" echo docker run \ --rm \ -it \ --network host \ $CONFIG_DOCKER_ENV \ $CONFIG_DOCKER_MOUNTS \ --name ansible-busnet-runner \ $DOCKER_OPTIONS \ $ANSIBLE_IMAGE_NAME \ ansible-playbook $PLAYBOOK --extra-vars "${EXTRA_VARS}" } # Regular 'ole docker run, non-interactively. # # $1 docker image name to run # $2 name of the container # $3 command to run. Defaults to sleep infinity (what's the point in that) # $4 optional workdir # # Set the variable DOCKER_OPTIONS with any custom options you want, and we'll pass this to the docker # program verbatim. # docker_run() { IMAGE_NAME=$1 CONTAINER_NAME=$2 if [[ -z "$3" ]]; then COMMAND="sleep infinity" else COMMAND=$3 fi if [[ -z "$4" ]]; then WORKDIR="" else WORKDIR="-w ${4}" fi docker inspect $IMAGE_NAME &>/dev/null if [ $? != 0 ]; then if [ "${IMAGE_NAME}" == "ansible-builder" ]; then echo "You need to build the ansible docker image first with --ansible-build" exit 1 else echo "I just attempted to run the container $IMAGE_NAME, but I could not because it doesn't exist on your system. You may need to build it first with build.sh" exit 1 fi; fi # Stop it if it's already running. docker_stop $CONTAINER_NAME docker run --rm --network host ${WORKDIR} \ $CONFIG_DOCKER_MOUNTS \ $CONFIG_DOCKER_ENV \ --name $CONTAINER_NAME \ $DOCKER_OPTIONS \ $IMAGE_NAME \ $COMMAND } # Run a container in interactive mode. When you exit, the container exists and is removed automatically. # # $1 docker image name to run # $2 name of the container # $3 command to run. Defaults to sleep infinity (what's the point in that) # $4 optional workdir # # Set the variable DOCKER_OPTIONS with any custom options you want, and we'll pass this to the docker # program verbatim. # docker_run_it() { IMAGE_NAME=$1 CONTAINER_NAME=$2 if [[ -z "$3" ]]; then COMMAND="sleep infinity" else COMMAND=$3 fi if [[ -z "$4" ]]; then WORKDIR="" else WORKDIR="-w ${4}" fi docker inspect $IMAGE_NAME &>/dev/null if [ $? != 0 ]; then if [ "${IMAGE_NAME}" == "ansible-builder" ]; then echo "You need to build the ansible docker image first with --ansible-build" exit 1 else echo "I just attempted to run the container $IMAGE_NAME, but I could not because it doesn't exist on your system. You may need to build it first with build.sh" exit 1 fi; fi echo "COmmand: ${COMMAND}" # Stop it if it's already running. docker_stop $CONTAINER_NAME docker run --rm -it --network host ${WORKDIR} \ $CONFIG_DOCKER_MOUNTS \ $CONFIG_DOCKER_ENV \ --name $CONTAINER_NAME \ $DOCKER_OPTIONS \ $IMAGE_NAME \ $COMMAND } # Same as docker_run_it() but do not check if the image exists. This works only if the image is # publicly available or in a repo that we have access to. Docker will just fetch the image automatically, # whereas docker_run_it() will throw an error if it doesn't exst locally. Ugh. # $1 docker image name to run # $2 name of the container # $3 command to run. Defaults to sleep infinity (what's the point in that) # $4 optional workdir # # Set the variable DOCKER_OPTIONS with any custom options you want, and we'll pass this to the docker # program verbatim. # docker_run_it_nocheck() { IMAGE_NAME=$1 CONTAINER_NAME=$2 if [[ -z "$3" ]]; then COMMAND="sleep infinity" else COMMAND=$3 fi if [[ -z "$4" ]]; then WORKDIR="" else WORKDIR="-w ${4}" fi # Stop it if it's already running. docker_stop $CONTAINER_NAME docker run --rm -it --network host ${WORKDIR} \ $CONFIG_DOCKER_MOUNTS \ $CONFIG_DOCKER_ENV \ --name $CONTAINER_NAME \ $DOCKER_OPTIONS \ $IMAGE_NAME \ $COMMAND } # Run and detach a container with the command "sleep infinity". We'll then connect to this container # with Ansible and do some tasks on it, then save its state. # $1 docker image name to run # $2 name of the container # $3 command to run. Defaults to sleep infinity # # Set the variable DOCKER_OPTIONS with any custom options you want, and we'll pass this to the docker # program verbatim. # # Set the variable DOCKER_FORCE_NEW=1 to always start a new container each time. The default is to re-use # already running containers. # docker_run_detach() { IMAGE_NAME=$1 CONTAINER_NAME=$2 if [[ -z "$3" ]]; then COMMAND="sleep infinity" else COMMAND=$3 fi docker inspect $IMAGE_NAME &>/dev/null if [ $? != 0 ]; then if [ "${IMAGE_NAME}" == "ansible-builder" ]; then echo "You need to build the ansible docker image first with --ansible-build" exit 1 else echo "I just attempted to run the image $IMAGE_NAME, but I could not because it doesn't exist on your system." exit 1 fi; fi # Always create a new container if this flag is set. # This stops any previously running containers. If none are running, then move on. if [ "${DOCKER_FORCE_NEW}" == "1" ]; then docker_stop $CONTAINER_NAME fi # Run the image in a container if it's not already running. if [ $( docker ps | grep $CONTAINER_NAME | wc -l ) == 0 ]; then echo "Running docker image ${IMAGE_NAME} in container ${CONTAINER_NAME} ..." docker run \ --rm \ --detach \ --network host \ $CONFIG_DOCKER_MOUNTS \ $CONFIG_DOCKER_ENV \ --name $CONTAINER_NAME \ $DOCKER_OPTIONS \ $IMAGE_NAME \ $COMMAND else echo "Docker image ${IMAGE_NAME} already running in container ${CONTAINER_NAME}." fi } # Same as docker_run_detach() but do not check if the image exists. This works only if the image is # publicly available or in a repo that we have access to. Docker will just fetch the image automatically, # whereas docker_run_detach() will throw an error if it doesn't exst locally. Ugh. # $1 docker image name to run # $2 name of the container # $3 command to run. Defaults to sleep infinity # # Set the variable DOCKER_OPTIONS with any custom options you want, and we'll pass this to the docker # program verbatim. # # Set the variable DOCKER_FORCE_NEW=1 to always start a new container each time. The default is to re-use # already running containers. # docker_run_detach_nocheck() { IMAGE_NAME=$1 CONTAINER_NAME=$2 if [[ -z "$3" ]]; then COMMAND="sleep infinity" else COMMAND=$3 fi # Always create a new container if this flag is set. # This stops any previously running containers. If none are running, then move on. if [ "${DOCKER_FORCE_NEW}" == "1" ]; then docker_stop $CONTAINER_NAME fi # Run the image in a container if it's not already running. if [ $( docker ps | grep $CONTAINER_NAME | wc -l ) == 0 ]; then echo "Running docker image ${IMAGE_NAME} in container ${CONTAINER_NAME} ..." docker run \ --rm \ --detach \ --network host \ $CONFIG_DOCKER_MOUNTS \ $CONFIG_DOCKER_ENV \ --name $CONTAINER_NAME \ $DOCKER_OPTIONS \ $IMAGE_NAME \ $COMMAND else echo "Docker image ${IMAGE_NAME} already running in container ${CONTAINER_NAME}." fi } # Stop a docker container docker_stop() { CONTAINER_NAME=$1 if [ $(docker container ls | grep $CONTAINER_NAME | wc -l) -ge 1 ]; then echo "Stopping docker container ${CONTAINER_NAME}" docker stop $CONTAINER_NAME fi }