From c86ce4e2151ba2aad3c2c9d5a836450ac739077e Mon Sep 17 00:00:00 2001 From: shmick Date: Tue, 21 Nov 2023 22:35:11 +0200 Subject: [PATCH] Sanitized for CV --- hatarashi-hako.sh | 57 +++++++++++++++++++++++++++++++-------------- hatarashi-hako.yaml | 4 ++-- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/hatarashi-hako.sh b/hatarashi-hako.sh index b8169b9..1a96192 100755 --- a/hatarashi-hako.sh +++ b/hatarashi-hako.sh @@ -1,8 +1,19 @@ #!/bin/bash -##Script to update Docker container images occasionally and alert when update is done. +## This script is a helper script to automatically update Docker containers in a less aggressive way than +#+ existing solutions. It avoids database upgrades and generates its' log in a Prometheus-ready template, +#+ which enables generation of Prometheus alerts when a container is updated, or fails to update. +## The process: +#+ 1. Iterate over the primary container directory and search for Docker stacks +#+ 2. For each Docker stack found, iterate over the images and get their checksum +#+ 3. For each image found, login to docker.io and get the remote checksum +#+ (new repositories, such as quay.io and ghcr.io will be added in the future, if needed) +#+ 4. Compare the checksums. If they differ (assumed newer), pull the remote image +#+ 5. Bring the stack up with the new image +## +#+ The key outcomes of this process (container updated, failed to update, update status undetermined) +#+ Are written to a file monitored by Prometheus. Based on the code assigned to each, Prometheus +#+ sends out an alert (container xyz has been updated, etc). -#This is where containers live -CONTAINER_DIR="/var/Red-Vol/Media/Containers/" show_help() @@ -12,7 +23,8 @@ show_help() echo " {-h|--help} -- Print this help message and exit" exit 0 } -#Pass arguments to the script + +# Pass arguments to the script flags() { while test $# -gt 0 @@ -24,7 +36,7 @@ flags() if [[ -d $CONTAINER_DIR/$1 ]]; then export CONTAINER_PATHS="$CONTAINER_DIR/$1" else - CONTAINER_PATHS="$(find /var/Red-Vol/Media/Containers/ -maxdepth 1 -type d -name "*$1*" | head -1)" + CONTAINER_PATHS="$(find $CONTAINER_DIR -maxdepth 1 -type d -name "*$1*" | head -1)" if [[ -n $CONTAINER_PATHS ]]; then export CONTAINER_PATHS fi @@ -42,32 +54,38 @@ flags() } flags "$@" +## Defaults +# Directory to search for stacks +CONTAINER_DIR="/some/container/path/" -#Remember where you are to change back to later -LOCAL_DIR=`pwd` - -#File to write results to; picked up by Prometheus and yells about changes +# File to write results to; picked up by Prometheus and yells about changes PROM_FILE="$CONTAINER_DIR/prometheus/data/hatarashi-hako.prom" -#Remove log from last runs, if present +# Remove log from last runs, if present if [[ -f $PROM_FILE ]]; then rm $PROM_FILE fi -#Check if path is already set by user specified stack; otherwise, find all containers. +# Remember initial execution directory, to return to after the script has finished +LOCAL_DIR=`pwd` + + +# Check if path is already set by user specified stack (-s, --stack); otherwise, find all containers. if [[ -z $CONTAINER_PATHS ]]; then CONTAINER_PATHS=$(find $CONTAINER_DIR -maxdepth 2 -type f -name docker-compose.yml ! -path '*Archive*' | xargs dirname ) +# Find containers in ^ base dir ^ in base container path ^ by finding compose files ^ (not here) ^ and getting their directory name. fi -#Find containers in ^ base dir ^ in base container path ^ by finding compose files ^ (not here) ^ and getting their directory name. for container_path in ${CONTAINER_PATHS[@]}; do + cd $container_path echo -e "Working on container directory" "$container_path" container_stack=$(basename $container_path) echo -e "Working on stack" "$container_stack" - #It's deadly to update tagless database images; this line is safe because it only catches tagged images. + # It's deadly to update tagless database images; this line is safe because it only catches tagged images. container_images="$(cat $container_path/docker-compose.yml | grep -E "image: ([a-z]+)((/)|(:))([a-z]+)?(:)?([a-z0-9].*$)?" | awk '{print $2}')" # search for a pattern of something:something with optional :tag print ^ image name + for container_image in $container_images; do echo -e "$container_stack has image" "$container_image" echo -e "echo $container_image | awk -F/ '{print \$2}' | sed 's/\:.*//'" @@ -77,6 +95,7 @@ for container_path in ${CONTAINER_PATHS[@]}; do export container_name="$container_image" fi echo -e "$container_image has name" "$container_name" + if [[ -n $(echo $container_image | grep -E "(.*:[a-z0-9].*$)") ]]; then # check if there is a :tag present ^ image_tag=":$(echo $container_image | awk -F: '{print $NF}')" @@ -87,6 +106,7 @@ for container_path in ${CONTAINER_PATHS[@]}; do export container_name=$(echo $container_name | awk -F: '{print $1}') fi echo -e "Fetching local image checksum with:" "docker inspect \"$container_image$image_tag\" | grep -Eo \"($container_image@)?sha256:([0-9a-zA-Z].*)(\\\")\" | sed -e 's/\"//g' | awk -F@ '{print \$2}" + local_image=$(docker inspect "$container_image$image_tag" | grep -Eo "($container_image@)?sha256:([0-9a-zA-Z].*)(\")" | sed -e 's/"//g' -e 's/\s+//g' | awk -F@ '{print $2}') # remember, this bit ^ is empty without an image ^ this is the main image checksum remove ^ " and whitespace and^ get the checksum after the @ if [[ -z $local_image ]]; then @@ -97,14 +117,16 @@ for container_path in ${CONTAINER_PATHS[@]}; do else echo -e "Local SHA256 for $container_image is" "$local_image" fi - echo -e "Fetching remote image with:" "skopeo inspect --creds \"dkd6:$PASS\" docker://docker.io/$container_image$image_tag | grep Digest | head -1 | grep -Eo 'sha256:([0-9a-zA-Z].*)(\")' | sed -e 's/\"//g'" + + echo -e "Fetching remote image with:" "skopeo inspect --creds \"$SKOPEO_USER:$PASS\" docker://docker.io/$container_image$image_tag | grep Digest | head -1 | grep -Eo 'sha256:([0-9a-zA-Z].*)(\")' | sed -e 's/\"//g'" #Use Skopeo, a Red Hat tool, with my Docker Hub account to register the remote image checksum - remote_image=$(skopeo inspect --creds "dkd6:$PASS" docker://docker.io/$container_image$image_tag | grep Digest | head -1 | grep -Eo 'sha256:([0-9a-zA-Z].*)(")' | sed -e 's/"//g' -e 's/\s+//g' ) + remote_image=$(skopeo inspect --creds "$SKOPEO_USER:$PASS" docker://docker.io/$container_image$image_tag | grep Digest | head -1 | grep -Eo 'sha256:([0-9a-zA-Z].*)(")' | sed -e 's/"//g' -e 's/\s+//g' ) #Sometimes; Docker hub hangs up; try again if you failed if [[ -z $remote_image ]]; then - remote_image=$(skopeo inspect --creds "dkd6:$PASS" docker://docker.io/$container_image$image_tag | grep Digest | head -1 | grep -Eo 'sha256:([0-9a-zA-Z].*)(")' | sed -e 's/"//g') + remote_image=$(skopeo inspect --creds "$SKOPEO_USER:$PASS" docker://docker.io/$container_image$image_tag | grep Digest | head -1 | grep -Eo 'sha256:([0-9a-zA-Z].*)(")' | sed -e 's/"//g') fi - #Now, if you still don't have an image after the second try, something's fuckey. + + #Now, if you still don't have an image after the second try, something's up. if [[ -z $remote_image ]]; then echo -e "Error fetching remote image checksum for container" "$container_name!" echo "container_updated{name=\"$container_name\"} -1" >> $PROM_FILE @@ -112,6 +134,7 @@ for container_path in ${CONTAINER_PATHS[@]}; do else echo -e "Remote SHA256 for $container_image is" "$remote_image" fi + #If we have both checksums, compare them; they should be identical, or the container is outdated. if [[ -n $local_image ]] && [[ -n $remote_image ]] && [[ "$local_image" =~ "$remote_image" ]]; then echo -e "$container_name" "is up to date!" diff --git a/hatarashi-hako.yaml b/hatarashi-hako.yaml index 3d993c6..4d55a8a 100644 --- a/hatarashi-hako.yaml +++ b/hatarashi-hako.yaml @@ -1,4 +1,4 @@ -- hosts: takahe +- hosts: potato gather_facts: no become: yes vars: @@ -6,7 +6,7 @@ ansible_sudo_pass: "{{ ANSIBLE_SUDO_PASS }}" tasks: - - name: Install Skopeo + - name: Make sure Skopeo is present and up to date ansible.builtin.package: name: skopeo state: latest