Ingress Info Service
Introduction
The Ingress Info Service exposes a read-only, filtered, and typed view of Kubernetes Ingress resources to internal cluster clients. The service MUST provide a single REST endpoint to list ingresses, protect business endpoints via bearer keys, and support deterministic caching with ETag. The scope excludes active reachability probing and any mutating APIs.
Primary use case: power an end-user links portal by exposing host- and path-level metadata for web services deployed in the cluster (see ADR-0001).
1. Glossary
| Term | Definition |
|---|---|
| Ingress | Kubernetes Networking resource mapping hosts/paths to backends |
| Endpoint/EndpointSlice | Kubernetes resources exposing ready backends for Services |
| Projection | In-memory view derived from informers, used to serve API |
| ETag | Hash-based validator computed from resourceVersions for cache control |
| Huma | HTTP framework used to define typed operations and expose OpenAPI/UI |
2. Goals and Non-Goals
- Goals:
- G-001: Provide a deterministic, typed list of ingresses with host/path metadata and best-effort reachability.
- G-002: Offer conditional GET via ETag (If-None-Match) for efficient caching.
- G-003: Secure API endpoints under /v1/* with static bearer keys from a Kubernetes Secret.
- G-004: Expose OpenAPI JSON and a minimal UI via Huma v2.
- G-005: Provide readiness/liveness probes and Prometheus metrics for operations.
- G-006: Optimize the response shape for a links portal client by exposing a top-level hosts array with nested paths (per ADR-0001).
- Non-Goals:
- NG-001: No mutation of cluster resources.
- NG-002: No active network probing; reachability remains passive and best-effort.
- NG-003: No pagination or detail-by-name endpoint in MVP.
3. Stakeholders
| Role | Responsibilities | Contact |
|---|---|---|
| Platform Team | Owns build/release, operations, SLOs, and roadmap | platform-team |
| Service Consumers | Read ingresses to build internal dashboards/pages | App teams |
| Security | Reviews auth controls and Secret handling | SecOps |
4. System Context
- Context overview and external dependencies
- Runs as a Deployment in-cluster (ClusterIP Service), consumes the Kubernetes API (in-cluster or
kubeconfigwhen run locally), reads a ConfigMap for selection/mapping and a Secret for API keys. Exposes HTTP on a configurable address (default :8080). - Primary consumer is an end-user Links Portal that renders web-service links using host and path metadata from this service (see ADR-0001).
- Runs as a Deployment in-cluster (ClusterIP Service), consumes the Kubernetes API (in-cluster or
- Diagram:
flowchart TD
subgraph Cluster[Kubernetes Cluster]
LinksPortal[Links Portal (primary consumer)] -->|GET /v1/ingresses| API[(Ingress Info Service)]
Client[Other Internal Clients] -->|GET /v1/ingresses| API
API -->|List/Watch| K8sAPI[(Kubernetes API)]
ConfigMap[(ConfigMap ingress-info-config)] --> API
Secret[(Secret ingress-info-keys)] --> API
API -->|/metrics| Prometheus[(Prometheus)]
API -->|/openapi.json + UI| Devs[Developers]
end
5. Technology Stack
Enumerate the selected technologies and rationale. This section MUST be used by subsequent plans and breakdowns.
| Area | Selection | Version/Target | Rationale |
|---|---|---|---|
| Programming Language | Go | 1.25 | Modern features, matches module; strong Kubernetes ecosystem |
| Framework/Runtime | Huma v2 over net/http | v2.x | Typed handlers, OpenAPI generation, built-in UI |
| Data Store | None | N/A | Read-only view; in-memory projection only |
| Messaging/Streaming | None | N/A | Not required |
| API Style | REST | RFC 7231 | Simple interoperability |
| AuthN/AuthZ | Static Bearer Keys (K8s Secret) | N/A | Simple internal auth; easy rotation |
| Observability | Prometheus + structured JSON logs | client_golang 1.23.x |
Standard metrics and logs in platform |
| Testing | Go test | stdlib | Native testing, table-driven style |
| CI/CD | GitHub Actions | multi-job | Hosted runners, caching, standard workflows |
| Packaging/Deploy | Docker + Kubernetes | linux (amd64, arm64) |
Standard platform; scratch runtime image |
6. Functional Requirements
Use FR-XXX identifiers; each MUST be testable.
| ID | Statement | Rationale | Acceptance Criteria (AC-IDs) |
|---|---|---|---|
| FR-001 | The service SHALL expose GET /v1/ingresses returning a typed list of ingresses. | Core product capability | AC-001, AC-002 |
| FR-002 | The service SHALL compute and return an ETag header derived deterministically from resourceVersions. | Efficient caching | AC-003 |
| FR-003 | The service SHALL honor If-None-Match and return 304 when the ETag matches. | HTTP cache validation | AC-004 |
| FR-004 | The service SHALL protect all /v1/* endpoints with Authorization: Bearer KEY validated against the Secret. | Access control | AC-005 |
| FR-005 | The service SHALL expose /healthz and /readyz, reporting ready after initial informer sync and successful config load. | K8s readiness | AC-006 |
| FR-006 | The service SHALL expose /metrics with Prometheus metrics for requests, durations, and projection size. | Observability | AC-007 |
| FR-007 | The service SHALL serve OpenAPI JSON and a minimal UI via Huma v2. | Developer experience | AC-008 |
| FR-008 | The service SHALL sort results deterministically by (namespace, name, host, path). | Stable ETag and diffability | AC-002 |
7. Non-Functional Requirements
Use NFR-XXX identifiers covering performance, reliability, security, compliance, operability.
| ID | Category | Statement | Metric/Target |
|---|---|---|---|
| NFR-001 | Performance | List endpoint median latency under low load | p50 < 50ms in-cluster |
| NFR-002 | Performance | List endpoint tail latency | p99 < 500ms in-cluster |
| NFR-003 | Reliability | Startup readiness after cache sync and config load | < 30s under normal cluster conditions |
| NFR-004 | Security | No secrets in logs; bearer tokens never logged | Zero occurrences (audit) |
| NFR-005 | Operability | Service reports request metrics and durations | http_requests_total and http_response_duration_seconds present |
| NFR-006 | Compatibility | Support Kubernetes 1.30β1.33 | Verified in CI matrix |
| NFR-007 | Portability | Configurable listen address | LISTEN_ADDR env; default :8080 |
8. Domain and Data Model
8.1 Domain Entities
classDiagram
class IngressRecord {
string namespace
string name
HostEntry[] hosts
}
class HostEntry {
string host
string servicename
string user_group
string description
string contact_information
bool missing_annotations
PathEntry[] paths
}
class PathEntry {
string path
string pathType
string reachability // reachable | unreachable | unknown
string backendService
}
IngressRecord --> HostEntry
HostEntry --> PathEntry
8.2 Data Schemas
Provide JSON Schemas for API-visible structures.
Ingress list response (Snapshot):
Error response model:
9. API Specification
Use API-XXX identifiers per endpoint. Include method, path, auth, request/response schemas, status codes, examples, and error model.
API-001: GET /v1/ingresses
- Auth: Authorization: Bearer KEY
- Request Headers:
- If-None-Match (optional)
- Query/Path Params: none
- Request Body: none
- Responses:
- 200 OK β application/json schema: HostsList
- 304 Not Modified β no body
- 401 Unauthorized β Error schema
- 500 Internal Server Error β Error schema
- Headers:
- ETag: strong validator computed from resourceVersions map
- Cache-Control: no-store (SHOULD be configurable per deployment policy)
10. Configuration
Enumerate all configuration keys, sources, defaults, and validation.
| Key | Source | Type | Default | Required | Description |
|---|---|---|---|---|---|
| LISTEN_ADDR | Env | string | :8080 | false | HTTP listen address (host:port or :port) |
| INGRESS_INFO_CONFIG_FILE | Env | string | /etc/ingress-info/config.yaml | false | Path to YAML/JSON config file; if absent, fall back to ConfigMap |
| INGRESS_INFO_KEYS_FILE | Env | string | empty | false | Optional file with one key per line; overrides Secret for local runs |
| POD_NAMESPACE | Env | string | default | false | Namespace for ConfigMap/Secret lookup when in-cluster |
| KUBECONFIG | Env | string | empty | false | kubeconfig path for out-of-cluster runs |
| ConfigMap name | K8s | string | ingress-info-config | true | Name of ConfigMap providing labelSelector and mapping.* keys |
| Secret name | K8s | string | ingress-info-keys | true | Name of Secret providing valid API keys |
| config.labelSelector | File/ConfigMap | string | none | true | Label selector for ingresses |
| config.mapping.service_name | File/ConfigMap | string | empty | false | annotation.KEY or label.KEY or plain key name; populates host service_name |
| config.mapping.user_group | File/ConfigMap | string | empty | false | annotation.KEY or label.KEY; populates user_group |
| config.mapping.description | File/ConfigMap | string | empty | false | annotation.KEY or label.KEY; populates description |
| config.mapping.contact_information | File/ConfigMap | string | empty | false | annotation.KEY or label.KEY; populates contact_information |
Validation rules:
- LISTEN_ADDR MUST parse as valid TCP address.
- labelSelector MUST be a valid Kubernetes label selector string.
11. Security
Use SEC-XXX identifiers. Cover authentication, authorization, data protection, secrets, and supply chain.
| ID | Area | Control/Requirement |
|---|---|---|
| SEC-001 | AuthN | /v1/* requires Authorization: Bearer KEY; exact-match against keys loaded from Secret or file. |
| SEC-002 | Secrets | Keys are loaded into memory and never logged. |
| SEC-003 | Transport | In-cluster traffic; TLS termination may be handled by ingress/gateway if externally exposed. |
| SEC-004 | Least Privilege | ServiceAccount bound to ClusterRole with read-only on Ingress, Endpoints, EndpointSlice. |
| SEC-005 | Logging | No sensitive headers or token values in logs; only derived client_id may be logged. |
| SEC-006 | Supply Chain | Build with pinned versions and reproducible Docker (multi-stage; scratch runtime). |
12. Observability
Use OBS-XXX identifiers. Define metrics, logs, traces, and alerts.
Note: For the Links Portal use case, request metrics and handler latency for GET /v1/ingresses, as well as projection_size, are especially relevant to monitor perceived portal responsiveness and data freshness.
| ID | Type | Name/Field | Labels | Semantics |
|---|---|---|---|---|
| OBS-001 | Metric | http_requests_total | method, path, code | Count of HTTP requests; code is HTTP status text |
| OBS-002 | Metric | http_response_duration_seconds | method, path | Histogram of handler latency (seconds) |
| OBS-003 | Metric | projection_size | none | Count of typed objects in the projection snapshot (ingresses + endpoints + endpointSlices) |
| OBS-004 | Logs | request fields | request_id, client_id, method, path, status, latency_ms | Structured JSON log per request |
| OBS-005 | Probes | /healthz, /readyz | n/a | Readiness gates on initial cache sync and config load |
Alerts (suggested):
- High 5xx rate on /v1/ingresses over 5 minutes.
- Ready=false for > 5 minutes after rollout.
13. Operations
Runbooks, SLOs, scaling, rollout, rollback, backups, DR, upgrades.
| ID | Area | Procedure |
|---|---|---|
| OPS-001 | Deployment | Roll out new image via standard Deployment; readiness probe ensures safe traffic shift. |
| OPS-002 | Key Rotation | Update Secret ingress-info-keys; service reloads in background within 30s. |
| OPS-003 | Config Change | Update ConfigMap ingress-info-config; mount file or reload via periodic loader; restart if required. |
| OPS-004 | Scaling | Horizontal scaling supported; clients use ClusterIP Service. |
| OPS-005 | Upgrade Policy | Validate against K8s 1.30β1.33; bump client-go as needed. |
14. Compatibility and Versioning
Use COMP-XXX identifiers. Define versioning policy, API stability, and deprecation.
- COMP-001: API versioning via path prefix /v1; backward-compatible additions allowed; breaking changes require /v2.
- COMP-002: ETag semantics MUST remain stable (resourceVersions-hash) within a minor line.
- COMP-003: Supported Kubernetes versions: 1.30β1.33; other versions are best-effort.
15. Dependencies
Use DEP-XXX identifiers. List runtime and build-time dependencies with versions and licenses.
| ID | Name | Version | License | Purpose |
|---|---|---|---|---|
| DEP-001 | k8s.io/api | v0.34.1 | Apache-2.0 | Object types |
| DEP-002 | k8s.io/apimachinery | v0.34.1 | Apache-2.0 | Meta/runtime utilities |
| DEP-003 | k8s.io/client-go | v0.34.1 | Apache-2.0 | Informers and clients |
| DEP-004 | github.com/prometheus/client_golang | v1.23.2 | Apache-2.0 | Metrics exposition |
| DEP-005 | sigs.k8s.io/yaml | v1.6.0 | Apache-2.0 | YAML parsing |
| DEP-006 | github.com/danielgtaylor/huma | v2.x | MIT | HTTP framework + OpenAPI |
| DEP-007 | github.com/alecthomas/kong | vX | MIT | CLI parsing |
16. Risks, Assumptions, Decisions
| Type | ID | Statement |
|---|---|---|
| Risk | RISK-001 | Reachability heuristic may misclassify when host/service resolution is ambiguous. |
| Risk | RISK-002 | Static keys require operational rotation discipline. |
| Decision | DECISION-001 | Adopt Huma v2 for routing and OpenAPI/UI. |
| Decision | DECISION-002 | Keep best-effort reachability; no active probes. |
| Decision | DECISION-003 | Deterministic ordering by (namespace, name, host, path). |
Assumptions are maintained in Appendix (ASM-###).
17. Acceptance Criteria
Use AC-XXX identifiers; map each to FR/NFR.
| ID | Verifiable Statement | Maps To |
|---|---|---|
| AC-001 | GET /v1/ingresses returns HTTP 200 with schema-valid body when authorized. | FR-001 |
| AC-002 | Hosts and nested paths are sorted deterministically by (host, path). | FR-008 |
| AC-003 | Response includes an ETag header that changes when underlying resourceVersions change. | FR-002 |
| AC-004 | If-None-Match equal to ETag yields HTTP 304 with no body. | FR-003 |
| AC-005 | Missing/invalid Authorization returns HTTP 401. | FR-004, SEC-001 |
| AC-006 | /readyz returns 200 only after informer cache sync and config load. | FR-005 |
| AC-007 | /metrics exposes http_requests_total and http_response_duration_seconds. | FR-006, NFR-005 |
| AC-008 | /openapi.json is served and the UI renders the list operation. | FR-007 |
18. Test Plan
Use TEST-XXX identifiers. Outline unit, integration, e2e, performance, and security tests. Link ACs.
| ID | Type | Scope | Tools | ACs |
|---|---|---|---|---|
| TEST-001 | Unit | Projection ETag computation | go test | AC-003 |
| TEST-002 | Unit | Auth middleware (valid/invalid bearer) | go test | AC-005 |
| TEST-003 | Unit | Sorting order of hosts and paths | go test | AC-002 |
| TEST-004 | Unit | Reachability classification | go test | AC-001 |
| TEST-005 | Integration | Ready after cache sync + config load | go test / kind | AC-006 |
| TEST-006 | Integration | Conditional GET 304 path | go test / http client | AC-004 |
| TEST-007 | Integration | Metrics presence | curl /metrics | AC-007 |
| TEST-008 | Integration | OpenAPI served and validates | curl /openapi.json | AC-008 |
19. Documentation Plan
Following DiΓ‘taxis Framework principles. Use DOC-XXX identifiers for traceability.
| ID | Type | Audience | Purpose | Location | Linked Requirements |
|---|---|---|---|---|---|
| DOC-001 | Tutorial | New users | Run locally against a kubeconfig using simulator | docs/tutorials/ | FR-001, AC-001 |
| DOC-002 | How-to Guide | Practitioners | Rotate API keys safely | docs/how-to/ | OPS-002, SEC-002 |
| DOC-003 | Reference | All users | API reference (OpenAPI link) | docs/reference/ | API-001, AC-008 |
| DOC-004 | Explanation | Architects | Rationale for passive reachability | docs/explanations/ | DECISION-002 |
Documentation Types
- Tutorials: Step-by-step learning experiences for new users
- How-to Guides: Task-oriented instructions for specific goals
- Reference: Complete, accurate descriptions of the system
- Explanations: Conceptual discussions of design decisions and architecture
20. Out of Scope
- Pagination and detail-by-name endpoints
- Multi-tenant authorization and per-key scoping
- Active reachability probes (HTTP/TCP checks)
- Non-Kubernetes deployments
21. Appendix
ADR Changes
- Changed by ADR-0001: Hosts Top-Level Response Shape for GET /v1/ingresses on 2025-09-15.
References, prior art, ADR links, and diagrams.
Assumptions Ledger
| ID | Statement | Source | Rationale | Status |
|---|---|---|---|---|
| ASM-001 | Service is cluster-internal (ClusterIP). | Inferred | Deployment/Service internal usage. | Accepted |
| ASM-002 | MVP exposes only GET /v1/ingresses. | Detected | Single handler wired; minimizes scope. | Accepted |
| ASM-003 | ETag is a hash of resourceVersions and remains stable. | Detected | Current implementation; deterministic. | Accepted |
| ASM-004 | Static bearer keys are sufficient; no JWT/OIDC. | User-Provided | Simplicity for internal use. | Accepted |
| ASM-005 | Canonical port is 8080 and is configurable. | User-Provided | Aligns manifests; flexibility. | Accepted |
| ASM-006 | Use Huma v2 with OpenAPI and UI. | User-Provided | Developer experience and typed contract. | Accepted |
| ASM-007 | Reachability is best-effort via Endpoints/EndpointSlice only. | Detected | No active probing. | Accepted |
| ASM-008 | Supported Kubernetes range is 1.30β1.33. | User-Provided | Target platforms. | Accepted |
| ASM-009 | Build with Go 1.25; scratch runtime. | Detected | Align toolchain and image. | Accepted |
| ASM-010 | ConfigMap/Secret names remain ingress-info-config/ingress-info-keys. | User-Provided | Consistency with manifests. | Accepted |
Provenance
- Interactive Q&A conducted with one-at-a-time confirmations.
- Key confirmations: name (Ingress Info Service), port 8080 (configurable), static bearer auth, single endpoint, ETag-only, Huma v2 + OpenAPI/UI, strict typed schemas, K8s 1.30β1.33, Go 1.25, object names unchanged, structured logs with request_id/client_id, CI with GitHub Actions.
Additional Notes
- Implementation MUST update server listen address to be configurable and default to :8080.
- Implementation SHOULD wire Huma v2 as the primary router, exposing /openapi.json and the built-in UI. Legacy net/http mux MAY be retained internally if needed during migration.
- Deterministic ordering MUST be enforced for the response body to aid diffability; ETag remains based on the resourceVersions map and is independent of host/path order.
Change History
- 2025-09-15: Specification approved; status set to Approved
- 2025-09-15: Updated response schema and examples to use top-level hosts per ADR-0001 (docs/adr/adr-0001-hosts-top-level-response.md)