.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | CLI tool to automate docker image updates or notifying when updates are available.
14 | selective updates, exclude containers, custom labels, notification plugins, prune when done etc.
15 |
16 | :whale: Docker Hub pull limit :chart_with_downwards_trend: not an issue for checks but for actual pulls - read more
17 |
18 | For Podman - see the fork sudo-kraken/podcheck!
19 |
20 | ___
21 | ## :bell: Changelog
22 |
23 | - **v0.6.6**: Notify_v2 bugfixes
24 | - Clearer readme and error messages
25 | - Sourcing templates from either project root or subdirectory
26 | - Consistent newline handling
27 | - Added (when using `-d`) days old message to notification title
28 | - Added ntfy self hosted domain option to config
29 | - jq fixes to templates (and properly using $jqbin)
30 | - **v0.6.5**: Refactored notification logic. See notify_templates/notify_v2.sh for upgrade steps.
31 | - Added helper functions to simplify sourcing files and executing functions if they exist.
32 | - Created notify_v2.sh wrapper script.
33 | - Simplified and consolidated notification logic within notify_v2.sh.
34 | - Added support for notification management via environment variables.
35 | - Moved notification secrets to **dockcheck.config**.
36 | - Added retries to wget/curl to not get empty responses when github is slow.
37 | - **v0.6.4**: Restructured the update process - first pulls all updates, then recreates all containers.
38 | - Added logic to skip update check on non-compose containers (unless `-r` option).
39 | - Added option `-F` to revert to `compose up -d ` targeting specific container and not the stack.
40 | - Also added corresponding label and config-option.
41 | - Added markdown formatting to `notify_ntfy.sh` template.
42 | - **v0.6.3**: Some fixes and changes:
43 | - Stops when a container recreation (compose up -d) fails, also `up`s the whole stack now.
44 | - `-M`, Markdown format url-releasenotes in notification (requires template rework, look at gotify!)
45 | - Added [addons/DSM/README.md](./addons/DSM/README.md) for more info Synology DSM info.
46 | - Permission checks - graceful exit if no docker permissions + checking if root for pkg-manager.
47 | ___
48 |
49 |
50 | 
51 |
52 | ## :mag_right: `dockcheck.sh`
53 | ```
54 | $ ./dockcheck.sh -h
55 | Syntax: dockcheck.sh [OPTION] [part of name to filter]
56 | Example: dockcheck.sh -y -x 10 -d 10 -e nextcloud,heimdall
57 |
58 | Options:
59 | -a|y Automatic updates, without interaction.
60 | -c D Exports metrics as prom file for the prometheus node_exporter. Provide the collector textfile directory.
61 | -d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower.
62 | -e X Exclude containers, separated by comma.
63 | -f Force stop+start stack after update. Caution: restarts once for every updated container within stack.
64 | -F Only compose up the specific container, not the whole compose stack (useful for master-compose structure).
65 | -h Print this Help.
66 | -i Inform - send a preconfigured notification.
67 | -I Prints custom releasenote urls alongside each container with updates in CLI output (requires urls.list).
68 | -l Only update if label is set. See readme.
69 | -m Monochrome mode, no printf colour codes and hides progress bar.
70 | -M Prints custom releasenote urls as markdown (requires template support).
71 | -n No updates, only checking availability.
72 | -p Auto-Prune dangling images after update.
73 | -r Allow checking for updates/updating images for docker run containers. Won't update the container.
74 | -s Include stopped containers in the check. (Logic: docker ps -a).
75 | -t N Set a timeout (in seconds) per container for registry checkups, 10 is default.
76 | -u Allow automatic self updates - caution as this will pull new code and autorun it.
77 | -v Prints current version.
78 | -x N Set max asynchronous subprocesses, 1 default, 0 to disable, 32+ tested.
79 | ```
80 |
81 | ### Basic example:
82 | ```
83 | $ ./dockcheck.sh
84 | . . .
85 | Containers on latest version:
86 | glances
87 | homer
88 |
89 | Containers with updates available:
90 | 1) adguardhome
91 | 2) syncthing
92 | 3) whoogle-search
93 |
94 | Choose what containers to update:
95 | Enter number(s) separated by comma, [a] for all - [q] to quit:
96 | ```
97 | Then it proceeds to run `pull` and `up -d` on every container with updates.
98 | After the updates are complete, you'll get prompted if you'd like to prune dangling images.
99 |
100 | ___
101 |
102 | ## :nut_and_bolt: Dependencies
103 | - Running docker (duh) and compose, either standalone or plugin. (see [Podman fork](https://github.com/sudo-kraken/podcheck)
104 | - Bash shell or compatible shell of at least v4.3
105 | - POSIX `xargs`, usually default but can be installed with the `findutils` package - to enable async.
106 | - [jq](https://github.com/jqlang/jq)
107 | - User will be prompted to install with package manager or download static binary.
108 | - [regclient/regctl](https://github.com/regclient/regclient) (Licensed under [Apache-2.0 License](http://www.apache.org/licenses/LICENSE-2.0))
109 | - User will be prompted to download `regctl` if not in `PATH` or `PWD`.
110 | - regctl requires `amd64/arm64` - see [workaround](#roller_coaster-workaround-for-non-amd64--arm64) if other architecture is used.
111 |
112 | ## :tent: Install Instructions
113 | Download the script to a directory in **PATH**, I'd suggest using `~/.local/bin` as that's usually in **PATH**.
114 | For OSX/macOS preferably use `/usr/local/bin`.
115 | ```sh
116 | # basic example with curl:
117 | curl -L https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh -o ~/.local/bin/dockcheck.sh
118 | chmod +x ~/.local/bin/dockcheck.sh
119 |
120 | # or oneliner with wget:
121 | wget -O ~/.local/bin/dockcheck.sh "https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh" && chmod +x ~/.local/bin/dockcheck.sh
122 |
123 | # OSX or macOS version with curl:
124 | curl -L https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh -o /usr/local/bin/dockcheck.sh && chmod +x /usr/local/bin/dockcheck.sh
125 | ```
126 | Then call the script anywhere with just `dockcheck.sh`.
127 | Add preferred `notify.sh`-template to the same directory - this will not be touched by the scripts self-update function.
128 |
129 | ## :handbag: Configuration
130 | To modify settings and have them persist through updates - copy the `default.config` to `dockcheck.config` alongside the script or in `~/.config/`.
131 | Alternatively create an alias where specific flags and values are set.
132 | Example `alias dc=dockcheck.sh -p -x 10 -t 3`.
133 |
134 | ## :loudspeaker: Notifications
135 | Trigger with the `-i` flag.
136 | If `notify.sh` is present and configured, it will be used. Otherwise, `notify_v2.sh` will be enabled.
137 | Will send a list of containers with updates available and a notification when `dockcheck.sh` itself has an update.
138 | Run it scheduled with `-ni` to only get notified when there's updates available!
139 |
140 | V2 installation and configuration (tag v0.6.5 or later):
141 | Remove or rename `notify.sh` if previously configured using the legacy method.
142 | Make certain your project directory is laid out as below. You only need the notify_v2.sh file and any notification templates you wish to enable, but there is no harm in having all of them present.
143 | ```
144 | .
145 | ├── notify_templates/
146 | │ ├── notify_DSM.sh
147 | │ ├── notify_apprise.sh
148 | │ ├── notify_discord.sh
149 | │ ├── notify_generic.sh
150 | │ ├── notify_gotify.sh
151 | │ ├── notify_matrix.sh
152 | │ ├── notify_ntfy.sh
153 | │ ├── notify_pushbullet.sh
154 | │ ├── notify_pushover.sh
155 | │ ├── notify_slack.sh
156 | │ ├── notify_smtp.sh
157 | │ ├── notify_telegram.sh
158 | │ └── notify_v2.sh
159 | ├── dockcheck.config
160 | ├── dockcheck.sh
161 | └── urls.list # optional
162 | ```
163 | If you wish to customize `notify_v2.sh` or the notify templates yourself, you may copy them to your project root directory alongside the main dockcheck.sh script (where they will also be ignored by git).
164 | Uncomment and set the NOTIFY_CHANNELS environment variable in `dockcheck.config` to a space separated string of your desired notification channels to enable.
165 | Uncomment and set the environment variables related to the enabled notification channels.
166 | It is recommended not to make changes directly to the `notify_X.sh` template files within the `notify_templates` subdirectory and instead use only environment variables defined in `dockcheck.config` using this method.
167 |
168 | Legacy installation and configuration:
169 | Use a previous version of a `notify_X.sh` template file (tag v0.6.4 or earlier) from the **notify_templates** directory,
170 | copy it to `notify.sh` alongside the script, modify it to your needs! (notify.sh is added to .gitignore)
171 |
172 |
173 | **Current templates:**
174 | - Synology [DSM](https://www.synology.com/en-global/dsm)
175 | - Email with [mSMTP](https://wiki.debian.org/msmtp) (or deprecated alternative [sSMTP](https://wiki.debian.org/sSMTP))
176 | - Apprise (with it's [multitude](https://github.com/caronc/apprise#supported-notifications) of notifications)
177 | - both native [caronc/apprise](https://github.com/caronc/apprise) and the standalone [linuxserver/docker-apprise-api](https://github.com/linuxserver/docker-apprise-api)
178 | - Read the [QuickStart](extras/apprise_quickstart.md)
179 | - [ntfy](https://ntfy.sh/) - HTTP-based pub-sub notifications.
180 | - [Gotify](https://gotify.net/) - a simple server for sending and receiving messages.
181 | - [Pushbullet](https://www.pushbullet.com/) - connecting different devices with cross-platform features.
182 | - [Telegram](https://telegram.org/) - Telegram chat API.
183 | - [Matrix-Synapse](https://github.com/element-hq/synapse) - [Matrix](https://matrix.org/), open, secure, decentralised communication.
184 | - [Pushover](https://pushover.net/) - Simple Notifications (to your phone, wearables, desktops)
185 | - [Discord](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) - Discord webhooks.
186 | - [Slack](https://api.slack.com/tutorials/tracks/posting-messages-with-curl) - Slack curl api
187 |
188 | Further additions are welcome - suggestions or PR!
189 | Initiated and first contributed by [yoyoma2](https://github.com/yoyoma2).
190 |
191 | ### :date: Release notes addon
192 | There's a function to use a lookup-file to add release note URL's to the notification message.
193 | Copy the notify_templates/`urls.list` file to the script directory, it will be used automatically if it's there.
194 | Modify it as necessary, the names of interest in the left column needs to match your container names.
195 | To also list the URL's in the CLI output (choose containers list) use the `-I` option or variable config.
196 | For Markdown formatting also add the `-M` option. (**this requires the template to be compatible - see gotify for example**)
197 |
198 | The output of the notification will look something like this:
199 | ```
200 | Containers on hostname with updates available:
201 | apprise-api -> https://github.com/linuxserver/docker-apprise-api/releases
202 | homer -> https://github.com/bastienwirtz/homer/releases
203 | nginx -> https://github.com/docker-library/official-images/blob/master/library/nginx
204 | ...
205 | ```
206 | 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.
207 |
208 | ## :fast_forward: Asyncronous update checks with **xargs**; `-x N` option. (default=1)
209 | Pass `-x N` where N is number of subprocesses allowed, experiment in your environment to find a suitable max!
210 | Change the default value by editing the `MaxAsync=N` variable in `dockcheck.sh`. To disable the subprocess function set `MaxAsync=0`.
211 |
212 |
213 | ## :chart_with_upwards_trend: Extra plugins and tools:
214 |
215 | ### :small_orange_diamond: Using dockcheck.sh with the Synology DSM
216 | If you run your container through the *Container Manager GUI* - only notifications are supported.
217 | While if running manual (vanilla docker compose CLI) will allow you to use the update function too.
218 | Some extra setup to tie together with Synology DSM - check out the [addons/DSM/README.md](./addons/DSM/README.md).
219 |
220 | ### :small_orange_diamond: Prometheus and node_exporter
221 | Dockcheck can be used together with [Prometheus](https://github.com/prometheus/prometheus) and [node_exporter](https://github.com/prometheus/node_exporter) to export metrics via the file collector, scheduled with cron or likely.
222 | This is done with the `-c` option, like this:
223 | ```
224 | dockcheck.sh -c /path/to/exporter/directory
225 | ```
226 |
227 | See the [README.md](./addons/prometheus/README.md) for more detailed information on how to set it up!
228 | Contributed by [tdralle](https://github.com/tdralle).
229 |
230 | ### :small_orange_diamond: Zabbix config to monitor docker image updates
231 | If you already use Zabbix - this config will Shows number of available docker image updates on host.
232 | Example: *2 Docker Image updates on host-xyz*
233 | See project: [thetorminal/zabbix-docker-image-updates](https://github.com/thetorminal/zabbix-docker-image-updates)
234 |
235 | ### :small_orange_diamond: Serve REST API to list all available updates
236 | A custom python script to serve a REST API to get pulled into other monitoring tools like [homepage](https://github.com/gethomepage/homepage).
237 | See [discussion here](https://github.com/mag37/dockcheck/discussions/146).
238 |
239 | ### :small_orange_diamond: Wrapper Script for Unraid's User Scripts
240 | A custom bash wrapper script to allow the usage of dockcheck as a Unraid User Script plugin.
241 | See [discussion here](https://github.com/mag37/dockcheck/discussions/145).
242 |
243 | ## :bookmark: Labels
244 | Optionally add labels to compose-files. Currently these are the usable labels:
245 | ```
246 | labels:
247 | mag37.dockcheck.update: true
248 | mag37.dockcheck.only-specific-container: true
249 | mag37.dockcheck.restart-stack: true
250 | ```
251 | - `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.
252 | - `mag37.dockcheck.only-specific-container: true` works instead of the `-F` option, specifying the updated container when doing compose up, like `docker compose up -d homer`.
253 | - `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).
254 |
255 | ## :roller_coaster: Workaround for non **amd64** / **arm64**
256 | `regctl` provides binaries for amd64/arm64, to use on other architecture you could try this workaround.
257 | Run regctl in a container wrapped in a shell script. Copied from [regclient/docs/install.md](https://github.com/regclient/regclient/blob/main/docs/install.md):
258 |
259 | ```sh
260 | cat >regctl <Unauthenticated users: 10 pulls/hour
278 | >Authenticated users with a free account: 100 pulls/hour
279 |
280 | This is not an issue for registry checks. But if you have a large stack and pull more than 10 updates at once consider updating more often or to create a free account.
281 | You could use/modify the login-wrapper function in the example below to automate the login prior to running `dockcheck.sh`.
282 |
283 | ### :guardsman: Function to auth with docker hub before running
284 | **Example** - Change names, paths, and remove cat+password flag if you rather get prompted:
285 | ```sh
286 | function dchk {
287 | cat ~/pwd.txt | docker login --username YourUser --password-stdin
288 | ~/dockcheck.sh "$@"
289 | }
290 | ```
291 |
292 | ## :warning: `-r flag` disclaimer and warning
293 | **Wont auto-update the containers, only their images. (compose is recommended)**
294 | `docker run` dont support using new images just by restarting a container.
295 | Containers need to be manually stopped, removed and created again to run on the new image.
296 | Using the `-r` option together with eg. `-i` and `-n` to just check for updates and send notifications and not update is safe though!
297 |
298 | ## :hammer: Known issues
299 | - No detailed error feedback (just skip + list what's skipped).
300 | - Not respecting `--profile` options when re-creating the container.
301 | - Not working well with containers created by **Portainer**.
302 | - **Watchtower** might cause issues due to retagging images when checking for updates (and thereby pulling new images).
303 |
304 | ## :wrench: Debugging
305 | If you hit issues, you could check the output of the `extras/errorCheck.sh` script for clues.
306 | 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`.
307 |
308 | ## :scroll: License
309 | dockcheck is created and released under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0-standalone.html) license.
310 |
311 | ## :heartpulse: Sponsorlist
312 |
313 | - [avegy](https://github.com/avegy)
314 | - [eichhorn](https://github.com/eichhorn)
315 | - [stepdg](https://github.com/stepdg)
316 |
317 | ___
318 |
319 | ### :floppy_disk: The [story](https://mag37.org/posts/project_dockcheck/) behind it. 1 year in retrospect.
320 |
--------------------------------------------------------------------------------
/addons/DSM/README.md:
--------------------------------------------------------------------------------
1 | ## Using Dockcheck in DSM
2 | Dockcheck cannot directly update containers managed in the Container Manager GUI, but it can still be used to notify you of containers with updates available. There are two ways to be notified, each with their own caveats:
3 |
4 | 1. Enabling email notifications within the Task Scheduler (_step 6i below_) will send an email that includes the entire script as run. This will not include the `urls.list` links to release notes, but it will show a full list of containers checked, up to date, and needing updates (following the args included in the scheduled task).
5 | 2. The [DSM notification template](https://github.com/mag37/dockcheck/blob/main/notify_templates/notify_DSM.sh) will enable Dockcheck to directly send an email when using the `-i` flag. This is most useful when paired with an accurate [urls.list](https://github.com/mag37/dockcheck/blob/next063/notify_templates/urls.list) file, and results in a neat succinct email notification of only containers to be updated.
6 |
7 | This is a user preference, and both notifications are not necessary. However, regardless of the notification method, it is necessary to set up a scheduled task to run Dockcheck at a set interval (otherwise it will only run when manually triggered).
8 |
9 |
10 | ## Automate Dockcheck with DSM Task Scheduler:
11 |
12 | 1. Open Control Panel and navigate to Task Scheduler
13 | 2. Create a Scheduled Task > User-defined script
14 | 3. Task Name: Dockcheck
15 | 4. User: root
16 | 5. Schedule: _User Preference_
17 | 6. Task Settings:
18 | 1. ✔ Send run details by email (include preferred email) _This is the optional step as described above)_
19 | 2. User-defined script: `export HOME=/root && cd /path/to/dockcheck && ./dockcheck.sh -n -i -I ` _or other custom args_
20 | 8. Click OK, accept warning message
21 |
22 |
23 | ## Set up the DSM Notification template
24 |
25 | Copy the [dockcheck/notify_templates/notify_DSM.sh](https://github.com/mag37/dockcheck/blob/main/notify_templates/notify_DSM.sh) to the same directory as where you keep `dockcheck.sh`.
26 | Use as is (uses your default notification email setting) or edit and override manually.
27 |
28 | 
29 |
30 | 
31 |
32 | 
33 |
34 |
35 | Made with much help and contribution from [@firmlyundecided](https://github.com/firmlyundecided) and [@yoyoma2](https://github.com/yoyoma2).
36 |
--------------------------------------------------------------------------------
/addons/DSM/dsm1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/addons/DSM/dsm1.png
--------------------------------------------------------------------------------
/addons/DSM/dsm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/addons/DSM/dsm2.png
--------------------------------------------------------------------------------
/addons/DSM/dsm3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/addons/DSM/dsm3.png
--------------------------------------------------------------------------------
/addons/prometheus/README.md:
--------------------------------------------------------------------------------
1 | ## [Prometheus](https://github.com/prometheus/prometheus) and [node_exporter](https://github.com/prometheus/node_exporter)
2 | Dockcheck is capable to export metrics to prometheus via the text file collector provided by the node_exporter.
3 | In order to do so the -c flag has to be specified followed by the file path that is configured in the text file collector of the node_exporter.
4 | A simple cron job can be configured to export these metrics on a regular interval as shown in the sample below:
5 |
6 | ```
7 | 0 1 * * * /root/dockcheck.sh -n -c /var/lib/node_exporter/textfile_collector
8 | ```
9 |
10 | The following metrics are exported to prometheus
11 |
12 | ```
13 | # HELP dockcheck_images_analyzed Docker images that have been analyzed
14 | # TYPE dockcheck_images_analyzed gauge
15 | dockcheck_images_analyzed 22
16 | # HELP dockcheck_images_outdated Docker images that are outdated
17 | # TYPE dockcheck_images_outdated gauge
18 | dockcheck_images_outdated 7
19 | # HELP dockcheck_images_latest Docker images that are outdated
20 | # TYPE dockcheck_images_latest gauge
21 | dockcheck_images_latest 14
22 | # HELP dockcheck_images_error Docker images with analysis errors
23 | # TYPE dockcheck_images_error gauge
24 | dockcheck_images_error 1
25 | # HELP dockcheck_images_analyze_timestamp_seconds Last dockercheck run time
26 | # TYPE dockcheck_images_analyze_timestamp_seconds gauge
27 | dockcheck_images_analyze_timestamp_seconds 1737924029
28 | ```
29 |
30 | Once those metrics are exported they can be used to define alarms as shown below
31 |
32 | ```
33 | - alert: dockcheck_images_outdated
34 | expr: sum by(instance) (dockcheck_images_outdated) > 0
35 | for: 15s
36 | labels:
37 | severity: warning
38 | annotations:
39 | summary: "{{ $labels.instance }} has {{ $value }} outdated docker images."
40 | description: "{{ $labels.instance }} has {{ $value }} outdated docker images."
41 | - alert: dockcheck_images_error
42 | expr: sum by(instance) (dockcheck_images_error) > 0
43 | for: 15s
44 | labels:
45 | severity: warning
46 | annotations:
47 | summary: "{{ $labels.instance }} has {{ $value }} docker images having an error."
48 | description: "{{ $labels.instance }} has {{ $value }} docker images having an error."
49 | - alert: dockercheck_image_last_analyze
50 | expr: (time() - dockcheck_images_analyze_timestamp_seconds) > (3600 * 24 * 3)
51 | for: 15s
52 | labels:
53 | severity: warning
54 | annotations:
55 | summary: "{{ $labels.instance }} has not updated the dockcheck statistics for more than 3 days."
56 | description: "{{ $labels.instance }} has not updated the dockcheck statistics for more than 3 days."
57 | ```
58 |
59 | There is a reference Grafana dashboard in [grafana/grafana_dashboard.json](./grafana/grafana_dashboard.json).
60 |
61 | 
62 |
--------------------------------------------------------------------------------
/addons/prometheus/grafana/grafana_dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [
3 | {
4 | "name": "DS_PROMETHEUS",
5 | "label": "prometheus",
6 | "description": "",
7 | "type": "datasource",
8 | "pluginId": "prometheus",
9 | "pluginName": "Prometheus"
10 | }
11 | ],
12 | "__elements": {},
13 | "__requires": [
14 | {
15 | "type": "grafana",
16 | "id": "grafana",
17 | "name": "Grafana",
18 | "version": "11.4.0"
19 | },
20 | {
21 | "type": "datasource",
22 | "id": "prometheus",
23 | "name": "Prometheus",
24 | "version": "1.0.0"
25 | },
26 | {
27 | "type": "panel",
28 | "id": "table",
29 | "name": "Table",
30 | "version": ""
31 | }
32 | ],
33 | "annotations": {
34 | "list": [
35 | {
36 | "builtIn": 1,
37 | "datasource": {
38 | "type": "grafana",
39 | "uid": "-- Grafana --"
40 | },
41 | "enable": true,
42 | "hide": true,
43 | "iconColor": "rgba(0, 211, 255, 1)",
44 | "name": "Annotations & Alerts",
45 | "type": "dashboard"
46 | }
47 | ]
48 | },
49 | "editable": true,
50 | "fiscalYearStartMonth": 0,
51 | "graphTooltip": 0,
52 | "id": null,
53 | "links": [],
54 | "panels": [
55 | {
56 | "datasource": {
57 | "type": "prometheus",
58 | "uid": "${DS_PROMETHEUS}"
59 | },
60 | "fieldConfig": {
61 | "defaults": {
62 | "color": {
63 | "mode": "thresholds"
64 | },
65 | "custom": {
66 | "align": "auto",
67 | "cellOptions": {
68 | "type": "auto"
69 | },
70 | "inspect": false
71 | },
72 | "mappings": [],
73 | "thresholds": {
74 | "mode": "absolute",
75 | "steps": [
76 | {
77 | "color": "green",
78 | "value": null
79 | },
80 | {
81 | "color": "red",
82 | "value": 80
83 | }
84 | ]
85 | }
86 | },
87 | "overrides": [
88 | {
89 | "matcher": {
90 | "id": "byName",
91 | "options": "last_analyze_timestamp"
92 | },
93 | "properties": [
94 | {
95 | "id": "unit",
96 | "value": "dateTimeAsIso"
97 | }
98 | ]
99 | },
100 | {
101 | "matcher": {
102 | "id": "byName",
103 | "options": "last_analyze_since"
104 | },
105 | "properties": [
106 | {
107 | "id": "unit",
108 | "value": "s"
109 | },
110 | {
111 | "id": "custom.cellOptions",
112 | "value": {
113 | "mode": "gradient",
114 | "type": "color-background"
115 | }
116 | },
117 | {
118 | "id": "thresholds",
119 | "value": {
120 | "mode": "absolute",
121 | "steps": [
122 | {
123 | "color": "green",
124 | "value": null
125 | },
126 | {
127 | "color": "red",
128 | "value": 259200
129 | }
130 | ]
131 | }
132 | }
133 | ]
134 | },
135 | {
136 | "matcher": {
137 | "id": "byName",
138 | "options": "images_outdated"
139 | },
140 | "properties": [
141 | {
142 | "id": "custom.cellOptions",
143 | "value": {
144 | "mode": "gradient",
145 | "type": "color-background"
146 | }
147 | },
148 | {
149 | "id": "thresholds",
150 | "value": {
151 | "mode": "absolute",
152 | "steps": [
153 | {
154 | "color": "green",
155 | "value": null
156 | },
157 | {
158 | "color": "red",
159 | "value": 1
160 | }
161 | ]
162 | }
163 | }
164 | ]
165 | },
166 | {
167 | "matcher": {
168 | "id": "byName",
169 | "options": "images_error"
170 | },
171 | "properties": [
172 | {
173 | "id": "custom.cellOptions",
174 | "value": {
175 | "mode": "gradient",
176 | "type": "color-background"
177 | }
178 | },
179 | {
180 | "id": "thresholds",
181 | "value": {
182 | "mode": "absolute",
183 | "steps": [
184 | {
185 | "color": "green",
186 | "value": null
187 | },
188 | {
189 | "color": "red",
190 | "value": 1
191 | }
192 | ]
193 | }
194 | }
195 | ]
196 | }
197 | ]
198 | },
199 | "gridPos": {
200 | "h": 14,
201 | "w": 24,
202 | "x": 0,
203 | "y": 0
204 | },
205 | "id": 2,
206 | "options": {
207 | "cellHeight": "sm",
208 | "footer": {
209 | "countRows": false,
210 | "fields": "",
211 | "reducer": [
212 | "sum"
213 | ],
214 | "show": false
215 | },
216 | "frameIndex": 1,
217 | "showHeader": true,
218 | "sortBy": []
219 | },
220 | "pluginVersion": "11.4.0",
221 | "targets": [
222 | {
223 | "disableTextWrap": false,
224 | "editorMode": "code",
225 | "exemplar": false,
226 | "expr": "sum by(instance) (dockcheck_images_analyzed)",
227 | "format": "table",
228 | "fullMetaSearch": false,
229 | "hide": false,
230 | "includeNullMetadata": true,
231 | "instant": true,
232 | "interval": "",
233 | "legendFormat": "{{instance}}",
234 | "range": false,
235 | "refId": "dockcheck_images_analyzed",
236 | "useBackend": false,
237 | "datasource": {
238 | "type": "prometheus",
239 | "uid": "${DS_PROMETHEUS}"
240 | }
241 | },
242 | {
243 | "datasource": {
244 | "type": "prometheus",
245 | "uid": "${DS_PROMETHEUS}"
246 | },
247 | "disableTextWrap": false,
248 | "editorMode": "code",
249 | "exemplar": false,
250 | "expr": "sum by(instance) (dockcheck_images_outdated)",
251 | "format": "table",
252 | "fullMetaSearch": false,
253 | "hide": false,
254 | "includeNullMetadata": true,
255 | "instant": true,
256 | "legendFormat": "{{instance}}",
257 | "range": false,
258 | "refId": "dockcheck_images_outdated",
259 | "useBackend": false
260 | },
261 | {
262 | "datasource": {
263 | "type": "prometheus",
264 | "uid": "${DS_PROMETHEUS}"
265 | },
266 | "disableTextWrap": false,
267 | "editorMode": "code",
268 | "exemplar": false,
269 | "expr": "sum by(instance) (dockcheck_images_latest)",
270 | "format": "table",
271 | "fullMetaSearch": false,
272 | "hide": false,
273 | "includeNullMetadata": true,
274 | "instant": true,
275 | "legendFormat": "{{instance}}",
276 | "range": false,
277 | "refId": "dockcheck_images_latest",
278 | "useBackend": false
279 | },
280 | {
281 | "datasource": {
282 | "type": "prometheus",
283 | "uid": "${DS_PROMETHEUS}"
284 | },
285 | "editorMode": "code",
286 | "exemplar": false,
287 | "expr": "sum by(instance) (dockcheck_images_error)",
288 | "format": "table",
289 | "hide": false,
290 | "instant": true,
291 | "legendFormat": "{{instance}}",
292 | "range": false,
293 | "refId": "dockcheck_images_error"
294 | },
295 | {
296 | "datasource": {
297 | "type": "prometheus",
298 | "uid": "${DS_PROMETHEUS}"
299 | },
300 | "editorMode": "code",
301 | "exemplar": false,
302 | "expr": "dockcheck_images_analyze_timestamp_seconds * 1000",
303 | "format": "table",
304 | "hide": false,
305 | "instant": true,
306 | "legendFormat": "{{instance}}",
307 | "range": false,
308 | "refId": "dockcheck_images_analyze_timestamp_seconds"
309 | },
310 | {
311 | "datasource": {
312 | "type": "prometheus",
313 | "uid": "${DS_PROMETHEUS}"
314 | },
315 | "editorMode": "code",
316 | "exemplar": false,
317 | "expr": "time() - dockcheck_images_analyze_timestamp_seconds",
318 | "format": "table",
319 | "hide": false,
320 | "instant": true,
321 | "legendFormat": "{{instance}}",
322 | "range": false,
323 | "refId": "dockcheck_images_last_analyze"
324 | }
325 | ],
326 | "title": "Dockcheck Status",
327 | "transformations": [
328 | {
329 | "id": "merge",
330 | "options": {}
331 | },
332 | {
333 | "id": "organize",
334 | "options": {
335 | "excludeByName": {
336 | "Time": true,
337 | "__name__": true,
338 | "job": true
339 | },
340 | "includeByName": {},
341 | "indexByName": {
342 | "Time": 0,
343 | "Value #dockcheck_images_analyze_timestamp_seconds": 2,
344 | "Value #dockcheck_images_analyzed": 4,
345 | "Value #dockcheck_images_error": 7,
346 | "Value #dockcheck_images_last_analyze": 3,
347 | "Value #dockcheck_images_latest": 5,
348 | "Value #dockcheck_images_outdated": 6,
349 | "instance": 1,
350 | "job": 8
351 | },
352 | "renameByName": {
353 | "Value #A": "analyze_timestamp",
354 | "Value #dockcheck_images_analyze_timestamp_seconds": "last_analyze_timestamp",
355 | "Value #dockcheck_images_analyzed": "images_analyzed",
356 | "Value #dockcheck_images_error": "images_error",
357 | "Value #dockcheck_images_last_analyze": "last_analyze_since",
358 | "Value #dockcheck_images_latest": "images_latest",
359 | "Value #dockcheck_images_outdated": "images_outdated"
360 | }
361 | }
362 | }
363 | ],
364 | "type": "table"
365 | }
366 | ],
367 | "schemaVersion": 40,
368 | "tags": [],
369 | "templating": {
370 | "list": []
371 | },
372 | "time": {
373 | "from": "now-6h",
374 | "to": "now"
375 | },
376 | "timepicker": {},
377 | "timezone": "browser",
378 | "title": "Dockcheck Status",
379 | "uid": "feb4pv3kv1hxca",
380 | "version": 17,
381 | "weekStart": ""
382 | }
--------------------------------------------------------------------------------
/addons/prometheus/grafana/grafana_dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/addons/prometheus/grafana/grafana_dashboard.png
--------------------------------------------------------------------------------
/addons/prometheus/prometheus_collector.sh:
--------------------------------------------------------------------------------
1 | prometheus_exporter() {
2 | checkedImages=$(($1 + $2 + $3))
3 | checkTimestamp=$(date +%s)
4 |
5 | promFileContent=()
6 | promFileContent+=("# HELP dockcheck_images_analyzed Docker images that have been analyzed")
7 | promFileContent+=("# TYPE dockcheck_images_analyzed gauge")
8 | promFileContent+=("dockcheck_images_analyzed $checkedImages")
9 |
10 | promFileContent+=("# HELP dockcheck_images_outdated Docker images that are outdated")
11 | promFileContent+=("# TYPE dockcheck_images_outdated gauge")
12 | promFileContent+=("dockcheck_images_outdated ${#GotUpdates[@]}")
13 |
14 | promFileContent+=("# HELP dockcheck_images_latest Docker images that are outdated")
15 | promFileContent+=("# TYPE dockcheck_images_latest gauge")
16 | promFileContent+=("dockcheck_images_latest ${#NoUpdates[@]}")
17 |
18 | promFileContent+=("# HELP dockcheck_images_error Docker images with analysis errors")
19 | promFileContent+=("# TYPE dockcheck_images_error gauge")
20 | promFileContent+=("dockcheck_images_error ${#GotErrors[@]}")
21 |
22 | promFileContent+=("# HELP dockcheck_images_analyze_timestamp_seconds Last dockercheck run time")
23 | promFileContent+=("# TYPE dockcheck_images_analyze_timestamp_seconds gauge")
24 | promFileContent+=("dockcheck_images_analyze_timestamp_seconds $checkTimestamp")
25 |
26 | printf "%s\n" "${promFileContent[@]}" > "$CollectorTextFileDirectory/dockcheck_info.prom\$\$"
27 | mv -f "$CollectorTextFileDirectory/dockcheck_info.prom\$\$" "$CollectorTextFileDirectory/dockcheck_info.prom"
28 | }
29 |
--------------------------------------------------------------------------------
/default.config:
--------------------------------------------------------------------------------
1 | ### Custom user variables
2 | ## Copy this file to "dockcheck.config" to make it active
3 | ## Can be placed in ~/.config/ or alongside dockcheck.sh
4 | ##
5 | ## Uncomment and set your preferred configuration variables here
6 | ## This will not be replaced on updates
7 |
8 | #Timeout=10 # Set a timeout (in seconds) per container for registry checkups.
9 | #MaxAsync=10 # Set max asynchronous subprocesses, 1 default, 0 to disable.
10 | #BarWidth=50 # The character width of the progress bar
11 | #AutoMode=true # Automatic updates, without interaction.
12 | #DontUpdate=true # No updates; only checking availability without interaction.
13 | #AutoPrune=true # Auto-Prune dangling images after update.
14 | #AutoSelfUpdate=true # Allow automatic self updates - caution as this will pull new code and autorun it.
15 | #Notify=true # Inform - send a preconfigured notification.
16 | #Exclude="one,two" # Exclude containers, separated by comma.
17 | #DaysOld="5" # Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower.
18 | #Stopped="-a" # Include stopped containers in the check. (Logic: docker ps -a).
19 | #OnlyLabel=true # Only update if label is set. See readme.
20 | #ForceRestartStacks=true # Force stop+start stack after update. Caution: restarts once for every updated container within stack.
21 | #DRunUp=true # Allow updating images for docker run, wont update the container.
22 | #MonoMode=true # Monochrome mode, no printf colour codes and hides progress bar.
23 | #PrintReleaseURL=true # Prints custom releasenote urls alongside each container with updates (requires urls.list)`
24 | #PrintMarkdownURL=true # Prints custom releasenote urls as markdown
25 | #OnlySpecific=true # Only compose up the specific container, not the whole compose. (useful for master-compose structure).
26 |
27 | ### Notify settings
28 | ## All commented values are examples only. Modify as needed.
29 | ##
30 | ## Uncomment the line below and specify the notification channels you wish to enable in a space separated string
31 | # NOTIFY_CHANNELS="apprise discord DSM generic gotify matrix ntfy pushbullet pushover slack smtp telegram"
32 | #
33 | ## Uncomment to not send notifications when dockcheck itself has updates.
34 | # DISABLE_DOCKCHECK_NOTIFICATION=false
35 | ## Uncomment to not send notifications when notify scripts themselves have updates.
36 | # DISABLE_NOTIFY_NOTIFICATION=false
37 | #
38 | ## Apprise configuration variables. Set APPRISE_PAYLOAD to make a CLI call or set APPRISE_URL to make an API request instead.
39 | # APPRISE_PAYLOAD='mailto://myemail:mypass@gmail.com
40 | # mastodons://{token}@{host}
41 | # pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b
42 | # tgram://{bot_token}/{chat_id}/'
43 | # APPRISE_URL="http://apprise.mydomain.tld:1234/notify/apprise"
44 | #
45 | # DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/"
46 | #
47 | # DSM_SENDMAILTO="me@mydomain.com"
48 | # DSM_SUBJECTTAG="Email Subject Prefix"
49 | #
50 | # GOTIFY_DOMAIN="https://gotify.domain.tld"
51 | # GOTIFY_TOKEN="token-value"
52 | #
53 | # MATRIX_ACCESS_TOKEN="token-value"
54 | # MATRIX_ROOM_ID="myroom"
55 | # MATRIX_SERVER_URL="https://matrix.yourdomain.tld"
56 | #
57 | ## ntfy.sh or your custom domain with no trailing /
58 | # NTFY_DOMAIN="ntfy.sh"
59 | # NTFY_TOPIC_NAME="YourUniqueTopicName"
60 | #
61 | # PUSHBULLET_URL="https://api.pushbullet.com/v2/pushes"
62 | # PUSHBULLET_TOKEN="token-value"
63 | #
64 | # PUSHOVER_URL="https://api.pushover.net/1/messages.json"
65 | # PUSHOVER_USER_KEY="userkey"
66 | # PUSHOVER_TOKEN="token-value"
67 | #
68 | # SLACK_CHANNEL_ID=mychannel
69 | # SLACK_ACCESS_TOKEN=xoxb-token-value
70 | #
71 | # SMTP_MAIL_FROM="me@mydomain.tld"
72 | # SMTP_MAIL_TO="you@yourdomain.tld"
73 | # SMTP_SUBJECT_TAG="dockcheck"
74 | #
75 | # TELEGRAM_CHAT_ID="mychatid"
76 | # TELEGRAM_TOKEN="token-value"
77 | # TELEGRAM_TOPIC_ID="0"
78 |
--------------------------------------------------------------------------------
/dockcheck.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | VERSION="v0.6.6"
3 | # ChangeNotes: notify_v2 bugfixes - clarify readme and error messages, better sourcing templates, tweaks.
4 | Github="https://github.com/mag37/dockcheck"
5 | RawUrl="https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh"
6 |
7 | set -uo pipefail
8 | shopt -s nullglob
9 | shopt -s failglob
10 |
11 | # Variables for self updating
12 | ScriptArgs=( "$@" )
13 | ScriptPath="$(readlink -f "$0")"
14 | ScriptWorkDir="$(dirname "$ScriptPath")"
15 |
16 | # Check if there's a new release of the script
17 | LatestRelease="$(curl --retry 3 --retry-delay 1 --retry-max-time 10 -s -r 0-50 "$RawUrl" | sed -n "/VERSION/s/VERSION=//p" | tr -d '"')"
18 | LatestChanges="$(curl --retry 3 --retry-delay 1 --retry-max-time 10 -s -r 0-200 "$RawUrl" | sed -n "/ChangeNotes/s/# ChangeNotes: //p")"
19 |
20 | # Source helper functions
21 | source_if_exists() {
22 | if [[ -s "$1" ]]; then source "$1"; fi
23 | }
24 |
25 | source_if_exists_or_fail() {
26 | [[ -s "$1" ]] && source "$1"
27 | }
28 |
29 | # User customizable defaults
30 | source_if_exists_or_fail "${HOME}/.config/dockcheck.config" || source_if_exists "${ScriptWorkDir}/dockcheck.config"
31 |
32 | # Help Function
33 | Help() {
34 | echo "Syntax: dockcheck.sh [OPTION] [part of name to filter]"
35 | echo "Example: dockcheck.sh -y -x 10 -d 10 -e nextcloud,heimdall"
36 | echo
37 | echo "Options:"
38 | echo "-a|y Automatic updates, without interaction."
39 | echo "-c Exports metrics as prom file for the prometheus node_exporter. Provide the collector textfile directory."
40 | echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower."
41 | echo "-e X Exclude containers, separated by comma."
42 | echo "-f Force stop+start stack after update. Caution: restarts once for every updated container within stack."
43 | echo "-F Only compose up the specific container, not the whole compose stack (useful for master-compose structure)."
44 | echo "-h Print this Help."
45 | echo "-i Inform - send a preconfigured notification."
46 | echo "-I Prints custom releasenote urls alongside each container with updates in CLI output (requires urls.list)."
47 | echo "-l Only update if label is set. See readme."
48 | echo "-m Monochrome mode, no printf colour codes and hides progress bar."
49 | echo "-M Prints custom releasenote urls as markdown (requires template support)."
50 | echo "-n No updates; only checking availability without interaction."
51 | echo "-p Auto-prune dangling images after update."
52 | echo "-r Allow checking for updates/updating images for docker run containers. Won't update the container."
53 | echo "-s Include stopped containers in the check. (Logic: docker ps -a)."
54 | echo "-t Set a timeout (in seconds) per container for registry checkups, 10 is default."
55 | echo "-u Allow automatic self updates - caution as this will pull new code and autorun it."
56 | echo "-v Prints current version."
57 | echo "-x N Set max asynchronous subprocesses, 1 default, 0 to disable, 32+ tested."
58 | echo
59 | echo "Project source: $Github"
60 | }
61 |
62 | # Initialise variables
63 | Timeout=${Timeout:=10}
64 | MaxAsync=${MaxAsync:=1}
65 | BarWidth=${BarWidth:=50}
66 | AutoMode=${AutoMode:=false}
67 | DontUpdate=${DontUpdate:=false}
68 | AutoPrune=${AutoPrune:=false}
69 | AutoSelfUpdate=${AutoSelfUpdate:=false}
70 | OnlyLabel=${OnlyLabel:=false}
71 | Notify=${Notify:=false}
72 | ForceRestartStacks=${ForceRestartStacks:=false}
73 | DRunUp=${DRunUp:=false}
74 | MonoMode=${MonoMode:=false}
75 | PrintReleaseURL=${PrintReleaseURL:=false}
76 | PrintMarkdownURL=${PrintMarkdownURL:=false}
77 | Stopped=${Stopped:=""}
78 | CollectorTextFileDirectory=${CollectorTextFileDirectory:-}
79 | Exclude=${Exclude:-}
80 | DaysOld=${DaysOld:-}
81 | OnlySpecific=${OnlySpecific:=false}
82 | SpecificContainer=${SpecificContainer:=""}
83 | Excludes=()
84 | GotUpdates=()
85 | NoUpdates=()
86 | GotErrors=()
87 | SelectedUpdates=()
88 | regbin=""
89 | jqbin=""
90 |
91 | # Colours
92 | c_red="\033[0;31m"
93 | c_green="\033[0;32m"
94 | c_yellow="\033[0;33m"
95 | c_blue="\033[0;34m"
96 | c_teal="\033[0;36m"
97 | c_reset="\033[0m"
98 |
99 | while getopts "ayfFhiIlmMnprsuvc:e:d:t:x:" options; do
100 | case "${options}" in
101 | a|y) AutoMode=true ;;
102 | c) CollectorTextFileDirectory="${OPTARG}" ;;
103 | d) DaysOld=${OPTARG} ;;
104 | e) Exclude=${OPTARG} ;;
105 | f) ForceRestartStacks=true ;;
106 | F) OnlySpecific=true ;;
107 | i) Notify=true ;;
108 | I) PrintReleaseURL=true ;;
109 | l) OnlyLabel=true ;;
110 | m) MonoMode=true ;;
111 | M) PrintMarkdownURL=true ;;
112 | n) DontUpdate=true; AutoMode=true;;
113 | p) AutoPrune=true ;;
114 | r) DRunUp=true ;;
115 | s) Stopped="-a" ;;
116 | t) Timeout="${OPTARG}" ;;
117 | u) AutoSelfUpdate=true ;;
118 | v) printf "%s\n" "$VERSION"; exit 0 ;;
119 | x) MaxAsync=${OPTARG} ;;
120 | h|*) Help; exit 2 ;;
121 | esac
122 | done
123 | shift "$((OPTIND-1))"
124 |
125 | # Set $1 to a variable for name filtering later
126 | SearchName="${1:-}"
127 |
128 | # Basic notify configuration check
129 | if [[ "${Notify}" == true ]] && [[ ! -s "${ScriptWorkDir}/notify.sh" ]] && [[ -z "${NOTIFY_CHANNELS:-}" ]]; then
130 | printf "Using v2 notifications with -i flag passed but no notify channels configured in dockcheck.config. This will result in no notifications being sent.\n"
131 | fi
132 |
133 | # Setting up options and sourcing functions
134 | if [[ "$DontUpdate" == true ]]; then AutoMode=true; fi
135 | if [[ "$MonoMode" == true ]]; then declare c_{red,green,yellow,blue,teal,reset}=""; fi
136 | if [[ "$Notify" == true ]]; then
137 | source_if_exists_or_fail "${ScriptWorkDir}/notify.sh" || source_if_exists_or_fail "${ScriptWorkDir}/notify_templates/notify_v2.sh" || Notify=false
138 | fi
139 | if [[ -n "$Exclude" ]]; then
140 | IFS=',' read -ra Excludes <<< "$Exclude"
141 | unset IFS
142 | fi
143 | if [[ -n "$DaysOld" ]]; then
144 | if ! [[ $DaysOld =~ ^[0-9]+$ ]]; then
145 | printf "Days -d argument given (%s) is not a number.\n" "$DaysOld"
146 | exit 2
147 | fi
148 | fi
149 | if [[ -n "$CollectorTextFileDirectory" ]]; then
150 | if ! [[ -d $CollectorTextFileDirectory ]]; then
151 | printf "The directory (%s) does not exist.\n" "$CollectorTextFileDirectory"
152 | exit 2
153 | else
154 | source "${ScriptWorkDir}/addons/prometheus/prometheus_collector.sh"
155 | fi
156 | fi
157 |
158 | exec_if_exists() {
159 | if [[ $(type -t $1) == function ]]; then "$@"; fi
160 | }
161 |
162 | exec_if_exists_or_fail() {
163 | [[ $(type -t $1) == function ]] && "$@"
164 | }
165 |
166 | self_update_curl() {
167 | cp "$ScriptPath" "$ScriptPath".bak
168 | if command -v curl &>/dev/null; then
169 | curl --retry 3 --retry-delay 1 --retry-max-time 10 -L $RawUrl > "$ScriptPath"; chmod +x "$ScriptPath"
170 | printf "\n%b---%b starting over with the updated version %b---%b\n" "$c_yellow" "$c_teal" "$c_yellow" "$c_reset"
171 | exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
172 | exit 1 # Exit the old instance
173 | elif command -v wget &>/dev/null; then
174 | wget --waitretry=1 --timeout=15 -t 10 $RawUrl -O "$ScriptPath"; chmod +x "$ScriptPath"
175 | printf "\n%b---%b starting over with the updated version %b---%b\n" "$c_yellow" "$c_teal" "$c_yellow" "$c_reset"
176 | exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
177 | exit 0 # exit the old instance
178 | else
179 | printf "\n%bcurl/wget not available %b- download the update manually: %b%s %b\n" "$c_red" "$c_reset" "$c_teal" "$Github" "$c_reset"
180 | fi
181 | }
182 |
183 | self_update() {
184 | cd "$ScriptWorkDir" || { printf "%bPath error,%b skipping update.\n" "$c_red" "$c_reset"; return; }
185 | if command -v git &>/dev/null && [[ "$(git ls-remote --get-url 2>/dev/null)" =~ .*"mag37/dockcheck".* ]]; then
186 | printf "\n%s\n" "Pulling the latest version."
187 | git pull --force || { printf "%bGit error,%b manually pull/clone.\n" "$c_red" "$c_reset"; return; }
188 | printf "\n%s\n" "--- starting over with the updated version ---"
189 | cd - || { printf "%bPath error.%b\n" "$c_red"; return; }
190 | exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
191 | exit 0 # exit the old instance
192 | else
193 | cd - || { printf "%bPath error.%b\n" "$c_red"; return; }
194 | self_update_curl
195 | fi
196 | }
197 |
198 | choosecontainers() {
199 | while [[ -z "${ChoiceClean:-}" ]]; do
200 | read -r -p "Enter number(s) separated by comma, [a] for all - [q] to quit: " Choice
201 | if [[ "$Choice" =~ [qQnN] ]]; then
202 | exit 0
203 | elif [[ "$Choice" =~ [aAyY] ]]; then
204 | SelectedUpdates=( "${GotUpdates[@]}" )
205 | ChoiceClean=${Choice//[,.:;]/ }
206 | else
207 | ChoiceClean=${Choice//[,.:;]/ }
208 | for CC in $ChoiceClean; do
209 | if [[ "$CC" -lt 1 || "$CC" -gt $UpdCount ]]; then # Reset choice if out of bounds
210 | echo "Number not in list: $CC"; unset ChoiceClean; break 1
211 | else
212 | SelectedUpdates+=( "${GotUpdates[$CC-1]}" )
213 | fi
214 | done
215 | fi
216 | done
217 | }
218 |
219 | datecheck() {
220 | ImageDate=$("$regbin" -v error image inspect "$RepoUrl" --format='{{.Created}}' | cut -d" " -f1)
221 | ImageEpoch=$(date -d "$ImageDate" +%s 2>/dev/null) || ImageEpoch=$(date -f "%Y-%m-%d" -j "$ImageDate" +%s)
222 | ImageAge=$(( ( $(date +%s) - ImageEpoch )/86400 ))
223 | if [[ "$ImageAge" -gt "$DaysOld" ]]; then
224 | return 0
225 | else
226 | return 1
227 | fi
228 | }
229 |
230 | progress_bar() {
231 | QueCurrent="$1"
232 | QueTotal="$2"
233 | BarWidth=${BarWidth:-50}
234 | ((Percent=100*QueCurrent/QueTotal))
235 | ((Complete=BarWidth*Percent/100))
236 | ((Left=BarWidth-Complete)) || true # to not throw error when result is 0
237 | BarComplete=$(printf "%${Complete}s" | tr " " "#")
238 | BarLeft=$(printf "%${Left}s" | tr " " "-")
239 | if [[ "$QueTotal" != "$QueCurrent" ]]; then
240 | printf "\r[%s%s] %s/%s " "$BarComplete" "$BarLeft" "$QueCurrent" "$QueTotal"
241 | else
242 | printf "\r[%b%s%b] %s/%s \n" "$c_teal" "$BarComplete" "$c_reset" "$QueCurrent" "$QueTotal"
243 | fi
244 | }
245 |
246 | # Function to add user-provided urls to releasenotes
247 | releasenotes() {
248 | unset Updates
249 | for update in "${GotUpdates[@]}"; do
250 | found=false
251 | while read -r container url; do
252 | if [[ "$update" == "$container" ]] && [[ "$PrintMarkdownURL" == true ]]; then Updates+=("- [$update]($url)"); found=true;
253 | elif [[ "$update" == "$container" ]]; then Updates+=("$update -> $url"); found=true;
254 | fi
255 | done < "${ScriptWorkDir}/urls.list"
256 | if [[ "$found" == false ]] && [[ "$PrintMarkdownURL" == true ]]; then Updates+=("- $update -> url missing");
257 | elif [[ "$found" == false ]]; then Updates+=("$update -> url missing");
258 | else continue;
259 | fi
260 | done
261 | }
262 |
263 | # Static binary downloader for dependencies
264 | binary_downloader() {
265 | BinaryName="$1"
266 | BinaryUrl="$2"
267 | case "$(uname -m)" in
268 | x86_64|amd64) architecture="amd64" ;;
269 | arm64|aarch64) architecture="arm64";;
270 | *) printf "\n%bArchitecture not supported, exiting.%b\n" "$c_red" "$c_reset"; exit 1;;
271 | esac
272 | GetUrl="${BinaryUrl/TEMP/"$architecture"}"
273 | if command -v curl &>/dev/null; then curl --retry 3 --retry-delay 1 --retry-max-time 10 -L "$GetUrl" > "$ScriptWorkDir/$BinaryName";
274 | elif command -v wget &>/dev/null; then wget --waitretry=1 --timeout=15 -t 10 "$GetUrl" -O "$ScriptWorkDir/$BinaryName";
275 | else printf "\n%bcurl/wget not available - get %s manually from the repo link, exiting.%b" "$c_red" "$BinaryName" "$c_reset"; exit 1;
276 | fi
277 | [[ -f "$ScriptWorkDir/$BinaryName" ]] && chmod +x "$ScriptWorkDir/$BinaryName"
278 | }
279 |
280 | distro_checker() {
281 | isRoot=false
282 | [[ ${EUID:-} == 0 ]] && isRoot=true
283 | if [[ -f /etc/alpine-release ]] ; then
284 | [[ "$isRoot" == true ]] && PkgInstaller="apk add" || PkgInstaller="doas apk add"
285 | elif [[ -f /etc/arch-release ]]; then
286 | [[ "$isRoot" == true ]] && PkgInstaller="pacman -S" || PkgInstaller="sudo pacman -S"
287 | elif [[ -f /etc/debian_version ]]; then
288 | [[ "$isRoot" == true ]] && PkgInstaller="apt-get install" || PkgInstaller="sudo apt-get install"
289 | elif [[ -f /etc/redhat-release ]]; then
290 | [[ "$isRoot" == true ]] && PkgInstaller="dnf install" || PkgInstaller="sudo dnf install"
291 | elif [[ -f /etc/SuSE-release ]]; then
292 | [[ "$isRoot" == true ]] && PkgInstaller="zypper install" || PkgInstaller="sudo zypper install"
293 | elif [[ $(uname -s) == "Darwin" ]]; then PkgInstaller="brew install"
294 | else PkgInstaller="ERROR"; printf "\n%bNo distribution could be determined%b, falling back to static binary.\n" "$c_yellow" "$c_reset"
295 | fi
296 | }
297 |
298 | # Dependency check + installer function
299 | dependency_check() {
300 | AppName="$1"
301 | AppVar="$2"
302 | AppUrl="$3"
303 | if command -v "$AppName" &>/dev/null; then export "$AppVar"="$AppName";
304 | elif [[ -f "$ScriptWorkDir/$AppName" ]]; then export "$AppVar"="$ScriptWorkDir/$AppName";
305 | else
306 | printf "\nRequired dependency %b'%s'%b missing, do you want to install it?\n" "$c_teal" "$AppName" "$c_reset"
307 | read -r -p "y: With packagemanager (sudo). / s: Download static binary. y/s/[n] " GetBin
308 | GetBin=${GetBin:-no} # set default to no if nothing is given
309 | if [[ "$GetBin" =~ [yYsS] ]]; then
310 | [[ "$GetBin" =~ [yY] ]] && distro_checker
311 | if [[ -n "${PkgInstaller:-}" && "${PkgInstaller:-}" != "ERROR" ]]; then
312 | [[ $(uname -s) == "Darwin" && "$AppName" == "regctl" ]] && AppName="regclient"
313 | if $PkgInstaller "$AppName"; then
314 | AppName="$1"
315 | export "$AppVar"="$AppName"
316 | printf "\n%b%b installed.%b\n" "$c_green" "$AppName" "$c_reset"
317 | else
318 | PkgInstaller="ERROR"
319 | printf "\n%bPackagemanager install failed%b, falling back to static binary.\n" "$c_yellow" "$c_reset"
320 | fi
321 | fi
322 | if [[ "$GetBin" =~ [sS] ]] || [[ "$PkgInstaller" == "ERROR" ]]; then
323 | binary_downloader "$AppName" "$AppUrl"
324 | [[ -f "$ScriptWorkDir/$AppName" ]] && { export "$AppVar"="$ScriptWorkDir/$1" && printf "\n%b%s downloaded.%b\n" "$c_green" "$AppName" "$c_reset"; }
325 | fi
326 | else printf "\n%bDependency missing, exiting.%b\n" "$c_red" "$c_reset"; exit 1;
327 | fi
328 | fi
329 | # Final check if binary is correct
330 | [[ "$1" == "jq" ]] && VerFlag="--version"
331 | [[ "$1" == "regctl" ]] && VerFlag="version"
332 | ${!AppVar} "$VerFlag" &> /dev/null || { printf "%s\n" "$AppName is not working - try to remove it and re-download it, exiting."; exit 1; }
333 | }
334 |
335 | dependency_check "regctl" "regbin" "https://github.com/regclient/regclient/releases/latest/download/regctl-linux-TEMP"
336 | dependency_check "jq" "jqbin" "https://github.com/jqlang/jq/releases/latest/download/jq-linux-TEMP"
337 |
338 | # Numbered List function
339 | # if urls.list exists add release note url per line
340 | list_options() {
341 | num=1
342 | for update in "${Updates[@]}"; do
343 | echo "$num) $update"
344 | ((num++))
345 | done
346 | }
347 |
348 | # Version check & initiate self update
349 | if [[ "$VERSION" != "$LatestRelease" ]]; then
350 | 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"
351 | if [[ "$AutoMode" == false ]]; then
352 | read -r -p "Would you like to update? y/[n]: " SelfUpdate
353 | [[ "$SelfUpdate" =~ [yY] ]] && self_update
354 | elif [[ "$AutoMode" == true ]] && [[ "$AutoSelfUpdate" == true ]]; then self_update;
355 | else
356 | [[ "$Notify" == true ]] && { exec_if_exists_or_fail dockcheck_notification "$VERSION" "$LatestRelease" "$LatestChanges" || printf "Could not source notification function.\n"; }
357 | fi
358 | fi
359 |
360 | # Version check for notify templates
361 | [[ "$Notify" == true ]] && [[ ! -s "${ScriptWorkDir}/notify.sh" ]] && { exec_if_exists_or_fail notify_update_notification || printf "Could not source notify notification function.\n"; }
362 |
363 | # Check docker compose binary
364 | docker info &>/dev/null || { printf "\n%bYour current user does not have permissions to the docker socket - may require root / docker group. Exiting.%b\n" "$c_red" "$c_reset"; exit 1; }
365 | if docker compose version &>/dev/null; then DockerBin="docker compose" ;
366 | elif docker-compose -v &>/dev/null; then DockerBin="docker-compose" ;
367 | elif docker -v &>/dev/null; then
368 | printf "%s\n" "No docker compose binary available, using plain docker (Not recommended!)"
369 | printf "%s\n" "'docker run' will ONLY update images, not the container itself."
370 | else
371 | printf "%s\n" "No docker binaries available, exiting."
372 | exit 1
373 | fi
374 |
375 | # Listing typed exclusions
376 | if [[ -n ${Excludes[*]:-} ]]; then
377 | printf "\n%bExcluding these names:%b\n" "$c_blue" "$c_reset"
378 | printf "%s\n" "${Excludes[@]}"
379 | printf "\n"
380 | fi
381 |
382 | # Variables for progress_bar function
383 | ContCount=$(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}' | wc -l)
384 | RegCheckQue=0
385 |
386 | # Testing and setting timeout binary
387 | t_out=$(command -v timeout || echo "")
388 | if [[ $t_out ]]; then
389 | t_out=$(realpath "$t_out" 2>/dev/null || readlink -f "$t_out")
390 | if [[ $t_out =~ "busybox" ]]; then
391 | t_out="timeout ${Timeout}"
392 | else t_out="timeout --foreground ${Timeout}"
393 | fi
394 | else t_out=""
395 | fi
396 |
397 | check_image() {
398 | i="$1"
399 | local Excludes=($Excludes_string)
400 | for e in "${Excludes[@]}"; do
401 | if [[ "$i" == "$e" ]]; then
402 | printf "%s\n" "Skip $i"
403 | return
404 | fi
405 | done
406 |
407 | # Skipping non-compose containers unless option is set
408 | ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}')
409 | ContPath=$($jqbin -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels")
410 | [[ "$ContPath" == "null" ]] && ContPath=""
411 | if [[ -z "$ContPath" ]] && [[ "$DRunUp" == false ]]; then
412 | printf "%s\n" "NoUpdates !$i - not checked, no compose file"
413 | return
414 | fi
415 |
416 | local NoUpdates GotUpdates GotErrors
417 | ImageId=$(docker inspect "$i" --format='{{.Image}}')
418 | RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}')
419 | LocalHash=$(docker image inspect "$ImageId" --format '{{.RepoDigests}}')
420 |
421 | # Checking for errors while setting the variable
422 | if RegHash=$($t_out "$regbin" -v error image digest --list "$RepoUrl" 2>&1); then
423 | if [[ "$LocalHash" = *"$RegHash"* ]]; then
424 | printf "%s\n" "NoUpdates $i"
425 | else
426 | if [[ -n "${DaysOld:-}" ]] && ! datecheck; then
427 | printf "%s\n" "NoUpdates +$i ${ImageAge}d"
428 | else
429 | printf "%s\n" "GotUpdates $i"
430 | fi
431 | fi
432 | else
433 | printf "%s\n" "GotErrors $i - ${RegHash}" # Reghash contains an error code here
434 | fi
435 | }
436 |
437 | # Make required functions and variables available to subprocesses
438 | export -f check_image datecheck
439 | export Excludes_string="${Excludes[*]:-}" # Can only export scalar variables
440 | export t_out regbin RepoUrl DaysOld DRunUp jqbin
441 |
442 | # Check for POSIX xargs with -P option, fallback without async
443 | if (echo "test" | xargs -P 2 >/dev/null 2>&1) && [[ "$MaxAsync" != 0 ]]; then
444 | XargsAsync="-P $MaxAsync"
445 | else
446 | XargsAsync=""
447 | [[ "$MaxAsync" != 0 ]] && printf "%bMissing POSIX xargs, consider installing 'findutils' for asynchronous lookups.%b\n" "$c_yellow" "$c_reset"
448 | fi
449 |
450 | # Asynchronously check the image-hash of every running container VS the registry
451 | while read -r line; do
452 | ((RegCheckQue+=1))
453 | if [[ "$MonoMode" == false ]]; then progress_bar "$RegCheckQue" "$ContCount"; fi
454 |
455 | Got=${line%% *} # Extracts the first word (NoUpdates, GotUpdates, GotErrors)
456 | item=${line#* }
457 |
458 | case "$Got" in
459 | NoUpdates) NoUpdates+=("$item") ;;
460 | GotUpdates) GotUpdates+=("$item") ;;
461 | GotErrors) GotErrors+=("$item") ;;
462 | Skip) ;;
463 | *) echo "Error! Unexpected output from subprocess: ${line}" ;;
464 | esac
465 | done < <( \
466 | docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}' | \
467 | xargs $XargsAsync -I {} bash -c 'check_image "{}"' \
468 | )
469 |
470 | # Sort arrays alphabetically
471 | IFS=$'\n'
472 | NoUpdates=($(sort <<<"${NoUpdates[*]:-}"))
473 | GotUpdates=($(sort <<<"${GotUpdates[*]:-}"))
474 | unset IFS
475 |
476 | # Run the prometheus exporter function
477 | if [[ -n "${CollectorTextFileDirectory:-}" ]]; then
478 | exec_if_exists_or_fail prometheus_exporter ${#NoUpdates[@]} ${#GotUpdates[@]} ${#GotErrors[@]} || printf "%s\n" "Could not source prometheus exporter function."
479 | fi
480 |
481 | # Define how many updates are available
482 | UpdCount="${#GotUpdates[@]}"
483 |
484 | # List what containers got updates or not
485 | if [[ -n ${NoUpdates[*]:-} ]]; then
486 | printf "\n%bContainers on latest version:%b\n" "$c_green" "$c_reset"
487 | printf "%s\n" "${NoUpdates[@]}"
488 | fi
489 | if [[ -n ${GotErrors[*]:-} ]]; then
490 | printf "\n%bContainers with errors, won't get updated:%b\n" "$c_red" "$c_reset"
491 | printf "%s\n" "${GotErrors[@]}"
492 | printf "%binfo:%b 'unauthorized' often means not found in a public registry.\n" "$c_blue" "$c_reset"
493 | fi
494 | if [[ -n ${GotUpdates[*]:-} ]]; then
495 | printf "\n%bContainers with updates available:%b\n" "$c_yellow" "$c_reset"
496 | if [[ -s "$ScriptWorkDir/urls.list" ]] && [[ "$PrintReleaseURL" == true ]]; then releasenotes; else Updates=("${GotUpdates[@]}"); fi
497 | [[ "$AutoMode" == false ]] && list_options || printf "%s\n" "${Updates[@]}"
498 | [[ "$Notify" == true ]] && { exec_if_exists_or_fail send_notification "${GotUpdates[@]}" || printf "\nCould not source notification function.\n"; }
499 | fi
500 |
501 | # Optionally get updates if there's any
502 | if [[ -n "${GotUpdates:-}" ]]; then
503 | if [[ "$AutoMode" == false ]]; then
504 | printf "\n%bChoose what containers to update.%b\n" "$c_teal" "$c_reset"
505 | choosecontainers
506 | else
507 | SelectedUpdates=( "${GotUpdates[@]}" )
508 | fi
509 | if [[ "$DontUpdate" == false ]]; then
510 | printf "\n%bUpdating container(s):%b\n" "$c_blue" "$c_reset"
511 | printf "%s\n" "${SelectedUpdates[@]}"
512 |
513 | NumberofUpdates="${#SelectedUpdates[@]}"
514 |
515 | CurrentQue=0
516 | for i in "${SelectedUpdates[@]}"; do
517 | ((CurrentQue+=1))
518 | printf "\n%bNow updating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset"
519 | ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}')
520 | ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
521 | ContPath=$($jqbin -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels")
522 | [[ "$ContPath" == "null" ]] && ContPath=""
523 | ContUpdateLabel=$($jqbin -r '."mag37.dockcheck.update"' <<< "$ContLabels")
524 | [[ "$ContUpdateLabel" == "null" ]] && ContUpdateLabel=""
525 | # Checking if Label Only -option is set, and if container got the label
526 | [[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != true ]] && { echo "No update label, skipping."; continue; } }
527 |
528 | # Checking if compose-values are empty - hence started with docker run
529 | if [[ -z "$ContPath" ]]; then
530 | if [[ "$DRunUp" == true ]]; then
531 | docker pull "$ContImage"
532 | printf "%s\n" "$i got a new image downloaded, rebuild manually with preferred 'docker run'-parameters"
533 | else
534 | 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"
535 | fi
536 | continue
537 | fi
538 |
539 | docker pull "$ContImage" || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; }
540 | done
541 | printf "\n%bDone pulling updates. %bRecreating updated containers.%b\n" "$c_green" "$c_blue" "$c_reset"
542 |
543 | CurrentQue=0
544 | for i in "${SelectedUpdates[@]}"; do
545 | ((CurrentQue+=1))
546 | unset CompleteConfs
547 | # Extract labels and metadata
548 | ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}')
549 | ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
550 | ContPath=$($jqbin -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels")
551 | [[ "$ContPath" == "null" ]] && ContPath=""
552 | ContConfigFile=$($jqbin -r '."com.docker.compose.project.config_files"' <<< "$ContLabels")
553 | [[ "$ContConfigFile" == "null" ]] && ContConfigFile=""
554 | ContName=$($jqbin -r '."com.docker.compose.service"' <<< "$ContLabels")
555 | [[ "$ContName" == "null" ]] && ContName=""
556 | ContEnv=$($jqbin -r '."com.docker.compose.project.environment_file"' <<< "$ContLabels")
557 | [[ "$ContEnv" == "null" ]] && ContEnv=""
558 | ContUpdateLabel=$($jqbin -r '."mag37.dockcheck.update"' <<< "$ContLabels")
559 | [[ "$ContUpdateLabel" == "null" ]] && ContUpdateLabel=""
560 | ContRestartStack=$($jqbin -r '."mag37.dockcheck.restart-stack"' <<< "$ContLabels")
561 | [[ "$ContRestartStack" == "null" ]] && ContRestartStack=""
562 | ContOnlySpecific=$($jqbin -r '."mag37.dockcheck.only-specific-container"' <<< "$ContLabels")
563 | [[ "$ContOnlySpecific" == "null" ]] && ContRestartStack=""
564 |
565 | # Checking if compose-values are empty - hence started with docker run
566 | [[ -z "$ContPath" ]] && continue
567 |
568 | # cd to the compose-file directory to account for people who use relative volumes
569 | cd "$ContPath" || { printf "\n%bPath error - skipping%b %s" "$c_red" "$c_reset" "$i"; continue; }
570 | ## Reformatting path + multi compose
571 | if [[ $ContConfigFile = '/'* ]]; then
572 | CompleteConfs=$(for conf in ${ContConfigFile//,/ }; do printf -- "-f %s " "$conf"; done)
573 | else
574 | CompleteConfs=$(for conf in ${ContConfigFile//,/ }; do printf -- "-f %s/%s " "$ContPath" "$conf"; done)
575 | fi
576 | # Check if the container got an environment file set and reformat it
577 | ContEnvs=""
578 | if [[ -n "$ContEnv" ]]; then ContEnvs=$(for env in ${ContEnv//,/ }; do printf -- "--env-file %s " "$env"; done); fi
579 | # Set variable when compose up should only target the specific container, not the stack
580 | if [[ $OnlySpecific == true ]] || [[ $ContOnlySpecific == true ]]; then SpecificContainer="$ContName"; fi
581 |
582 | printf "\n%bNow recreating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset"
583 | # Check if the whole stack should be restarted
584 | if [[ "$ContRestartStack" == true ]] || [[ "$ForceRestartStacks" == true ]]; then
585 | ${DockerBin} ${CompleteConfs} stop; ${DockerBin} ${CompleteConfs} ${ContEnvs} up -d || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; }
586 | else
587 | ${DockerBin} ${CompleteConfs} ${ContEnvs} up -d ${SpecificContainer} || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; }
588 | fi
589 | done
590 | if [[ "$AutoPrune" == false ]] && [[ "$AutoMode" == false ]]; then printf "\n"; read -rep "Would you like to prune dangling images? y/[n]: " AutoPrune; fi
591 | if [[ "$AutoPrune" == true ]] || [[ "$AutoPrune" =~ [yY] ]]; then printf "\n Auto pruning.."; docker image prune -f; fi
592 | printf "\n%bAll done!%b\n" "$c_green" "$c_reset"
593 | else
594 | printf "\nNo updates installed, exiting.\n"
595 | fi
596 | else
597 | printf "\nNo updates available, exiting.\n"
598 | fi
599 |
600 | exit 0
601 |
--------------------------------------------------------------------------------
/extras/apprise-ex1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/extras/apprise-ex1.png
--------------------------------------------------------------------------------
/extras/apprise-ex2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/extras/apprise-ex2.png
--------------------------------------------------------------------------------
/extras/apprise_quickstart.md:
--------------------------------------------------------------------------------
1 | # A small guide on getting started with Apprise notifications.
2 |
3 |
4 | ## Standalone docker container: [linuxserver/apprise-api](https://hub.docker.com/r/linuxserver/apprise-api)
5 |
6 | Set up the docker compose as preferred:
7 | ```yaml
8 | ---
9 | version: "2.1"
10 | services:
11 | apprise-api:
12 | image: lscr.io/linuxserver/apprise-api:latest
13 | container_name: apprise-api
14 | environment:
15 | - PUID=1000
16 | - PGID=1000
17 | - TZ=Etc/UTC
18 | volumes:
19 | - /path/to/apprise-api/config:/config
20 | ports:
21 | - 8000:8000
22 | restart: unless-stopped
23 | ```
24 |
25 | Then browse to the webui.
26 | 
27 | Here you'll click **Configuration Manager**, read the overview and then click on **Configuration**.
28 | Under **Configuration** you'll craft/paste your notification config.
29 |
30 | 
31 | The simplest way is just paste the url's as is (like in the example above).
32 | There are many ways to customize with tags, groups, json and more. Read [caronc/apprise-api](https://github.com/caronc/apprise-api) for more info!
33 |
34 | Look at the [apprise wiki: Notification Services](https://github.com/caronc/apprise/wiki) for more info about how the url syntax for different services works.
35 |
36 |
37 | You can also use the [caronc/apprise-api](https://github.com/caronc/apprise-api) to host the api as a frontend to an already existing **Apprise**-setup on the host.
38 |
39 |
40 | ### Customize the **notify.sh** file.
41 | After you're done with the setup of the container and tried your notifications, you can copy the `notify_apprise.sh` file to `notify.sh` and start editing it.
42 |
43 | Comment out/remove the bare metal apprise-command (starting with `apprise -vv -t...`).
44 | Uncomment and edit the `AppriseURL` variable and *curl* line
45 | It should look something like this when curling the API:
46 | ```bash
47 | send_notification() {
48 | Updates=("$@")
49 | UpdToString=$( printf "%s\n" "${Updates[@]}" )
50 | FromHost=$(hostname)
51 |
52 | printf "\nSending Apprise notification\n"
53 |
54 | MessageTitle="$FromHost - updates available."
55 | # Setting the MessageBody variable here.
56 | read -d '\n' MessageBody << __EOF
57 | Containers on $FromHost with updates available:
58 |
59 | $UpdToString
60 |
61 | __EOF
62 |
63 | AppriseURL="http://IP.or.mydomain.tld:8000/notify/apprise"
64 | curl -X POST -F "title=$MessageTitle" -F "body=$MessageBody" -F "tags=all" $AppriseURL
65 |
66 | }
67 | ```
68 |
69 | That's all!
70 | ___
71 | ___
72 |
73 |
74 | ## On host installed **Apprise**
75 | Follow the official guide on [caronc/apprise](https://github.com/caronc/apprise)!
76 |
77 | ### A brief, basic "get started"
78 |
79 | - Install **apprise**
80 | - python package `pip install apprise`
81 | - packaged in EPEL/Fedora `dnf install apprise`
82 | - packaged in AUR `[yay/pikaur/paru/other] apprise`
83 |
84 | - Create a config file with your notification credentials (source of notifications):
85 | ```ini
86 | mailto://user:password@yahoo.com
87 | slack://token_a/token_b/token_c
88 | kodi://example.com
89 | ```
90 | Then either source the notifications with `-c=/path/to/config/apprise` or store them in *PATH* to skip referencing (`~/.apprise` or `~/.config/apprise`).
91 |
92 | - Test apprise with a single notification:
93 | - `apprise -vv -t 'test title' -b 'test notification body' 'mailto://myemail:mypass@gmail.com'`
94 | - Set up your notification URL's and test them.
95 | - Look at the [apprise wiki: Notification Services](https://github.com/caronc/apprise/wiki) for more info about how the url syntax for different services works.
96 |
97 | ### When done, customize the **notify.sh** file.
98 | After you're done with the setup of the container and tried your notifications, you can copy the `notify_apprise.sh` file to `notify.sh` and start editing it.
99 |
100 | Replace the url's corresponding to the services you've configured.
101 | ```bash
102 | send_notification() {
103 | Updates=("$@")
104 | UpdToString=$( printf "%s\n" "${Updates[@]}" )
105 | FromHost=$(hostname)
106 |
107 | printf "\nSending Apprise notification\n"
108 |
109 | MessageTitle="$FromHost - updates available."
110 | # Setting the MessageBody variable here.
111 | read -d '\n' MessageBody << __EOF
112 | Containers on $FromHost with updates available:
113 |
114 | $UpdToString
115 |
116 | __EOF
117 |
118 | # Modify to fit your setup:
119 | apprise -vv -t "$MessageTitle" -b "$MessageBody" \
120 | mailto://myemail:mypass@gmail.com \
121 | mastodons://{token}@{host} \
122 | pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b \
123 | tgram://{bot_token}/{chat_id}/
124 |
125 | }
126 | ```
127 |
128 | That's all!
129 | ___
130 | ___
131 |
--------------------------------------------------------------------------------
/extras/dc_brief.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ### If not in PATH, set full path. Else just "regctl"
4 | regbin="regctl"
5 | ### options to allow exclude:
6 | while getopts "e:" options; do
7 | case "${options}" in
8 | e) Exclude=${OPTARG} ;;
9 | *) exit 0 ;;
10 | esac
11 | done
12 | shift "$((OPTIND-1))"
13 | ### Create array of excludes
14 | IFS=',' read -r -a Excludes <<< "$Exclude" ; unset IFS
15 |
16 | SearchName="$1"
17 |
18 | for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
19 | for e in "${Excludes[@]}" ; do [[ "$i" == "$e" ]] && continue 2 ; done
20 | printf ". "
21 | RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}')
22 | LocalHash=$(docker image inspect "$RepoUrl" --format '{{.RepoDigests}}')
23 | ### Checking for errors while setting the variable:
24 | if RegHash=$($regbin image digest --list "$RepoUrl" 2>/dev/null) ; then
25 | if [[ "$LocalHash" = *"$RegHash"* ]] ; then NoUpdates+=("$i"); else GotUpdates+=("$i"); fi
26 | else
27 | GotErrors+=("$i")
28 | fi
29 | done
30 |
31 | ### Sort arrays alphabetically
32 | IFS=$'\n'
33 | NoUpdates=($(sort <<<"${NoUpdates[*]}"))
34 | GotUpdates=($(sort <<<"${GotUpdates[*]}"))
35 | GotErrors=($(sort <<<"${GotErrors[*]}"))
36 | unset IFS
37 |
38 | ### List what containers got updates or not
39 | if [[ -n ${NoUpdates[*]} ]] ; then
40 | printf "\n\033[0;32mContainers on latest version:\033[0m\n"
41 | printf "%s\n" "${NoUpdates[@]}"
42 | fi
43 | if [[ -n ${GotErrors[*]} ]] ; then
44 | printf "\n\033[0;31mContainers with errors, wont get updated:\033[0m\n"
45 | printf "%s\n" "${GotErrors[@]}"
46 | fi
47 | if [[ -n ${GotUpdates[*]} ]] ; then
48 | printf "\n\033[0;33mContainers with updates available:\033[0m\n"
49 | printf "%s\n" "${GotUpdates[@]}"
50 | fi
51 | printf "\n\n"
52 |
--------------------------------------------------------------------------------
/extras/dockcheck_logo_by_booYah187.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/extras/dockcheck_logo_by_booYah187.png
--------------------------------------------------------------------------------
/extras/errorCheck.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | SearchName="$1"
3 | for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
4 | echo "------------ $i ------------"
5 | ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}')
6 | ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
7 | ContPath=$(jq -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels")
8 | [ "$ContPath" == "null" ] && ContPath=""
9 | [ -z "$ContPath" ] && { "$i has no compose labels - skipping" ; continue ; }
10 | ContConfigFile=$(jq -r '."com.docker.compose.project.config_files"' <<< "$ContLabels")
11 | [ "$ContConfigFile" == "null" ] && ContConfigFile=""
12 | ContName=$(jq -r '."com.docker.compose.service"' <<< "$ContLabels")
13 | [ "$ContName" == "null" ] && ContName=""
14 | ContEnv=$(jq -r '."com.docker.compose.project.environment_file"' <<< "$ContLabels")
15 | [ "$ContEnv" == "null" ] && ContEnv=""
16 | ContUpdateLabel=$(jq -r '."mag37.dockcheck.update"' <<< "$ContLabels")
17 | [ "$ContUpdateLabel" == "null" ] && ContUpdateLabel=""
18 | ContRestartStack=$(jq -r '."mag37.dockcheck.restart-stack"' <<< "$ContLabels")
19 | [ "$ContRestartStack" == "null" ] && ContRestartStack=""
20 |
21 | if [[ $ContConfigFile = '/'* ]] ; then
22 | ComposeFile="$ContConfigFile"
23 | else
24 | ComposeFile="$ContPath/$ContConfigFile"
25 | fi
26 |
27 | echo -e "Service name:\t\t$ContName"
28 | echo -e "Project working dir:\t$ContPath"
29 | echo -e "Compose files:\t\t$ComposeFile"
30 | echo -e "Environment files:\t$ContEnv"
31 | echo -e "Container image:\t$ContImage"
32 | echo -e "Update label:\t$ContUpdateLabel"
33 | echo -e "Restart Stack label:\t$ContRestartStack"
34 | echo
35 | echo "Mounts:"
36 | docker inspect -f '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ printf "\n" }}{{ end }}' "$i"
37 | echo
38 | done
39 |
--------------------------------------------------------------------------------
/extras/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/extras/example.gif
--------------------------------------------------------------------------------
/extras/example_old.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mag37/dockcheck/272615166e4c4d6953f6a027a2736f406ee37195/extras/example_old.gif
--------------------------------------------------------------------------------
/extras/temp:
--------------------------------------------------------------------------------
1 | temp
2 |
--------------------------------------------------------------------------------
/notify_templates/notify_DSM.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_DSM_VERSION="v0.2"
3 | # INFO: ssmtp is deprecated - consider to use msmtp instead.
4 | #
5 | # mSMTP/sSMTP has to be installed and configured manually.
6 | # The existing DSM Notification Email configuration will be used automatically.
7 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
8 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
9 | # Do not modify this file directly within the "notify_templates" subdirectory. Set DSM_SENDMAILTO and DSM_SUBJECTTAG in your dockcheck.config file.
10 |
11 | MSMTP=$(which msmtp)
12 | SSMTP=$(which ssmtp)
13 |
14 | if [ -n "$MSMTP" ] ; then
15 | MailPkg=$MSMTP
16 | elif [ -n "$SSMTP" ] ; then
17 | MailPkg=$SSMTP
18 | else
19 | echo "No msmtp or ssmtp binary found in PATH: $PATH" ; exit 1
20 | fi
21 |
22 | trigger_DSM_notification() {
23 | CfgFile="/usr/syno/etc/synosmtp.conf"
24 |
25 | # User variables:
26 | # Automatically sends to your usual destination for synology DSM notification emails.
27 | # You can also manually override by assigning something else to DSM_SENDMAILTO in dockcheck.config.
28 | SendMailTo=${DSM_SENDMAILTO:-$(grep 'eventmail1' $CfgFile | sed -n 's/.*"\([^"]*\)".*/\1/p')}
29 | # e.g. DSM_SENDMAILTO="me@mydomain.com"
30 |
31 | SubjectTag=${DSM_SUBJECTTAG:-$(grep 'eventsubjectprefix' $CfgFile | sed -n 's/.*"\([^"]*\)".*/\1/p')}
32 | # e.g. DSM_SUBJECTTAG="Email Subject Prefix"
33 | SenderName=$(grep 'smtp_from_name' $CfgFile | sed -n 's/.*"\([^"]*\)".*/\1/p')
34 | SenderMail=$(grep 'smtp_from_mail' $CfgFile | sed -n 's/.*"\([^"]*\)".*/\1/p')
35 | SenderMail=${SenderMail:-$(grep 'eventmail1' $CfgFile | sed -n 's/.*"\([^"]*\)".*/\1/p')}
36 |
37 | $MailPkg $SendMailTo << __EOF
38 | From: "$SenderName" <$SenderMail>
39 | date:$(date -R)
40 | To: <$SendMailTo>
41 | Subject: $SubjectTag $MessageTitle
42 | Content-Type: text/plain; charset=UTF-8; format=flowed
43 | Content-Transfer-Encoding: 7bit
44 |
45 | $MessageBody
46 | From $SenderName
47 | __EOF
48 | # This ensures DSM's container manager will also see the update
49 | /var/packages/ContainerManager/target/tool/image_upgradable_checker
50 | }
51 |
--------------------------------------------------------------------------------
/notify_templates/notify_apprise.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_APPRISE_VERSION="v0.2"
3 | #
4 | # Required receiving services must already be set up.
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set APPRISE_PAYLOAD in your dockcheck.config file.
8 | # If API, set APPRISE_URL instead.
9 |
10 | if [[ -z "${APPRISE_PAYLOAD:-}" ]] && [[ -z "${APPRISE_URL:-}" ]]; then
11 | printf "Apprise notification channel enabled, but required configuration variables are missing. Apprise notifications will not be sent.\n"
12 |
13 | remove_channel apprise
14 | fi
15 |
16 | trigger_apprise_notification() {
17 |
18 | if [[ -n "${APPRISE_PAYLOAD:-}" ]]; then
19 | apprise -vv -t "$MessageTitle" -b "$MessageBody" \
20 | ${APPRISE_PAYLOAD}
21 | fi
22 |
23 | # e.g. APPRISE_PAYLOAD='mailto://myemail:mypass@gmail.com
24 | # mastodons://{token}@{host}
25 | # pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b
26 | # tgram://{bot_token}/{chat_id}/'
27 |
28 | if [[ -n "${APPRISE_URL:-}" ]]; then
29 | AppriseURL="${APPRISE_URL}"
30 | curl -X POST -F "title=$MessageTitle" -F "body=$MessageBody" -F "tags=all" $AppriseURL # e.g. APPRISE_URL=http://apprise.mydomain.tld:1234/notify/apprise
31 | fi
32 | }
--------------------------------------------------------------------------------
/notify_templates/notify_discord.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_DISCORD_VERSION="v0.3"
3 | #
4 | # Required receiving services must already be set up.
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set DISCORD_WEBHOOK_URL in your dockcheck.config file.
8 |
9 | if [[ -z "${DISCORD_WEBHOOK_URL:-}" ]]; then
10 | printf "Discord notification channel enabled, but required configuration variables are missing. Discord notifications will not be sent.\n"
11 |
12 | remove_channel discord
13 | fi
14 |
15 | trigger_discord_notification() {
16 | DiscordWebhookUrl="${DISCORD_WEBHOOK_URL}" # e.g. DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/
17 |
18 | JsonData=$( "$jqbin" -n \
19 | --arg username "$FromHost" \
20 | --arg body "$MessageBody" \
21 | '{"username": $username, "content": $body}' )
22 |
23 | curl -sS -o /dev/null --fail -X POST -H "Content-Type: application/json" -d "$JsonData" "$DiscordWebhookUrl"
24 | }
25 |
--------------------------------------------------------------------------------
/notify_templates/notify_generic.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_GENERIC_VERSION="v0.2"
3 | #
4 | # generic sample, the "Hello World" of notification addons
5 |
6 | trigger_generic_notification() {
7 | printf "\n$MessageTitle\n"
8 | printf "\n$MessageBody\n"
9 | }
--------------------------------------------------------------------------------
/notify_templates/notify_gotify.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_GOTIFY_VERSION="v0.3"
3 | #
4 | # Required receiving services must already be set up.
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set GOTIFY_TOKEN and GOTIFY_DOMAIN in your dockcheck.config file.
8 |
9 | if [[ -z "${GOTIFY_TOKEN:-}" ]] || [[ -z "${GOTIFY_DOMAIN:-}" ]]; then
10 | printf "Gotify notification channel enabled, but required configuration variables are missing. Gotify notifications will not be sent.\n"
11 |
12 | remove_channel gotify
13 | fi
14 |
15 | trigger_gotify_notification() {
16 | GotifyToken="${GOTIFY_TOKEN}" # e.g. GOTIFY_TOKEN=token-value
17 | GotifyUrl="${GOTIFY_DOMAIN}/message?token=${GotifyToken}" # e.g. GOTIFY_URL=https://gotify.domain.tld
18 |
19 | if [[ "$PrintMarkdownURL" == true ]]; then
20 | ContentType="text/markdown"
21 | else
22 | ContentType="text/plain"
23 | fi
24 |
25 | JsonData=$( "$jqbin" -n \
26 | --arg body "$MessageBody" \
27 | --arg title "$MessageTitle" \
28 | --arg type "$ContentType" \
29 | '{message: $body, title: $title, priority: 5, extras: {"client::display": {"contentType": $type}}}' )
30 |
31 | curl -s -S --data "${JsonData}" -H 'Content-Type: application/json' -X POST "${GotifyUrl}" 1> /dev/null
32 | }
33 |
--------------------------------------------------------------------------------
/notify_templates/notify_matrix.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_MATRIX_VERSION="v0.2"
3 | #
4 | # Required receiving services must already be set up.
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set MATRIX_ACCESS_TOKEN, MATRIX_ROOM_ID, and MATRIX_SERVER_URL in your dockcheck.config file.
8 |
9 | if [[ -z "${MATRIX_ACCESS_TOKEN:-}" ]] || [[ -z "${MATRIX_ROOM_ID}:-" ]] || [[ -z "${MATRIX_SERVER_URL}:-" ]]; then
10 | printf "Matrix notification channel enabled, but required configuration variables are missing. Matrix notifications will not be sent.\n"
11 |
12 | remove_channel matrix
13 | fi
14 |
15 | trigger_matrix_notification() {
16 | AccessToken="${MATRIX_ACCESS_TOKEN}" # e.g. MATRIX_ACCESS_TOKEN=token-value
17 | Room_id="${MATRIX_ROOM_ID}" # e.g. MATRIX_ROOM_ID=myroom
18 | MatrixServer="${MATRIX_SERVER_URL}" # e.g. MATRIX_SERVER_URL=http://matrix.yourdomain.tld
19 | MsgBody="{\"msgtype\":\"m.text\",\"body\":\"$MessageBody\"}"
20 |
21 | # URL Example: https://matrix.org/_matrix/client/r0/rooms/!xxxxxx:example.com/send/m.room.message?access_token=xxxxxxxx
22 | curl -sS -o /dev/null --fail -X POST "$MatrixServer/_matrix/client/r0/rooms/$Room_id/send/m.room.message?access_token=$AccessToken" -H 'Content-Type: application/json' -d "$MsgBody"
23 | }
--------------------------------------------------------------------------------
/notify_templates/notify_ntfy.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_NTFY_VERSION="v0.4"
3 | #
4 | # Setup app and subscription at https://ntfy.sh
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set NTFY_DOMAIN and NTFY_TOPIC_NAME in your dockcheck.config file.
8 |
9 | if [[ -z "${NTFY_DOMAIN:-}" ]] || [[ -z "${NTFY_TOPIC_NAME:-}" ]]; then
10 | printf "Ntfy notification channel enabled, but required configuration variables are missing. Ntfy notifications will not be sent.\n"
11 |
12 | remove_channel ntfy
13 | fi
14 |
15 | trigger_ntfy_notification() {
16 | NtfyUrl="${NTFY_DOMAIN}/${NTFY_TOPIC_NAME}"
17 | # e.g.
18 | # NTFY_DOMAIN=ntfy.sh
19 | # NTFY_TOPIC_NAME=YourUniqueTopicName
20 |
21 | if [[ "$PrintMarkdownURL" == true ]]; then
22 | ContentType="Markdown: yes"
23 | else
24 | ContentType="Markdown: no" #text/plain
25 | fi
26 |
27 | curl -sS -o /dev/null --show-error --fail \
28 | -H "Title: $MessageTitle" \
29 | -H "$ContentType" \
30 | -d "$MessageBody" \
31 | "$NtfyUrl"
32 | }
33 |
--------------------------------------------------------------------------------
/notify_templates/notify_pushbullet.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_PUSHBULLET_VERSION="v0.2"
3 | #
4 | # Required receiving services must already be set up.
5 | # Requires jq installed and in PATH.
6 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
7 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
8 | # Do not modify this file directly within the "notify_templates" subdirectory. Set PUSHBULLET_TOKEN and PUSHBULLET_URL in your dockcheck.config file.
9 |
10 | if [[ -z "${PUSHBULLET_URL:-}" ]] || [[ -z "${PUSHBULLET_TOKEN:-}" ]]; then
11 | printf "Pushbullet notification channel enabled, but required configuration variables are missing. Pushbullet notifications will not be sent.\n"
12 |
13 | remove_channel pushbullet
14 | fi
15 |
16 | trigger_pushbullet_notification() {
17 | PushUrl="${PUSHBULLET_URL}" # e.g. PUSHBULLET_URL=https://api.pushbullet.com/v2/pushes
18 | PushToken="${PUSHBULLET_TOKEN}" # e.g. PUSHBULLET_TOKEN=token-value
19 |
20 | # Requires jq to process json data
21 | "$jqbin" -n --arg title "$MessageTitle" --arg body "$MessageBody" '{body: $body, title: $title, type: "note"}' | curl -sS -o /dev/null --show-error --fail -X POST -H "Access-Token: $PushToken" -H "Content-type: application/json" $PushUrl -d @-
22 | }
23 |
--------------------------------------------------------------------------------
/notify_templates/notify_pushover.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_PUSHOVER_VERSION="v0.2"
3 | #
4 | # Required receiving services must already be set up.
5 | # Requires jq installed and in PATH.
6 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
7 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
8 | # Do not modify this file directly within the "notify_templates" subdirectory. Set PUSHOVER_USER_KEY, PUSHOVER_TOKEN, and PUSHOVER_URL in your dockcheck.config file.
9 |
10 | if [[ -z "${PUSHOVER_URL:-}" ]] || [[ -z "${PUSHOVER_USER_KEY:-}" ]] || [[ -z "${PUSHOVER_TOKEN:-}" ]]; then
11 | printf "Pushover notification channel enabled, but required configuration variables are missing. Pushover notifications will not be sent.\n"
12 |
13 | remove_channel pushover
14 | fi
15 |
16 | trigger_pushover_notification() {
17 | PushoverUrl="${PUSHOVER_URL}" # e.g. PUSHOVER_URL=https://api.pushover.net/1/messages.json
18 | PushoverUserKey="${PUSHOVER_USER_KEY}" # e.g. PUSHOVER_USER_KEY=userkey
19 | PushoverToken="${PUSHOVER_TOKEN}" # e.g. PUSHOVER_TOKEN=token-value
20 |
21 | # Sending the notification via Pushover
22 | curl -sS -o /dev/null --show-error --fail -X POST \
23 | -F "token=$PushoverToken" \
24 | -F "user=$PushoverUserKey" \
25 | -F "title=$MessageTitle" \
26 | -F "message=$MessageBody" \
27 | $PushoverUrl
28 | }
--------------------------------------------------------------------------------
/notify_templates/notify_slack.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_SLACK_VERSION="v0.2"
3 | #
4 | # Setup app and token at https://api.slack.com/tutorials/tracks/posting-messages-with-curl
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set SLACK_ACCESS_TOKEN, and SLACK_CHANNEL_ID in your dockcheck.config file.
8 |
9 | if [[ -z "${SLACK_ACCESS_TOKEN:-}" ]] || [[ -z "${SLACK_CHANNEL_ID:-}" ]]; then
10 | printf "Slack notification channel enabled, but required configuration variables are missing. Slack notifications will not be sent.\n"
11 |
12 | remove_channel slack
13 | fi
14 |
15 | trigger_slack_notification() {
16 | AccessToken="${SLACK_ACCESS_TOKEN}" # e.g. SLACK_ACCESS_TOKEN=some-token
17 | ChannelID="${SLACK_CHANNEL_ID}" # e.g. CHANNEL_ID=mychannel
18 | SlackUrl="https://slack.com/api/chat.postMessage"
19 |
20 | curl -sS -o /dev/null --show-error --fail \
21 | -d "text=$MessageBody" -d "channel=$ChannelID" \
22 | -H "Authorization: Bearer $AccessToken" \
23 | -X POST $SlackUrl
24 | }
25 |
--------------------------------------------------------------------------------
/notify_templates/notify_smtp.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_SMTP_VERSION="v0.2"
3 | # INFO: ssmtp is depcerated - consider to use msmtp instead.
4 | #
5 | # mSMTP/sSMTP has to be installed and configured manually.
6 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
7 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
8 | # Do not modify this file directly within the "notify_templates" subdirectory. Set SMTP_MAIL_FROM, SMTP_MAIL_TO, and SMTP_SUBJECT_TAG in your dockcheck.config file.
9 |
10 | if [[ -z "${SMTP_MAIL_FROM:-}" ]] || [[ -z "${SMTP_MAIL_TO:-}" ]] || [[ -z "${SMTP_SUBJECT_TAG:-}" ]]; then
11 | printf "SMTP notification channel enabled, but required configuration variables are missing. SMTP notifications will not be sent.\n"
12 |
13 | remove_channel smtp
14 | fi
15 |
16 | MSMTP=$(which msmtp)
17 | SSMTP=$(which ssmtp)
18 |
19 | if [ -n "$MSMTP" ] ; then
20 | MailPkg=$MSMTP
21 | elif [ -n "$SSMTP" ] ; then
22 | MailPkg=$SSMTP
23 | else
24 | echo "No msmtp or ssmtp binary found in PATH: $PATH" ; exit 1
25 | fi
26 |
27 | trigger_smtp_notification() {
28 | SendMailFrom="${SMTP_MAIL_FROM}" # e.g. MAIL_FROM=me@mydomain.tld
29 | SendMailTo="${SMTP_MAIL_TO}" # e.g. MAIL_TO=me@mydomain.tld
30 | SubjectTag="${SMTP_SUBJECT_TAG}" # e.g. SUBJECT_TAG=dockcheck
31 |
32 | $MailPkg $SendMailTo << __EOF
33 | From: "$FromHost" <$SendMailFrom>
34 | date:$(date -R)
35 | To: <$SendMailTo>
36 | Subject: [$SubjectTag] $MessageTitle $FromHost
37 | Content-Type: text/plain; charset=UTF-8; format=flowed
38 | Content-Transfer-Encoding: 7bit
39 |
40 | $MessageBody
41 |
42 | __EOF
43 | }
44 |
--------------------------------------------------------------------------------
/notify_templates/notify_telegram.sh:
--------------------------------------------------------------------------------
1 | ### DISCLAIMER: This is a third party addition to dockcheck - best effort testing.
2 | NOTIFY_TELEGRAM_VERSION="v0.3"
3 | #
4 | # Required receiving services must already be set up.
5 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
6 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
7 | # Do not modify this file directly within the "notify_templates" subdirectory. Set TELEGRAM_CHAT_ID and TELEGRAM_TOKEN in your dockcheck.config file.
8 |
9 | if [[ -z "${TELEGRAM_CHAT_ID:-}" ]] || [[ -z "${TELEGRAM_TOKEN:-}" ]]; then
10 | printf "Telegram notification channel enabled, but required configuration variables are missing. Telegram notifications will not be sent.\n"
11 |
12 | remove_channel telegram
13 | fi
14 |
15 | trigger_telegram_notification() {
16 | if [[ "$PrintMarkdownURL" == true ]]; then
17 | ParseMode="Markdown"
18 | else
19 | ParseMode="HTML"
20 | fi
21 |
22 | TelegramToken="${TELEGRAM_TOKEN}" # e.g. TELEGRAM_TOKEN=token-value
23 | TelegramChatId="${TELEGRAM_CHAT_ID}" # e.g. TELEGRAM_CHAT_ID=mychatid
24 | TelegramUrl="https://api.telegram.org/bot$TelegramToken"
25 | TelegramTopicID=${TELEGRAM_TOPIC_ID:="0"}
26 |
27 | JsonData=$( "$jqbin" -n \
28 | --arg chatid "$TelegramChatId" \
29 | --arg text "$MessageBody" \
30 | --arg thread "$TelegramTopicID" \
31 | --arg parse_mode "$ParseMode" \
32 | '{"chat_id": $chatid, "text": $text, "message_thread_id": $thread, "disable_notification": false, "parse_mode": $parse_mode, "disable_web_page_preview": true}' )
33 |
34 | curl -sS -o /dev/null --fail -X POST "$TelegramUrl/sendMessage" -H 'Content-Type: application/json' -d "$JsonData"
35 | }
36 |
--------------------------------------------------------------------------------
/notify_templates/notify_v2.sh:
--------------------------------------------------------------------------------
1 | NOTIFY_V2_VERSION="v0.2"
2 | #
3 | # If migrating from an older notify template, remove your existing notify.sh file.
4 | # Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
5 | # If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script.
6 | # Enable and configure all required notification variables in your dockcheck.config file, e.g.:
7 | # NOTIFY_CHANNELS=apprise gotify slack
8 | # SLACK_TOKEN=xoxb-some-token-value
9 | # GOTIFY_TOKEN=some.token
10 |
11 | enabled_notify_channels=( ${NOTIFY_CHANNELS:-} )
12 |
13 | FromHost=$(cat /etc/hostname)
14 |
15 | remove_channel() {
16 | local temp_array=()
17 | for channel in "${enabled_notify_channels[@]}"; do
18 | [[ "${channel}" != "$1" ]] && temp_array+=("${channel}")
19 | done
20 | enabled_notify_channels=( "${temp_array[@]}" )
21 | }
22 |
23 | for channel in "${enabled_notify_channels[@]}"; do
24 | source_if_exists_or_fail "${ScriptWorkDir}/notify_${channel}.sh" || \
25 | source_if_exists_or_fail "${ScriptWorkDir}/notify_templates/notify_${channel}.sh" || \
26 | printf "The notification channel ${channel} is enabled, but notify_${channel}.sh was not found. Check the ${ScriptWorkDir} directory or the notify_templates subdirectory.\n"
27 | done
28 |
29 | send_notification() {
30 | [[ -s "$ScriptWorkDir"/urls.list ]] && releasenotes || Updates=("$@")
31 | UpdToString=$( printf '%s\\n' "${Updates[@]}" )
32 | UpdToString=${UpdToString%\\n}
33 |
34 | for channel in "${enabled_notify_channels[@]}"; do
35 | printf "\nSending ${channel} notification\n"
36 |
37 | # To be added in the MessageBody if "-d X" was used
38 | # leading space is left intentionally for clean output
39 | [[ -n "$DaysOld" ]] && msgdaysold="with images ${DaysOld}+ days old " || msgdaysold=""
40 |
41 | MessageTitle="$FromHost - updates ${msgdaysold}available."
42 | # Setting the MessageBody variable here.
43 | printf -v MessageBody "🐋 Containers on $FromHost with updates available:\n${UpdToString}\n"
44 |
45 | exec_if_exists_or_fail trigger_${channel}_notification || \
46 | printf "Attempted to send notification to channel ${channel}, but the function was not found. Make sure notify_${channel}.sh is available in the ${ScriptWorkDir} directory or notify_templates subdirectory.\n"
47 | done
48 | }
49 |
50 | ### Set DISABLE_DOCKCHECK_NOTIFICATION=false in dockcheck.config
51 | ### to not send notifications when dockcheck itself has updates.
52 | dockcheck_notification() {
53 | if [[ ! "${DISABLE_DOCKCHECK_NOTIFICATION:-}" = "true" ]]; then
54 | MessageTitle="$FromHost - New version of dockcheck available."
55 | # Setting the MessageBody variable here.
56 | printf -v MessageBody "Installed version: $1\nLatest version: $2\n\nChangenotes: $3\n"
57 |
58 | if [[ ${#enabled_notify_channels[@]} -gt 0 ]]; then printf "\n"; fi
59 | for channel in "${enabled_notify_channels[@]}"; do
60 | printf "Sending dockcheck update notification - ${channel}\n"
61 | exec_if_exists_or_fail trigger_${channel}_notification || \
62 | printf "Attempted to send notification to channel ${channel}, but the function was not found. Make sure notify_${channel}.sh is available in the ${ScriptWorkDir} directory or notify_templates subdirectory.\n"
63 | done
64 | fi
65 | }
66 |
67 | ### Set DISABLE_NOTIFY_UPDATE_NOTIFICATION=false in dockcheck.config
68 | ### to not send notifications when notify scripts themselves have updates.
69 | notify_update_notification() {
70 | if [[ ! "${DISABLE_NOTIFY_UPDATE_NOTIFICATION:-}" = "true" ]]; then
71 | update_channels=( "${enabled_notify_channels[@]}" "v2" )
72 |
73 | for notify_script in "${update_channels[@]}"; do
74 | upper_channel=$(tr '[:lower:]' '[:upper:]' <<< "$notify_script")
75 | VersionVar="NOTIFY_${upper_channel}_VERSION"
76 | if [[ -n "${!VersionVar:-}" ]]; then
77 | RawNotifyUrl="https://raw.githubusercontent.com/mag37/dockcheck/main/notify_templates/notify_${notify_script}.sh"
78 | LatestNotifyRelease="$(curl -s -r 0-150 $RawNotifyUrl | sed -n "/NOTIFY_${upper_channel}_VERSION/s/NOTIFY_${upper_channel}_VERSION=//p" | tr -d '"')"
79 | LatestNotifyRelease=${LatestNotifyRelease:-undefined}
80 | if [[ ! "${LatestNotifyRelease}" = "undefined" ]]; then
81 | if [[ "${!VersionVar}" != "$LatestNotifyRelease" ]] ; then
82 | MessageTitle="$FromHost - New version of notify_${notify_script}.sh available."
83 |
84 | printf -v MessageBody "notify_${notify_script}.sh update available:\n ${!VersionVar} -> $LatestNotifyRelease\n"
85 |
86 | for channel in "${enabled_notify_channels[@]}"; do
87 | printf "Sending notify_${notify_script}.sh update notification - ${channel}\n"
88 | exec_if_exists_or_fail trigger_${channel}_notification || \
89 | printf "Attempted to send notification to channel ${channel}, but the function was not found. Make sure notify_${channel}.sh is available in the ${ScriptWorkDir} directory or notify_templates subdirectory.\n"
90 | done
91 | fi
92 | fi
93 | fi
94 | done
95 | fi
96 | }
97 |
--------------------------------------------------------------------------------
/notify_templates/urls.list:
--------------------------------------------------------------------------------
1 | # Additions are welcome! Append your list to the git-repo, use generic names and sensible urls.
2 | # Modify, add and (if necessary) remove to fit your needs.
3 | # This is a list of container names and releasenote urls, separated by space.
4 |
5 | actual_server https://actualbudget.org/blog
6 | apprise-api https://github.com/linuxserver/docker-apprise-api/releases
7 | audiobookshelf https://github.com/advplyr/audiobookshelf/releases
8 | bazarr https://github.com/morpheus65535/bazarr/releases
9 | bazarr-ls https://github.com/linuxserver/docker-bazarr/releases
10 | beszel https://github.com/henrygd/beszel/releases
11 | bookstack https://github.com/BookStackApp/BookStack/releases
12 | bruceforce-vaultwarden-backup https://github.com/Bruceforce/vaultwarden-backup/blob/main/CHANGELOG.md
13 | caddy https://github.com/caddyserver/caddy/releases
14 | calibre https://github.com/linuxserver/docker-calibre/releases
15 | calibre-web https://github.com/linuxserver/docker-calibre-web/releases
16 | cleanuperr https://github.com/flmorg/cleanuperr/releases
17 | cross-seed https://github.com/cross-seed/cross-seed/releases
18 | cup https://github.com/sergi0g/cup/releases
19 | dockge https://github.com/louislam/dockge/releases
20 | dozzle https://github.com/amir20/dozzle/releases
21 | flatnotes https://github.com/dullage/flatnotes/releases
22 | forgejo https://codeberg.org/forgejo/forgejo/releases
23 | fressrss https://github.com/FreshRSS/FreshRSS/releases
24 | gluetun https://github.com/qdm12/gluetun/releases
25 | go2rtc https://github.com/AlexxIT/go2rtc/releases
26 | gotify https://github.com/gotify/server/releases
27 | hbbr https://github.com/rustdesk/rustdesk-server/releases
28 | hbbs https://github.com/rustdesk/rustdesk-server/releases
29 | homarr https://github.com/homarr-labs/homarr/releases
30 | home-assistant https://github.com/home-assistant/core/releases/
31 | homer https://github.com/bastienwirtz/homer/releases
32 | immich_machine_learning https://github.com/immich-app/immich/releases
33 | immich_postgres https://github.com/tensorchord/VectorChord/releases
34 | immich_redis https://github.com/valkey-io/valkey/releases
35 | immich_server https://github.com/immich-app/immich/releases
36 | jellyfin https://github.com/jellyfin/jellyfin/releases
37 | jellyseerr https://github.com/Fallenbagel/jellyseerr/releases
38 | jellystat https://github.com/CyferShepard/Jellystat/releases
39 | librespeed https://github.com/librespeed/speedtest/releases
40 | lidarr https://github.com/Lidarr/Lidarr/releases/
41 | lidarr-ls https://github.com/linuxserver/docker-lidarr/releases
42 | lubelogger https://github.com/hargata/lubelog/releases
43 | mattermost https://github.com/mattermost/mattermost/releases
44 | mealie https://github.com/mealie-recipes/mealie/releases
45 | meilisearch https://github.com/meilisearch/meilisearch/releases
46 | monica https://github.com/monicahq/monica/releases
47 | mqtt https://github.com/eclipse/mosquitto/tags
48 | nextcloud-aio-mastercontainer https://github.com/nextcloud/all-in-one/releases
49 | nginx https://github.com/docker-library/official-images/blob/master/library/nginx
50 | owncast https://github.com/owncast/owncast/releases
51 | prowlarr https://github.com/Prowlarr/Prowlarr/releases
52 | prowlarr-ls https://github.com/linuxserver/docker-prowlarr/releases
53 | qbittorrent https://www.qbittorrent.org/news
54 | qbittorrent-nox https://www.qbittorrent.org/news
55 | radarr https://github.com/Radarr/Radarr/releases/
56 | radarr-ls https://github.com/linuxserver/docker-radarr/releases
57 | readarr https://github.com/Readarr/Readarr/releases
58 | readeck https://codeberg.org/readeck/readeck/releases
59 | recyclarr https://github.com/recyclarr/recyclarr/releases
60 | roundcubemail https://github.com/roundcube/roundcubemail/releases
61 | sabnzbd https://github.com/linuxserver/docker-sabnzbd/releases
62 | scrutiny https://github.com/AnalogJ/scrutiny/releases
63 | sftpgo https://github.com/drakkan/sftpgo/releases
64 | slskd https://github.com/slskd/slskd/releases
65 | snappymail https://github.com/the-djmaze/snappymail/releases
66 | sonarr https://github.com/Sonarr/Sonarr/releases/
67 | sonarr-ls https://github.com/linuxserver/docker-sonarr/releases
68 | syncthing https://github.com/syncthing/syncthing/releases
69 | tautulli https://github.com/Tautulli/Tautulli/releases
70 | thelounge https://github.com/thelounge/thelounge/releases
71 | traefik https://github.com/traefik/traefik/releases
72 | vaultwarden-server https://github.com/dani-garcia/vaultwarden/releases
73 | watchtower https://github.com/beatkind/watchtower/releases
74 | wud https://github.com/getwud/wud/releases
75 | zigbee2mqtt https://github.com/Koenkk/zigbee2mqtt/releases
76 |
--------------------------------------------------------------------------------