Skip to content

feat: add Docker registry mirror support for digest comparison - #1693

Merged
nicholas-fedor merged 8 commits into
mainfrom
feat/registry-mirror-support
May 28, 2026
Merged

feat: add Docker registry mirror support for digest comparison#1693
nicholas-fedor merged 8 commits into
mainfrom
feat/registry-mirror-support

Conversation

@nicholas-fedor

@nicholas-fedor nicholas-fedor commented May 28, 2026

Copy link
Copy Markdown
Owner

Add Docker registry mirror support for digest comparison to enable update checks when primary registries are inaccessible.

Problem

Watchtower was not using the Docker daemon's configured registry-mirrors when comparing image digests. Users in regions with restricted access to Docker Hub could not detect available updates.

Solution

Watchtower now fetches the global registry-mirrors configuration from the Docker daemon and tries each mirror in order before falling back to the canonical registry host.

Changes

  • Added mirror configuration resolution from Docker daemon Info()
  • Updated digest comparison to try multiple endpoints with fallback
  • Added auth and manifest URL support for endpoint overrides
  • Added registry mirrors documentation

Summary by CodeRabbit

  • New Features

    • Support for Docker registry mirrors during image update checks, using configured mirrors with automatic fallback to the canonical registry.
  • Documentation

    • Added a Registry Mirrors guide with configuration examples, resolution order, troubleshooting (network, auth, TLS), and platform-specific guidance.

Review Change Stack

- Resolve Docker daemon mirror configuration via Info() API
- Build ordered endpoint list: per-registry mirrors, global mirrors, then canonical fallback
- Thread mirror endpoints through GetToken, GetChallengeURL, CompareDigest, and fetchDigest
- Add mirror resolution and endpoint building logic in pkg/container/mirror.go
- Update all call sites and tests to pass optional endpoint parameter
- Document registry mirror configuration and behavior in docs/advanced-features/registry-mirrors
…d clarify host override priority

- Allow BuildManifestURL to accept full endpoint URLs (e.g. "https://mirror.example.com") as hostOverride, extracting host and scheme
- Document host override priority in fetchDigest: redirect host, mirror endpoint, then canonical host
- Clarify in manifest.BuildManifestURL that it always uses the canonical host and callers must apply overrides separately
- Remove outdated comment about scheme usage in manifest.go
- Validate nil return for malformed image references
- Test per-registry mirror fallback to global mirrors
- Verify empty per-registry mirrors with no global fallback returns nil
- Confirm mirror URLs with paths, queries, and fragments are preserved
- Support IPv6 mirror addresses in endpoint construction
…estURL

- Drop redundant "Empty string defaults to https" note since scheme behavior is now handled by caller
- Remove per-registry mirror support (IndexConfigs) from endpoint resolution
- Drop container image parameter from buildMirrorEndpoints since registry-specific lookup is no longer needed
- Update documentation to reflect global-only mirror configuration
- Remove test cases covering per-registry mirror precedence and fallback behavior
@nicholas-fedor nicholas-fedor linked an issue May 28, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4339996f-9026-401f-a823-05b64a8f0f13

📥 Commits

Reviewing files that changed from the base of the PR and between cffb316 and 336b326.

📒 Files selected for processing (4)
  • pkg/container/image_test.go
  • pkg/container/mirror.go
  • pkg/registry/auth/auth.go
  • pkg/registry/digest/digest.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/container/image_test.go
  • pkg/container/mirror.go
  • pkg/registry/digest/digest.go
  • pkg/registry/auth/auth.go

Walkthrough

Adds mirror-aware digest comparison: Watchtower discovers Docker daemon registry mirrors, builds ordered endpoints (mirrors then canonical), passes endpoints through auth and digest paths to try mirrors for digest fetches, and documents configuration with tests covering discovery, endpoint building, and fallback behavior.

Changes

Docker Registry Mirrors Support

Layer / File(s) Summary
Documentation and MkDocs nav
build/mkdocs/mkdocs.yaml, docs/advanced-features/registry-mirrors/index.md
Adds a Registry Mirrors guide and inserts it into the Advanced Features nav.
Daemon mirror discovery and endpoint builder
pkg/container/mirror.go, pkg/container/mirror_test.go
New helpers call Docker Info(), parse RegistryConfig.Mirrors, sanitize/parse entries, and produce ordered mirror endpoints with an empty-string canonical fallback; unit tests cover nil/empty, multiple mirrors, whitespace, IPv6, and verbatim URLs.
Auth challenge endpoint override
pkg/registry/auth/auth.go, pkg/registry/auth/auth_test.go, pkg/registry/auth/auth_benchmark_test.go
GetToken and GetChallengeURL accept an endpoint override (bare host or full URL) so token acquisition can target mirror endpoints; debug logs include sanitized mirror info; tests/bench updated.
Digest fetch/compare with mirror iteration
pkg/registry/digest/digest.go, pkg/registry/digest/digest_integration_test.go, pkg/registry/manifest/manifest.go
CompareDigest/FetchDigest accept endpoints ...string; internal fetchDigest iterates endpoints (default canonical), obtains tokens per endpoint, chooses manifest host by redirect > mirror > canonical precedence, and returns first successful digest; BuildManifestURL accepts full-URL or bare-host overrides; integration tests updated to new auth signature.
Image update wiring and tests
pkg/container/image.go, pkg/container/image_test.go, pkg/container/cooldown_test.go, pkg/registry/age.go
shouldSkipPull resolves mirror endpoints and forwards them to CompareDigest; tests add /info mock handling and new mirror-fallback cases; FetchImageCreationTime updated to pass the new auth parameter.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main feature being introduced: Docker registry mirror support for digest comparison, which is the core focus of all the changes across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.85714% with 72 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/registry/digest/digest.go 51.85% 45 Missing and 7 partials ⚠️
pkg/container/mirror.go 64.10% 14 Missing ⚠️
pkg/registry/auth/auth.go 79.16% 4 Missing and 1 partial ⚠️
pkg/registry/age.go 0.00% 1 Missing ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1693      +/-   ##
==========================================
- Coverage   74.79%   74.69%   -0.10%     
==========================================
  Files          59       60       +1     
  Lines        9949    10037      +88     
==========================================
+ Hits         7441     7497      +56     
- Misses       2243     2272      +29     
- Partials      265      268       +3     
Files with missing lines Coverage Δ
pkg/container/image.go 79.56% <100.00%> (+0.22%) ⬆️
pkg/registry/manifest/manifest.go 100.00% <ø> (ø)
pkg/registry/age.go 42.00% <0.00%> (-0.07%) ⬇️
pkg/registry/auth/auth.go 79.68% <79.16%> (+0.29%) ⬆️
pkg/container/mirror.go 64.10% <64.10%> (ø)
pkg/registry/digest/digest.go 68.80% <51.85%> (-1.79%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codacy-production

codacy-production Bot commented May 28, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 medium

Alerts:
⚠ 1 issue (≤ 0 issues of at least minor severity)

Results:
1 new issue

Category Results
Complexity 1 medium

View in Codacy

🟢 Metrics 26 complexity · 11 duplication

Metric Results
Complexity 26
Duplication 11

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/container/image_test.go`:
- Around line 197-200: The test currently only asserts that i.PullImage(...)
returned an error, which allows any failure to pass; instead assert the specific
pull-stage error ErrPullImageUnauthorized to confirm the fallback canonical pull
path was exercised. Replace the generic
gomega.Expect(err).To(gomega.HaveOccurred()) for the i.PullImage calls (the ones
using pullContainer, WarnAuto, types.UpdateParams{}) with an assertion that err
equals ErrPullImageUnauthorized (use the package error symbol
ErrPullImageUnauthorized), and make the same change for the other mirror test
block referenced (the similar i.PullImage call around lines 223–228).

In `@pkg/container/mirror.go`:
- Around line 47-49: The debug log currently emits raw mirror endpoints via
logrus.WithFields using info.Info.RegistryConfig.Mirrors, which can leak
userinfo; update the logging to sanitize each mirror URL before logging by
parsing each mirror string (use net/url.Parse) and replacing it with the host
(u.Host) or, if parsing fails, a redacted form without userinfo; then pass the
sanitized slice into logrus.WithFields instead of the raw
info.Info.RegistryConfig.Mirrors so the log only contains host-only values.
Reference the log call (logrus.WithFields) and the mirrors value
(info.Info.RegistryConfig.Mirrors) when making the change.

In `@pkg/registry/auth/auth.go`:
- Around line 418-422: The log call is currently writing the raw endpoint
(variable endpoint) which may include embedded credentials; parse the endpoint
string (e.g., using url.Parse) and log a sanitized value such as u.Host (or
u.Scheme + "://" + u.Host) instead of the original endpoint, falling back to a
redacted placeholder if parsing fails; update the logrus.WithFields call that
currently uses "mirrors": endpoint to use the sanitizedEndpoint and ensure any
u.User information is not logged.

In `@pkg/registry/digest/digest.go`:
- Around line 353-356: The code currently attaches the raw endpoint string to
epFields["registry_endpoint"], which can leak URL userinfo; update the logic
that builds epFields (epFields, endpoint) to parse endpoint with url.Parse and
store only the host portion (u.Host or u.Hostname()+":"+u.Port if port present)
or a safe fallback when parsing fails, so logs contain the hostname:port only
and never userinfo; preserve the existing empty-endpoint check and use the
sanitized host value when setting "registry_endpoint".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 539ecdf0-aba6-424e-9571-a1e4313c775a

📥 Commits

Reviewing files that changed from the base of the PR and between fbf5cc6 and cffb316.

📒 Files selected for processing (14)
  • build/mkdocs/mkdocs.yaml
  • docs/advanced-features/registry-mirrors/index.md
  • pkg/container/cooldown_test.go
  • pkg/container/image.go
  • pkg/container/image_test.go
  • pkg/container/mirror.go
  • pkg/container/mirror_test.go
  • pkg/registry/age.go
  • pkg/registry/auth/auth.go
  • pkg/registry/auth/auth_benchmark_test.go
  • pkg/registry/auth/auth_test.go
  • pkg/registry/digest/digest.go
  • pkg/registry/digest/digest_integration_test.go
  • pkg/registry/manifest/manifest.go

Comment thread pkg/container/image_test.go Outdated
Comment thread pkg/container/mirror.go
Comment thread pkg/registry/auth/auth.go
Comment thread pkg/registry/digest/digest.go
…al leakage

- Parse and redact full URLs in mirror resolution, auth challenge, and digest fetch logging
- Replace raw endpoint strings with host-only or `<redacted>` placeholders in log output
- Update test assertions to verify specific `ErrPullImageUnauthorized` error type instead of generic occurrence check
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Actionable comments posted: 0

@nicholas-fedor
nicholas-fedor merged commit 6ad2410 into main May 28, 2026
18 of 20 checks passed
@nicholas-fedor
nicholas-fedor deleted the feat/registry-mirror-support branch May 28, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is it possible to support docker registry mirrors

1 participant