#!/bin/bash set -e # # This script can be used to facilitate supervisor development. Its core feature is allowing # faster development iterations by bind-mounting the local './dist' directly into the running # supervisor container. # # Setting the '--mount-nm' flag in either 'run' or 'buildrun' action will bind-mount # './node_modules/' into the running supervisor. In this case, it's up to the developer # to make sure that the correct dependencies are installed. # # Usage: dindctl action [options] # # Actions: # build build local supervisor image. By default it will be balena/amd64-supervisor:master, you can override the tag with --tag. # run [options] build dind host container, run it (with name balena_supervisor_1), which will include the specified supervisor image and run it. # buildrun [options] run 'build' and then immediately 'run' the built container. # refresh recompile sources in './src' and restart supervisor container on dind host - requires --mount-dist in order to work properly. # logs [-f] print out supervisor log files - use '-f' to follow instead, or any other arguments you'd send to journalctl. # stop stop dind supervisor host container. # Options: # --arch | -a [arch] architecture of the supervisor to build (default: amd64 ) # --image | -i [image] image name for supervisor image to build/use ( default: balena/$ARCH-supervisor:master ) # --dind-image [image] image to use for the balenaos-in-container host (default: resin/resinos:2.12.5_rev1-intel-nuc) # --dind-container [name] container name suffix for the dind host container ( default: "supervisor", which will produce a container named balena-container-supervisor) # --mount-dist bind-mount './dist/' (where webpack stores the built js) from local development environment into supervisor container. # --mount-nm bind-mount './node_modules/' from local development environment into supervisor container. # --mount-backup bind-mount './tools/dind/backup.tgz' to simulate a migration backup. # --preload | -p use tools/dind/apps.json to preload an application image into the dind host. # --config | -c [file] path to config.json, relative to tools/dind ( default: config.json ) # --tag | -t [tag] for the "build" action, specify the tag to build (default: master) # --no-clean for the "stop" action, skip removing the data, boot and state volumes # # See README.md for examples. # # The script requires make and docker. # THIS_FILE=$0 set -o errexit set -o pipefail DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) SUPERVISOR_BASE_DIR="${DIR}" DIND_DIR="${SUPERVISOR_BASE_DIR}/tools/dind/" ARCH="amd64" SUPERVISOR_IMAGE="balena/${ARCH}-supervisor:master" DIND_IMAGE="resin/resinos:2.48.0_rev3-intel-nuc" MOUNT_DIST="false" MOUNT_NODE_MODULES="false" CONTAINER_NAME="supervisor" PRELOADED_IMAGE="" OPTIMIZE="true" CONFIG_FILENAME="${DIND_DIR}config.json" TAG="master" CLEAN_VOLUMES="true" MOUNT_BACKUP="false" function showHelp { cat $THIS_FILE | awk '{if(/^#/)print;else exit}' | tail -n +2 | sed 's/\#//' | sed 's|dindctl|'$THIS_FILE'|' } function parseOptions { while [[ $# -ge 1 ]] do case $1 in --mount-dist) MOUNT_DIST="true" ;; --mount-nm) MOUNT_NODE_MODULES="true" ;; --mount-backup) MOUNT_BACKUP="true" ;; -p|--preload) PRELOADED_IMAGE="true" ;; -i|--image) SUPERVISOR_IMAGE="$2" shift || { echo "--image provided not specified" && exit 1; } ;; -c|--config) CONFIG_FILENAME="$2" shift || { echo "--config provided not specified" && exit 1; } ;; --dind-image) DIND_IMAGE="$2" shift || { echo "--dind-image provided not specified" && exit 1; } ;; --dind-container) CONTAINER_NAME="$2" shift || { echo "--dind-container provided not specified" && exit 1; } ;; -a|--arch) ARCH="$2" shift || { echo "--arch provided not specified" && exit 1; } ;; -t|--tag) TAG="$2" shift || { echo "--tag provided not specified" && exit 1; } ;; -n|--no-optimize) OPTIMIZE="false" ;; --no-clean) CLEAN_VOLUMES="false" ;; *) echo "Warning: unknown argument: $1" ;; esac shift done } SUPERVISOR_DIND_MOUNTS="-v ${DIND_DIR}/config/supervisor-image.tar:/usr/src/supervisor-image.tar:ro" SUPERVISOR_DIND_MOUNTS="${SUPERVISOR_DIND_MOUNTS} -v ${DIND_DIR}/config/supervisor.conf:/etc/balena-supervisor/supervisor.conf" if [ "${MOUNT_DIST}" == "true" ]; then SUPERVISOR_DIND_MOUNTS="${SUPERVISOR_DIND_MOUNTS} -v ${DIND_DIR}/../../dist:/balena-supervisor/dist" fi if [ "${PRELOADED_IMAGE}" == "true" ]; then SUPERVISOR_DIND_MOUNTS="${SUPERVISOR_DIND_MOUNTS} -v ${DIND_DIR}/apps.json:/mnt/data/apps.json" fi if [ "${MOUNT_NODE_MODULES}" == "true" ]; then SUPERVISOR_DIND_MOUNTS="${SUPERVISOR_DIND_MOUNTS} -v ${DIND_DIR}/../../node_modules:/balena-supervisor/node_modules" fi if [ "${MOUNT_BACKUP}" == "true" ]; then SUPERVISOR_DIND_MOUNTS="${SUPERVISOR_DIND_MOUNTS} -v ${DIND_DIR}/backup.tgz:/mnt/data/backup.tgz.mounted" fi if [ "${PRELOADED_IMAGE}" == "true" ]; then SUPERVISOR_DIND_MOUNTS="${SUPERVISOR_DIND_MOUNTS} -v ${DIND_DIR}/apps.json:/mnt/data/apps.json" fi function buildSupervisor { echo "Building supervisor image for architecture $ARCH and tagging as $TAG" ARCH="$ARCH" TAG="$TAG" bash automation/build.sh } function buildSupervisorSrc { if [ "$OPTIMIZE" = "true" ]; then echo "Rebuilding supervisor source" ( cd "$SUPERVISOR_BASE_DIR" && npm install && npm run build ) else echo "Rebuilding supervisor source without optimizations" ( cd "$SUPERVISOR_BASE_DIR" && npm install && npm run build -- --env.noOptimize ) fi } function refreshSupervisorSrc { buildSupervisorSrc echo "Restarting the supervisor container" docker exec -ti balena-container-$CONTAINER_NAME systemctl restart balena-supervisor } function runDind { if [ ! -f "${DIND_DIR}/balenaos-in-container/balenaos-in-container.sh" ]; then (cd $SUPERVISOR_BASE_DIR; git submodule update --init) fi if [ "$MOUNT_DIST" = "true" ]; then buildSupervisorSrc echo "Running with mounted dist folder" fi if [ "$PRELOADED_IMAGE" = "true" ]; then echo "Running with preloaded apps" fi if ! ( docker inspect $SUPERVISOR_IMAGE &> /dev/null ); then echo "$SUPERVISOR_IMAGE not available locally, pulling" docker pull $SUPERVISOR_IMAGE fi # Save the requested image into a tar file mkdir -p "${DIND_DIR}/config" && docker save --output "${DIND_DIR}/config/supervisor-image.tar" "${SUPERVISOR_IMAGE}" echo "${SUPERVISOR_IMAGE}" | awk -F: '{print "SUPERVISOR_IMAGE="$1"\nSUPERVISOR_TAG="$2"\nLED_FILE=/dev/null"}' > "${DIND_DIR}/config/supervisor.conf" echo "Starting dind supervisor" "${DIND_DIR}/balenaos-in-container/balenaos-in-container.sh" \ --detach \ --config "${CONFIG_FILENAME}" \ --image "${DIND_IMAGE}" \ --id "${CONTAINER_NAME}" \ --extra-args "${SUPERVISOR_DIND_MOUNTS}" } function stopDind { echo "Stopping dind supervisor" docker stop balena-container-${CONTAINER_NAME} > /dev/null 2>&1 || true docker rm -f --volumes balena-container-${CONTAINER_NAME} > /dev/null 2>&1 || true if [ "$CLEAN_VOLUMES" = "true" ]; then cleanDind fi } function cleanDind { echo "Cleaning dind supervisor volumes" docker volume rm "resin-boot-$CONTAINER_NAME" "resin-state-$CONTAINER_NAME" "resin-data-$CONTAINER_NAME" &> /dev/null || true } function logs { docker exec -ti balena-container-$CONTAINER_NAME journalctl $@ } action="$1" shift || true if [ "$action" = "logs" ]; then logs "$@" else parseOptions "$@" case $action in build) buildSupervisor ;; run) stopDind runDind ;; buildrun) buildSupervisor && runDind ;; refresh) refreshSupervisorSrc ;; stop) stopDind ;; *) showHelp ;; esac fi