This commit is contained in:
@@ -1,8 +1,19 @@
|
|||||||
#!/bin/bash
|
#!/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()
|
show_help()
|
||||||
@@ -12,6 +23,7 @@ show_help()
|
|||||||
echo " {-h|--help} -- Print this help message and exit"
|
echo " {-h|--help} -- Print this help message and exit"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Pass arguments to the script
|
# Pass arguments to the script
|
||||||
flags()
|
flags()
|
||||||
{
|
{
|
||||||
@@ -24,7 +36,7 @@ flags()
|
|||||||
if [[ -d $CONTAINER_DIR/$1 ]]; then
|
if [[ -d $CONTAINER_DIR/$1 ]]; then
|
||||||
export CONTAINER_PATHS="$CONTAINER_DIR/$1"
|
export CONTAINER_PATHS="$CONTAINER_DIR/$1"
|
||||||
else
|
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
|
if [[ -n $CONTAINER_PATHS ]]; then
|
||||||
export CONTAINER_PATHS
|
export CONTAINER_PATHS
|
||||||
fi
|
fi
|
||||||
@@ -42,9 +54,9 @@ flags()
|
|||||||
}
|
}
|
||||||
flags "$@"
|
flags "$@"
|
||||||
|
|
||||||
|
## Defaults
|
||||||
#Remember where you are to change back to later
|
# Directory to search for stacks
|
||||||
LOCAL_DIR=`pwd`
|
CONTAINER_DIR="/some/container/path/"
|
||||||
|
|
||||||
# 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"
|
PROM_FILE="$CONTAINER_DIR/prometheus/data/hatarashi-hako.prom"
|
||||||
@@ -54,13 +66,18 @@ if [[ -f $PROM_FILE ]]; then
|
|||||||
rm $PROM_FILE
|
rm $PROM_FILE
|
||||||
fi
|
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
|
if [[ -z $CONTAINER_PATHS ]]; then
|
||||||
CONTAINER_PATHS=$(find $CONTAINER_DIR -maxdepth 2 -type f -name docker-compose.yml ! -path '*Archive*' | xargs dirname )
|
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
|
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
|
for container_path in ${CONTAINER_PATHS[@]}; do
|
||||||
|
|
||||||
cd $container_path
|
cd $container_path
|
||||||
echo -e "Working on container directory" "$container_path"
|
echo -e "Working on container directory" "$container_path"
|
||||||
container_stack=$(basename $container_path)
|
container_stack=$(basename $container_path)
|
||||||
@@ -68,6 +85,7 @@ for container_path in ${CONTAINER_PATHS[@]}; do
|
|||||||
# 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}')"
|
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
|
# search for a pattern of something:something with optional :tag print ^ image name
|
||||||
|
|
||||||
for container_image in $container_images; do
|
for container_image in $container_images; do
|
||||||
echo -e "$container_stack has image" "$container_image"
|
echo -e "$container_stack has image" "$container_image"
|
||||||
echo -e "echo $container_image | awk -F/ '{print \$2}' | sed 's/\:.*//'"
|
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"
|
export container_name="$container_image"
|
||||||
fi
|
fi
|
||||||
echo -e "$container_image has name" "$container_name"
|
echo -e "$container_image has name" "$container_name"
|
||||||
|
|
||||||
if [[ -n $(echo $container_image | grep -E "(.*:[a-z0-9].*$)") ]]; then
|
if [[ -n $(echo $container_image | grep -E "(.*:[a-z0-9].*$)") ]]; then
|
||||||
# check if there is a :tag present ^
|
# check if there is a :tag present ^
|
||||||
image_tag=":$(echo $container_image | awk -F: '{print $NF}')"
|
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}')
|
export container_name=$(echo $container_name | awk -F: '{print $1}')
|
||||||
fi
|
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}"
|
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}')
|
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 @
|
# 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
|
if [[ -z $local_image ]]; then
|
||||||
@@ -97,14 +117,16 @@ for container_path in ${CONTAINER_PATHS[@]}; do
|
|||||||
else
|
else
|
||||||
echo -e "Local SHA256 for $container_image is" "$local_image"
|
echo -e "Local SHA256 for $container_image is" "$local_image"
|
||||||
fi
|
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
|
#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
|
#Sometimes; Docker hub hangs up; try again if you failed
|
||||||
if [[ -z $remote_image ]]; then
|
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
|
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
|
if [[ -z $remote_image ]]; then
|
||||||
echo -e "Error fetching remote image checksum for container" "$container_name!"
|
echo -e "Error fetching remote image checksum for container" "$container_name!"
|
||||||
echo "container_updated{name=\"$container_name\"} -1" >> $PROM_FILE
|
echo "container_updated{name=\"$container_name\"} -1" >> $PROM_FILE
|
||||||
@@ -112,6 +134,7 @@ for container_path in ${CONTAINER_PATHS[@]}; do
|
|||||||
else
|
else
|
||||||
echo -e "Remote SHA256 for $container_image is" "$remote_image"
|
echo -e "Remote SHA256 for $container_image is" "$remote_image"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#If we have both checksums, compare them; they should be identical, or the container is outdated.
|
#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
|
if [[ -n $local_image ]] && [[ -n $remote_image ]] && [[ "$local_image" =~ "$remote_image" ]]; then
|
||||||
echo -e "$container_name" "is up to date!"
|
echo -e "$container_name" "is up to date!"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- hosts: takahe
|
- hosts: potato
|
||||||
gather_facts: no
|
gather_facts: no
|
||||||
become: yes
|
become: yes
|
||||||
vars:
|
vars:
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
ansible_sudo_pass: "{{ ANSIBLE_SUDO_PASS }}"
|
ansible_sudo_pass: "{{ ANSIBLE_SUDO_PASS }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Install Skopeo
|
- name: Make sure Skopeo is present and up to date
|
||||||
ansible.builtin.package:
|
ansible.builtin.package:
|
||||||
name: skopeo
|
name: skopeo
|
||||||
state: latest
|
state: latest
|
||||||
|
|||||||
Reference in New Issue
Block a user