Skip to content

fix(kind): expose gateway metrics and fix control plane connectivity - #211

Open
maknop wants to merge 10 commits into
mainfrom
fix/local-dev-metrics-and-controller-dns
Open

fix(kind): expose gateway metrics and fix control plane connectivity#211
maknop wants to merge 10 commits into
mainfrom
fix/local-dev-metrics-and-controller-dns

Conversation

@maknop

@maknop maknop commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two local development fixes discovered while debugging why provisioned gateways were not appearing in the Prometheus metrics, plus the ServiceMonitor from #202 folded in so everything ships together.

Changes

Expose gateway metrics in the Kind cluster

The API server's metrics server was binding to localhost:4433 inside the pod, making it unreachable from outside. This adds:

  • --metrics-server-bindaddress=0.0.0.0:4433 to the base API server manifest so the port is always reachable
  • An HTTPRoute for observability.hypershell.localhost routing to port 4433, giving developers a browser-accessible URL for raw Prometheus metrics in Kind
  • METRICS_HOSTNAME variable wired through the Makefile and printed in the kind-up summary alongside the other service URLs

Add ServiceMonitor for Prometheus scraping (from #202)

Adds deploy/base/servicemonitor.yaml and registers it in the base kustomization, wiring Prometheus Operator scraping to the metrics port (4433) on the API server service. Closes #202.

Fix control plane losing connectivity to the API server

The hypershell-service-account-provisioner NetworkPolicy selects the controller pod but only declared Ingress in policyTypes. kindnet implicitly blocks all egress from any pod selected by a NetworkPolicy, regardless of what policyTypes declares. This caused the control plane to be unable to reach CoreDNS, which prevented it from connecting to the API server via gRPC and left all gateways with a blank phase, invisible to the metrics collector.

Fix: explicitly declare Egress in policyTypes with an allow-all egress: [{}] rule, preserving the intended ingress restriction while restoring unrestricted outbound connectivity.

Also adds fully-qualified service names and a dnsConfig.use-vc option (TCP DNS) to the Kind controller overlay as hardening against DNS resolution issues.

Testing

Verified in a local Kind cluster:

  • https://metrics.hypershell.localhost/metrics loads in the browser and shows hypershell_gateways_total{phase="Running"} correctly
  • Control plane watch streams connect and stay connected after cluster startup
  • Gateway phases are reconciled within seconds of controller startup

Closes #202

- Add HTTPRoute for metrics.hypershell.localhost routing to the API
  server's dedicated metrics port (4433), making Prometheus-format
  gateway metrics browsable in the local Kind cluster

- Bind the metrics server to 0.0.0.0:4433 (was localhost-only) so the
  gateway can reach it from outside the pod

- Fix the controller NetworkPolicy to explicitly allow all egress;
  kindnet's implementation implicitly blocks all egress when any
  NetworkPolicy selects a pod, which prevented the control plane from
  reaching CoreDNS and caused gateways to remain in a blank phase

- Use fully-qualified service names for the control plane's gRPC and
  HTTP API server addresses in the Kind overlay to avoid DNS search
  domain ambiguity

- Print the metrics URL in the kind-up summary alongside the other
  service URLs

Co-Authored-By: Crush <crush@charm.land>
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 131e6bfa-55ec-4f29-8013-11ef295b63e0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

maknop and others added 9 commits August 26, 2026 12:52
Brings in the ServiceMonitor from #202, wiring Prometheus Operator
scraping to the metrics port (4433) already exposed by the API server.

Co-Authored-By: Crush <crush@charm.land>
The ServiceMonitor was included in deploy/base/kustomization.yaml which
is applied unconditionally, causing kind-up to fail in environments
without Prometheus Operator installed (including CI).

Moves it into deploy/base/prometheus/ alongside the existing Prometheus
resources, where it is only applied by overlays that have Prometheus
Operator available.

Co-Authored-By: Crush <crush@charm.land>
…cture

Introduces a gateway metrics dashboard in the management UI backed by a
Prometheus stack wired into the Kind dev cluster. Includes RBAC, scrape
config, the Prometheus operator bundle, a new metrics data layer, and
the platform spec describing the desired dashboard state.

Co-Authored-By: Crush <crush@charm.land>
- Replace import from non-existent operational-dashboard-ui package
  with locally defined types in the web-console adapter, eliminating
  all unsafe-assignment and unsafe-call ESLint errors
- Export metrics symbols from the gateway-management-ui public index
  so the web-console adapter can resolve them with full type safety
- Replace em dashes in the metrics dashboard spec with hyphens to
  satisfy the repository forbidden-terms policy check

Co-Authored-By: Crush <crush@charm.land>
@jsell-rh

jsell-rh commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Amber review

Status: Complete

Verdict

REQUEST_CHANGES (advisory COMMENT event). The two genuine local-dev fixes (metrics bind address, controller egress) are sound, but the PR also carries a large, undocumented gateway-metrics feature whose Kubernetes wiring and backend/UI endpoints are not actually connected, so the metrics pipeline this PR claims to enable does not function as shipped.

Hi — Amber here. I reviewed the diff against the HyperShell conventions (CLAUDE.md, security + control-plane specs) and the review checklist. Summary: the local-dev connectivity/metrics fixes are correct and useful, but a substantial amount of the added Prometheus infrastructure and dashboard UI is orphaned (not referenced by any kustomization/script) or points at endpoints that don't exist yet, so it can't work end-to-end in its current form.

Blockers

None.

Major

1. The Prometheus infrastructure and ServiceMonitor are never deployed (orphaned resources).
deploy/base/prometheus/ (its kustomization.yaml, prometheus.yaml, rbac.yaml, servicemonitor.yaml), deploy/base/servicemonitor.yaml, and the 75,414-line deploy/kind/infrastructure/prometheus-operator-bundle.yaml are not referenced by any parent kustomization or script:

  • deploy/base/kustomization.yaml is unchanged (does not list servicemonitor.yaml or prometheus/), which contradicts the PR body's claim that the ServiceMonitor is "registered in the base kustomization."
  • deploy/kind/infrastructure/kustomization.yaml is unchanged, so prometheus-operator-bundle.yaml is never applied — the Prometheus Operator CRDs (monitoring.coreos.com/v1) are never installed, which means the Prometheus/ServiceMonitor CRs would fail to apply even if they were wired in.
    Net effect: nothing scrapes the API server, so the metrics pipeline the PR describes is not operational. Either wire these resources into the appropriate overlays (guarded so the CRDs exist first) or drop them from this PR.

2. Duplicate ServiceMonitor definition.
deploy/base/servicemonitor.yaml and deploy/base/prometheus/servicemonitor.yaml are byte-for-byte the same object (ServiceMonitor/hypershell-api-server in hypershell-system). If both are ever pulled into the same overlay they collide on name+namespace. Keep exactly one.

3. The gateway-metrics dashboard is non-functional as shipped.
GatewayMetricsDashboard and dashboardOperations are exported but never rendered by any route or wired into the app shell in components/web-console/app. Additionally, fetchGatewayMetrics calls GET /api/metrics/gateways, an endpoint that does not exist in the API server — there is no BFF proxy route and no hypershell_gateways_total Collector code in this PR (the accompanying spec DASH-01/DASH-04 describes both, but the implementation is absent). As written, the dashboard would always land in its error state. Please either land the backend Collector + BFF route and mount the dashboard on a route, or split the UI/spec out until the backend exists.

4. Scope and description mismatch.
The PR body describes "two local development fixes plus the ServiceMonitor from #202," but the diff also introduces a whole gateway-metrics dashboard feature (shared UI component, web-console adapter/composition, i18n, a 247-line spec) and a 75k-line vendored operator bundle. Undocumented scope is hard to review and mixes a self-contained dev fix with an incomplete feature. Please describe the full scope or split the feature into its own PR once it's functional.

Minor

  • Vendored operator bundle vs. URL reference. Other infra dependencies in deploy/kind/infrastructure/kustomization.yaml (gateway-api, cert-manager, cnpg, agent-sandbox) are referenced by release URL. The 75,414-line inline prometheus-operator-bundle.yaml bloats the repo and diverges from that pattern; prefer referencing the upstream release.
  • Hostname naming inconsistency. The PR body/testing notes mention metrics.hypershell.localhost, but the code uses observability.hypershell.localhost (METRICS_HOSTNAME). Align the docs/testing notes with the actual host.
  • Prometheus pod securityContext. deploy/base/prometheus/prometheus.yaml sets runAsNonRoot/runAsUser/fsGroup but no seccompProfile: RuntimeDefault. It's operator-managed so low priority, but adding it keeps it consistent with the control-plane SecurityContext convention.
  • Base NetworkPolicy egress. Declaring Egress + egress: [{}] in deploy/base/networkpolicies.yaml is functionally a no-op on spec-compliant CNIs (a policy without Egress in policyTypes already leaves egress unrestricted) and correctly works around kindnet. That's fine; just note the allow-all now lives in base, so if per-controller egress restriction is ever desired it must be reintroduced deliberately.

Cross-PR coordination

Two open pull requests introduce the same new file components/web-console/app/composition/dashboard-composition.ts and the same dashboardOperations export as this PR, but with an incompatible design: they build the operational dashboard from a new packages/operational-dashboard-ui package fed by a mock control-plane adapter, whereas this PR wires an api adapter in packages/gateway-management-ui that fetches live gateway-phase counts. These are competing implementations of the same dashboard surface (both claim the composition entry point and the dashboardOperations symbol), so only one can own that file. Maintainers should decide which dashboard package and adapter model is canonical, and in what order these land, before any of them merges — see #209 and #214.

Findings Summary (ordered by severity, highest first)

  1. [Major] Prometheus infra + ServiceMonitor + operator bundle are orphaned/unreferenced; metrics pipeline is not actually deployed - Spec Consistency / Config (deploy/base/prometheus/kustomization.yaml, deploy/base/servicemonitor.yaml, deploy/kind/infrastructure/prometheus-operator-bundle.yaml)
  2. [Major] Duplicate ServiceMonitor/hypershell-api-server in two files - Config (deploy/base/servicemonitor.yaml, deploy/base/prometheus/servicemonitor.yaml)
  3. [Major] Dashboard unwired to any route and calls non-existent /api/metrics/gateways; no Collector/BFF route in PR - API Design / Completeness (packages/gateway-management-ui/src/metrics/gateway-metrics-data.ts)
  4. [Major] PR scope far exceeds description (full dashboard feature + 75k-line bundle undocumented) - Reviewability
  5. [Minor] Vendored 75k-line operator bundle instead of URL reference - Maintainability
  6. [Minor] Hostname mismatch: metrics. (docs) vs observability. (code) - Docs
  7. [Minor] Prometheus pod securityContext missing seccompProfile - Security

Convention Checklist

Convention Result
Image references consistent across manifests Fail (metrics pipeline manifests unreferenced; can't be applied consistently)
SecurityContext on pod specs Partial (Prometheus CR missing seccompProfile)
Config wired into kustomize overlays Fail
Conventional commit / PR title Pass
Input validated N/A
No secrets in logs/responses Pass (metrics endpoint intentionally unauthenticated per spec)

@jsell-rh jsell-rh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict

REQUEST_CHANGES (advisory COMMENT event). The two genuine local-dev fixes (metrics bind address, controller egress) are sound, but the PR also carries a large, undocumented gateway-metrics feature whose Kubernetes wiring and backend/UI endpoints are not actually connected, so the metrics pipeline this PR claims to enable does not function as shipped.

Hi — Amber here. I reviewed the diff against the HyperShell conventions (CLAUDE.md, security + control-plane specs) and the review checklist. Summary: the local-dev connectivity/metrics fixes are correct and useful, but a substantial amount of the added Prometheus infrastructure and dashboard UI is orphaned (not referenced by any kustomization/script) or points at endpoints that don't exist yet, so it can't work end-to-end in its current form.

Blockers

None.

Major

1. The Prometheus infrastructure and ServiceMonitor are never deployed (orphaned resources).
deploy/base/prometheus/ (its kustomization.yaml, prometheus.yaml, rbac.yaml, servicemonitor.yaml), deploy/base/servicemonitor.yaml, and the 75,414-line deploy/kind/infrastructure/prometheus-operator-bundle.yaml are not referenced by any parent kustomization or script:

  • deploy/base/kustomization.yaml is unchanged (does not list servicemonitor.yaml or prometheus/), which contradicts the PR body's claim that the ServiceMonitor is "registered in the base kustomization."
  • deploy/kind/infrastructure/kustomization.yaml is unchanged, so prometheus-operator-bundle.yaml is never applied — the Prometheus Operator CRDs (monitoring.coreos.com/v1) are never installed, which means the Prometheus/ServiceMonitor CRs would fail to apply even if they were wired in.
    Net effect: nothing scrapes the API server, so the metrics pipeline the PR describes is not operational. Either wire these resources into the appropriate overlays (guarded so the CRDs exist first) or drop them from this PR.

2. Duplicate ServiceMonitor definition.
deploy/base/servicemonitor.yaml and deploy/base/prometheus/servicemonitor.yaml are byte-for-byte the same object (ServiceMonitor/hypershell-api-server in hypershell-system). If both are ever pulled into the same overlay they collide on name+namespace. Keep exactly one.

3. The gateway-metrics dashboard is non-functional as shipped.
GatewayMetricsDashboard and dashboardOperations are exported but never rendered by any route or wired into the app shell in components/web-console/app. Additionally, fetchGatewayMetrics calls GET /api/metrics/gateways, an endpoint that does not exist in the API server — there is no BFF proxy route and no hypershell_gateways_total Collector code in this PR (the accompanying spec DASH-01/DASH-04 describes both, but the implementation is absent). As written, the dashboard would always land in its error state. Please either land the backend Collector + BFF route and mount the dashboard on a route, or split the UI/spec out until the backend exists.

4. Scope and description mismatch.
The PR body describes "two local development fixes plus the ServiceMonitor from #202," but the diff also introduces a whole gateway-metrics dashboard feature (shared UI component, web-console adapter/composition, i18n, a 247-line spec) and a 75k-line vendored operator bundle. Undocumented scope is hard to review and mixes a self-contained dev fix with an incomplete feature. Please describe the full scope or split the feature into its own PR once it's functional.

Minor

  • Vendored operator bundle vs. URL reference. Other infra dependencies in deploy/kind/infrastructure/kustomization.yaml (gateway-api, cert-manager, cnpg, agent-sandbox) are referenced by release URL. The 75,414-line inline prometheus-operator-bundle.yaml bloats the repo and diverges from that pattern; prefer referencing the upstream release.
  • Hostname naming inconsistency. The PR body/testing notes mention metrics.hypershell.localhost, but the code uses observability.hypershell.localhost (METRICS_HOSTNAME). Align the docs/testing notes with the actual host.
  • Prometheus pod securityContext. deploy/base/prometheus/prometheus.yaml sets runAsNonRoot/runAsUser/fsGroup but no seccompProfile: RuntimeDefault. It's operator-managed so low priority, but adding it keeps it consistent with the control-plane SecurityContext convention.
  • Base NetworkPolicy egress. Declaring Egress + egress: [{}] in deploy/base/networkpolicies.yaml is functionally a no-op on spec-compliant CNIs (a policy without Egress in policyTypes already leaves egress unrestricted) and correctly works around kindnet. That's fine; just note the allow-all now lives in base, so if per-controller egress restriction is ever desired it must be reintroduced deliberately.

Cross-PR coordination

Two open pull requests introduce the same new file components/web-console/app/composition/dashboard-composition.ts and the same dashboardOperations export as this PR, but with an incompatible design: they build the operational dashboard from a new packages/operational-dashboard-ui package fed by a mock control-plane adapter, whereas this PR wires an api adapter in packages/gateway-management-ui that fetches live gateway-phase counts. These are competing implementations of the same dashboard surface (both claim the composition entry point and the dashboardOperations symbol), so only one can own that file. Maintainers should decide which dashboard package and adapter model is canonical, and in what order these land, before any of them merges — see #209 and #214.

Findings Summary (ordered by severity, highest first)

  1. [Major] Prometheus infra + ServiceMonitor + operator bundle are orphaned/unreferenced; metrics pipeline is not actually deployed - Spec Consistency / Config (deploy/base/prometheus/kustomization.yaml, deploy/base/servicemonitor.yaml, deploy/kind/infrastructure/prometheus-operator-bundle.yaml)
  2. [Major] Duplicate ServiceMonitor/hypershell-api-server in two files - Config (deploy/base/servicemonitor.yaml, deploy/base/prometheus/servicemonitor.yaml)
  3. [Major] Dashboard unwired to any route and calls non-existent /api/metrics/gateways; no Collector/BFF route in PR - API Design / Completeness (packages/gateway-management-ui/src/metrics/gateway-metrics-data.ts)
  4. [Major] PR scope far exceeds description (full dashboard feature + 75k-line bundle undocumented) - Reviewability
  5. [Minor] Vendored 75k-line operator bundle instead of URL reference - Maintainability
  6. [Minor] Hostname mismatch: metrics. (docs) vs observability. (code) - Docs
  7. [Minor] Prometheus pod securityContext missing seccompProfile - Security

Convention Checklist

Convention Result
Image references consistent across manifests Fail (metrics pipeline manifests unreferenced; can't be applied consistently)
SecurityContext on pod specs Partial (Prometheus CR missing seccompProfile)
Config wired into kustomize overlays Fail
Conventional commit / PR title Pass
Input validated N/A
No secrets in logs/responses Pass (metrics endpoint intentionally unauthenticated per spec)

@@ -0,0 +1,16 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This ServiceMonitor/hypershell-api-server is duplicated verbatim in deploy/base/prometheus/servicemonitor.yaml, and neither file is referenced by deploy/base/kustomization.yaml. Result: the ServiceMonitor is never deployed (contradicting the PR body's "registers it in the base kustomization"), and if both copies are ever wired into one overlay they collide on name+namespace. Keep one copy and add it to a kustomization that only loads after the Prometheus Operator CRDs exist.

@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This prometheus/ kustomization (and the Prometheus CR + RBAC it aggregates) is not referenced by any parent kustomization, so it is never applied. It also depends on monitoring.coreos.com/v1 CRDs from the operator bundle, which is itself unreferenced (see deploy/kind/infrastructure/prometheus-operator-bundle.yaml). Wire the operator install first, then this overlay, or drop these from the PR.

export async function fetchGatewayMetrics(
signal?: AbortSignal,
): Promise<GatewayPhaseCounts> {
const response = await fetch("/api/metrics/gateways", {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

/api/metrics/gateways does not exist anywhere in the API server or the web-console BFF in this PR (spec DASH-04 describes it, but no route/Collector is implemented). Combined with GatewayMetricsDashboard not being mounted on any route, the dashboard always resolves to its error state. Land the BFF proxy + hypershell_gateways_total Collector and mount the dashboard, or split this UI out until the backend exists.

}

export const dashboardOperations: DashboardOperations = {
controlPlane: createDashboardControlPlaneAdapter(),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This new dashboard-composition.ts / dashboardOperations export collides with a competing design in other open dashboard PRs that create the same file from a different package (mock-adapter based). Only one can own this composition entry point; the canonical dashboard package/adapter and merge order need a maintainer decision before this lands.

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.

2 participants