mirror of
https://github.com/mag37/dockcheck.git
synced 2026-02-15 07:48:14 +01:00
Podcheck Initial Release
This commit is contained in:
parent
0009adc2cf
commit
61a38ad6c1
8 changed files with 213 additions and 193 deletions
154
README.md
154
README.md
|
|
@ -1,104 +1,100 @@
|
|||
<p align="center">
|
||||
<img src="extras/dockcheck_logo_by_booYah187.png" width="160" title="dockcheck">
|
||||
<img src="extras/podcheck.png" width="160" title="Podcheck">
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/bash-4.3-green?style=flat-square&logo=gnubash" alt="bash">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/badge/license-GPLv3-red?style=flat-square" alt="GPLv3"></a>
|
||||
<img src="https://img.shields.io/github/v/tag/mag37/dockcheck?style=flat-square&label=release" alt="release">
|
||||
<a href="https://ko-fi.com/mag37"><img src="https://img.shields.io/badge/-Ko--fi-grey?style=flat-square&logo=Ko-fi" alt="Buy me a Coffee"></a>
|
||||
<a href="https://liberapay.com/user-bin-rob/donate"><img src="https://img.shields.io/badge/-LiberaPay-grey?style=flat-square&logo=liberapay" alt="LiberaPay"></a>
|
||||
<a href="https://github.com/sponsors/mag37"><img src="https://img.shields.io/badge/-Sponsor-grey?style=flat-square&logo=github" alt="Github Sponsor"></a>
|
||||
<img src="https://img.shields.io/github/v/tag/sudo-kraken/podcheck?style=flat-square&label=release" alt="release">
|
||||
<a href="https://www.buymeacoffee.com/jharrison94"><img src="https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee" alt="Buy Me A Coffee">
|
||||
</p>
|
||||
|
||||
<h3 align="center">CLI tool to automate docker image updates. <br>No <b>pre-pull</b>, selective, optional notifications and prune when done.</h3>
|
||||
<h3 align="center">CLI tool to automate Podman image updates. <br>Selective updates, optional notifications, and image pruning when done.</h3>
|
||||
<h2 align="center">Now with simple notification integrations!</h2>
|
||||
<h4 align="center">With features like excluding specific containers, custom container labels, auto-prune when done and more.</h4>
|
||||
<h4 align="center">Features include excluding specific containers, custom container labels, auto-prune when done, and more.</h4>
|
||||
|
||||
___
|
||||
## :bell: Changelog
|
||||
|
||||
- **v0.5.0**: Rewritten notify logic - all templates are adjusted and should be migrated!
|
||||
- Copy the custom settings from your current template to the new version of the same template.
|
||||
- Look into, copy and customize the `urls.list` file if that's of interest.
|
||||
- Other changes:
|
||||
- Added Discord notify template.
|
||||
- Verbosity changed of `regctl`.
|
||||
- **v0.4.9**: Added a function to enrich the notify-message with release note URLs. See [Release notes addon](https://github.com/mag37/dockcheck#date-release-notes-addon-to-notifications)
|
||||
- **v0.4.8**: Rewrote prune logic to not prompt with options `-a|-y` or `-n`. Auto prune with `-p`.
|
||||
- **v0.4.7**: Notification Template changes to gotify(new!), DSM(improved), SMTP(deprecation alternative).
|
||||
- **v0.4.6**: Compatibility changes to timeout, due to busybox.
|
||||
- **v0.4.5**: Bugfixes, compatibility changes to timeout and arrays.
|
||||
- **v0.4.3**: Added timeout option to skip container if registry check takes too long (10s default).
|
||||
- **v0.5.6**: Directly checking for systemd units matching container names.
|
||||
- Improved Quadlet detection by checking for systemd units named after the container.
|
||||
- Ensures better compatibility with Quadlet-managed containers.
|
||||
- **v0.5.5**: Switched to podman compose command.
|
||||
- Adjusted the script to use podman compose instead of podman-compose.
|
||||
- Removed unnecessary messages.
|
||||
- **v0.5.4**: Improved Quadlet detection by matching container IDs with systemd units.
|
||||
- The script now searches systemd unit files for references to the container ID.
|
||||
- Provides reliable detection of Quadlet-managed containers.
|
||||
- **v0.5.0**: Initial release of Podcheck, inspired by Dockcheck.
|
||||
- Supports updating containers managed by Podman Compose and Quadlet.
|
||||
- Includes options for automatic updates, notifications, and more.
|
||||
___
|
||||
|
||||
|
||||

|
||||
|
||||
## :mag_right: `dockcheck.sh`
|
||||
## :mag_right: `podcheck.sh`
|
||||
```
|
||||
$ ./dockcheck.sh -h
|
||||
Syntax: dockcheck.sh [OPTION] [part of name to filter]
|
||||
Example: dockcheck.sh -y -d 10 -e nextcloud,heimdall
|
||||
$ ./podcheck.sh -h
|
||||
Syntax: podcheck.sh [OPTION] [part of name to filter]
|
||||
Example: podcheck.sh -y -d 10 -e nextcloud,heimdall
|
||||
|
||||
Options:"
|
||||
Options:
|
||||
-a|y Automatic updates, without interaction.
|
||||
-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower.
|
||||
-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age.
|
||||
-e X Exclude containers, separated by comma.
|
||||
-f Force stack restart after update. Caution: restarts once for every updated container within stack.
|
||||
-f Force pod restart after update.
|
||||
-h Print this Help.
|
||||
-i Inform - send a preconfigured notification.
|
||||
-l Only update if label is set. See readme.
|
||||
-m Monochrome mode, no printf color codes.
|
||||
-n No updates, only checking availability.
|
||||
-p Auto-Prune dangling images after update.
|
||||
-r Allow updating images for docker run, wont update the container.
|
||||
-s Include stopped containers in the check. (Logic: docker ps -a).
|
||||
-n No updates; only checking availability.
|
||||
-p Auto-prune dangling images after update.
|
||||
-r Allow updating images for podman run; won't update the container.
|
||||
-s Include stopped containers in the check.
|
||||
-t Set a timeout (in seconds) per container for registry checkups, 10 is default.
|
||||
-v Prints current version.
|
||||
```
|
||||
|
||||
|
||||
### Basic example:
|
||||
```
|
||||
$ ./dockcheck.sh
|
||||
. . .
|
||||
$ ./podcheck.sh
|
||||
...
|
||||
Containers on latest version:
|
||||
glances
|
||||
homer
|
||||
filebrowser
|
||||
foundryvtt
|
||||
|
||||
Containers with updates available:
|
||||
1) adguardhome
|
||||
2) syncthing
|
||||
3) whoogle-search
|
||||
1) joplin-db
|
||||
2) it-tools
|
||||
|
||||
Choose what containers to update:
|
||||
Enter number(s) separated by comma, [a] for all - [q] to quit:
|
||||
```
|
||||
Then it proceedes to run `pull` and `up -d` on every container with updates.
|
||||
After the updates are complete, you'll get prompted if you'd like to prune dangling images.
|
||||
Then it proceeds to run podman pull and podman compose up -d, or restarts systemd units for every container with updates.
|
||||
After the updates are complete, you'll be prompted if you'd like to prune dangling images
|
||||
|
||||
___
|
||||
|
||||
## :nut_and_bolt: Dependencies
|
||||
- Running docker (duh) and compose, either standalone or plugin.
|
||||
- Podman: Ensure you have Podman installed and properly configured.
|
||||
- Podman Compose: For containers managed with podman compose, make sure it's installed.
|
||||
- Note: podman compose is included in recent versions of Podman.
|
||||
- Quadlet: If you're using systemd units to manage your containers, ensure they are correctly set up.
|
||||
- Bash shell or compatible shell of at least v4.3
|
||||
- [regclient/regctl](https://github.com/regclient/regclient) (Licensed under [Apache-2.0 License](http://www.apache.org/licenses/LICENSE-2.0))
|
||||
- User will be prompted to download `regctl` if not in `PATH` or `PWD`.
|
||||
- regctl requires `amd64/arm64` - see [workaround](#roller_coaster-workaround-for-non-amd64--arm64) if other architecture is used.
|
||||
- jq: Used for parsing JSON output from podman inspect.
|
||||
- timeout: Optional but recommended for setting timeouts on registry checks.
|
||||
|
||||
## :tent: Install Instructions
|
||||
Download the script to a directory in **PATH**, I'd suggest using `~/.local/bin` as that's usually in **PATH**.
|
||||
```sh
|
||||
# basic example with curl:
|
||||
curl -L https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh -o ~/.local/bin/dockcheck.sh
|
||||
chmod +x ~/.local/bin/dockcheck.sh
|
||||
# Using curl:
|
||||
curl -L https://raw.githubusercontent.com/sudo-kraken/podcheck/main/podcheck.sh -o ~/.local/bin/podcheck.sh
|
||||
chmod +x ~/.local/bin/podcheck.sh
|
||||
|
||||
# or oneliner with wget:
|
||||
wget -O ~/.local/bin/dockcheck.sh "https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh" && chmod +x ~/.local/bin/dockcheck.sh
|
||||
# Or using wget:
|
||||
wget -O ~/.local/bin/podcheck.sh "https://raw.githubusercontent.com/sudo-kraken/podcheck/main/podcheck.sh" && chmod +x ~/.local/bin/podcheck.sh
|
||||
```
|
||||
Then call the script anywhere with just `dockcheck.sh`.
|
||||
Add preferred `notify.sh`-template to the same directory - this will not be touched by the scripts self-update function.
|
||||
|
||||
Then call the script anywhere with `podcheck.sh`.
|
||||
Add your preferred notify.sh template to the same directory—this will not be touched by the script's self-update function.
|
||||
|
||||
## :loudspeaker: Notifications
|
||||
Trigger with the `-i` flag.
|
||||
|
|
@ -120,30 +116,33 @@ Use a `notify_X.sh` template file from the **notify_templates** directory, copy
|
|||
- [Discord](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) - Discord webhooks.
|
||||
|
||||
Further additions are welcome - suggestions or PR!
|
||||
<sub><sup>Initiated and first contributed by [yoyoma2](https://github.com/yoyoma2).</sup></sub>
|
||||
<sub><sup>Initiated and first contributed by [mag37](https://github.com/mag37) as dockcheck.</sup></sub>
|
||||
|
||||
### :date: Release notes addon to Notifications
|
||||
There's a function to use a lookup-file to add release note URL's to the notification message.
|
||||
Copy the notify_templates/`urls.list` file to the script directory, it will be used automatically if it's there. Modify it as necessary, the names of interest in the left column needs to match your container names.
|
||||
There's a function to use a lookup file to add release note URLs to the notification message.
|
||||
|
||||
Copy the notify_templates/urls.list file to the script directory—it will be used automatically if it's there. Modify it as necessary; the names of interest in the left column need to match your container names.
|
||||
|
||||
The output of the notification will look something like this:
|
||||
```
|
||||
Containers on hostname with updates available:
|
||||
apprise-api -> https://github.com/linuxserver/docker-apprise-api/releases
|
||||
homer -> https://github.com/bastienwirtz/homer/releases
|
||||
nginx -> https://github.com/docker-library/official-images/blob/master/library/nginx
|
||||
joplin-db -> https://github.com/laurent22/joplin/releases
|
||||
it-tools -> https://github.com/CorentinTh/it-tools/releases
|
||||
...
|
||||
```
|
||||
The `urls.list` file is just an example and I'd gladly see that people contribute back when they add their preferred URLs to their lists.
|
||||
|
||||
## :bookmark: Labels
|
||||
Optionally add labels to compose-files. Currently these are the usable labels:
|
||||
|
||||
Optionally, you can add labels to your containers to control how Podcheck handles them. Currently, these are the usable labels:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
sudo-kraken.podcheck.restart-stack: true
|
||||
sudo-kraken.podcheck.update: true
|
||||
```
|
||||
labels:
|
||||
mag37.dockcheck.restart-stack: true
|
||||
mag37.dockcheck.update: true
|
||||
```
|
||||
- `mag37.dockcheck.restart-stack: true` works instead of the `-f` option, forcing stop+restart on the whole compose-stack (Caution: Will restart on every updated container within stack).
|
||||
- `mag37.dockcheck.update: true` will when used with the `-l` option only update containers with this label and skip the rest. Will still list updates as usual.
|
||||
- `sudo-kraken.podcheck.restart-stack`: true works instead of the `-f` option, forcing a restart of the entire pod or compose stack when an update is applied. Caution: This will restart the entire stack for every updated container within it.
|
||||
- `sudo-kraken.podcheck.update`: true will, when used with the `-l` option, only update containers with this label and skip the rest. It will still list all available updates.
|
||||
|
||||
## :roller_coaster: Workaround for non **amd64** / **arm64**
|
||||
`regctl` provides binaries for amd64/arm64, to use on other architecture you could try this workaround.
|
||||
|
|
@ -169,8 +168,8 @@ Test it with `./regctl --help` and then either add the file to the same path as
|
|||
**Example** - Change names, paths, and remove cat+password flag if you rather get prompted:
|
||||
```sh
|
||||
function dchk {
|
||||
cat ~/pwd.txt | docker login --username YourUser --password-stdin
|
||||
~/dockcheck.sh "$@"
|
||||
cat ~/pwd.txt | podman login --username YourUser --password-stdin docker.io
|
||||
~/podcheck.sh "$@"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -182,21 +181,22 @@ function dchk {
|
|||
|
||||
## :warning: `-r flag` disclaimer and warning
|
||||
**Wont auto-update the containers, only their images. (compose is recommended)**
|
||||
`docker run` dont support using new images just by restarting a container.
|
||||
`podman run` does not support using new images just by restarting a container.
|
||||
Containers need to be manually stopped, removed and created again to run on the new image.
|
||||
|
||||
## :wrench: Debugging
|
||||
If you hit issues, you could check the output of the `extras/errorCheck.sh` script for clues.
|
||||
Another option is to run the main script with debugging in a subshell `bash -x dockcheck.sh` - if there's a particular container/image that's causing issues you can filter for just that through `bash -x dockcheck.sh nginx`.
|
||||
Another option is to run the main script with debugging in a subshell `bash -x podcheck.sh` - if there's a particular container/image that's causing issues you can filter for just that through `bash -x podcheck.sh nginx`.
|
||||
|
||||
## :scroll: License
|
||||
dockcheck is created and released under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0-standalone.html) license.
|
||||
|
||||
## :heartpulse: Sponsorlist
|
||||
|
||||
- [avegy](https://github.com/avegy)
|
||||
|
||||
podcheck is created and released under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0-standalone.html) license.
|
||||
___
|
||||
|
||||
### :floppy_disk: The [story](https://mag37.org/posts/project_dockcheck/) behind it. 1 year in retrospect.
|
||||
### :floppy_disk: The Story Behind Podcheck
|
||||
Podcheck was created to bring the convenience of automated container updates to the Podman ecosystem. As a user of Dockcheck for Docker, the need for a similar tool for Podman became apparent. Podcheck aims to provide the same ease of use and automation, tailored for Podman users.
|
||||
|
||||
## :star2: Acknowledgments
|
||||
Podcheck is inspired by the original Dockcheck script. Without Dockcheck, there wouldn't have been a Podcheck. Many thanks to mag37 and all the contributors to Dockcheck for their work and inspiration.
|
||||
___
|
||||
Please feel free to contribute, open issues, or submit pull requests to improve Podcheck!
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ IFS=',' read -r -a Excludes <<< "$Exclude" ; unset IFS
|
|||
|
||||
SearchName="$1"
|
||||
|
||||
for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
|
||||
for i in $(podman ps --filter "name=$SearchName" --format '{{.Names}}') ; do
|
||||
for e in "${Excludes[@]}" ; do [[ "$i" == "$e" ]] && continue 2 ; done
|
||||
printf ". "
|
||||
RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}')
|
||||
LocalHash=$(docker image inspect "$RepoUrl" --format '{{.RepoDigests}}')
|
||||
RepoUrl=$(podman inspect "$i" --format='{{.ImageName}}')
|
||||
LocalHash=$(podman image inspect "$RepoUrl" --format '{{.Digest}}')
|
||||
### Checking for errors while setting the variable:
|
||||
if RegHash=$($regbin image digest --list "$RepoUrl" 2>/dev/null) ; then
|
||||
if [[ "$LocalHash" = *"$RegHash"* ]] ; then NoUpdates+=("$i"); else GotUpdates+=("$i"); fi
|
||||
if [[ "$LocalHash" == "$RegHash" ]] ; then NoUpdates+=("$i"); else GotUpdates+=("$i"); fi
|
||||
else
|
||||
GotErrors+=("$i")
|
||||
fi
|
||||
|
|
@ -41,7 +41,7 @@ if [[ -n ${NoUpdates[*]} ]] ; then
|
|||
printf "%s\n" "${NoUpdates[@]}"
|
||||
fi
|
||||
if [[ -n ${GotErrors[*]} ]] ; then
|
||||
printf "\n\033[0;31mContainers with errors, wont get updated:\033[0m\n"
|
||||
printf "\n\033[0;31mContainers with errors; won't get updated:\033[0m\n"
|
||||
printf "%s\n" "${GotErrors[@]}"
|
||||
fi
|
||||
if [[ -n ${GotUpdates[*]} ]] ; then
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
|
|
@ -1,13 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
SearchName="$1"
|
||||
for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
|
||||
for i in $(podman ps --filter "name=$SearchName" --format '{{.Names}}') ; do
|
||||
echo "------------ $i ------------"
|
||||
ContPath=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')
|
||||
[ -z "$ContPath" ] && { "$i has no compose labels - skipping" ; continue ; }
|
||||
ContConfigFile=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}')
|
||||
ContName=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.service" }}')
|
||||
ContEnv=$(docker inspect "$i" --format '{{index .Config.Labels "com.docker.compose.project.environment_file" }}')
|
||||
ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
|
||||
ContPath=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')
|
||||
if [ -z "$ContPath" ]; then
|
||||
echo "$i has no compose labels - skipping"
|
||||
continue
|
||||
fi
|
||||
ContConfigFile=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}')
|
||||
ContName=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.service" }}')
|
||||
ContEnv=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.environment_file" }}')
|
||||
ContImage=$(podman inspect "$i" --format='{{.ImageName}}')
|
||||
|
||||
if [[ $ContConfigFile = '/'* ]] ; then
|
||||
ComposeFile="$ContConfigFile"
|
||||
|
|
@ -22,6 +25,6 @@ for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
|
|||
echo -e "Container image:\t$ContImage"
|
||||
echo
|
||||
echo "Mounts:"
|
||||
docker inspect -f '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ printf "\n" }}{{ end }}' "$i"
|
||||
podman inspect -f '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ printf "\n" }}{{ end }}' "$i"
|
||||
echo
|
||||
done
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 472 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 404 KiB |
BIN
extras/podcheck.png
Normal file
BIN
extras/podcheck.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
223
dockcheck.sh → podcheck.sh
Executable file → Normal file
223
dockcheck.sh → podcheck.sh
Executable file → Normal file
|
|
@ -1,43 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
VERSION="v0.5.0"
|
||||
### ChangeNotes: Rewritten notify logic - all templates adjusted, transfer your current settings to a new template! See README.
|
||||
Github="https://github.com/mag37/dockcheck"
|
||||
RawUrl="https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh"
|
||||
VERSION="v0.5.6"
|
||||
# ChangeNotes: Directly checking for systemd units matching container names.
|
||||
Github="https://github.com/sudo-kraken/podcheck"
|
||||
RawUrl="https://raw.githubusercontent.com/sudo-kraken/podcheck/main/podcheck.sh"
|
||||
|
||||
### Variables for self updating
|
||||
# Variables for self-updating
|
||||
ScriptArgs=( "$@" )
|
||||
ScriptPath="$(readlink -f "$0")"
|
||||
ScriptWorkDir="$(dirname "$ScriptPath")"
|
||||
|
||||
### Check if there's a new release of the script:
|
||||
LatestRelease="$(curl -s -r 0-50 $RawUrl | sed -n "/VERSION/s/VERSION=//p" | tr -d '"')"
|
||||
LatestChanges="$(curl -s -r 0-200 $RawUrl | sed -n "/ChangeNotes/s/### ChangeNotes: //p")"
|
||||
# Check if there's a new release of the script
|
||||
LatestRelease="$(curl -s -r 0-100 $RawUrl | sed -n "/VERSION/s/VERSION=//p" | tr -d '"')"
|
||||
LatestChanges="$(curl -s -r 0-200 $RawUrl | sed -n "/ChangeNotes/s/# ChangeNotes: //p")"
|
||||
|
||||
### Help Function:
|
||||
# Help Function
|
||||
Help() {
|
||||
echo "Syntax: dockcheck.sh [OPTION] [part of name to filter]"
|
||||
echo "Example: dockcheck.sh -y -d 10 -e nextcloud,heimdall"
|
||||
echo "Syntax: podcheck.sh [OPTION] [part of name to filter]"
|
||||
echo "Example: podcheck.sh -y -d 10 -e nextcloud,heimdall"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo "-a|y Automatic updates, without interaction."
|
||||
echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower."
|
||||
echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age."
|
||||
echo "-e X Exclude containers, separated by comma."
|
||||
echo "-f Force stack restart after update. Caution: restarts once for every updated container within stack."
|
||||
echo "-f Force pod restart after update."
|
||||
echo "-h Print this Help."
|
||||
echo "-i Inform - send a preconfigured notification."
|
||||
echo "-l Only update if label is set. See readme."
|
||||
echo "-m Monochrome mode, no printf color codes."
|
||||
echo "-n No updates, only checking availability."
|
||||
echo "-p Auto-Prune dangling images after update."
|
||||
echo "-r Allow updating images for docker run, wont update the container."
|
||||
echo "-s Include stopped containers in the check. (Logic: docker ps -a)."
|
||||
echo "-n No updates; only checking availability."
|
||||
echo "-p Auto-prune dangling images after update."
|
||||
echo "-r Allow updating images for podman run; won't update the container."
|
||||
echo "-s Include stopped containers in the check."
|
||||
echo "-t Set a timeout (in seconds) per container for registry checkups, 10 is default."
|
||||
echo "-v Prints current version."
|
||||
echo
|
||||
echo "Project source: $Github"
|
||||
}
|
||||
|
||||
### Colors:
|
||||
# Colors
|
||||
c_red="\033[0;31m"
|
||||
c_green="\033[0;32m"
|
||||
c_yellow="\033[0;33m"
|
||||
|
|
@ -54,7 +54,7 @@ while getopts "aynpfrhlisvme:d:t:" options; do
|
|||
r) DRunUp="yes" ;;
|
||||
p) AutoPrune="yes" ;;
|
||||
l) OnlyLabel=true ;;
|
||||
f) ForceRestartStacks=true ;;
|
||||
f) ForceRestartPods=true ;;
|
||||
i) [ -s "$ScriptWorkDir"/notify.sh ] && { source "$ScriptWorkDir"/notify.sh ; Notify="yes" ; } ;;
|
||||
e) Exclude=${OPTARG} ;;
|
||||
m) declare c_{red,green,yellow,blue,teal,reset}="" ;;
|
||||
|
|
@ -68,18 +68,19 @@ while getopts "aynpfrhlisvme:d:t:" options; do
|
|||
done
|
||||
shift "$((OPTIND-1))"
|
||||
|
||||
# Self-update functions
|
||||
self_update_curl() {
|
||||
cp "$ScriptPath" "$ScriptPath".bak
|
||||
if [[ $(builtin type -P curl) ]]; then
|
||||
if [[ $(command -v curl) ]]; then
|
||||
curl -L $RawUrl > "$ScriptPath" ; chmod +x "$ScriptPath"
|
||||
printf "\n%s\n" "--- starting over with the updated version ---"
|
||||
exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
|
||||
exit 1 # exit the old instance
|
||||
elif [[ $(builtin type -P wget) ]]; then
|
||||
exec "$ScriptPath" "${ScriptArgs[@]}" # Run the new script with old arguments
|
||||
exit 1 # Exit the old instance
|
||||
elif [[ $(command -v wget) ]]; then
|
||||
wget $RawUrl -O "$ScriptPath" ; chmod +x "$ScriptPath"
|
||||
printf "\n%s\n" "--- starting over with the updated version ---"
|
||||
exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
|
||||
exit 1 # exit the old instance
|
||||
exec "$ScriptPath" "${ScriptArgs[@]}" # Run the new script with old arguments
|
||||
exit 1 # Exit the old instance
|
||||
else
|
||||
printf "curl/wget not available - download the update manually: %s \n" "$Github"
|
||||
fi
|
||||
|
|
@ -87,20 +88,20 @@ self_update_curl() {
|
|||
|
||||
self_update() {
|
||||
cd "$ScriptWorkDir" || { printf "Path error, skipping update.\n" ; return ; }
|
||||
if [[ $(builtin type -P git) ]] && [[ "$(git ls-remote --get-url 2>/dev/null)" =~ .*"mag37/dockcheck".* ]] ; then
|
||||
if [[ $(command -v git) ]] && [[ "$(git ls-remote --get-url 2>/dev/null)" =~ .*"yourusername/podcheck".* ]] ; then
|
||||
printf "\n%s\n" "Pulling the latest version."
|
||||
git pull --force || { printf "Git error, manually pull/clone.\n" ; return ; }
|
||||
printf "\n%s\n" "--- starting over with the updated version ---"
|
||||
cd - || { printf "Path error.\n" ; return ; }
|
||||
exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
|
||||
exit 1 # exit the old instance
|
||||
exec "$ScriptPath" "${ScriptArgs[@]}" # Run the new script with old arguments
|
||||
exit 1 # Exit the old instance
|
||||
else
|
||||
cd - || { printf "Path error.\n" ; return ; }
|
||||
self_update_curl
|
||||
fi
|
||||
}
|
||||
|
||||
### Choose from list -function:
|
||||
# Choose from list function
|
||||
choosecontainers() {
|
||||
while [[ -z "$ChoiceClean" ]]; do
|
||||
read -r -p "Enter number(s) separated by comma, [a] for all - [q] to quit: " Choice
|
||||
|
|
@ -112,7 +113,7 @@ choosecontainers() {
|
|||
else
|
||||
ChoiceClean=${Choice//[,.:;]/ }
|
||||
for CC in $ChoiceClean ; do
|
||||
if [[ "$CC" -lt 1 || "$CC" -gt $UpdCount ]] ; then # reset choice if out of bounds
|
||||
if [[ "$CC" -lt 1 || "$CC" -gt $UpdCount ]] ; then
|
||||
echo "Number not in list: $CC" ; unset ChoiceClean ; break 1
|
||||
else
|
||||
SelectedUpdates+=( "${GotUpdates[$CC-1]}" )
|
||||
|
|
@ -139,27 +140,16 @@ progress_bar() {
|
|||
QueCurrent="$1"
|
||||
QueTotal="$2"
|
||||
((Percent=100*QueCurrent/QueTotal))
|
||||
((Complete=50*Percent/100)) # change first number for width (50)
|
||||
((Left=50-Complete)) # change first number for width (50)
|
||||
((Complete=50*Percent/100)) # Change first number for width (50)
|
||||
((Left=50-Complete)) # Change first number for width (50)
|
||||
BarComplete=$(printf "%${Complete}s" | tr " " "#")
|
||||
BarLeft=$(printf "%${Left}s" | tr " " "-")
|
||||
[[ "$QueTotal" == "$QueCurrent" ]] || printf "\r[%s%s] %s/%s " "$BarComplete" "$BarLeft" "$QueCurrent" "$QueTotal"
|
||||
[[ "$QueTotal" == "$QueCurrent" ]] && printf "\r[%b%s%b] %s/%s \n" "$c_teal" "$BarComplete" "$c_reset" "$QueCurrent" "$QueTotal"
|
||||
}
|
||||
|
||||
### Function to add user-provided urls to releasenotes
|
||||
releasenotes() {
|
||||
for update in ${GotUpdates[@]}; do
|
||||
found=false
|
||||
while read -r container url; do
|
||||
[[ $update == $container ]] && Updates+=("$update -> $url") && found=true
|
||||
done < "$ScriptWorkDir"/urls.list
|
||||
[[ $found == false ]] && Updates+=("$update -> url missing") || continue
|
||||
done
|
||||
}
|
||||
|
||||
### Version check & initiate self update
|
||||
if [[ "$VERSION" != "$LatestRelease" ]] ; then
|
||||
# Version check & initiate self update
|
||||
if [[ "$VERSION" != "$LatestRelease" ]] && [[ -n "$LatestRelease" ]]; then
|
||||
printf "New version available! %b%s%b ⇒ %b%s%b \n Change Notes: %s \n" "$c_yellow" "$VERSION" "$c_reset" "$c_green" "$LatestRelease" "$c_reset" "$LatestChanges"
|
||||
if [[ -z "$AutoUp" ]] ; then
|
||||
read -r -p "Would you like to update? y/[n]: " SelfUpdate
|
||||
|
|
@ -167,26 +157,26 @@ if [[ "$VERSION" != "$LatestRelease" ]] ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
### Set $1 to a variable for name filtering later.
|
||||
# Set $1 to a variable for name filtering later
|
||||
SearchName="$1"
|
||||
### Create array of excludes
|
||||
# Create array of excludes
|
||||
IFS=',' read -r -a Excludes <<< "$Exclude" ; unset IFS
|
||||
|
||||
### Check if required binary exists in PATH or directory:
|
||||
if [[ $(builtin type -P "regctl") ]]; then regbin="regctl" ;
|
||||
# Check if required binary exists in PATH or directory
|
||||
if [[ $(command -v "regctl") ]]; then regbin="regctl" ;
|
||||
elif [[ -f "$ScriptWorkDir/regctl" ]]; then regbin="$ScriptWorkDir/regctl" ;
|
||||
else
|
||||
read -r -p "Required dependency 'regctl' missing, do you want it downloaded? y/[n] " GetDep
|
||||
if [[ "$GetDep" =~ [yY] ]] ; then
|
||||
### Check arch:
|
||||
# Check architecture
|
||||
case "$(uname --machine)" in
|
||||
x86_64|amd64) architecture="amd64" ;;
|
||||
arm64|aarch64) architecture="arm64";;
|
||||
*) echo "Architecture not supported, exiting." ; exit 1;;
|
||||
esac
|
||||
RegUrl="https://github.com/regclient/regclient/releases/latest/download/regctl-linux-$architecture"
|
||||
if [[ $(builtin type -P curl) ]]; then curl -L $RegUrl > "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
|
||||
elif [[ $(builtin type -P wget) ]]; then wget $RegUrl -O "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
|
||||
if [[ $(command -v curl) ]]; then curl -L $RegUrl > "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
|
||||
elif [[ $(command -v wget) ]]; then wget $RegUrl -O "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
|
||||
else
|
||||
printf "%s\n" "curl/wget not available - get regctl manually from the repo link, quitting."
|
||||
fi
|
||||
|
|
@ -195,21 +185,20 @@ else
|
|||
exit 1
|
||||
fi
|
||||
fi
|
||||
### final check if binary is correct
|
||||
# Final check if binary is correct
|
||||
$regbin version &> /dev/null || { printf "%s\n" "regctl is not working - try to remove it and re-download it, exiting."; exit 1; }
|
||||
|
||||
### Check docker compose binary:
|
||||
if docker compose version &> /dev/null ; then DockerBin="docker compose" ;
|
||||
elif docker-compose -v &> /dev/null; then DockerBin="docker-compose" ;
|
||||
elif docker -v &> /dev/null; then
|
||||
printf "%s\n" "No docker compose binary available, using plain docker (Not recommended!)"
|
||||
printf "%s\n" "'docker run' will ONLY update images, not the container itself."
|
||||
# Check podman compose binary
|
||||
if podman compose version &> /dev/null ; then PodmanComposeBin="podman compose" ;
|
||||
elif command -v podman-compose &> /dev/null; then PodmanComposeBin="podman-compose" ;
|
||||
elif podman version &> /dev/null; then
|
||||
printf "%s\n" "No podman-compose binary available, using plain podman"
|
||||
else
|
||||
printf "%s\n" "No docker binaries available, exiting."
|
||||
printf "%s\n" "No podman binaries available, exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
### Numbered List -function:
|
||||
# Numbered List function
|
||||
options() {
|
||||
num=1
|
||||
for i in "${GotUpdates[@]}"; do
|
||||
|
|
@ -218,7 +207,7 @@ for i in "${GotUpdates[@]}"; do
|
|||
done
|
||||
}
|
||||
|
||||
### Listing typed exclusions:
|
||||
# Listing typed exclusions
|
||||
if [[ -n ${Excludes[*]} ]] ; then
|
||||
printf "\n%bExcluding these names:%b\n" "$c_blue" "$c_reset"
|
||||
printf "%s\n" "${Excludes[@]}"
|
||||
|
|
@ -226,11 +215,11 @@ if [[ -n ${Excludes[*]} ]] ; then
|
|||
fi
|
||||
|
||||
# Variables for progress_bar function
|
||||
DocCount=$(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}' | wc -l)
|
||||
DocCount=$(podman ps $Stopped --filter "name=$SearchName" --format '{{.Names}}' | wc -l)
|
||||
RegCheckQue=0
|
||||
|
||||
### Testing and setting timeout binary
|
||||
t_out=$(type -P "timeout")
|
||||
# Testing and setting timeout binary
|
||||
t_out=$(command -v "timeout")
|
||||
if [[ $t_out ]]; then
|
||||
t_out=$(realpath $t_out 2>/dev/null || readlink -f $t_out)
|
||||
if [[ $t_out =~ "busybox" ]]; then
|
||||
|
|
@ -240,17 +229,17 @@ if [[ $t_out ]]; then
|
|||
else t_out=""
|
||||
fi
|
||||
|
||||
### Check the image-hash of every running container VS the registry
|
||||
for i in $(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}') ; do
|
||||
# Check the image-hash of every running container VS the registry
|
||||
for i in $(podman ps $Stopped --filter "name=$SearchName" --format '{{.Names}}') ; do
|
||||
((RegCheckQue+=1))
|
||||
progress_bar "$RegCheckQue" "$DocCount"
|
||||
### Looping every item over the list of excluded names and skipping:
|
||||
# Looping every item over the list of excluded names and skipping
|
||||
for e in "${Excludes[@]}" ; do [[ "$i" == "$e" ]] && continue 2 ; done
|
||||
RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}')
|
||||
LocalHash=$(docker image inspect "$RepoUrl" --format '{{.RepoDigests}}')
|
||||
# Checking for errors while setting the variable:
|
||||
RepoUrl=$(podman inspect "$i" --format='{{.ImageName}}')
|
||||
LocalHash=$(podman image inspect "$RepoUrl" --format '{{.Digest}}')
|
||||
# Checking for errors while setting the variable
|
||||
if RegHash=$(${t_out} $regbin -v error image digest --list "$RepoUrl" 2>&1) ; then
|
||||
if [[ "$LocalHash" = *"$RegHash"* ]] ; then
|
||||
if [[ "$LocalHash" == "$RegHash" ]] ; then
|
||||
NoUpdates+=("$i")
|
||||
else
|
||||
if [[ -n "$DaysOld" ]] && ! datecheck ; then
|
||||
|
|
@ -260,27 +249,27 @@ for i in $(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}')
|
|||
fi
|
||||
fi
|
||||
else
|
||||
# Here the RegHash is the result of an error code.
|
||||
# Here the RegHash is the result of an error code
|
||||
GotErrors+=("$i - ${RegHash}")
|
||||
fi
|
||||
done
|
||||
|
||||
### Sort arrays alphabetically
|
||||
# Sort arrays alphabetically
|
||||
IFS=$'\n'
|
||||
NoUpdates=($(sort <<<"${NoUpdates[*]}"))
|
||||
GotUpdates=($(sort <<<"${GotUpdates[*]}"))
|
||||
unset IFS
|
||||
|
||||
### Define how many updates are available
|
||||
# Define how many updates are available
|
||||
UpdCount="${#GotUpdates[@]}"
|
||||
|
||||
### List what containers got updates or not
|
||||
# List what containers got updates or not
|
||||
if [[ -n ${NoUpdates[*]} ]] ; then
|
||||
printf "\n%bContainers on latest version:%b\n" "$c_green" "$c_reset"
|
||||
printf "%s\n" "${NoUpdates[@]}"
|
||||
fi
|
||||
if [[ -n ${GotErrors[*]} ]] ; then
|
||||
printf "\n%bContainers with errors, wont get updated:%b\n" "$c_red" "$c_reset"
|
||||
printf "\n%bContainers with errors; won't get updated:%b\n" "$c_red" "$c_reset"
|
||||
printf "%s\n" "${GotErrors[@]}"
|
||||
printf "%binfo:%b 'unauthorized' often means not found in a public registry.\n" "$c_blue" "$c_reset"
|
||||
fi
|
||||
|
|
@ -290,11 +279,11 @@ if [[ -n ${GotUpdates[*]} ]] ; then
|
|||
[[ -n "$Notify" ]] && { [[ $(type -t send_notification) == function ]] && send_notification "${GotUpdates[@]}" || printf "Could not source notification function.\n" ; }
|
||||
fi
|
||||
|
||||
### Optionally get updates if there's any
|
||||
# Optionally get updates if there's any
|
||||
if [ -n "$GotUpdates" ] ; then
|
||||
if [ -z "$AutoUp" ] ; then
|
||||
printf "\n%bChoose what containers to update.%b\n" "$c_teal" "$c_reset"
|
||||
choosecontainers
|
||||
printf "\n%bChoose what containers to update.%b\n" "$c_teal" "$c_reset"
|
||||
choosecontainers
|
||||
else
|
||||
SelectedUpdates=( "${GotUpdates[@]}" )
|
||||
fi
|
||||
|
|
@ -305,47 +294,75 @@ if [ -n "$GotUpdates" ] ; then
|
|||
do
|
||||
((CurrentQue+=1))
|
||||
unset CompleteConfs
|
||||
ContPath=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')
|
||||
ContConfigFile=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}')
|
||||
ContName=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.service" }}')
|
||||
ContEnv=$(docker inspect "$i" --format '{{index .Config.Labels "com.docker.compose.project.environment_file" }}')
|
||||
ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
|
||||
ContUpdateLabel=$(docker inspect "$i" --format '{{ index .Config.Labels "mag37.dockcheck.update" }}')
|
||||
ContRestartStack=$(docker inspect "$i" --format '{{ index .Config.Labels "mag37.dockcheck.restart-stack" }}')
|
||||
### Checking if compose-values are empty - hence started with docker run:
|
||||
# Extract labels and metadata
|
||||
ContLabels=$(podman inspect "$i" --format '{{json .Config.Labels}}')
|
||||
ContPath=$(echo "$ContLabels" | jq -r '."com.docker.compose.project.working_dir"')
|
||||
[ "$ContPath" == "null" ] && ContPath=""
|
||||
|
||||
ContConfigFile=$(echo "$ContLabels" | jq -r '."com.docker.compose.project.config_files"')
|
||||
[ "$ContConfigFile" == "null" ] && ContConfigFile=""
|
||||
|
||||
ContName=$(echo "$ContLabels" | jq -r '."com.docker.compose.service"')
|
||||
[ "$ContName" == "null" ] && ContName=""
|
||||
|
||||
ContEnv=$(echo "$ContLabels" | jq -r '."com.docker.compose.project.environment_file"')
|
||||
[ "$ContEnv" == "null" ] && ContEnv=""
|
||||
|
||||
ContImage=$(podman inspect "$i" --format='{{.ImageName}}')
|
||||
|
||||
ContUpdateLabel=$(echo "$ContLabels" | jq -r '."sudo-kraken.podcheck.update"')
|
||||
[ "$ContUpdateLabel" == "null" ] && ContUpdateLabel=""
|
||||
|
||||
ContRestartStack=$(echo "$ContLabels" | jq -r '."sudo-kraken.podcheck.restart-stack"')
|
||||
[ "$ContRestartStack" == "null" ] && ContRestartStack=""
|
||||
|
||||
# Checking if compose-values are empty - possibly started with podman run or managed by Quadlet
|
||||
if [ -z "$ContPath" ] ; then
|
||||
if [ "$DRunUp" == "yes" ] ; then
|
||||
docker pull "$ContImage"
|
||||
printf "%s\n" "$i got a new image downloaded, rebuild manually with preferred 'docker run'-parameters"
|
||||
# Check if a systemd unit exists with the same name as the container
|
||||
if systemctl --user status "$i.service" &> /dev/null; then
|
||||
echo "Detected Quadlet-managed container: $i (unit: $i.service)"
|
||||
podman pull "$ContImage"
|
||||
systemctl --user restart "$i.service"
|
||||
echo "Quadlet container $i updated and restarted."
|
||||
elif [ "$(id -u)" -eq 0 ] && systemctl status "$i.service" &> /dev/null; then
|
||||
echo "Detected Quadlet-managed container: $i (unit: $i.service)"
|
||||
podman pull "$ContImage"
|
||||
systemctl restart "$i.service"
|
||||
echo "Quadlet container $i updated and restarted."
|
||||
else
|
||||
printf "\n%b%s%b has no compose labels, probably started with docker run - %bskipping%b\n\n" "$c_yellow" "$i" "$c_reset" "$c_yellow" "$c_reset"
|
||||
if [ "$DRunUp" == "yes" ] ; then
|
||||
podman pull "$ContImage"
|
||||
printf "%s\n" "$i got a new image downloaded; rebuild manually with preferred 'podman run' parameters"
|
||||
else
|
||||
printf "\n%b%s%b has no compose labels or associated systemd unit; %bskipping%b\n\n" "$c_yellow" "$i" "$c_reset" "$c_yellow" "$c_reset"
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
### cd to the compose-file directory to account for people who use relative volumes, eg - ${PWD}/data:data
|
||||
# cd to the compose-file directory to account for people who use relative volumes
|
||||
cd "$ContPath" || { echo "Path error - skipping $i" ; continue ; }
|
||||
## Reformatting path + multi compose
|
||||
# Reformatting path + multi compose
|
||||
if [[ $ContConfigFile = '/'* ]] ; then
|
||||
CompleteConfs=$(for conf in ${ContConfigFile//,/ } ; do printf -- "-f %s " "$conf"; done)
|
||||
else
|
||||
CompleteConfs=$(for conf in ${ContConfigFile//,/ } ; do printf -- "-f %s/%s " "$ContPath" "$conf"; done)
|
||||
fi
|
||||
printf "\n%bNow updating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset"
|
||||
### Checking if Label Only -option is set, and if container got the label
|
||||
[[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != true ]] && { echo "No update label, skipping." ; continue ; } }
|
||||
docker pull "$ContImage"
|
||||
### Check if the container got an environment file set and reformat it
|
||||
# Checking if Label Only option is set, and if container got the label
|
||||
[[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != "true" ]] && { echo "No update label, skipping." ; continue ; } }
|
||||
podman pull "$ContImage"
|
||||
# Check if the container got an environment file set and reformat it
|
||||
if [ -n "$ContEnv" ]; then ContEnvs=$(for env in ${ContEnv//,/ } ; do printf -- "--env-file %s " "$env"; done) ; fi
|
||||
### Check if the whole stack should be restarted
|
||||
if [[ "$ContRestartStack" == true ]] || [[ "$ForceRestartStacks" == true ]] ; then
|
||||
$DockerBin ${CompleteConfs} stop ; $DockerBin ${CompleteConfs} ${ContEnvs} up -d
|
||||
# Check if the whole pod should be restarted
|
||||
if [[ "$ContRestartStack" == "true" ]] || [[ "$ForceRestartPods" == true ]] ; then
|
||||
$PodmanComposeBin ${CompleteConfs} down ; $PodmanComposeBin ${CompleteConfs} ${ContEnvs} up -d
|
||||
else
|
||||
$DockerBin ${CompleteConfs} ${ContEnvs} up -d ${ContName}
|
||||
$PodmanComposeBin ${CompleteConfs} ${ContEnvs} up -d ${ContName}
|
||||
fi
|
||||
done
|
||||
printf "\n%bAll done!%b\n" "$c_green" "$c_reset"
|
||||
if [[ -z "$AutoPrune" ]] && [[ -z "$AutoUp" ]]; then read -r -p "Would you like to prune dangling images? y/[n]: " AutoPrune ; fi
|
||||
[[ "$AutoPrune" =~ [yY] ]] && docker image prune -f
|
||||
[[ "$AutoPrune" =~ [yY] ]] && podman image prune -f
|
||||
else
|
||||
printf "\nNo updates installed, exiting.\n"
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue