I use Docker to run several microservices across my websites. Generally, they are standalone tools and demonstrations, mostly made to teach myself how to get these things done in microservices.

I recently came across the concept of utility containers. Up to now, all the containers I run are meant to run all the time, and be easily replaced by spinning up a replacement container. Utility containers, on the other hand, are literally containers that hold the bits needed to process input data into output data, do that process, then exit.

At the same time, I’ve been transitioning from Google’s blogger platform onto my in-home hardware, using Hugo. I’ve written my own little Docker utility container to hold the go language and the Hugo executable, specifically made to process data checked out from my hugo-content and hugo-static (private) repositories and automatically check the outputs back into my blog-htdocs repository.

I’m writing this post directly on my gitlab website, and it will be the first post that I’m writing directly into source control, to be processed by my hugo-builder container, and posted publicly.

Detail: Dockerfile

NOTE: As this is a test-post, the below is VERY likely to change (possibly, in many ways).

# Docker 20.10.16
FROM		alpine:latest
MAINTAINER	Gary Allen Vollink g.hugo@vollink.com

RUN apk update \
    && apk upgrade \
    && apk add coreutils shadow bash openssh curl go git \
    && mkdir /root/.ssh \
    && ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 \
        -N '' -C 'git@hugobuilder' -q \
    && chmod 700 /root/.ssh \
    && chmod 600 /root/.ssh/id_ed25519 \
    && chmod 644 /root/.ssh/id_ed25519.pub \
    && curl -LOs https://github.com/gohugoio/hugo/releases/download/v0.99.1/hugo_0.99.1_Linux-64bit.tar.gz \
    && cd /usr/local/bin \
    && tar xfz /hugo_0.99.1_Linux-64bit.tar.gz \
    && rm LICENSE README.md \
    && /bin/echo "#######################" \
    && /bin/echo "## Add key to gitlab." \
    && /bin/echo "#######################" \
    && cat /root/.ssh/id_ed25519.pub \
    && /bin/echo "#######################"

COPY src/* /run/
CMD /run/entry.sh

Detail: entry.sh

For now, I’m not sharing the whole thing. Key points:

  • Does /work exist (it came from -v on the command line):
    • Store the owner’s UID SAVED_USERID
    • Store the owner’s GID SAVID_GROUPID
  • If There’s no user for SAVED_USERID
    • Create the user (and group if needed)
  • If there is a user for SAVED_USERID
    • Modify the user to make sure it can be used for work
    • Get or add a home directory
    • Make sure the shell is set to bash
  • If /work doesn’t exist, use UID/GID 33 (Ubuntu’s www-data userid)
    • Create /work
  • Back up any .ssh and .gitconfig that are “in the way”
  • Copy the /root/.ssh to the user folder.
  • Execute the go_hugo.sh script (see below).
  • Cleanup/revert any .ssh changes
  • Cleanup/revert any .gitconfig changes

Details go_hugo.sh

#!/bin/bash
#############################################################################
VAR_ERROR=""
cd /work
# Read the environment package that entry.sh left us.
if [ -r "$1" ]
then
    echo "Reading $1"
    eval $(cat "$1")
else
    echo "Unable to read $1"
    ls -ld "$1"
fi
# Read any environment package that a user put in /work
if [ -r "/work/hugobuilder.env" ]
then
    eval $(cat "/work/hugobuilder.env")
fi
# Check for expected variables
#   These should all have something, even if left unused.
if [ -z "$GROUP_NAME" ]
then
    VAR_ERROR="${VAR_ERROR}GROUP_NAME:"
fi
if [ -z "$WORK_GID" ]
then
    VAR_ERROR="${VAR_ERROR}WORK_GID:"
fi
if [ -z "$WORK_NAME" ]
then
    VAR_ERROR="${VAR_ERROR}WORK_NAME:"
fi
if [ -z "$WORK_HOME" ]
then
    VAR_ERROR="${VAR_ERROR}WORK_HOME:"
fi
if [ -z "$WORK_UID" ]
then
    VAR_ERROR="${VAR_ERROR}WORK_UID:"
fi
if [ -z "$HAS_CONFIG" ]
then
    VAR_ERROR="${VAR_ERROR}HAS_CONFIG:"
fi
if [ ! -z "$VAR_ERROR" ]
then
    echo "ERR: Expected variables missing: ${VAR_ERROR}"
    echo "HAS_CONFIG=$HAS_CONFIG"
    exit 2
fi

if [ -z "$GIT_SSH_COMMAND" ]
then
    # ONLY if the user has not given us a better one.
    GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null"
    GIT_SSH_COMMAND="${GIT_SSH_COMMAND} -o StrictHostKeyChecking=no"
fi
export GIT_SSH_COMMAND

if [ -r "/work/.gitconfig" ]
then
    # If it exists, this was already backed up by entry.sh
    cp "/work/.gitconfig" "${WORK_HOME}/.gitconfig"
fi

# See if we have the two settings needed for `git commit`
# Assume we don't
_NEED_GCFG_E=1
_NEED_GCFG_N=1
if [ -r "${WORK_HOME}/.gitconfig" ]
then
    grep 'user.email' "${WORK_HOME}/.gitconfig" 2>&1 >/dev/null 
    if [ "0" = "$?" ]
    then
        # Unless we find it
        _NEED_GCFG_E=0
    fi
    grep 'user.name' "${WORK_HOME}/.gitconfig" 2>&1 >/dev/null 
    if [ "0" = "$?" ]
    then
        _NEED_GCFG_N=0
    fi
fi

# Add needed git settings.
if [ "1" = "${_NEED_GCFG_E}" ]
then
    git config --global user.email "hugobuilder-auto@vollink.com"
fi
if [ "1" = "${_NEED_GCFG_N}" ]
then
    git config --global user.name "Hugo Builder Automation"
fi

if [ ! -d "/work/blog/.git" ]
then
    git clone --recursive \
        ssh://git@gitlab.home.vollink.com:30022/external/blog-htdocs.git \
        "/work/blog"
    if [ "0" -ne "$?" ]
    then
        echo "ERROR: git failed."
        echo "Was key added to gitlab?"
        echo "===>"
        cat ${WORK_HOME}/.ssh/id_ed25519.pub
        echo "<==="
        exit 1
    fi
    if [ ! -d "/work/blog/htdocs" ]
    then
        echo "ERROR: git claims success, but blog/htdocs was not created."
        exit 1
    fi
fi
cd /work
if [ ! -d "/work/hugo-blog/.git" ]
then
    git clone --recursive \
        ssh://git@gitlab.home.vollink.com:30022/home/web/hugo-blog.git \
        "/work/hugo-blog"
    if [ "0" -ne "$?" ]
    then
        echo "ERROR: git failed."
        echo "Was key added to gitlab?"
        echo "===>"
        cat ${WORK_HOME}/.ssh/id_ed25519.pub
        echo "<==="
        exit 1
    fi
    if [ ! -d "/work/hugo-blog" ]
    then
        echo "ERROR: git claims success, but hugo-blog/ was not created."
        exit 1
    fi
fi

cd /work/hugo-blog
git fetch --all
git pull
git submodule foreach git pull origin master

##
# before running hugo: do I have a config?
if [ -r "${HAS_CONFIG}" ]
then
    cp "${HAS_CONFIG}" "/work/hugo-blog/."
fi
/usr/local/bin/hugo --destination "/work/blog/htdocs"
# This is bullshit, by the way... if there is a git FOLDER in the
# destination, hugo will delete it entirely before replacing everything,
# so I've set this up so the destination is one layer deep.
rm /work/blog/htdocs/.git
cd /work/blog
git add .
if [ "0" = "$?" ]
then
    git commit -m 'docker hugobuilder automated check-in.'
    if [ "0" = "$?" ]
    then
        git push origin master
        if [ "0" -ne "$?" ]
        then
            exit 1
        fi
    fi
fi

Update

The page above was run through the hugobuilder container and deployed using a git pull from my web server. With this edit, I’m going to attempt to let the hugobuilder and my various crontabs deploy this automatically (checks are done on a 10 minute schedule). There won’t need to be a further update if this just works.