[docs] Document hostname inheritance for Ingress paths and Gateway routes - #1574
[docs] Document hostname inheritance for Ingress paths and Gateway routes#1574aspire-repo-bot[bot] wants to merge 1 commit into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Frontend HTML artifact readyThe latest frontend build uploaded the This comment updates automatically when a new frontend build artifact is uploaded. |
David Pine (IEvangelist)
left a comment
There was a problem hiding this comment.
🤖 Automated docs-accuracy review — PR #1574
Source of truth: microsoft/aspire@main @ 1cdf7d17248ae78ee018abbc314f772ace5e624d — contains source PR microsoft/aspire#19430 (merge 6785869656, "Fix Kubernetes hostname publishing and routing").
⚠️ Branch mismatch (non-blocking). This PR targetsrelease/13.6, which does not exist inmicrosoft/aspire. Verified againstmain(milestone 13.6), which contains the change. The13.6-onward version claim is corroborated below.
Phase A — claims: 7 non-narrative claims → 6 verified · 1 verified-with-nuance · 0 contradicted · 0 unverifiable.
Phase B — doc-tester: exercised /deployment/kubernetes-ingress/ → HTTP 200, 0 console errors/warnings; insertion region + reused components (Tabs, titled code, <Aside>) render; no new links/components. Knowledge gap: local build predates this PR.
Verdict: 🟡 COMMENT
The behavior — hostless-path/route hostname inheritance, explicit-hostname precedence, default-backend catch-all + TLS-compat rule, and the 13.6-onward attribution — is all verified against source. One minor nuance worth flagging (multi-hostname behavior); nothing blocking.
Phase A — Claim verification
✅ Version framing is accurate this time
Unlike the DevTunnel note (#1570), the 13.6-onward claim here checks out precisely:
- The inheritance logic (
AddPathForHost/ResolveHostnamesAsyncinKubernetesEnvironmentResource.cs) is present onmainonly and is absent fromrelease/13.5, which still usespathsByHost = ingressResource.Paths.GroupBy(p => p.Host ?? string.Empty)— i.e., hostless paths → empty host → catch-all. That exactly matches the doc's "in earlier versions, hostless paths and routes were generated as catch-all rules." upstreamnow carries GA tags v13.5.0–v13.5.3; there is still no v13.6 tag (13.6 is the currentmainmilestone). Since 13.5.x GA lacks the change and it lives on the 13.6 milestone, "from Aspire 13.6 onward" is correct.
ℹ️ Nuance worth flagging (non-blocking) — multi-hostname fan-out & Gateway limit
WithHostname(...) can be called more than once. When it is, a single hostless path/route is emitted once per configured hostname, not merged into one rule. The source snapshot AddIngress_WithHostname_AppliesToHostlessPath sets two hostnames and produces the /api path under both api.example.com and www.example.com. Additionally, for Gateways, a hostless route inheriting more than 16 hostnames throws (HttpRouteHostnameLimit, pointing users to explicit WithRoute(hostname, path, endpoint)). The doc's singular "set the hostname once and reuse it" framing is correct for its single-hostname example, but a one-line note about the multi-hostname fan-out (and the Gateway 16-hostname cap) would make the section complete. Optional.
Claim verdicts (7) with evidence, against microsoft/aspire@1cdf7d17
| # | Type | Claim | Verdict & evidence |
|---|---|---|---|
| C1 | api-shape | WithHostname(...) exists on Ingress & Gateway (incl. string overload) |
verified — api/Aspire.Hosting.Kubernetes.cs: Ingress L150/L153, Gateway L99/L102. |
| C2 | api-shape | WithPath/WithRoute exist, incl. host-scoped WithPath(host, path, endpoint) / WithRoute(host, path, endpoint) |
verified — WithPath L168 & L171; WithRoute L105 & L108. |
| C3 | api-behavior | WithHostname(...) applies to hostless WithPath/WithRoute (they inherit the hostname) |
verified-with-nuance — hostless path → foreach (hostname in resolvedHostnames) AddPathForHost(hostname, path); Gateway → httpRoute.Spec.Hostnames.AddRange(resolvedHostnames). Snapshots AddIngress_WithHostname_AppliesToHostlessPath, AddGateway_WithHostname_AppliesToHostlessRoute. Nuance: multi-hostname fan-out (one rule per hostname) + Gateway HttpRouteHostnameLimit (16) not mentioned. |
| C4 | api-behavior | An explicit per-path/route hostname takes precedence over WithHostname(...) |
verified — if (path.Host is { } explicitHost) AddPathForHost(explicitHost, path); (explicit branch first). Test AddIngress_WithHostAndPath_GeneratesHostRule. |
| C5 | api-behavior | WithDefaultBackend(endpoint) stays a catch-all (no host) incl. the TLS compatibility rule |
verified — comment "A default backend remains catch-all even when hostnames are configured. Only synthesize host rules for TLS…"; guard DefaultBackend is not null && Tls.Count > 0. Tests AddIngress_HostnameWithDefaultBackendWithoutTls_DoesNotGenerateHostRule + AddIngress_TlsWithDefaultBackend_AutoGeneratesHostRule. |
| C6 | api-shape | WithTls() (parameterless) exists on Ingress |
verified — api/...cs L180 (parameterless; string/Parameter overloads L174/L177). |
| C7 | version / api-behavior | Inheritance applies "from Aspire 13.6 onward"; earlier versions were catch-all | verified — logic on main only; absent from release/13.5 (retains GroupBy(p => p.Host ?? string.Empty)). No v13.6 tag yet; change on main milestone 13.6; 13.5.x GA lacks it. |
Phase B — doc-tester report
Focus: new "Hostname inheritance for paths and routes" section · Route: http://localhost:51482/deployment/kubernetes-ingress/ · Tester: doc-tester skill (blind-user; no source reading).
| Category | Passed | Failed | Warnings |
|---|---|---|---|
| Content accuracy (rendered) | n/a | 0 | 0 |
| Components / rendering | 3 | 0 | 0 |
| Links | 0 new | 0 | 0 |
Critical issues: none. Warnings: none.
Passed checks
- Page health:
GET /deployment/kubernetes-ingress/→ 200, title "Expose services with Ingress and Gateway API | Aspire". 0 console errors, 0 warnings. - Insertion region intact: new H2 sits between "Routing paths to services" and "## TLS and certificates" (
#tls-and-certificates, renders). The preceding paragraph already documents the host-scopedWithPath("api.example.com", "/", endpoint)overload andWithDefaultBackend(endpoint)— the exact APIs the new section elaborates. - Code shape already renders: the page already shows
.WithHostname("app.example.com").WithTls();andingress.WithPath("/api", …)/ingress.WithPath("/", …)in titled Tabs — identical to the new example ⇒ zero render risk. - Components reused, none new: the new
<Aside type="note">maps to the already-rendering "Note" complementary; Tabs + titled code figures already render. - Links: added prose has no new links.
Knowledge gap — running build predates PR #1574
- The local frontend doesn't contain this change, so the literal new section couldn't be rendered. I validated page health, the insertion anchors, that the new section's code shape + Aside/Tabs already render on this page, and that no new links/components are introduced. Reused-pattern, no-link addition ⇒ render failure effectively impossible.
Automated review · Phase A read microsoft/aspire@main 1cdf7d17 (source of truth = upstream) · Phase B via doc-tester (blind-user, Playwright).
Adam Ratzman (adamint)
left a comment
There was a problem hiding this comment.
No issues from this review.
Alistair Matthews (alistairmatthews)
left a comment
There was a problem hiding this comment.
Some rewording suggestions for clarity.
| `WithHostname(...)` on the Ingress or Gateway sets the hostname that applies | ||
| to any path (`WithPath`) or route (`WithRoute`) you add **without** an | ||
| explicit hostname of its own. This lets you set the hostname once and reuse it | ||
| across every hostless path or route: |
There was a problem hiding this comment.
| `WithHostname(...)` on the Ingress or Gateway sets the hostname that applies | |
| to any path (`WithPath`) or route (`WithRoute`) you add **without** an | |
| explicit hostname of its own. This lets you set the hostname once and reuse it | |
| across every hostless path or route: | |
| The `WithHostname()` method on the Ingress or Gateway sets the hostname that applies | |
| to any path or route you add **without** an | |
| explicit hostname of its own, by using `WithPath` or `WithRoute`. This arrangement lets you set the hostname once and reuse it across every hostless path or route: |
| If a path or route specifies its own hostname — via the host-scoped | ||
| `WithPath("api.example.com", "/", endpoint)` overload, or the equivalent on | ||
| `WithRoute` — that explicit hostname takes precedence over the one from | ||
| `WithHostname(...)`. |
There was a problem hiding this comment.
I think this will be clearer if we disentangle the clauses:
| If a path or route specifies its own hostname — via the host-scoped | |
| `WithPath("api.example.com", "/", endpoint)` overload, or the equivalent on | |
| `WithRoute` — that explicit hostname takes precedence over the one from | |
| `WithHostname(...)`. | |
| If a path or route specifies its own hostname, that explicit hostname takes precedence over the one from `WithHostname()`. You can specify the hostname by using the host-scoped | |
| `WithPath("api.example.com", "/", endpoint)` overload, or the equivalent on | |
| `WithRoute` . |
| `WithDefaultBackend(endpoint)` is unaffected by `WithHostname(...)`: the | ||
| default backend always generates a catch-all rule (no `host` restriction) so | ||
| it keeps accepting any traffic that doesn't match a more specific path or | ||
| route, including the TLS compatibility rule Aspire generates for it. |
There was a problem hiding this comment.
| `WithDefaultBackend(endpoint)` is unaffected by `WithHostname(...)`: the | |
| default backend always generates a catch-all rule (no `host` restriction) so | |
| it keeps accepting any traffic that doesn't match a more specific path or | |
| route, including the TLS compatibility rule Aspire generates for it. | |
| `WithDefaultBackend(endpoint)` is unaffected by `WithHostname(...)`: the | |
| default backend always generates a catch-all rule, no `host` restriction. Thus | |
| it keeps accepting any traffic that doesn't match a more specific path or | |
| route, including the TLS compatibility rule Aspire generates for it. |
Documents changes from microsoft/aspire#19430
@mitchdennyTargeting
release/13.6based on the source PR milestone13.6.Why
microsoft/aspire#19430 fixes Kubernetes hostname publishing and routing:
WithHostname(...)on anAddIngress/AddGatewaynow applies to hostlessWithPath/WithRoutecalls instead of leaving them as catch-all rules, whileWithDefaultBackend(endpoint)keeps generating a catch-all rule as before. The PR body's "User-facing usage" section shows this new scoping behavior in both C# and TypeScript AppHost samples, which triggered thepr_body_has_user_facing_sectionsignal — the existing docs describeWithHostname/WithPath/WithDefaultBackendbut didn't previously explain this inheritance rule.What changed
src/frontend/src/content/docs/deployment/kubernetes-ingress.mdx: added a new "Hostname inheritance for paths and routes" section after the path-routing example, explaining thatWithHostname(...)now scopes hostless paths/routes, that explicit per-path/route hostnames still take precedence, and thatWithDefaultBackend(...)remains an unscoped catch-all (including its TLS compatibility rule). Includes a version note that this inheritance behavior applies from Aspire 13.6 onward.No new pages were created; this is a targeted addition to the existing Ingress/Gateway walkthrough page since the behavior applies to both
AddIngressandAddGateway(the Gateway-specific AKS walkthrough already links back to this page for the shared concepts).