docs(networking): publish Kubernetes API endpoint via external-dns with kuberture#539
docs(networking): publish Kubernetes API endpoint via external-dns with kuberture#539lexfrei wants to merge 1 commit into
Conversation
✅ Deploy Preview for cozystack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThis PR introduces a comprehensive documentation page for the Changeskuberture DNS Publishing Documentation
Estimated Code Review Effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces documentation for kuberture, a component designed to bridge the gap between Kubernetes EndpointSlices and external-dns by creating annotated headless Services. The review feedback identifies a potential compatibility issue regarding how standard external-dns instances handle custom annotation prefixes for data extraction. Additionally, a suggestion was made to improve the clarity of the configuration documentation by better explaining the default behavior when the annotation prefix is omitted.
|
|
||
| ## Routing to multiple external-dns instances | ||
|
|
||
| A single `kuberture` install can address any number of `external-dns` instances by varying `annotationPrefix` per output. Each `external-dns` instance is configured (via `--annotation-filter`) to act on a specific prefix; `kuberture` stamps the matching prefix on each Service: |
There was a problem hiding this comment.
Standard external-dns does not support custom prefixes for data annotations like hostname, target, or ttl. It typically expects these keys to be prefixed with external-dns.alpha.kubernetes.io/. While --annotation-filter can be used to filter which Services an instance processes, it does not change the prefix used for data extraction. If kuberture only stamps annotations with a custom prefix, standard external-dns instances will not find the required DNS data. Please clarify if this feature requires a specific external-dns configuration or if kuberture should also stamp the standard annotations alongside the filter annotation.
|
|
||
| Each Service carries **only** its own prefix's annotations — there is no cross-pollution between outputs. | ||
|
|
||
| `annotationPrefix` accepts two forms: omit the field to inherit the controller default `external-dns.alpha.kubernetes.io/`, or set it to a non-empty string ending in `/`. The empty string `""` is rejected by the chart's values schema; omission is the only zero-prefix path. |
There was a problem hiding this comment.
The term "zero-prefix path" is confusing here. Since omitting the field results in the default prefix (external-dns.alpha.kubernetes.io/) being used, it is not actually a "zero" (empty) prefix. It would be clearer to state that omission is the way to use the default prefix.
| `annotationPrefix` accepts two forms: omit the field to inherit the controller default `external-dns.alpha.kubernetes.io/`, or set it to a non-empty string ending in `/`. The empty string `""` is rejected by the chart's values schema; omission is the only zero-prefix path. | |
| `annotationPrefix` accepts two forms: omit the field to inherit the controller default `external-dns.alpha.kubernetes.io/`, or set it to a non-empty string ending in `/`. The empty string `""` is rejected by the chart's values schema; omission is the only way to use the default prefix. |
…th kuberture Document the kuberture system package: the EndpointSlice-to-Service bridge that lets external-dns publish the cluster's own API endpoint. Covers the problem (external-dns does not support EndpointSlice as a source), the kuberture flow, single-instance enabling against the platform external-dns, multi-instance routing via annotationPrefix, the four addressSource strategies, the disable path, and the maintainer-personal-namespace supply-chain caveat. Companion to cozystack/cozystack#2647 which adds the package. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
ad08b7b to
4b586a3
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@content/en/docs/next/networking/kuberture.md`:
- Line 40: The phrase "wants published" in the kuberture docs sentence is
awkward; update the sentence in content/en/docs/next/networking/kuberture.md
(the paragraph describing the operator DNS requirement for `kuberture`) to use
clearer wording such as "wants to publish" or "wants to be published" (e.g.,
replace "the DNS hostname the operator actually wants published" with "the DNS
hostname the operator actually wants to publish" or "the DNS hostname the
operator actually wants to be published") so the operator guidance reads
naturally.
- Around line 117-118: The docs incorrectly suggest using
--annotation-filter=internal-dns.example.com to match annotations like
internal-dns.example.com/hostname; update the text to use a full annotation key
(e.g., --annotation-filter=internal-dns.example.com/hostname) or note that
--annotation-filter requires exact annotation key matches, and reference the
example resource name kuberture-internal and the actual annotation keys
(internal-dns.example.com/hostname, internal-dns.example.com/target,
internal-dns.example.com/ttl) so readers know to pick one exact suffixed key for
the filter.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 783947a6-813d-4389-bb83-24d0682751be
📒 Files selected for processing (1)
content/en/docs/next/networking/kuberture.md
|
|
||
| ## Enabling on the host | ||
|
|
||
| `kuberture` is an optional system package, opt-in via `bundles.enabledPackages`. The package itself ships no usable default beyond enabling `ServiceMonitor` for the platform Prometheus — the deployment fails fast if `config.outputs` is empty. The operator must declare at least one output, because the only thing `kuberture` cannot infer is the DNS hostname the operator actually wants published. |
There was a problem hiding this comment.
Tighten wording on Line 40
“wants published” reads awkwardly. Prefer “wants to publish” (or “wants to be published”) for clarity in operator guidance.
Suggested edit
-...the DNS hostname the operator actually wants published.
+...the DNS hostname the operator actually wants to publish.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| `kuberture` is an optional system package, opt-in via `bundles.enabledPackages`. The package itself ships no usable default beyond enabling `ServiceMonitor` for the platform Prometheus — the deployment fails fast if `config.outputs` is empty. The operator must declare at least one output, because the only thing `kuberture` cannot infer is the DNS hostname the operator actually wants published. | |
| `kuberture` is an optional system package, opt-in via `bundles.enabledPackages`. The package itself ships no usable default beyond enabling `ServiceMonitor` for the platform Prometheus — the deployment fails fast if `config.outputs` is empty. The operator must declare at least one output, because the only thing `kuberture` cannot infer is the DNS hostname the operator actually wants to publish. |
🧰 Tools
🪛 LanguageTool
[style] ~40-~40: The double modal “wants published” is nonstandard (only accepted in certain dialects). Consider “to be published”.
Context: ...NS hostname the operator actually wants published. Single output, consumed by the platfo...
(NEEDS_FIXED)
🤖 Prompt for 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.
In `@content/en/docs/next/networking/kuberture.md` at line 40, The phrase "wants
published" in the kuberture docs sentence is awkward; update the sentence in
content/en/docs/next/networking/kuberture.md (the paragraph describing the
operator DNS requirement for `kuberture`) to use clearer wording such as "wants
to publish" or "wants to be published" (e.g., replace "the DNS hostname the
operator actually wants published" with "the DNS hostname the operator actually
wants to publish" or "the DNS hostname the operator actually wants to be
published") so the operator guidance reads naturally.
| - `kuberture-internal` carries `internal-dns.example.com/*` annotations and is invisible to the platform `external-dns`; a second `external-dns` instance configured with `--annotation-filter=internal-dns.example.com` consumes it. | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
external-dns --annotation-filter syntax: does a bare key selector (e.g., internal-dns.example.com) match annotation keys that only exist as internal-dns.example.com/hostname, internal-dns.example.com/target, internal-dns.example.com/ttl? Please provide official docs/examples.
💡 Result:
A bare key selector like external-dns.example.com does NOT match annotation keys that only exist as external-dns.example.com/hostname, external-dns.example.com/target, or external-dns.example.com/ttl. Reason (based on external-dns docs + Kubernetes label/selector semantics): external-dns documents that --annotation-filter uses label selector semantics [1]. In Kubernetes label selectors, the selector key must match the label key exactly (a selector key is not a prefix match) [2]. Therefore, a selector for key internal-dns.example.com will only match an annotation whose key is exactly internal-dns.example.com—it will not match keys that merely share that prefix but have additional path segments such as /hostname. Official docs/examples to cite: 1) external-dns flag definition: --annotation-filter “Filter resources queried for endpoints by annotation, using label selector semantics” [1]. 2) external-dns annotations/keys are treated as exact annotation keys (e.g., separate annotations for .../hostname, .../target, .../ttl) [3]. Example (correct for your intent if you want to filter on only the sub-annotation): use the full key including the suffix, e.g.: - --annotation-filter=external-dns.alpha.kubernetes.io/hostname - --annotation-filter=external-dns.alpha.kubernetes.io/target - --annotation-filter=external-dns.alpha.kubernetes.io/ttl If you instead want a prefix-based match, external-dns’s --annotation-filter is not documented as supporting prefix/pattern matching for the key portion; you would need another approach (e.g., separate external-dns instances/config, or label filtering where available) [1].
Citations:
- 1: https://kubernetes-sigs.github.io/external-dns/latest/docs/flags/
- 2: https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/label-selector/
- 3: https://kubernetes-sigs.github.io/external-dns/latest/docs/annotations/annotations/
Correct the --annotation-filter example on Line 118—bare key selectors don't match suffixed annotation keys
The filter --annotation-filter=internal-dns.example.com will not match the annotation keys you describe (internal-dns.example.com/hostname, internal-dns.example.com/target, internal-dns.example.com/ttl). The --annotation-filter flag uses Kubernetes label selector semantics, which require exact key matching—internal-dns.example.com only matches an annotation with that exact key, not keys with additional path segments. To filter kuberture-internal resources, specify the full key: --annotation-filter=internal-dns.example.com/hostname (or choose one of the suffixed keys, or document the filter limitation if filtering on one suffix is insufficient).
🤖 Prompt for 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.
In `@content/en/docs/next/networking/kuberture.md` around lines 117 - 118, The
docs incorrectly suggest using --annotation-filter=internal-dns.example.com to
match annotations like internal-dns.example.com/hostname; update the text to use
a full annotation key (e.g.,
--annotation-filter=internal-dns.example.com/hostname) or note that
--annotation-filter requires exact annotation key matches, and reference the
example resource name kuberture-internal and the actual annotation keys
(internal-dns.example.com/hostname, internal-dns.example.com/target,
internal-dns.example.com/ttl) so readers know to pick one exact suffixed key for
the filter.
Arsolitt
left a comment
There was a problem hiding this comment.
Page is well-structured and aligned with the companion package PR. One blocker: the multi-instance routing recipe documents --annotation-filter where external-dns actually needs --annotation-prefix. Please also address the unresolved CodeRabbit/Gemini bot threads — note that the high-priority Gemini one is wrong on its premise (external-dns does support custom annotation prefixes via --annotation-prefix), but its underlying observation that the doc's current flag choice doesn't match standard external-dns behaviour is correct.
| This renders two headless Services in `cozy-kuberture`: | ||
|
|
||
| - `kuberture-public` carries the default `external-dns.alpha.kubernetes.io/*` annotations and is consumed by the platform `external-dns`. | ||
| - `kuberture-internal` carries `internal-dns.example.com/*` annotations and is invisible to the platform `external-dns`; a second `external-dns` instance configured with `--annotation-filter=internal-dns.example.com` consumes it. |
There was a problem hiding this comment.
The recipe says a second external-dns instance configured with --annotation-filter=internal-dns.example.com consumes kuberture-internal. That flag does not produce the documented behaviour.
--annotation-filter is a label-selector filter on which objects external-dns considers at all; it uses Kubernetes label-selector semantics with exact key matching (source/annotations/filter.go at v0.20.0). A bare key internal-dns.example.com does not match an annotation key internal-dns.example.com/hostname. Even rewriting it as --annotation-filter=internal-dns.example.com/hostname (an existence selector) only filters which services external-dns looks at; the instance still reads hostname/target/ttl under its configured --annotation-prefix (default external-dns.alpha.kubernetes.io/), so the suffixed annotations on kuberture-internal would be invisible to it.
The flag that does what the docs describe is --annotation-prefix=internal-dns.example.com/. external-dns calls annotations.SetAnnotationPrefix(cfg.AnnotationPrefix) on startup (controller/execute.go:66 at v0.20.0) and rebuilds all hostname/target/ttl annotation keys under the supplied prefix. The upstream Split Horizon DNS doc (docs/advanced/split-horizon.md in kubernetes-sigs/external-dns) states verbatim: "To enable split horizon DNS, configure each instance to use a different annotation prefix using the --annotation-prefix flag."
Same wording on line 85 ("via --annotation-filter"). Both occurrences should be --annotation-prefix=<your-prefix>/ (note the trailing slash, matching the chart's values.schema.json pattern: /$).
What this PR does
Adds documentation for the
kuberturesystem package being introduced in cozystack/cozystack#2647. New page atcontent/en/docs/next/networking/kuberture.md, weight 26 (slots in right afterhairpin-proxy-protocolat weight 25 in the networking section).The page covers:
kuberturesolves:external-dnsdoes not supportEndpointSliceas a source, so the canonical control-plane endpoint atdefault/kubernetescannot be published to DNS without a bridge.kubertureworks: a small in-cluster controller that watches EndpointSlice and stamps annotated headless Services (one per output) forexternal-dnsto consume.cozystack.external-dnswith a copy-pasteablePackageCR.kubertureinstall can serve multipleexternal-dnsinstances simultaneously by stamping a differentannotationPrefixper output, with the matchingexternal-dnsrunning with--annotation-filter. Includes a worked two-output example.addressSourcestrategies (endpointslice/node-internal/node-external/node-public) and when each fits.external-dnspolicy: upsert-onlyinteraction on record retraction.lexfrei/*registry namespace, are pinned by digest in the cozystack package, and air-gapped operators must mirror both into their internal registry.Style follows
networking/hairpin-proxy-protocol.md(the sibling system-addon doc forouroboros): handwritten, narrative, single-line markdown paragraphs.Release note
Summary by CodeRabbit