From 36406815ec924e8c99e85e1074a97ccf9231ee24 Mon Sep 17 00:00:00 2001 From: Manfred Martin Date: Fri, 11 Oct 2024 18:55:30 +0200 Subject: [PATCH 1/5] :star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache) --- app/model/container.js | 1 + app/registries/providers/acr/Acr.test.js | 1 + app/registries/providers/ecr/Ecr.test.js | 5 + app/registries/providers/gcr/Gcr.test.js | 8 + app/registries/providers/ghcr/Ghcr.test.js | 5 + app/registries/providers/gitea/Gitea.test.js | 5 + .../providers/gitlab/Gitlab.test.js | 2 + app/registries/providers/lscr/Lscr.test.js | 5 + app/registries/providers/quay/Quay.test.js | 5 + app/watchers/providers/docker/Docker.js | 10 +- app/watchers/providers/docker/label.js | 5 + docs/changelog/README.md | 6 + docs/configuration/watchers/README.md | 27 +- docs/configuration/watchers/README.md.backup | 475 ++++++++++++++++++ e2e/features/api-container.feature | 28 ++ e2e/features/prometheus.feature | 24 + ui/src/components/ContainerImage.vue | 7 +- 17 files changed, 615 insertions(+), 4 deletions(-) create mode 100644 docs/configuration/watchers/README.md.backup diff --git a/app/model/container.js b/app/model/container.js index eb9f4679..fd69de18 100644 --- a/app/model/container.js +++ b/app/model/container.js @@ -26,6 +26,7 @@ const schema = joi.object({ .object({ name: joi.string().min(1).required(), url: joi.string().min(1).required(), + lookupUrl: joi.string(), }) .required(), name: joi.string().min(1).required(), diff --git a/app/registries/providers/acr/Acr.test.js b/app/registries/providers/acr/Acr.test.js index fc85e7dd..fb30921d 100644 --- a/app/registries/providers/acr/Acr.test.js +++ b/app/registries/providers/acr/Acr.test.js @@ -39,6 +39,7 @@ test('match should return true when registry url is from acr', () => { }, }), ).toBeTruthy(); + expect(acr.match('test.azurecr.io')).toBeTruthy(); }); test('match should return false when registry url is not from acr', () => { diff --git a/app/registries/providers/ecr/Ecr.test.js b/app/registries/providers/ecr/Ecr.test.js index 201d41c3..7d3dca65 100644 --- a/app/registries/providers/ecr/Ecr.test.js +++ b/app/registries/providers/ecr/Ecr.test.js @@ -79,6 +79,11 @@ test('match should return false when registry url is not from ecr', () => { }, }), ).toBeFalsy(); + expect(ecr.match('123456789.dkr.ecr.eu-west-1.amazonaws.com')).toBeTruthy(); +}); + +test('match should return false when registry url is not from ecr', () => { + expect(ecr.match('123456789.dkr.ecr.eu-west-1.acme.com')).toBeFalsy(); }); test('maskConfiguration should mask configuration secrets', () => { diff --git a/app/registries/providers/gcr/Gcr.test.js b/app/registries/providers/gcr/Gcr.test.js index 32b7bd50..2ef92126 100644 --- a/app/registries/providers/gcr/Gcr.test.js +++ b/app/registries/providers/gcr/Gcr.test.js @@ -78,6 +78,14 @@ test('match should return false when registry url is not from gcr', () => { }, }), ).toBeFalsy(); + expect(gcr.match('gcr.io')).toBeTruthy(); + expect(gcr.match('us.gcr.io')).toBeTruthy(); + expect(gcr.match('eu.gcr.io')).toBeTruthy(); + expect(gcr.match('asia.gcr.io')).toBeTruthy(); +}); + +test('match should return false when registry url is not from gcr', () => { + expect(gcr.match('grr.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/ghcr/Ghcr.test.js b/app/registries/providers/ghcr/Ghcr.test.js index bc0dc826..fe7502c6 100644 --- a/app/registries/providers/ghcr/Ghcr.test.js +++ b/app/registries/providers/ghcr/Ghcr.test.js @@ -43,6 +43,11 @@ test('match should return false when registry url is not from ghcr', () => { }, }), ).toBeFalsy(); + expect(ghcr.match('ghcr.io')).toBeTruthy(); +}); + +test('match should return false when registry url is not from ghcr', () => { + expect(ghcr.match('grr.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/gitea/Gitea.test.js b/app/registries/providers/gitea/Gitea.test.js index 1568f6ff..dc12f264 100644 --- a/app/registries/providers/gitea/Gitea.test.js +++ b/app/registries/providers/gitea/Gitea.test.js @@ -48,6 +48,11 @@ test('match should return false when registry url is not from custom', () => { }, }), ).toBeFalsy(); + expect(gitea.match('gitea.acme.com')).toBeTruthy(); +}); + +test('match should return false when registry url is not from custom', () => { + expect(gitea.match('gitea.notme.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/gitlab/Gitlab.test.js b/app/registries/providers/gitlab/Gitlab.test.js index d8023610..2838e975 100644 --- a/app/registries/providers/gitlab/Gitlab.test.js +++ b/app/registries/providers/gitlab/Gitlab.test.js @@ -55,6 +55,7 @@ test('match should return true when registry url is from gitlab.com', () => { }, }), ).toBeTruthy(); + expect(gitlab.match('registry.gitlab.com')).toBeTruthy(); }); test('match should return true when registry url is from custom gitlab', () => { @@ -71,6 +72,7 @@ test('match should return true when registry url is from custom gitlab', () => { }, }), ).toBeTruthy(); + expect(gitlabCustom.match('custom.com')).toBeTruthy(); }); test('authenticate should perform authenticate request', () => { diff --git a/app/registries/providers/lscr/Lscr.test.js b/app/registries/providers/lscr/Lscr.test.js index 8e770c52..452b0cf4 100644 --- a/app/registries/providers/lscr/Lscr.test.js +++ b/app/registries/providers/lscr/Lscr.test.js @@ -50,6 +50,11 @@ test('match should return false when registry url is not from lscr', () => { }, }), ).toBeFalsy(); + expect(lscr.match('lscr.io')).toBeTruthy(); +}); + +test('match should return false when registry url is not from lscr', () => { + expect(lscr.match('wrong.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/quay/Quay.test.js b/app/registries/providers/quay/Quay.test.js index 10125adf..74ad1243 100644 --- a/app/registries/providers/quay/Quay.test.js +++ b/app/registries/providers/quay/Quay.test.js @@ -72,6 +72,11 @@ test('match should return false when registry url is not from quay.io', () => { }, }), ).toBeFalsy(); + expect(quay.match('quay.io')).toBeTruthy(); +}); + +test('match should return false when registry url is not from quay.io', () => { + expect(quay.match('error.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/watchers/providers/docker/Docker.js b/app/watchers/providers/docker/Docker.js index 4c154d05..7c8df788 100644 --- a/app/watchers/providers/docker/Docker.js +++ b/app/watchers/providers/docker/Docker.js @@ -21,6 +21,7 @@ const { wudDisplayIcon, wudTriggerInclude, wudTriggerExclude, + wudRegistryLookupUrl, } = require('./label'); const storeContainer = require('../../../store/container'); const log = require('../../../log'); @@ -112,8 +113,12 @@ function getTagCandidates(container, tags, logContainer) { function normalizeContainer(container) { const containerWithNormalizedImage = container; + // Create a temporary image with lookupUrl if present for registry matching + const imageForMatching = container.image.registry.lookupUrl + ? { ...container.image, registry: { ...container.image.registry, url: container.image.registry.lookupUrl } } + : container.image; const registryProvider = Object.values(getRegistries()).find((provider) => - provider.match(container.image), + provider.match(imageForMatching), ); if (!registryProvider) { log.warn(`${fullName(container)} - No Registry Provider found`); @@ -545,6 +550,7 @@ class Docker extends Component { container.Labels[wudDisplayIcon], container.Labels[wudTriggerInclude], container.Labels[wudTriggerExclude], + container.Labels[wudRegistryLookupUrl], ), ); const containersWithImage = await Promise.all(containerPromises); @@ -670,6 +676,7 @@ class Docker extends Component { displayIcon, triggerInclude, triggerExclude, + wudRegistryLookupUrlValue, ) { const containerId = container.Id; @@ -738,6 +745,7 @@ class Docker extends Component { id: imageId, registry: { url: parsedImage.domain, + lookupUrl: wudRegistryLookupUrlValue, }, name: parsedImage.path, tag: { diff --git a/app/watchers/providers/docker/label.js b/app/watchers/providers/docker/label.js index 77745450..afc52d89 100644 --- a/app/watchers/providers/docker/label.js +++ b/app/watchers/providers/docker/label.js @@ -51,4 +51,9 @@ module.exports = { * Optional list of triggers to exclude */ wudTriggerExclude: 'wud.trigger.exclude', + + /** + * Optional Lookup registry url (can be useful when pulling containers from a registry cache) + */ + wudRegistryLookupUrl: 'wud.registry.lookup.url', }; diff --git a/docs/changelog/README.md b/docs/changelog/README.md index fd0a6ba1..d824c64e 100644 --- a/docs/changelog/README.md +++ b/docs/changelog/README.md @@ -1,7 +1,13 @@ # Changelog +<<<<<<< HEAD ## 8.1.1 - :fire: [TELEGRAM] - Fix markdown character escape +======= +# 6.7.0 (wip) +- :star: [UI] - Add support for [Selfh.st](https://selfh.st/icons/) icons +- :star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache) +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) ## 8.1.0 - :star: Add 60s default jitter in docker watcher to avoid load spike on Docker Hub diff --git a/docs/configuration/watchers/README.md b/docs/configuration/watchers/README.md index 237b20c5..92d853b9 100644 --- a/docs/configuration/watchers/README.md +++ b/docs/configuration/watchers/README.md @@ -183,8 +183,9 @@ To fine-tune the behaviour of WUD _per container_, you can add labels on them. |-----------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| | `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | | `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | -| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | -| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | +| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | +| `wud.registry.lookup.url` | :white_circle: | Custom registry url to use for update lookups | Any valid Docker registry url | | +| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | | `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | | `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | | `wud.trigger.exclude` | :white_circle: | Optional list of triggers to exclude | `$trigger_1_id,$trigger_2_id:$threshold` | | @@ -437,3 +438,25 @@ docker run -d --name my_important_service --label 'wud.trigger.include=smtp.gmai ?> Threshold `minor` means that the trigger will run only if this is a `minor` or `patch` semver change ?> Threshold `patch` means that the trigger will run only if this is a `patch` semver change + +### Use an alternative Registry url for update lookups +In some situations (e.g. when using a Docker Registry cache), it can be needed to give the url of the upstream registry (e.g. the Docker hub registry url) + + +#### **Docker Compose** +```yaml +version: '3' + +services: + + mariadb: + image: localhost:5000/mariadb:10.6.4 + labels: + - wud.registry.lookup.url=https://registry-1.docker.io +``` + +#### **Docker** +```bash +docker run -d --name mariadb --label 'wud.registry.lookup.url=https://registry-1.docker.io' localhost:5000/mariadb:10.6.4 +``` + diff --git a/docs/configuration/watchers/README.md.backup b/docs/configuration/watchers/README.md.backup new file mode 100644 index 00000000..07c11905 --- /dev/null +++ b/docs/configuration/watchers/README.md.backup @@ -0,0 +1,475 @@ +# Docker Watchers +![logo](docker.png) + +Watchers are responsible for scanning Docker containers. + +The `docker` watcher lets you configure the Docker hosts you want to watch. + +## Variables + +| Env var | Required | Description | Supported values | Default value when missing | +| --------------------------------------------------------- |:--------------:| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | --------------------------------------------------------------- | +| `WUD_WATCHER_{watcher_name}_CAFILE` | :white_circle: | CA pem file path (only for TLS connection) | | | +| `WUD_WATCHER_{watcher_name}_CERTFILE` | :white_circle: | Certificate pem file path (only for TLS connection) | | | +| `WUD_WATCHER_{watcher_name}_CRON` | :white_circle: | Scheduling options | [Valid CRON expression](https://crontab.guru/) | `0 * * * *` (every hour) | +| `WUD_WATCHER_{watcher_name}_HOST` | :white_circle: | Docker hostname or ip of the host to watch | | | +| `WUD_WATCHER_{watcher_name}_JITTER` | :white_circle: | Jitter in ms applied to the CRON to better distribute the load on the registries (on the Hub at the first place) | > 0 | `60000` (1 minute) | +| `WUD_WATCHER_{watcher_name}_KEYFILE` | :white_circle: | Key pem file path (only for TLS connection) | | | +| `WUD_WATCHER_{watcher_name}_PORT` | :white_circle: | Docker port of the host to watch | | `2375` | +| `WUD_WATCHER_{watcher_name}_SOCKET` | :white_circle: | Docker socket to watch | Valid unix socket | `/var/run/docker.sock` | +| `WUD_WATCHER_{watcher_name}_WATCHALL` | :white_circle: | If WUD must monitor all containers instead of just running ones | `true`, `false` | `false` | +| `WUD_WATCHER_{watcher_name}_WATCHATSTART` (deprecated) | :white_circle: | If WUD must check for image updates during startup | `true`, `false` | `true` if store is empy | +| `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` | :white_circle: | If WUD must monitor all containers by default | `true`, `false` | `true` | +| `WUD_WATCHER_{watcher_name}_WATCHEVENTS` | :white_circle: | If WUD must monitor docker events | `true`, `false` | `true` | + +?> If no watcher is configured, a default one named `local` will be automatically created (reading the Docker socket). + +?> Multiple watchers can be configured (if you have multiple Docker hosts to watch). +You just need to give them different names. + +!> Socket configuration and host/port configuration are mutually exclusive. + +!> If socket configuration is used, don't forget to mount the Docker socket on your WUD container. + +!> If host/port configuration is used, don't forget to enable the Docker remote API. \ +[See dockerd documentation](https://docs.docker.com/engine/reference/commandline/dockerd/#description) + +!> If the Docker remote API is secured with TLS, don't forget to mount and configure the TLS certificates. \ +[See dockerd documentation](https://docs.docker.com/engine/security/protect-access/#use-tls-https-to-protect-the-docker-daemon-socket) + +!> Watching image digests causes an extensive usage of _Docker Registry Pull API_ which is restricted by [**Quotas on the Docker Hub**](https://docs.docker.com/docker-hub/download-rate-limit/). \ +By default, WUD enables it only for **non semver** image tags. \ +You can tune this behavior per container using the `wud.watch.digest` label. \ +If you face [quota related errors](https://docs.docker.com/docker-hub/download-rate-limit/#how-do-i-know-my-pull-requests-are-being-limited), consider slowing down the watcher rate by adjusting the `WUD_WATCHER_{watcher_name}_CRON` variable. + +## Variable examples + +### Watch the local docker host every day at 1am + + +#### **Docker Compose** +```yaml +services: + whatsupdocker: + image: getwud/wud + ... + environment: + - WUD_WATCHER_LOCAL_CRON=0 1 * * * +``` + +#### **Docker** +```bash +docker run \ + -e WUD_WATCHER_LOCAL_CRON="0 1 * * *" \ + ... + getwud/wud +``` + + +### Watch all containers regardless of their status (created, paused, exited, restarting, running...) + + +#### **Docker Compose** +```yaml +services: + whatsupdocker: + image: getwud/wud + ... + environment: + - WUD_WATCHER_LOCAL_WATCHALL=true +``` + +#### **Docker** +```bash +docker run \ + -e WUD_WATCHER_LOCAL_WATCHALL="true" \ + ... + getwud/wud +``` + + +### Watch a remote docker host via TCP on 2375 + + +#### **Docker Compose** +```yaml +services: + whatsupdocker: + image: getwud/wud + ... + environment: + - WUD_WATCHER_MYREMOTEHOST_HOST=myremotehost +``` + +#### **Docker** +```bash +docker run \ + -e WUD_WATCHER_MYREMOTEHOST_HOST="myremotehost" \ + ... + getwud/wud +``` + + +### Watch a remote docker host via TCP with TLS enabled on 2376 + + +#### **Docker Compose** +```yaml +services: + whatsupdocker: + image: getwud/wud + ... + environment: + - WUD_WATCHER_MYREMOTEHOST_HOST=myremotehost + - WUD_WATCHER_MYREMOTEHOST_PORT=2376 + - WUD_WATCHER_MYREMOTEHOST_CAFILE=/certs/ca.pem + - WUD_WATCHER_MYREMOTEHOST_CERTFILE=/certs/cert.pem + - WUD_WATCHER_MYREMOTEHOST_KEYFILE=/certs/key.pem + volumes: + - /my-host/my-certs/ca.pem:/certs/ca.pem:ro + - /my-host/my-certs/ca.pem:/certs/cert.pem:ro + - /my-host/my-certs/ca.pem:/certs/key.pem:ro +``` + +#### **Docker** +```bash +docker run \ + -e WUD_WATCHER_MYREMOTEHOST_HOST="myremotehost" \ + -e WUD_WATCHER_MYREMOTEHOST_PORT="2376" \ + -e WUD_WATCHER_MYREMOTEHOST_CAFILE="/certs/ca.pem" \ + -e WUD_WATCHER_MYREMOTEHOST_CERTFILE="/certs/cert.pem" \ + -e WUD_WATCHER_MYREMOTEHOST_KEYFILE="/certs/key.pem" \ + -v /my-host/my-certs/ca.pem:/certs/ca.pem:ro \ + -v /my-host/my-certs/ca.pem:/certs/cert.pem:ro \ + -v /my-host/my-certs/ca.pem:/certs/key.pem:ro \ + ... + getwud/wud +``` + + +!> Don't forget to mount the certificates into the container! + +### Watch 1 local Docker host and 2 remote docker hosts at the same time + + +#### **Docker Compose** +```yaml +services: + whatsupdocker: + image: getwud/wud + ... + environment: + - WUD_WATCHER_LOCAL_SOCKET=/var/run/docker.sock + - WUD_WATCHER_MYREMOTEHOST1_HOST=myremotehost1 + - WUD_WATCHER_MYREMOTEHOST2_HOST=myremotehost2 +``` + +#### **Docker** +```bash +docker run \ + -e WUD_WATCHER_LOCAL_SOCKET="/var/run/docker.sock" \ + -e WUD_WATCHER_MYREMOTEHOST1_HOST="myremotehost1" \ + -e WUD_WATCHER_MYREMOTEHOST2_HOST="myremotehost2" \ + ... + getwud/wud +``` + + +## Labels + +To fine-tune the behaviour of WUD _per container_, you can add labels on them. + +<<<<<<< HEAD +| Label | Required | Description | Supported values | Default value when missing | +|-----------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| +| `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | +| `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | +| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | +| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | +| `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | +| `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | +| `wud.trigger.exclude` | :white_circle: | Optional list of triggers to exclude | `$trigger_1_id,$trigger_2_id:$threshold` | | +| `wud.trigger.include` | :white_circle: | Optional list of triggers to include | `$trigger_1_id,$trigger_2_id:$threshold` | | +| `wud.watch.digest` | :white_circle: | Watch this container digest | Valid Boolean | `false` | +| `wud.watch` | :white_circle: | Watch this container | Valid Boolean | `true` when `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` is `true` (`false` otherwise) | +======= +| Label | Required | Description | Supported values | Default value when missing | +|---------------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| +| `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | +| `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | +| `wud.link.template` | :white_circle: | Browsable link associated to the container version | String template with placeholders `${raw}` `${major}` `${minor}` `${patch}` `${prerelease}` | | +| `wud.registry.lookup.url` | :white_circle: | Custom registy url to use for update lookups | Any valid Docker registry url | | +| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | +| `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | +| `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | +| `wud.watch.digest` | :white_circle: | Watch this container digest | Valid Boolean | `false` | +| `wud.watch` | :white_circle: | Watch this container | Valid Boolean | `true` when `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` is `true` (`false` otherwise) | +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) + +## Label examples + +### Include specific containers to watch +Configure WUD to disable WATCHBYDEFAULT feature. + +#### **Docker Compose** +```yaml +services: + whatsupdocker: + image: getwud/wud + ... + environment: + - WUD_WATCHER_LOCAL_WATCHBYDEFAULT=false +``` + +#### **Docker** +```bash +docker run \ + -e WUD_WATCHER_LOCAL_WATCHBYDEFAULT="false" \ + ... + getwud/wud +``` + + +Then add the `wud.watch=true` label on the containers you want to watch. + +#### **Docker Compose** +```yaml +services: + mariadb: + image: mariadb:10.4.5 + ... + labels: + - wud.watch=true +``` + +#### **Docker** +```bash +docker run -d --name mariadb --label wud.watch=true mariadb:10.4.5 +``` + + +### Exclude specific containers to watch +Ensure `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` is true (default value). + +Then add the `wud.watch=false` label on the containers you want to exclude from being watched. + +#### **Docker Compose** +```yaml +services: + mariadb: + image: mariadb:10.4.5 + ... + labels: + - wud.watch=false +``` + +#### **Docker** +```bash +docker run -d --name mariadb --label wud.watch=false mariadb:10.4.5 +``` + + +### Include only 3 digits semver tags +You can filter (by inclusion or exclusion) which versions can be candidates for update. + +For example, you can indicate that you want to watch x.y.z versions only + +#### **Docker Compose** +```yaml +services: + + mariadb: + image: mariadb:10.4.5 + labels: + - wud.tag.include=^\d+\.\d+\.\d+$$ +``` + +#### **Docker** +```bash +docker run -d --name mariadb --label 'wud.tag.include=^\d+\.\d+\.\d+$' mariadb:10.4.5 +``` + + +### Transform the tags before performing the analysis +In certain cases, tag values are so badly formatted that the resolution algorithm cannot find any valid update candidates or, worst, find bad positive matches. + +For example, you can encounter such an issue if you need to deal with tags looking like `1.0.0-99-7b368146`, `1.0.0-273-21d7efa6`... +By default, WUD will report bad positive matches because of the `sha-1` part at the end of the tag value (`-7b368146`...). +That's a shame because `1.0.0-99` and `1.0.0-273` would have been valid semver values (`$major.$minor.$patch-$prerelease`). + +You can get around this issue by providing a function that keeps only the part you are interested in. + +How does it work? +The transform function must follow the following syntax: +``` +$valid_regex_with_capturing_groups => $valid_string_with_placeholders +``` + +For example: +```bash +^(\d+\.\d+\.\d+-\d+)-.*$ => $1 +``` + +The capturing groups are accessible with the syntax `$1`, `$2`, `$3`.... + +!> The first capturing group is accessible as `$1`! + +For example, you can indicate that you want to watch x.y.z versions only + +#### **Docker Compose** +```yaml +services: + + searx: + image: searx/searx:1.0.0-269-7b368146 + labels: + - wud.tag.include=^\d+\.\d+\.\d+-\d+-.*$$ + - wud.tag.transform=^(\d+\.\d+\.\d+-\d+)-.*$$ => $$1 +``` + +#### **Docker** +```bash +docker run -d --name searx \ +--label 'wud.tag.include=^\d+\.\d+\.\d+-\d+-.*$' \ +--label 'wud.tag.transform=^(\d+\.\d+\.\d+-\d+)-.*$ => $1' \ +searx/searx:1.0.0-269-7b368146 +``` + + +### Enable digest watching +Additionally to semver tag tracking, you can also track if the digest associated to the local tag has been updated. +It can be convenient to monitor image tags known to be overridden (`latest`, `10`, `10.6`...) + + +#### **Docker Compose** +```yaml +services: + + mariadb: + image: mariadb:10 + labels: + - wud.tag.include=^\d+$$ + - wud.watch.digest=true +``` +#### **Docker** +```bash +docker run -d --name mariadb --label 'wud.tag.include=^\d+$' --label wud.watch.digest=true mariadb:10 +``` + + +### Associate a link to the container version +You can associate a browsable link to the container version using a templated string. +For example, if you want to associate a mariadb version to a changelog (e.g. https://mariadb.com/kb/en/mariadb-1064-changelog), + +you would specify a template like `https://mariadb.com/kb/en/mariadb-${major}${minor}${patch}-changelog` + +The available variables are: +- `${original}` the original unparsed tag +- `${transformed}` the original unparsed tag transformed with the optional `wud.tag.transform` label option +- `${major}` the major version (if tag value is semver) +- `${minor}` the minor version (if tag value is semver) +- `${patch}` the patch version (if tag value is semver) +- `${prerelease}` the prerelease version (if tag value is semver) + + +#### **Docker Compose** +```yaml +services: + + mariadb: + image: mariadb:10.6.4 + labels: + - wud.link.template=https://mariadb.com/kb/en/mariadb-$${major}$${minor}$${patch}-changelog +``` + +#### **Docker** +```bash +docker run -d --name mariadb --label 'wud.link.template=https://mariadb.com/kb/en/mariadb-${major}${minor}${patch}-changelog' mariadb:10 +``` + + +### Customize the name and the icon to display +You can customize the name & the icon of a container (displayed in the UI, in Home-Assistant...) + +Icons must be prefixed with: +- `fab:` or `fab-` for [Fontawesome brand icons](https://fontawesome.com/) (`fab:github`, `fab-mailchimp`...) +- `far:` or `far-` for [Fontawesome regular icons](https://fontawesome.com/) (`far:heart`, `far-house`...) +- `fas:` or `fas-` for [Fontawesome solid icons](https://fontawesome.com/) (`fas:heart`, `fas-house`...) +- `hl:` or `hl-` for [Homarr Labs icons](https://dashboardicons.com/) (`hl:plex`, `hl-authelia`...) +- `mdi:` or `mdi-` for [Material Design icons](https://materialdesignicons.com/) (`mdi:database`, `mdi-server`...) +- `sh:` or `sh-` for [Selfh.st](https://selfh.st/icons/) (`sh:authentik`, `sh-authelia-light`...) (only works for logo available as `png`) +- `si:` or `si-` for [Simple icons](https://simpleicons.org/) (`si:mysql`, `si-plex`...) + +?> If you want to display Fontawesome icons or Simple icons in Home-Assistant, you need to install first the [HASS-fontawesome](https://github.com/thomasloven/hass-fontawesome) and the [HASS-simpleicons](https://github.com/vigonotion/hass-simpleicons) components. + + +#### **Docker Compose** +```yaml +services: + + mariadb: + image: mariadb:10.6.4 + labels: + - wud.display.name=Maria DB + - wud.display.icon=si:mariadb +``` + +#### **Docker** +```bash +docker run -d --name mariadb --label 'wud.display.name=Maria DB' --label 'wud.display.icon=mdi-database' mariadb:10.6.4 +``` + + +<<<<<<< HEAD +### Assign different triggers to containers +You can assign different triggers and thresholds on a per container basis. + +#### Example send a mail notification for all updates but auto-update only if minor or patch +======= +### Use an alternative Registry url for update lookups +In some situations (e.g. when using a Docker Registry cache), it can be needed to give the url of the upstream registry (e.g. the Docker hub registry url) +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) + + +#### **Docker Compose** +```yaml +<<<<<<< HEAD +services: + + my_important_service: + image: my_important_service:1.0.0 + labels: + - wud.trigger.include=smtp.gmail,dockercompose.local:minor +======= +version: '3' + +services: + + mariadb: + image: localhost:5000/mariadb:10.6.4 + labels: + - wud.registry.lookup.url=https://registry-1.docker.io +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) +``` + +#### **Docker** +```bash +<<<<<<< HEAD +docker run -d --name my_important_service --label 'wud.trigger.include=smtp.gmail,dockercompose.local:minor' my_important_service:1.0.0 +``` + + +?> `wud.trigger.include=smtp.gmail` is a shorthand for `wud.trigger.include=smtp.gmail:all` + +?> Threshold `all` means that the trigger will run regardless of the nature of the change + +?> Threshold `major` means that the trigger will run only if this is a `major`, `minor` or `patch` semver change + +?> Threshold `minor` means that the trigger will run only if this is a `minor` or `patch` semver change + +?> Threshold `patch` means that the trigger will run only if this is a `patch` semver change +======= +docker run -d --name mariadb --label 'wud.registry.lookup.url=https://registry-1.docker.io' localhost:5000/mariadb:10.6.4 +``` + +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) diff --git a/e2e/features/api-container.feature b/e2e/features/api-container.feature index ae932f0b..843a9537 100644 --- a/e2e/features/api-container.feature +++ b/e2e/features/api-container.feature @@ -17,6 +17,7 @@ Feature: WUD Container API Exposure And response body path $[].result.tag should be And response body path $[].updateAvailable should be Examples: +<<<<<<< HEAD | index | registry | containerName | registryUrl | imageName | tag | resultTag | updateAvailable | | 0 | ecr.private | ecr_sub_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | | 1 | ecr.private | ecr_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | @@ -38,6 +39,29 @@ Feature: WUD Container API Exposure | 17 | hub.public | hub_youtubedb_latest | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | | 18 | lscr.private | lscr_radarr | https://lscr.io/v2 | linuxserver/radarr | 5.14.0.9383-ls245 | 5.26.2.10099-ls277 | true | | 19 | quay.public | quay_prometheus | https://quay.io/v2 | prometheus/prometheus | v2.52.0 | v3.4.2 | true | +======= + | index | registry | containerName | registryUrl | imageName | tag | resultTag | updateAvailable | + | 0 | ecr | ecr_sub_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | + | 1 | ecr | ecr_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | + | 2 | ecr | ecr_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | test | 1.0.0 | 2.0.0 | true | + | 3 | ghcr | ghcr_radarr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.11.0.9244-ls241 | true | + | 4 | gitlab | gitlab_test | https://registry.gitlab.com/v2 | manfred-martin/docker-registry-test | 1.0.0 | 2.0.0 | true | + | 5 | hub | hub_homeassistant_202161 | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.10.2 | true | + | 6 | hub | hub_homeassistant_latest | https://registry-1.docker.io/v2 | homeassistant/home-assistant | latest | latest | false | + | 7 | hub | hub_nginx_120 | https://registry-1.docker.io/v2 | library/nginx | 1.20-alpine | 1.27-alpine | true | + | 8 | hub | hub_nginx_latest | https://registry-1.docker.io/v2 | library/nginx | latest | latest | true | + | 9 | hub | hub_omnidb_latest | https://registry-1.docker.io/v2 | omnidbteam/omnidb | latest | latest | false | + | 10 | hub | hub_pihole_57 | https://registry-1.docker.io/v2 | pihole/pihole | v5.7 | v5.8.1 | true | + | 11 | hub | hub_pihole_latest | https://registry-1.docker.io/v2 | pihole/pihole | latest | latest | false | + | 12 | hub | hub_pyload_latest | https://registry-1.docker.io/v2 | writl/pyload | latest | latest | false | + | 13 | hub | hub_traefik_245 | https://registry-1.docker.io/v2 | library/traefik | 2.4.5 | 3.1.6 | true | + | 14 | hub | hub_traefik_latest | https://registry-1.docker.io/v2 | library/traefik | latest | latest | false | + | 15 | hub | hub_vaultwarden_1222 | https://registry-1.docker.io/v2 | vaultwarden/server | 1.32.1-alpine | 1.32.1-alpine | false | + | 16 | hub | hub_vaultwarden_latest | https://registry-1.docker.io/v2 | vaultwarden/server | latest | latest | false | + | 17 | hub | hub_youtubedb_latest | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | + | 18 | lscr | lscr_radarr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.11.0.9244-ls241 | true | + | 19 | quay | quay_prometheus | https://quay.io/v2 | prometheus/prometheus | v2.52.0 | v2.54.1 | true | +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) Scenario: WUD must allow to get a container with semver Given I GET /api/containers @@ -85,7 +109,11 @@ Feature: WUD Container API Exposure Then response code should be 200 And response body should be valid json And response body path $.link should be https://github.com/home-assistant/core/releases/tag/2021.6.1 +<<<<<<< HEAD And response body path $.result.link should be https://github.com/home-assistant/core/releases/tag/2025.7.1 +======= + And response body path $.result.link should be https://github.com/home-assistant/core/releases/tag/2024.10.2 +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) Scenario: WUD must allow to trigger a watch on a container Given I GET /api/containers diff --git a/e2e/features/prometheus.feature b/e2e/features/prometheus.feature index 96c276a2..b273c849 100644 --- a/e2e/features/prometheus.feature +++ b/e2e/features/prometheus.feature @@ -21,6 +21,7 @@ Feature: Prometheus exposure And response body should contain result_tag="" And response body should contain update_available="" Examples: +<<<<<<< HEAD | containerName | registry | registryUrl | imageName | tag | resultTag | updateAvailable | | ecr_sub_sub_test | ecr.private | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | | ecr_sub_test | ecr.private | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | @@ -42,3 +43,26 @@ Feature: Prometheus exposure | hub_youtubedb_latest | hub.public | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | | lscr_radarr | lscr.private | https://lscr.io/v2 | linuxserver/radarr | 5.14.0.9383-ls245 |5.26.2.10099-ls277 | true | | quay_prometheus | quay.public | https://quay.io/v2 | prometheus/prometheus | v2.52.0 |v3.4.2 | true | +======= + | containerName | registry | registryUrl | imageName | tag | resultTag | updateAvailable | + | ecr_sub_sub_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | + | ecr_sub_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | + | ecr_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | test | 1.0.0 | 2.0.0 | true | + | ghcr_radarr | ghcr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.11.0.9244-ls241 | true | + | gitlab_test | gitlab | https://registry.gitlab.com/v2 | manfred-martin/docker-registry-test | 1.0.0 | 2.0.0 | true | + | hub_homeassistant_202161 | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.10.2 | true | + | hub_homeassistant_latest | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | latest | latest | false | + | hub_nginx_120 | hub | https://registry-1.docker.io/v2 | library/nginx | 1.20-alpine | 1.27-alpine | true | + | hub_nginx_latest | hub | https://registry-1.docker.io/v2 | library/nginx | latest | latest | true | + | hub_omnidb_latest | hub | https://registry-1.docker.io/v2 | omnidbteam/omnidb | latest | latest | false | + | hub_pihole_57 | hub | https://registry-1.docker.io/v2 | pihole/pihole | v5.7 | v5.8.1 | true | + | hub_pihole_latest | hub | https://registry-1.docker.io/v2 | pihole/pihole | latest | latest | false | + | hub_pyload_latest | hub | https://registry-1.docker.io/v2 | writl/pyload | latest | latest | false | + | hub_traefik_245 | hub | https://registry-1.docker.io/v2 | library/traefik | 2.4.5 | 3.1.6 | true | + | hub_traefik_latest | hub | https://registry-1.docker.io/v2 | library/traefik | latest | latest | false | + | hub_vaultwarden_1222 | hub | https://registry-1.docker.io/v2 | vaultwarden/server | 1.32.1-alpine | 1.32.1-alpine | false | + | hub_vaultwarden_latest | hub | https://registry-1.docker.io/v2 | vaultwarden/server | latest | latest | false | + | hub_youtubedb_latest | hub | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | + | lscr_radarr | lscr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.11.0.9244-ls241 | true | + | quay_prometheus | quay | https://quay.io/v2 | prometheus/prometheus | v2.52.0 |v2.54.1 | true | +>>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) diff --git a/ui/src/components/ContainerImage.vue b/ui/src/components/ContainerImage.vue index 36c5efb5..fb69c1ac 100644 --- a/ui/src/components/ContainerImage.vue +++ b/ui/src/components/ContainerImage.vue @@ -39,9 +39,14 @@ {{ registryIcon }} - Registry + Registry name {{ image.registry.name }} + + Registry url + {{ image.registry.url }} + {{ image.registry.lookupUrl }} (lookup) + From 250c563154ef6bbc91de006373049dca71709ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ne=C3=9Flauer?= Date: Sun, 12 Oct 2025 13:53:15 +0200 Subject: [PATCH 2/5] Improve registry lookup feature with full image path support This builds on Manfred Martin's original implementation from PR #454 (which was closed without merging). His work introduced the `wud.registry.lookup.url` label to help users bypass Docker registry caches when checking for updates. This commit merges the original feature branch with version 8.1.1 to bring it up to date with the latest stable release, ensuring compatibility with recent changes. However, while testing the original URL-based approach with my Harbor registry setup, I discovered a fundamental issue: it doesn't work with registries that use project prefixes or nested paths. The problem: The original implementation accepted registry URLs like "https://registry-1.docker.io" and tried to extract the image name from them. This breaks with enterprise registries like Harbor, where images look like: harbor.example.com/dockerhub-proxy/traefik:v3.5.3 The "dockerhub-proxy/" part is a Harbor project name, not part of the actual image name. The URL-based approach would incorrectly include it as part of the image path, causing registry matching to fail. This same problem affects other registries that use hierarchical project structures. The solution: Instead of accepting registry URLs and trying to parse them, accept full image paths directly. While this requires typing the full image path (which may be slightly longer than just a URL), it's more explicit and generic, works with any registry structure, and lets users specify exactly what upstream image WUD should check for updates. What changed: - Renamed label from `wud.registry.lookup.url` to `wud.registry.lookup.image` - Now accepts full image paths (e.g., "library/traefik" or "ghcr.io/user/image") - Automatically defaults to Docker Hub for short-form images without a registry - Fixed registry normalization to use the lookup image instead of the original - Updated documentation - Added Prometheus metric support for the lookup image field - Removed tests specific to URL-based parsing (no longer needed with generic image path approach) Example usage: image: harbor.example.com/dockerhub-proxy/traefik:v3.5.3 labels: - wud.watch=true - wud.registry.lookup.image=library/traefik This allows WUD to check Docker Hub for traefik updates while the container continues pulling from the Harbor proxy cache, solving the original issue where proxy registries don't have up-to-date tag information. Tested with: - Harbor proxy cache (Docker Hub, GHCR, Codeberg) - All 428 unit tests passing - Live deployment on my homelab --- app/model/container.js | 2 +- app/prometheus/container.js | 1 + app/registries/providers/acr/Acr.test.js | 1 - app/registries/providers/ecr/Ecr.test.js | 5 - app/registries/providers/gcr/Gcr.test.js | 8 - app/registries/providers/ghcr/Ghcr.test.js | 5 - app/registries/providers/gitea/Gitea.test.js | 5 - .../providers/gitlab/Gitlab.test.js | 2 - app/registries/providers/lscr/Lscr.test.js | 5 - app/registries/providers/quay/Quay.test.js | 5 - app/watchers/providers/docker/Docker.js | 36 +- app/watchers/providers/docker/label.js | 4 +- docs/configuration/watchers/README.md | 23 +- docs/configuration/watchers/README.md.backup | 475 ------------------ e2e/features/api-container.feature | 26 - e2e/features/prometheus.feature | 24 - ui/src/components/ContainerImage.vue | 2 +- 17 files changed, 45 insertions(+), 584 deletions(-) delete mode 100644 docs/configuration/watchers/README.md.backup diff --git a/app/model/container.js b/app/model/container.js index fd69de18..a6190455 100644 --- a/app/model/container.js +++ b/app/model/container.js @@ -26,7 +26,7 @@ const schema = joi.object({ .object({ name: joi.string().min(1).required(), url: joi.string().min(1).required(), - lookupUrl: joi.string(), + lookupImage: joi.string(), }) .required(), name: joi.string().min(1).required(), diff --git a/app/prometheus/container.js b/app/prometheus/container.js index 1badf12e..20c6ee29 100644 --- a/app/prometheus/container.js +++ b/app/prometheus/container.js @@ -55,6 +55,7 @@ function init() { 'image_id', 'image_name', 'image_os', + 'image_registry_lookup_image', 'image_registry_name', 'image_registry_url', 'image_tag_semver', diff --git a/app/registries/providers/acr/Acr.test.js b/app/registries/providers/acr/Acr.test.js index fb30921d..fc85e7dd 100644 --- a/app/registries/providers/acr/Acr.test.js +++ b/app/registries/providers/acr/Acr.test.js @@ -39,7 +39,6 @@ test('match should return true when registry url is from acr', () => { }, }), ).toBeTruthy(); - expect(acr.match('test.azurecr.io')).toBeTruthy(); }); test('match should return false when registry url is not from acr', () => { diff --git a/app/registries/providers/ecr/Ecr.test.js b/app/registries/providers/ecr/Ecr.test.js index 7d3dca65..201d41c3 100644 --- a/app/registries/providers/ecr/Ecr.test.js +++ b/app/registries/providers/ecr/Ecr.test.js @@ -79,11 +79,6 @@ test('match should return false when registry url is not from ecr', () => { }, }), ).toBeFalsy(); - expect(ecr.match('123456789.dkr.ecr.eu-west-1.amazonaws.com')).toBeTruthy(); -}); - -test('match should return false when registry url is not from ecr', () => { - expect(ecr.match('123456789.dkr.ecr.eu-west-1.acme.com')).toBeFalsy(); }); test('maskConfiguration should mask configuration secrets', () => { diff --git a/app/registries/providers/gcr/Gcr.test.js b/app/registries/providers/gcr/Gcr.test.js index 2ef92126..32b7bd50 100644 --- a/app/registries/providers/gcr/Gcr.test.js +++ b/app/registries/providers/gcr/Gcr.test.js @@ -78,14 +78,6 @@ test('match should return false when registry url is not from gcr', () => { }, }), ).toBeFalsy(); - expect(gcr.match('gcr.io')).toBeTruthy(); - expect(gcr.match('us.gcr.io')).toBeTruthy(); - expect(gcr.match('eu.gcr.io')).toBeTruthy(); - expect(gcr.match('asia.gcr.io')).toBeTruthy(); -}); - -test('match should return false when registry url is not from gcr', () => { - expect(gcr.match('grr.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/ghcr/Ghcr.test.js b/app/registries/providers/ghcr/Ghcr.test.js index fe7502c6..bc0dc826 100644 --- a/app/registries/providers/ghcr/Ghcr.test.js +++ b/app/registries/providers/ghcr/Ghcr.test.js @@ -43,11 +43,6 @@ test('match should return false when registry url is not from ghcr', () => { }, }), ).toBeFalsy(); - expect(ghcr.match('ghcr.io')).toBeTruthy(); -}); - -test('match should return false when registry url is not from ghcr', () => { - expect(ghcr.match('grr.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/gitea/Gitea.test.js b/app/registries/providers/gitea/Gitea.test.js index dc12f264..1568f6ff 100644 --- a/app/registries/providers/gitea/Gitea.test.js +++ b/app/registries/providers/gitea/Gitea.test.js @@ -48,11 +48,6 @@ test('match should return false when registry url is not from custom', () => { }, }), ).toBeFalsy(); - expect(gitea.match('gitea.acme.com')).toBeTruthy(); -}); - -test('match should return false when registry url is not from custom', () => { - expect(gitea.match('gitea.notme.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/gitlab/Gitlab.test.js b/app/registries/providers/gitlab/Gitlab.test.js index 2838e975..d8023610 100644 --- a/app/registries/providers/gitlab/Gitlab.test.js +++ b/app/registries/providers/gitlab/Gitlab.test.js @@ -55,7 +55,6 @@ test('match should return true when registry url is from gitlab.com', () => { }, }), ).toBeTruthy(); - expect(gitlab.match('registry.gitlab.com')).toBeTruthy(); }); test('match should return true when registry url is from custom gitlab', () => { @@ -72,7 +71,6 @@ test('match should return true when registry url is from custom gitlab', () => { }, }), ).toBeTruthy(); - expect(gitlabCustom.match('custom.com')).toBeTruthy(); }); test('authenticate should perform authenticate request', () => { diff --git a/app/registries/providers/lscr/Lscr.test.js b/app/registries/providers/lscr/Lscr.test.js index 452b0cf4..8e770c52 100644 --- a/app/registries/providers/lscr/Lscr.test.js +++ b/app/registries/providers/lscr/Lscr.test.js @@ -50,11 +50,6 @@ test('match should return false when registry url is not from lscr', () => { }, }), ).toBeFalsy(); - expect(lscr.match('lscr.io')).toBeTruthy(); -}); - -test('match should return false when registry url is not from lscr', () => { - expect(lscr.match('wrong.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/registries/providers/quay/Quay.test.js b/app/registries/providers/quay/Quay.test.js index 74ad1243..10125adf 100644 --- a/app/registries/providers/quay/Quay.test.js +++ b/app/registries/providers/quay/Quay.test.js @@ -72,11 +72,6 @@ test('match should return false when registry url is not from quay.io', () => { }, }), ).toBeFalsy(); - expect(quay.match('quay.io')).toBeTruthy(); -}); - -test('match should return false when registry url is not from quay.io', () => { - expect(quay.match('error.io')).toBeFalsy(); }); test('normalizeImage should return the proper registry v2 endpoint', () => { diff --git a/app/watchers/providers/docker/Docker.js b/app/watchers/providers/docker/Docker.js index 7c8df788..e7103393 100644 --- a/app/watchers/providers/docker/Docker.js +++ b/app/watchers/providers/docker/Docker.js @@ -21,7 +21,7 @@ const { wudDisplayIcon, wudTriggerInclude, wudTriggerExclude, - wudRegistryLookupUrl, + wudRegistryLookupImage, } = require('./label'); const storeContainer = require('../../../store/container'); const log = require('../../../log'); @@ -113,19 +113,35 @@ function getTagCandidates(container, tags, logContainer) { function normalizeContainer(container) { const containerWithNormalizedImage = container; - // Create a temporary image with lookupUrl if present for registry matching - const imageForMatching = container.image.registry.lookupUrl - ? { ...container.image, registry: { ...container.image.registry, url: container.image.registry.lookupUrl } } - : container.image; - const registryProvider = Object.values(getRegistries()).find((provider) => + // Create a temporary image with parsed lookupImage if present for registry matching + let imageForMatching = container.image; + if (container.image.registry.lookupImage) { + const parsedLookupImage = parse(container.image.registry.lookupImage); + // If no domain specified, default to Docker Hub registry + const registryUrl = parsedLookupImage.domain || 'registry-1.docker.io'; + log.info(`[DEBUG] lookupImage: ${container.image.registry.lookupImage}, parsed domain: ${parsedLookupImage.domain}, registryUrl: ${registryUrl}, path: ${parsedLookupImage.path}`); + imageForMatching = { + ...container.image, + registry: { + ...container.image.registry, + url: registryUrl, + }, + name: parsedLookupImage.path, + }; + } + const registries = getRegistries(); + log.info(`[DEBUG] Available registries: ${Object.keys(registries).join(', ')}`); + log.info(`[DEBUG] Matching against registry URL: ${imageForMatching.registry.url}`); + const registryProvider = Object.values(registries).find((provider) => provider.match(imageForMatching), ); if (!registryProvider) { log.warn(`${fullName(container)} - No Registry Provider found`); containerWithNormalizedImage.image.registry.name = 'unknown'; } else { + log.info(`[DEBUG] Found registry provider: ${registryProvider.getId()}`); containerWithNormalizedImage.image = registryProvider.normalizeImage( - container.image, + imageForMatching, ); containerWithNormalizedImage.image.registry.name = registryProvider.getId(); @@ -550,7 +566,7 @@ class Docker extends Component { container.Labels[wudDisplayIcon], container.Labels[wudTriggerInclude], container.Labels[wudTriggerExclude], - container.Labels[wudRegistryLookupUrl], + container.Labels[wudRegistryLookupImage], ), ); const containersWithImage = await Promise.all(containerPromises); @@ -676,7 +692,7 @@ class Docker extends Component { displayIcon, triggerInclude, triggerExclude, - wudRegistryLookupUrlValue, + wudRegistryLookupImageValue, ) { const containerId = container.Id; @@ -745,7 +761,7 @@ class Docker extends Component { id: imageId, registry: { url: parsedImage.domain, - lookupUrl: wudRegistryLookupUrlValue, + lookupImage: wudRegistryLookupImageValue, }, name: parsedImage.path, tag: { diff --git a/app/watchers/providers/docker/label.js b/app/watchers/providers/docker/label.js index afc52d89..5d91683e 100644 --- a/app/watchers/providers/docker/label.js +++ b/app/watchers/providers/docker/label.js @@ -53,7 +53,7 @@ module.exports = { wudTriggerExclude: 'wud.trigger.exclude', /** - * Optional Lookup registry url (can be useful when pulling containers from a registry cache) + * Optional Lookup image (can be useful when pulling containers from a registry cache) */ - wudRegistryLookupUrl: 'wud.registry.lookup.url', + wudRegistryLookupImage: 'wud.registry.lookup.image', }; diff --git a/docs/configuration/watchers/README.md b/docs/configuration/watchers/README.md index 92d853b9..d3bd28ee 100644 --- a/docs/configuration/watchers/README.md +++ b/docs/configuration/watchers/README.md @@ -183,9 +183,9 @@ To fine-tune the behaviour of WUD _per container_, you can add labels on them. |-----------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| | `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | | `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | -| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | -| `wud.registry.lookup.url` | :white_circle: | Custom registry url to use for update lookups | Any valid Docker registry url | | -| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | +| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | +| `wud.registry.lookup.image` | :white_circle: | Alternative image to use for update lookups | Full image path (e.g., `namespace/image` or `registry.example.com/namespace/image`) | | +| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | | `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | | `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | | `wud.trigger.exclude` | :white_circle: | Optional list of triggers to exclude | `$trigger_1_id,$trigger_2_id:$threshold` | | @@ -439,8 +439,8 @@ docker run -d --name my_important_service --label 'wud.trigger.include=smtp.gmai ?> Threshold `patch` means that the trigger will run only if this is a `patch` semver change -### Use an alternative Registry url for update lookups -In some situations (e.g. when using a Docker Registry cache), it can be needed to give the url of the upstream registry (e.g. the Docker hub registry url) +### Use an alternative image for update lookups +When using a Docker Registry cache (Harbor, Nexus, etc.), you need to specify the upstream image that WUD should check for updates. #### **Docker Compose** @@ -449,14 +449,19 @@ version: '3' services: - mariadb: - image: localhost:5000/mariadb:10.6.4 + traefik: + image: harbor.example.com/ghcr-proxy/traefik/traefik:v3.5.3 labels: - - wud.registry.lookup.url=https://registry-1.docker.io + - wud.watch=true + - wud.registry.lookup.image=ghcr.io/traefik/traefik ``` #### **Docker** ```bash -docker run -d --name mariadb --label 'wud.registry.lookup.url=https://registry-1.docker.io' localhost:5000/mariadb:10.6.4 +docker run -d \ + --name traefik \ + --label 'wud.watch=true' \ + --label 'wud.registry.lookup.image=ghcr.io/traefik/traefik' \ + harbor.example.com/ghcr-proxy/traefik/traefik:v3.5.3 ``` diff --git a/docs/configuration/watchers/README.md.backup b/docs/configuration/watchers/README.md.backup deleted file mode 100644 index 07c11905..00000000 --- a/docs/configuration/watchers/README.md.backup +++ /dev/null @@ -1,475 +0,0 @@ -# Docker Watchers -![logo](docker.png) - -Watchers are responsible for scanning Docker containers. - -The `docker` watcher lets you configure the Docker hosts you want to watch. - -## Variables - -| Env var | Required | Description | Supported values | Default value when missing | -| --------------------------------------------------------- |:--------------:| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | --------------------------------------------------------------- | -| `WUD_WATCHER_{watcher_name}_CAFILE` | :white_circle: | CA pem file path (only for TLS connection) | | | -| `WUD_WATCHER_{watcher_name}_CERTFILE` | :white_circle: | Certificate pem file path (only for TLS connection) | | | -| `WUD_WATCHER_{watcher_name}_CRON` | :white_circle: | Scheduling options | [Valid CRON expression](https://crontab.guru/) | `0 * * * *` (every hour) | -| `WUD_WATCHER_{watcher_name}_HOST` | :white_circle: | Docker hostname or ip of the host to watch | | | -| `WUD_WATCHER_{watcher_name}_JITTER` | :white_circle: | Jitter in ms applied to the CRON to better distribute the load on the registries (on the Hub at the first place) | > 0 | `60000` (1 minute) | -| `WUD_WATCHER_{watcher_name}_KEYFILE` | :white_circle: | Key pem file path (only for TLS connection) | | | -| `WUD_WATCHER_{watcher_name}_PORT` | :white_circle: | Docker port of the host to watch | | `2375` | -| `WUD_WATCHER_{watcher_name}_SOCKET` | :white_circle: | Docker socket to watch | Valid unix socket | `/var/run/docker.sock` | -| `WUD_WATCHER_{watcher_name}_WATCHALL` | :white_circle: | If WUD must monitor all containers instead of just running ones | `true`, `false` | `false` | -| `WUD_WATCHER_{watcher_name}_WATCHATSTART` (deprecated) | :white_circle: | If WUD must check for image updates during startup | `true`, `false` | `true` if store is empy | -| `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` | :white_circle: | If WUD must monitor all containers by default | `true`, `false` | `true` | -| `WUD_WATCHER_{watcher_name}_WATCHEVENTS` | :white_circle: | If WUD must monitor docker events | `true`, `false` | `true` | - -?> If no watcher is configured, a default one named `local` will be automatically created (reading the Docker socket). - -?> Multiple watchers can be configured (if you have multiple Docker hosts to watch). -You just need to give them different names. - -!> Socket configuration and host/port configuration are mutually exclusive. - -!> If socket configuration is used, don't forget to mount the Docker socket on your WUD container. - -!> If host/port configuration is used, don't forget to enable the Docker remote API. \ -[See dockerd documentation](https://docs.docker.com/engine/reference/commandline/dockerd/#description) - -!> If the Docker remote API is secured with TLS, don't forget to mount and configure the TLS certificates. \ -[See dockerd documentation](https://docs.docker.com/engine/security/protect-access/#use-tls-https-to-protect-the-docker-daemon-socket) - -!> Watching image digests causes an extensive usage of _Docker Registry Pull API_ which is restricted by [**Quotas on the Docker Hub**](https://docs.docker.com/docker-hub/download-rate-limit/). \ -By default, WUD enables it only for **non semver** image tags. \ -You can tune this behavior per container using the `wud.watch.digest` label. \ -If you face [quota related errors](https://docs.docker.com/docker-hub/download-rate-limit/#how-do-i-know-my-pull-requests-are-being-limited), consider slowing down the watcher rate by adjusting the `WUD_WATCHER_{watcher_name}_CRON` variable. - -## Variable examples - -### Watch the local docker host every day at 1am - - -#### **Docker Compose** -```yaml -services: - whatsupdocker: - image: getwud/wud - ... - environment: - - WUD_WATCHER_LOCAL_CRON=0 1 * * * -``` - -#### **Docker** -```bash -docker run \ - -e WUD_WATCHER_LOCAL_CRON="0 1 * * *" \ - ... - getwud/wud -``` - - -### Watch all containers regardless of their status (created, paused, exited, restarting, running...) - - -#### **Docker Compose** -```yaml -services: - whatsupdocker: - image: getwud/wud - ... - environment: - - WUD_WATCHER_LOCAL_WATCHALL=true -``` - -#### **Docker** -```bash -docker run \ - -e WUD_WATCHER_LOCAL_WATCHALL="true" \ - ... - getwud/wud -``` - - -### Watch a remote docker host via TCP on 2375 - - -#### **Docker Compose** -```yaml -services: - whatsupdocker: - image: getwud/wud - ... - environment: - - WUD_WATCHER_MYREMOTEHOST_HOST=myremotehost -``` - -#### **Docker** -```bash -docker run \ - -e WUD_WATCHER_MYREMOTEHOST_HOST="myremotehost" \ - ... - getwud/wud -``` - - -### Watch a remote docker host via TCP with TLS enabled on 2376 - - -#### **Docker Compose** -```yaml -services: - whatsupdocker: - image: getwud/wud - ... - environment: - - WUD_WATCHER_MYREMOTEHOST_HOST=myremotehost - - WUD_WATCHER_MYREMOTEHOST_PORT=2376 - - WUD_WATCHER_MYREMOTEHOST_CAFILE=/certs/ca.pem - - WUD_WATCHER_MYREMOTEHOST_CERTFILE=/certs/cert.pem - - WUD_WATCHER_MYREMOTEHOST_KEYFILE=/certs/key.pem - volumes: - - /my-host/my-certs/ca.pem:/certs/ca.pem:ro - - /my-host/my-certs/ca.pem:/certs/cert.pem:ro - - /my-host/my-certs/ca.pem:/certs/key.pem:ro -``` - -#### **Docker** -```bash -docker run \ - -e WUD_WATCHER_MYREMOTEHOST_HOST="myremotehost" \ - -e WUD_WATCHER_MYREMOTEHOST_PORT="2376" \ - -e WUD_WATCHER_MYREMOTEHOST_CAFILE="/certs/ca.pem" \ - -e WUD_WATCHER_MYREMOTEHOST_CERTFILE="/certs/cert.pem" \ - -e WUD_WATCHER_MYREMOTEHOST_KEYFILE="/certs/key.pem" \ - -v /my-host/my-certs/ca.pem:/certs/ca.pem:ro \ - -v /my-host/my-certs/ca.pem:/certs/cert.pem:ro \ - -v /my-host/my-certs/ca.pem:/certs/key.pem:ro \ - ... - getwud/wud -``` - - -!> Don't forget to mount the certificates into the container! - -### Watch 1 local Docker host and 2 remote docker hosts at the same time - - -#### **Docker Compose** -```yaml -services: - whatsupdocker: - image: getwud/wud - ... - environment: - - WUD_WATCHER_LOCAL_SOCKET=/var/run/docker.sock - - WUD_WATCHER_MYREMOTEHOST1_HOST=myremotehost1 - - WUD_WATCHER_MYREMOTEHOST2_HOST=myremotehost2 -``` - -#### **Docker** -```bash -docker run \ - -e WUD_WATCHER_LOCAL_SOCKET="/var/run/docker.sock" \ - -e WUD_WATCHER_MYREMOTEHOST1_HOST="myremotehost1" \ - -e WUD_WATCHER_MYREMOTEHOST2_HOST="myremotehost2" \ - ... - getwud/wud -``` - - -## Labels - -To fine-tune the behaviour of WUD _per container_, you can add labels on them. - -<<<<<<< HEAD -| Label | Required | Description | Supported values | Default value when missing | -|-----------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| -| `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | -| `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | -| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | -| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | -| `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | -| `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | -| `wud.trigger.exclude` | :white_circle: | Optional list of triggers to exclude | `$trigger_1_id,$trigger_2_id:$threshold` | | -| `wud.trigger.include` | :white_circle: | Optional list of triggers to include | `$trigger_1_id,$trigger_2_id:$threshold` | | -| `wud.watch.digest` | :white_circle: | Watch this container digest | Valid Boolean | `false` | -| `wud.watch` | :white_circle: | Watch this container | Valid Boolean | `true` when `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` is `true` (`false` otherwise) | -======= -| Label | Required | Description | Supported values | Default value when missing | -|---------------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| -| `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | -| `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | -| `wud.link.template` | :white_circle: | Browsable link associated to the container version | String template with placeholders `${raw}` `${major}` `${minor}` `${patch}` `${prerelease}` | | -| `wud.registry.lookup.url` | :white_circle: | Custom registy url to use for update lookups | Any valid Docker registry url | | -| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | -| `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | -| `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | -| `wud.watch.digest` | :white_circle: | Watch this container digest | Valid Boolean | `false` | -| `wud.watch` | :white_circle: | Watch this container | Valid Boolean | `true` when `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` is `true` (`false` otherwise) | ->>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) - -## Label examples - -### Include specific containers to watch -Configure WUD to disable WATCHBYDEFAULT feature. - -#### **Docker Compose** -```yaml -services: - whatsupdocker: - image: getwud/wud - ... - environment: - - WUD_WATCHER_LOCAL_WATCHBYDEFAULT=false -``` - -#### **Docker** -```bash -docker run \ - -e WUD_WATCHER_LOCAL_WATCHBYDEFAULT="false" \ - ... - getwud/wud -``` - - -Then add the `wud.watch=true` label on the containers you want to watch. - -#### **Docker Compose** -```yaml -services: - mariadb: - image: mariadb:10.4.5 - ... - labels: - - wud.watch=true -``` - -#### **Docker** -```bash -docker run -d --name mariadb --label wud.watch=true mariadb:10.4.5 -``` - - -### Exclude specific containers to watch -Ensure `WUD_WATCHER_{watcher_name}_WATCHBYDEFAULT` is true (default value). - -Then add the `wud.watch=false` label on the containers you want to exclude from being watched. - -#### **Docker Compose** -```yaml -services: - mariadb: - image: mariadb:10.4.5 - ... - labels: - - wud.watch=false -``` - -#### **Docker** -```bash -docker run -d --name mariadb --label wud.watch=false mariadb:10.4.5 -``` - - -### Include only 3 digits semver tags -You can filter (by inclusion or exclusion) which versions can be candidates for update. - -For example, you can indicate that you want to watch x.y.z versions only - -#### **Docker Compose** -```yaml -services: - - mariadb: - image: mariadb:10.4.5 - labels: - - wud.tag.include=^\d+\.\d+\.\d+$$ -``` - -#### **Docker** -```bash -docker run -d --name mariadb --label 'wud.tag.include=^\d+\.\d+\.\d+$' mariadb:10.4.5 -``` - - -### Transform the tags before performing the analysis -In certain cases, tag values are so badly formatted that the resolution algorithm cannot find any valid update candidates or, worst, find bad positive matches. - -For example, you can encounter such an issue if you need to deal with tags looking like `1.0.0-99-7b368146`, `1.0.0-273-21d7efa6`... -By default, WUD will report bad positive matches because of the `sha-1` part at the end of the tag value (`-7b368146`...). -That's a shame because `1.0.0-99` and `1.0.0-273` would have been valid semver values (`$major.$minor.$patch-$prerelease`). - -You can get around this issue by providing a function that keeps only the part you are interested in. - -How does it work? -The transform function must follow the following syntax: -``` -$valid_regex_with_capturing_groups => $valid_string_with_placeholders -``` - -For example: -```bash -^(\d+\.\d+\.\d+-\d+)-.*$ => $1 -``` - -The capturing groups are accessible with the syntax `$1`, `$2`, `$3`.... - -!> The first capturing group is accessible as `$1`! - -For example, you can indicate that you want to watch x.y.z versions only - -#### **Docker Compose** -```yaml -services: - - searx: - image: searx/searx:1.0.0-269-7b368146 - labels: - - wud.tag.include=^\d+\.\d+\.\d+-\d+-.*$$ - - wud.tag.transform=^(\d+\.\d+\.\d+-\d+)-.*$$ => $$1 -``` - -#### **Docker** -```bash -docker run -d --name searx \ ---label 'wud.tag.include=^\d+\.\d+\.\d+-\d+-.*$' \ ---label 'wud.tag.transform=^(\d+\.\d+\.\d+-\d+)-.*$ => $1' \ -searx/searx:1.0.0-269-7b368146 -``` - - -### Enable digest watching -Additionally to semver tag tracking, you can also track if the digest associated to the local tag has been updated. -It can be convenient to monitor image tags known to be overridden (`latest`, `10`, `10.6`...) - - -#### **Docker Compose** -```yaml -services: - - mariadb: - image: mariadb:10 - labels: - - wud.tag.include=^\d+$$ - - wud.watch.digest=true -``` -#### **Docker** -```bash -docker run -d --name mariadb --label 'wud.tag.include=^\d+$' --label wud.watch.digest=true mariadb:10 -``` - - -### Associate a link to the container version -You can associate a browsable link to the container version using a templated string. -For example, if you want to associate a mariadb version to a changelog (e.g. https://mariadb.com/kb/en/mariadb-1064-changelog), - -you would specify a template like `https://mariadb.com/kb/en/mariadb-${major}${minor}${patch}-changelog` - -The available variables are: -- `${original}` the original unparsed tag -- `${transformed}` the original unparsed tag transformed with the optional `wud.tag.transform` label option -- `${major}` the major version (if tag value is semver) -- `${minor}` the minor version (if tag value is semver) -- `${patch}` the patch version (if tag value is semver) -- `${prerelease}` the prerelease version (if tag value is semver) - - -#### **Docker Compose** -```yaml -services: - - mariadb: - image: mariadb:10.6.4 - labels: - - wud.link.template=https://mariadb.com/kb/en/mariadb-$${major}$${minor}$${patch}-changelog -``` - -#### **Docker** -```bash -docker run -d --name mariadb --label 'wud.link.template=https://mariadb.com/kb/en/mariadb-${major}${minor}${patch}-changelog' mariadb:10 -``` - - -### Customize the name and the icon to display -You can customize the name & the icon of a container (displayed in the UI, in Home-Assistant...) - -Icons must be prefixed with: -- `fab:` or `fab-` for [Fontawesome brand icons](https://fontawesome.com/) (`fab:github`, `fab-mailchimp`...) -- `far:` or `far-` for [Fontawesome regular icons](https://fontawesome.com/) (`far:heart`, `far-house`...) -- `fas:` or `fas-` for [Fontawesome solid icons](https://fontawesome.com/) (`fas:heart`, `fas-house`...) -- `hl:` or `hl-` for [Homarr Labs icons](https://dashboardicons.com/) (`hl:plex`, `hl-authelia`...) -- `mdi:` or `mdi-` for [Material Design icons](https://materialdesignicons.com/) (`mdi:database`, `mdi-server`...) -- `sh:` or `sh-` for [Selfh.st](https://selfh.st/icons/) (`sh:authentik`, `sh-authelia-light`...) (only works for logo available as `png`) -- `si:` or `si-` for [Simple icons](https://simpleicons.org/) (`si:mysql`, `si-plex`...) - -?> If you want to display Fontawesome icons or Simple icons in Home-Assistant, you need to install first the [HASS-fontawesome](https://github.com/thomasloven/hass-fontawesome) and the [HASS-simpleicons](https://github.com/vigonotion/hass-simpleicons) components. - - -#### **Docker Compose** -```yaml -services: - - mariadb: - image: mariadb:10.6.4 - labels: - - wud.display.name=Maria DB - - wud.display.icon=si:mariadb -``` - -#### **Docker** -```bash -docker run -d --name mariadb --label 'wud.display.name=Maria DB' --label 'wud.display.icon=mdi-database' mariadb:10.6.4 -``` - - -<<<<<<< HEAD -### Assign different triggers to containers -You can assign different triggers and thresholds on a per container basis. - -#### Example send a mail notification for all updates but auto-update only if minor or patch -======= -### Use an alternative Registry url for update lookups -In some situations (e.g. when using a Docker Registry cache), it can be needed to give the url of the upstream registry (e.g. the Docker hub registry url) ->>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) - - -#### **Docker Compose** -```yaml -<<<<<<< HEAD -services: - - my_important_service: - image: my_important_service:1.0.0 - labels: - - wud.trigger.include=smtp.gmail,dockercompose.local:minor -======= -version: '3' - -services: - - mariadb: - image: localhost:5000/mariadb:10.6.4 - labels: - - wud.registry.lookup.url=https://registry-1.docker.io ->>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) -``` - -#### **Docker** -```bash -<<<<<<< HEAD -docker run -d --name my_important_service --label 'wud.trigger.include=smtp.gmail,dockercompose.local:minor' my_important_service:1.0.0 -``` - - -?> `wud.trigger.include=smtp.gmail` is a shorthand for `wud.trigger.include=smtp.gmail:all` - -?> Threshold `all` means that the trigger will run regardless of the nature of the change - -?> Threshold `major` means that the trigger will run only if this is a `major`, `minor` or `patch` semver change - -?> Threshold `minor` means that the trigger will run only if this is a `minor` or `patch` semver change - -?> Threshold `patch` means that the trigger will run only if this is a `patch` semver change -======= -docker run -d --name mariadb --label 'wud.registry.lookup.url=https://registry-1.docker.io' localhost:5000/mariadb:10.6.4 -``` - ->>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) diff --git a/e2e/features/api-container.feature b/e2e/features/api-container.feature index 843a9537..2e3a7b67 100644 --- a/e2e/features/api-container.feature +++ b/e2e/features/api-container.feature @@ -17,7 +17,6 @@ Feature: WUD Container API Exposure And response body path $[].result.tag should be And response body path $[].updateAvailable should be Examples: -<<<<<<< HEAD | index | registry | containerName | registryUrl | imageName | tag | resultTag | updateAvailable | | 0 | ecr.private | ecr_sub_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | | 1 | ecr.private | ecr_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | @@ -39,29 +38,6 @@ Feature: WUD Container API Exposure | 17 | hub.public | hub_youtubedb_latest | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | | 18 | lscr.private | lscr_radarr | https://lscr.io/v2 | linuxserver/radarr | 5.14.0.9383-ls245 | 5.26.2.10099-ls277 | true | | 19 | quay.public | quay_prometheus | https://quay.io/v2 | prometheus/prometheus | v2.52.0 | v3.4.2 | true | -======= - | index | registry | containerName | registryUrl | imageName | tag | resultTag | updateAvailable | - | 0 | ecr | ecr_sub_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | - | 1 | ecr | ecr_sub_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | - | 2 | ecr | ecr_test | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | test | 1.0.0 | 2.0.0 | true | - | 3 | ghcr | ghcr_radarr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.11.0.9244-ls241 | true | - | 4 | gitlab | gitlab_test | https://registry.gitlab.com/v2 | manfred-martin/docker-registry-test | 1.0.0 | 2.0.0 | true | - | 5 | hub | hub_homeassistant_202161 | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.10.2 | true | - | 6 | hub | hub_homeassistant_latest | https://registry-1.docker.io/v2 | homeassistant/home-assistant | latest | latest | false | - | 7 | hub | hub_nginx_120 | https://registry-1.docker.io/v2 | library/nginx | 1.20-alpine | 1.27-alpine | true | - | 8 | hub | hub_nginx_latest | https://registry-1.docker.io/v2 | library/nginx | latest | latest | true | - | 9 | hub | hub_omnidb_latest | https://registry-1.docker.io/v2 | omnidbteam/omnidb | latest | latest | false | - | 10 | hub | hub_pihole_57 | https://registry-1.docker.io/v2 | pihole/pihole | v5.7 | v5.8.1 | true | - | 11 | hub | hub_pihole_latest | https://registry-1.docker.io/v2 | pihole/pihole | latest | latest | false | - | 12 | hub | hub_pyload_latest | https://registry-1.docker.io/v2 | writl/pyload | latest | latest | false | - | 13 | hub | hub_traefik_245 | https://registry-1.docker.io/v2 | library/traefik | 2.4.5 | 3.1.6 | true | - | 14 | hub | hub_traefik_latest | https://registry-1.docker.io/v2 | library/traefik | latest | latest | false | - | 15 | hub | hub_vaultwarden_1222 | https://registry-1.docker.io/v2 | vaultwarden/server | 1.32.1-alpine | 1.32.1-alpine | false | - | 16 | hub | hub_vaultwarden_latest | https://registry-1.docker.io/v2 | vaultwarden/server | latest | latest | false | - | 17 | hub | hub_youtubedb_latest | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | - | 18 | lscr | lscr_radarr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 | 5.11.0.9244-ls241 | true | - | 19 | quay | quay_prometheus | https://quay.io/v2 | prometheus/prometheus | v2.52.0 | v2.54.1 | true | ->>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) Scenario: WUD must allow to get a container with semver Given I GET /api/containers @@ -109,9 +85,7 @@ Feature: WUD Container API Exposure Then response code should be 200 And response body should be valid json And response body path $.link should be https://github.com/home-assistant/core/releases/tag/2021.6.1 -<<<<<<< HEAD And response body path $.result.link should be https://github.com/home-assistant/core/releases/tag/2025.7.1 -======= And response body path $.result.link should be https://github.com/home-assistant/core/releases/tag/2024.10.2 >>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) diff --git a/e2e/features/prometheus.feature b/e2e/features/prometheus.feature index b273c849..96c276a2 100644 --- a/e2e/features/prometheus.feature +++ b/e2e/features/prometheus.feature @@ -21,7 +21,6 @@ Feature: Prometheus exposure And response body should contain result_tag="" And response body should contain update_available="" Examples: -<<<<<<< HEAD | containerName | registry | registryUrl | imageName | tag | resultTag | updateAvailable | | ecr_sub_sub_test | ecr.private | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | | ecr_sub_test | ecr.private | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | @@ -43,26 +42,3 @@ Feature: Prometheus exposure | hub_youtubedb_latest | hub.public | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | | lscr_radarr | lscr.private | https://lscr.io/v2 | linuxserver/radarr | 5.14.0.9383-ls245 |5.26.2.10099-ls277 | true | | quay_prometheus | quay.public | https://quay.io/v2 | prometheus/prometheus | v2.52.0 |v3.4.2 | true | -======= - | containerName | registry | registryUrl | imageName | tag | resultTag | updateAvailable | - | ecr_sub_sub_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/sub/test | 1.0.0 | 2.0.0 | true | - | ecr_sub_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | sub/test | 1.0.0 | 2.0.0 | true | - | ecr_test | ecr | https://229211676173.dkr.ecr.eu-west-1.amazonaws.com/v2 | test | 1.0.0 | 2.0.0 | true | - | ghcr_radarr | ghcr | https://ghcr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.11.0.9244-ls241 | true | - | gitlab_test | gitlab | https://registry.gitlab.com/v2 | manfred-martin/docker-registry-test | 1.0.0 | 2.0.0 | true | - | hub_homeassistant_202161 | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | 2021.6.1 | 2024.10.2 | true | - | hub_homeassistant_latest | hub | https://registry-1.docker.io/v2 | homeassistant/home-assistant | latest | latest | false | - | hub_nginx_120 | hub | https://registry-1.docker.io/v2 | library/nginx | 1.20-alpine | 1.27-alpine | true | - | hub_nginx_latest | hub | https://registry-1.docker.io/v2 | library/nginx | latest | latest | true | - | hub_omnidb_latest | hub | https://registry-1.docker.io/v2 | omnidbteam/omnidb | latest | latest | false | - | hub_pihole_57 | hub | https://registry-1.docker.io/v2 | pihole/pihole | v5.7 | v5.8.1 | true | - | hub_pihole_latest | hub | https://registry-1.docker.io/v2 | pihole/pihole | latest | latest | false | - | hub_pyload_latest | hub | https://registry-1.docker.io/v2 | writl/pyload | latest | latest | false | - | hub_traefik_245 | hub | https://registry-1.docker.io/v2 | library/traefik | 2.4.5 | 3.1.6 | true | - | hub_traefik_latest | hub | https://registry-1.docker.io/v2 | library/traefik | latest | latest | false | - | hub_vaultwarden_1222 | hub | https://registry-1.docker.io/v2 | vaultwarden/server | 1.32.1-alpine | 1.32.1-alpine | false | - | hub_vaultwarden_latest | hub | https://registry-1.docker.io/v2 | vaultwarden/server | latest | latest | false | - | hub_youtubedb_latest | hub | https://registry-1.docker.io/v2 | jeeaaasustest/youtube-dl | latest | latest | false | - | lscr_radarr | lscr | https://lscr.io/v2 | linuxserver/radarr | 3.2.1.5070-ls105 |5.11.0.9244-ls241 | true | - | quay_prometheus | quay | https://quay.io/v2 | prometheus/prometheus | v2.52.0 |v2.54.1 | true | ->>>>>>> ab7cf7c (:star: [WATCHER] - Add support for alternative lookup registry url (useful when pulling from registry cache)) diff --git a/ui/src/components/ContainerImage.vue b/ui/src/components/ContainerImage.vue index fb69c1ac..b5473e25 100644 --- a/ui/src/components/ContainerImage.vue +++ b/ui/src/components/ContainerImage.vue @@ -45,7 +45,7 @@ Registry url {{ image.registry.url }} - {{ image.registry.lookupUrl }} (lookup) + {{ image.registry.lookupImage }} (lookup) From 6506dae49f7bf9ac21468cdffb355839b86affbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ne=C3=9Flauer?= Date: Wed, 29 Oct 2025 20:39:15 +0100 Subject: [PATCH 3/5] Revert label naming from lookupImage back to lookupUrl Changed the label from wud.registry.lookup.image back to wud.registry.lookup.url to maintain backward compatibility with users already using the previous PR #454 implementation. This prevents a breaking change for existing users like Edwin who were using the old label name. Changes: - app/model/container.js: Updated schema to use lookupUrl instead of lookupImage - app/watchers/providers/docker/Docker.js: Updated all references to use lookupUrl - app/watchers/providers/docker/label.js: Changed constant from wudRegistryLookupImage to wudRegistryLookupUrl - ui/src/components/ContainerImage.vue: Updated UI to display lookupUrl - docs/configuration/watchers/README.md: Updated documentation and examples --- app/model/container.js | 2 +- app/watchers/providers/docker/Docker.js | 20 ++++++++++---------- app/watchers/providers/docker/label.js | 4 ++-- docs/configuration/watchers/README.md | 10 +++++----- ui/src/components/ContainerImage.vue | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/model/container.js b/app/model/container.js index a6190455..fd69de18 100644 --- a/app/model/container.js +++ b/app/model/container.js @@ -26,7 +26,7 @@ const schema = joi.object({ .object({ name: joi.string().min(1).required(), url: joi.string().min(1).required(), - lookupImage: joi.string(), + lookupUrl: joi.string(), }) .required(), name: joi.string().min(1).required(), diff --git a/app/watchers/providers/docker/Docker.js b/app/watchers/providers/docker/Docker.js index e7103393..d333c99b 100644 --- a/app/watchers/providers/docker/Docker.js +++ b/app/watchers/providers/docker/Docker.js @@ -21,7 +21,7 @@ const { wudDisplayIcon, wudTriggerInclude, wudTriggerExclude, - wudRegistryLookupImage, + wudRegistryLookupUrl, } = require('./label'); const storeContainer = require('../../../store/container'); const log = require('../../../log'); @@ -113,20 +113,20 @@ function getTagCandidates(container, tags, logContainer) { function normalizeContainer(container) { const containerWithNormalizedImage = container; - // Create a temporary image with parsed lookupImage if present for registry matching + // Create a temporary image with parsed lookupUrl if present for registry matching let imageForMatching = container.image; - if (container.image.registry.lookupImage) { - const parsedLookupImage = parse(container.image.registry.lookupImage); + if (container.image.registry.lookupUrl) { + const parsedLookupUrl = parse(container.image.registry.lookupUrl); // If no domain specified, default to Docker Hub registry - const registryUrl = parsedLookupImage.domain || 'registry-1.docker.io'; - log.info(`[DEBUG] lookupImage: ${container.image.registry.lookupImage}, parsed domain: ${parsedLookupImage.domain}, registryUrl: ${registryUrl}, path: ${parsedLookupImage.path}`); + const registryUrl = parsedLookupUrl.domain || 'registry-1.docker.io'; + log.info(`[DEBUG] lookupUrl: ${container.image.registry.lookupUrl}, parsed domain: ${parsedLookupUrl.domain}, registryUrl: ${registryUrl}, path: ${parsedLookupUrl.path}`); imageForMatching = { ...container.image, registry: { ...container.image.registry, url: registryUrl, }, - name: parsedLookupImage.path, + name: parsedLookupUrl.path, }; } const registries = getRegistries(); @@ -566,7 +566,7 @@ class Docker extends Component { container.Labels[wudDisplayIcon], container.Labels[wudTriggerInclude], container.Labels[wudTriggerExclude], - container.Labels[wudRegistryLookupImage], + container.Labels[wudRegistryLookupUrl], ), ); const containersWithImage = await Promise.all(containerPromises); @@ -692,7 +692,7 @@ class Docker extends Component { displayIcon, triggerInclude, triggerExclude, - wudRegistryLookupImageValue, + wudRegistryLookupUrlValue, ) { const containerId = container.Id; @@ -761,7 +761,7 @@ class Docker extends Component { id: imageId, registry: { url: parsedImage.domain, - lookupImage: wudRegistryLookupImageValue, + lookupUrl: wudRegistryLookupUrlValue, }, name: parsedImage.path, tag: { diff --git a/app/watchers/providers/docker/label.js b/app/watchers/providers/docker/label.js index 5d91683e..c93b23fd 100644 --- a/app/watchers/providers/docker/label.js +++ b/app/watchers/providers/docker/label.js @@ -53,7 +53,7 @@ module.exports = { wudTriggerExclude: 'wud.trigger.exclude', /** - * Optional Lookup image (can be useful when pulling containers from a registry cache) + * Optional Lookup URL (can be useful when pulling containers from a registry cache) */ - wudRegistryLookupImage: 'wud.registry.lookup.image', + wudRegistryLookupUrl: 'wud.registry.lookup.url', }; diff --git a/docs/configuration/watchers/README.md b/docs/configuration/watchers/README.md index d3bd28ee..05a5163e 100644 --- a/docs/configuration/watchers/README.md +++ b/docs/configuration/watchers/README.md @@ -183,9 +183,9 @@ To fine-tune the behaviour of WUD _per container_, you can add labels on them. |-----------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| | `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | | `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | -| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | -| `wud.registry.lookup.image` | :white_circle: | Alternative image to use for update lookups | Full image path (e.g., `namespace/image` or `registry.example.com/namespace/image`) | | -| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | +| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | +| `wud.registry.lookup.url` | :white_circle: | Alternative image to use for update lookups | Full image path (e.g., `namespace/image` or `registry.example.com/namespace/image`) | | +| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | | `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | | `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | | `wud.trigger.exclude` | :white_circle: | Optional list of triggers to exclude | `$trigger_1_id,$trigger_2_id:$threshold` | | @@ -453,7 +453,7 @@ services: image: harbor.example.com/ghcr-proxy/traefik/traefik:v3.5.3 labels: - wud.watch=true - - wud.registry.lookup.image=ghcr.io/traefik/traefik + - wud.registry.lookup.url=ghcr.io/traefik/traefik ``` #### **Docker** @@ -461,7 +461,7 @@ services: docker run -d \ --name traefik \ --label 'wud.watch=true' \ - --label 'wud.registry.lookup.image=ghcr.io/traefik/traefik' \ + --label 'wud.registry.lookup.url=ghcr.io/traefik/traefik' \ harbor.example.com/ghcr-proxy/traefik/traefik:v3.5.3 ``` diff --git a/ui/src/components/ContainerImage.vue b/ui/src/components/ContainerImage.vue index b5473e25..fb69c1ac 100644 --- a/ui/src/components/ContainerImage.vue +++ b/ui/src/components/ContainerImage.vue @@ -45,7 +45,7 @@ Registry url {{ image.registry.url }} - {{ image.registry.lookupImage }} (lookup) + {{ image.registry.lookupUrl }} (lookup) From 7a25c2777d8ba8bd83a2b48e9f869bfa68c42224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ne=C3=9Flauer?= Date: Wed, 29 Oct 2025 20:53:26 +0100 Subject: [PATCH 4/5] Revert "Revert label naming from lookupImage back to lookupUrl" This reverts commit 6506dae49f7bf9ac21468cdffb355839b86affbd. --- app/model/container.js | 2 +- app/watchers/providers/docker/Docker.js | 20 ++++++++++---------- app/watchers/providers/docker/label.js | 4 ++-- docs/configuration/watchers/README.md | 10 +++++----- ui/src/components/ContainerImage.vue | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/model/container.js b/app/model/container.js index fd69de18..a6190455 100644 --- a/app/model/container.js +++ b/app/model/container.js @@ -26,7 +26,7 @@ const schema = joi.object({ .object({ name: joi.string().min(1).required(), url: joi.string().min(1).required(), - lookupUrl: joi.string(), + lookupImage: joi.string(), }) .required(), name: joi.string().min(1).required(), diff --git a/app/watchers/providers/docker/Docker.js b/app/watchers/providers/docker/Docker.js index d333c99b..e7103393 100644 --- a/app/watchers/providers/docker/Docker.js +++ b/app/watchers/providers/docker/Docker.js @@ -21,7 +21,7 @@ const { wudDisplayIcon, wudTriggerInclude, wudTriggerExclude, - wudRegistryLookupUrl, + wudRegistryLookupImage, } = require('./label'); const storeContainer = require('../../../store/container'); const log = require('../../../log'); @@ -113,20 +113,20 @@ function getTagCandidates(container, tags, logContainer) { function normalizeContainer(container) { const containerWithNormalizedImage = container; - // Create a temporary image with parsed lookupUrl if present for registry matching + // Create a temporary image with parsed lookupImage if present for registry matching let imageForMatching = container.image; - if (container.image.registry.lookupUrl) { - const parsedLookupUrl = parse(container.image.registry.lookupUrl); + if (container.image.registry.lookupImage) { + const parsedLookupImage = parse(container.image.registry.lookupImage); // If no domain specified, default to Docker Hub registry - const registryUrl = parsedLookupUrl.domain || 'registry-1.docker.io'; - log.info(`[DEBUG] lookupUrl: ${container.image.registry.lookupUrl}, parsed domain: ${parsedLookupUrl.domain}, registryUrl: ${registryUrl}, path: ${parsedLookupUrl.path}`); + const registryUrl = parsedLookupImage.domain || 'registry-1.docker.io'; + log.info(`[DEBUG] lookupImage: ${container.image.registry.lookupImage}, parsed domain: ${parsedLookupImage.domain}, registryUrl: ${registryUrl}, path: ${parsedLookupImage.path}`); imageForMatching = { ...container.image, registry: { ...container.image.registry, url: registryUrl, }, - name: parsedLookupUrl.path, + name: parsedLookupImage.path, }; } const registries = getRegistries(); @@ -566,7 +566,7 @@ class Docker extends Component { container.Labels[wudDisplayIcon], container.Labels[wudTriggerInclude], container.Labels[wudTriggerExclude], - container.Labels[wudRegistryLookupUrl], + container.Labels[wudRegistryLookupImage], ), ); const containersWithImage = await Promise.all(containerPromises); @@ -692,7 +692,7 @@ class Docker extends Component { displayIcon, triggerInclude, triggerExclude, - wudRegistryLookupUrlValue, + wudRegistryLookupImageValue, ) { const containerId = container.Id; @@ -761,7 +761,7 @@ class Docker extends Component { id: imageId, registry: { url: parsedImage.domain, - lookupUrl: wudRegistryLookupUrlValue, + lookupImage: wudRegistryLookupImageValue, }, name: parsedImage.path, tag: { diff --git a/app/watchers/providers/docker/label.js b/app/watchers/providers/docker/label.js index c93b23fd..5d91683e 100644 --- a/app/watchers/providers/docker/label.js +++ b/app/watchers/providers/docker/label.js @@ -53,7 +53,7 @@ module.exports = { wudTriggerExclude: 'wud.trigger.exclude', /** - * Optional Lookup URL (can be useful when pulling containers from a registry cache) + * Optional Lookup image (can be useful when pulling containers from a registry cache) */ - wudRegistryLookupUrl: 'wud.registry.lookup.url', + wudRegistryLookupImage: 'wud.registry.lookup.image', }; diff --git a/docs/configuration/watchers/README.md b/docs/configuration/watchers/README.md index 05a5163e..d3bd28ee 100644 --- a/docs/configuration/watchers/README.md +++ b/docs/configuration/watchers/README.md @@ -183,9 +183,9 @@ To fine-tune the behaviour of WUD _per container_, you can add labels on them. |-----------------------|:--------------:|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| | `wud.display.icon` | :white_circle: | Custom display icon for the container | Valid [Material Design Icon](https://materialdesignicons.com/), [Fontawesome Icon](https://fontawesome.com/) or [Simple icon](https://simpleicons.org/) (see details below) | `mdi:docker` | | `wud.display.name` | :white_circle: | Custom display name for the container | Valid String | Container name | -| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | -| `wud.registry.lookup.url` | :white_circle: | Alternative image to use for update lookups | Full image path (e.g., `namespace/image` or `registry.example.com/namespace/image`) | | -| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | +| `wud.link.template` | :white_circle: | Browsable link associated to the container version | JS string template with vars `${container}`, `${original}`, `${transformed}`, `${major}`, `${minor}`, `${patch}`, `${prerelease}` | | +| `wud.registry.lookup.image` | :white_circle: | Alternative image to use for update lookups | Full image path (e.g., `namespace/image` or `registry.example.com/namespace/image`) | | +| `wud.tag.exclude` | :white_circle: | Regex to exclude specific tags | Valid JavaScript Regex | | | `wud.tag.include` | :white_circle: | Regex to include specific tags only | Valid JavaScript Regex | | | `wud.tag.transform` | :white_circle: | Transform function to apply to the tag | `$valid_regex => $valid_string_with_placeholders` (see below) | | | `wud.trigger.exclude` | :white_circle: | Optional list of triggers to exclude | `$trigger_1_id,$trigger_2_id:$threshold` | | @@ -453,7 +453,7 @@ services: image: harbor.example.com/ghcr-proxy/traefik/traefik:v3.5.3 labels: - wud.watch=true - - wud.registry.lookup.url=ghcr.io/traefik/traefik + - wud.registry.lookup.image=ghcr.io/traefik/traefik ``` #### **Docker** @@ -461,7 +461,7 @@ services: docker run -d \ --name traefik \ --label 'wud.watch=true' \ - --label 'wud.registry.lookup.url=ghcr.io/traefik/traefik' \ + --label 'wud.registry.lookup.image=ghcr.io/traefik/traefik' \ harbor.example.com/ghcr-proxy/traefik/traefik:v3.5.3 ``` diff --git a/ui/src/components/ContainerImage.vue b/ui/src/components/ContainerImage.vue index fb69c1ac..b5473e25 100644 --- a/ui/src/components/ContainerImage.vue +++ b/ui/src/components/ContainerImage.vue @@ -45,7 +45,7 @@ Registry url {{ image.registry.url }} - {{ image.registry.lookupUrl }} (lookup) + {{ image.registry.lookupImage }} (lookup) From e6671c0e8226f990c97dca73b8fcd4b00fa35154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ne=C3=9Flauer?= Date: Wed, 29 Oct 2025 21:05:42 +0100 Subject: [PATCH 5/5] Remove debug logging from normalizeContainer function --- app/watchers/providers/docker/Docker.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/watchers/providers/docker/Docker.js b/app/watchers/providers/docker/Docker.js index e7103393..43cef2c2 100644 --- a/app/watchers/providers/docker/Docker.js +++ b/app/watchers/providers/docker/Docker.js @@ -119,7 +119,6 @@ function normalizeContainer(container) { const parsedLookupImage = parse(container.image.registry.lookupImage); // If no domain specified, default to Docker Hub registry const registryUrl = parsedLookupImage.domain || 'registry-1.docker.io'; - log.info(`[DEBUG] lookupImage: ${container.image.registry.lookupImage}, parsed domain: ${parsedLookupImage.domain}, registryUrl: ${registryUrl}, path: ${parsedLookupImage.path}`); imageForMatching = { ...container.image, registry: { @@ -130,8 +129,6 @@ function normalizeContainer(container) { }; } const registries = getRegistries(); - log.info(`[DEBUG] Available registries: ${Object.keys(registries).join(', ')}`); - log.info(`[DEBUG] Matching against registry URL: ${imageForMatching.registry.url}`); const registryProvider = Object.values(registries).find((provider) => provider.match(imageForMatching), ); @@ -139,7 +136,6 @@ function normalizeContainer(container) { log.warn(`${fullName(container)} - No Registry Provider found`); containerWithNormalizedImage.image.registry.name = 'unknown'; } else { - log.info(`[DEBUG] Found registry provider: ${registryProvider.getId()}`); containerWithNormalizedImage.image = registryProvider.normalizeImage( imageForMatching, );