Files
devops/orchestrate/lib/lib.sh
2025-09-23 10:54:10 -05:00

363 lines
9.9 KiB
Bash

#!/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
}