Initial Commit - Hatarashi Hako with working Actions
All checks were successful
Ansible Deploy / deploy (push) Successful in 1m1s
All checks were successful
Ansible Deploy / deploy (push) Successful in 1m1s
This commit is contained in:
31
.gitea/workflows/hatarashi-hako.yaml
Normal file
31
.gitea/workflows/hatarashi-hako.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Ansible Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
run: |
|
||||||
|
apt -y update
|
||||||
|
apt -y install python3 python3-pip skopeo
|
||||||
|
|
||||||
|
- name: Install Ansible
|
||||||
|
run: |
|
||||||
|
python3 -m pip install --upgrade pip
|
||||||
|
pip install ansible
|
||||||
|
|
||||||
|
- name: Set up SSH
|
||||||
|
uses: webfactory/ssh-agent@v0.5.0
|
||||||
|
with:
|
||||||
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Run Ansible Playbook
|
||||||
|
run: |
|
||||||
|
ansible-playbook -i inventory.yaml -e 'ANSIBLE_SUDO_PASS="${{ secrets.SUDO_PASS }}" SKOPEO_PASS="${{ secrets.SKOPEO_PASS }}"' hatarashi-hako.yaml -vv
|
||||||
8
hatarashi-hako.service.j2
Normal file
8
hatarashi-hako.service.j2
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Hatarashi Hako - Container update utility
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/bash /usr/local/bin/hatarashi-hako.sh -p {{ SKOPEO_PASS }}
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
143
hatarashi-hako.sh
Executable file
143
hatarashi-hako.sh
Executable file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
##Script to update Docker container images occasionally and alert when update is done.
|
||||||
|
|
||||||
|
#This is where containers live
|
||||||
|
CONTAINER_DIR="/var/Red-Vol/Media/Containers/"
|
||||||
|
|
||||||
|
|
||||||
|
show_help()
|
||||||
|
{
|
||||||
|
echo "Hatarashi Hako - Smartly update container stack"
|
||||||
|
echo " {-s|--stack} [name] -- Run update process just for specified stack (ex: vikunja)"
|
||||||
|
echo " {-h|--help} -- Print this help message and exit"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
#Pass arguments to the script
|
||||||
|
flags()
|
||||||
|
{
|
||||||
|
while test $# -gt 0
|
||||||
|
do
|
||||||
|
case "$1" in
|
||||||
|
#If a stack is specified, run the process for that stack only
|
||||||
|
(-s|--stack)
|
||||||
|
shift
|
||||||
|
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)"
|
||||||
|
if [[ -n $CONTAINER_PATHS ]]; then
|
||||||
|
export CONTAINER_PATHS
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
shift;;
|
||||||
|
(-p|--password)
|
||||||
|
shift
|
||||||
|
export PASS="$1"
|
||||||
|
shift;;
|
||||||
|
(-h|--help)
|
||||||
|
show_help;;
|
||||||
|
(*) show_help;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
flags "$@"
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
|
PROM_FILE="$CONTAINER_DIR/prometheus/data/hatarashi-hako.prom"
|
||||||
|
|
||||||
|
#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.
|
||||||
|
if [[ -z $CONTAINER_PATHS ]]; then
|
||||||
|
CONTAINER_PATHS=$(find $CONTAINER_DIR -maxdepth 2 -type f -name docker-compose.yml ! -path '*Archive*' | xargs dirname )
|
||||||
|
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.
|
||||||
|
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/\:.*//'"
|
||||||
|
container_name="$(echo $container_image | awk -F/ '{print $2}' | sed "s/\:.*//")"
|
||||||
|
# remove everything after the : ^
|
||||||
|
if [[ -z $container_name ]]; then #&& [[ -n $(echo $container_image | grep -Ev 'postgres|mariadb') ]]; then
|
||||||
|
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}')"
|
||||||
|
# !! Add : ^ before image !! so it is only added to later commands if there is an image at all
|
||||||
|
echo -e "$container_image has tag" "$image_tag"
|
||||||
|
export container_image=$(echo $container_image | awk -F: '{print $1}')
|
||||||
|
# If the container does have a tag, keep the base name ^ without it (before the :)
|
||||||
|
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
|
||||||
|
echo -e "Error fetching local image checksum for container $container_name!"
|
||||||
|
#The script will complain about failed containers later on
|
||||||
|
echo "container_updated{name=\"$container_name\"} -1" >> $PROM_FILE
|
||||||
|
continue 2
|
||||||
|
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'"
|
||||||
|
#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' )
|
||||||
|
#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')
|
||||||
|
fi
|
||||||
|
#Now, if you still don't have an image after the second try, something's fuckey.
|
||||||
|
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
|
||||||
|
continue 2
|
||||||
|
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!"
|
||||||
|
else
|
||||||
|
echo -e "$container_name" "is out of date!"
|
||||||
|
echo -e "cat \"$container_path/docker-compose.yml\" | grep -B1 \"image: $container_image\" | head -1 | sed -e 's/^[ \t]*//' -e 's/://g' | awk '{print \$NF}')"
|
||||||
|
service=$(cat "$container_path/docker-compose.yml" | grep -B1 "image: $container_image" | head -1 | sed -e 's/^[ \t]*//' -e 's/://g' | grep -v 'container_name')
|
||||||
|
# get container service name (1 line above image) ^ print service name^ ^ omit tabs and : and ^omit container_name
|
||||||
|
echo -e "Attempting to update service" "$service"
|
||||||
|
if docker compose pull $service; then
|
||||||
|
echo -e "Pulled latest image for" "$container_name"
|
||||||
|
if docker compose up -d --remove-orphans; then
|
||||||
|
echo -e "$container_stack" "has been updated sucessfully!"
|
||||||
|
echo "container_updated{name=\"$container_name\"} 1" >> $PROM_FILE
|
||||||
|
else
|
||||||
|
echo -e "Failed to update" "$container_name!"
|
||||||
|
echo "container_updated{name=\"$container_name\"} 0" >> $PROM_FILE
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "Failed to pull image for" "$container_name!"
|
||||||
|
echo "container_updated{name=\"$container_name\"} 0" >> $PROM_FILE
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
#If you found an image tag, reset it before moving on to another container
|
||||||
|
image_tag=""
|
||||||
|
done
|
||||||
|
cd $LOCAL_DIR
|
||||||
|
done
|
||||||
|
echo "All done!"
|
||||||
9
hatarashi-hako.timer.j2
Normal file
9
hatarashi-hako.timer.j2
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Hatarashi Hako - container update utility
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
32
hatarashi-hako.yaml
Normal file
32
hatarashi-hako.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
- hosts: takahe
|
||||||
|
gather_facts: no
|
||||||
|
become: yes
|
||||||
|
vars:
|
||||||
|
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
|
||||||
|
ansible_sudo_pass: "{{ ANSIBLE_SUDO_PASS }}"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Install Skopeo
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: skopeo
|
||||||
|
state: latest
|
||||||
|
|
||||||
|
- name: Install hatarashi-hako
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: hatarashi-hako.sh
|
||||||
|
dest: /usr/local/bin/hatarashi-hako.sh
|
||||||
|
mode: 'a+x'
|
||||||
|
|
||||||
|
- name: Template service and timer to host
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item }}.j2"
|
||||||
|
dest: /etc/systemd/system/{{ item }}
|
||||||
|
with_items:
|
||||||
|
- hatarashi-hako.service
|
||||||
|
- hatarashi-hako.timer
|
||||||
|
|
||||||
|
- name: Start hatarashi-hako timer
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: hatarashi-hako.timer
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
5
inventory.yaml
Normal file
5
inventory.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
hosts:
|
||||||
|
takahe:
|
||||||
|
ansible_host: 192.168.0.66
|
||||||
|
ansible_user: shmick
|
||||||
Reference in New Issue
Block a user