diff --git a/docs/developer-guide/01-architecture.md b/docs/developer-guide/01-architecture.md new file mode 100644 index 0000000..7c7624f --- /dev/null +++ b/docs/developer-guide/01-architecture.md @@ -0,0 +1,48 @@ +# Architecture + +How `chart-inspector` is organized, how it boots, and the central idea that makes it work: **resources are discovered by observing the API traffic of a server-side dry-run**, not by parsing rendered manifests. + +## A thin service over the shared Helm engine + +`chart-inspector` is a single, small HTTP service. The heavy lifting — downloading charts (from a Helm repo, OCI, or `.tgz`), caching them, rendering, and running the server-side dry-run — lives in the shared Krateo Helm library. This service is a thin layer around it: parse a request, run the chart through a dry-run with a request-scoped HTTP tracer attached, and return what the tracer saw. + +## The main parts + +- **The `/resources` handler** — the core. It fetches the target `Composition` and its `CompositionDefinition`, builds the chart's values, installs a tracer, runs the dry-run, and returns the captured resources. +- **The tracer** — a small HTTP interceptor attached to the dry-run's connection to the API server. It records every API resource the dry-run touches. +- **Small lookup helpers** — fetch the `Composition`, the `CompositionDefinition`, and (when the chart needs credentials) a `Secret`. +- **Health probes** — liveness and readiness endpoints. + +## The central idea: observe, don't parse + +The handler copies the cluster connection and attaches a per-request tracer to it. Every call Helm makes to the API server during the dry-run — looking objects up, validating CRDs, discovering capabilities — flows through that tracer, which turns each request into a resource entry `{group, version, resource, namespace, name}`. The service never parses the rendered manifest; it watches the traffic. That design choice has direct consequences (under-reporting objects that are never looked up, reporting read-only dependencies, and producing duplicates) — all covered in [`02-api-and-request-lifecycle.md`](./02-api-and-request-lifecycle.md). + +```mermaid +flowchart TB + CDC[CDC caller] -->|asks for the resource list| H[/resources handler] + H -->|fetch Composition + CompositionDefinition| K8s[(Kubernetes API)] + H -->|server-side dry-run| HELM[shared Helm engine] + HELM -->|lookup / validate / discover| K8s + H -.->|attaches a request-scoped tracer to the dry-run| TR[tracer] + HELM -.->|every API call flows through| TR + TR -->|captured resources| H + H -->|JSON list| CDC +``` + +## How it boots + +The startup sequence is short: + +1. Read configuration (debug flag, port, kubeconfig). +2. Build a structured JSON logger (for the logs-ingester). +3. Connect to the cluster — in-cluster by default, or from a kubeconfig — with client-side throttling disabled so the API server's own fairness controls govern load. +4. Build the **long-lived Helm client** once, with a chart cache and a CRD watch that persist across requests. This shared, stateful client is the main reason chart-inspector is a long-running service rather than a library. +5. Register the routes and start the HTTP server, with a generous write timeout to accommodate slow chart downloads and dry-runs. + +On shutdown it stops serving, closes the Helm client (stopping the cache cleanup and the CRD watch), and drains in-flight requests. + +## Conventions + +- Dependencies are assembled once at startup and passed into the handlers (simple dependency injection). +- Errors are returned through the shared HTTP-response helpers, not hand-rolled. +- The real Helm machinery is reused from the shared library; this service deliberately keeps only a minimal tracer of its own. diff --git a/docs/developer-guide/02-api-and-request-lifecycle.md b/docs/developer-guide/02-api-and-request-lifecycle.md new file mode 100644 index 0000000..907f136 --- /dev/null +++ b/docs/developer-guide/02-api-and-request-lifecycle.md @@ -0,0 +1,45 @@ +# API & request lifecycle + +What the service exposes, what happens during a request, what the result means, and how the tracer produces it. + +> The exact endpoint, parameters, and response shape are documented authoritatively by the generated Swagger, served at `/swagger/`. This page explains the behavior behind it. + +## What it exposes + +- **A liveness probe** and a **readiness probe** (readiness flips to "not ready" during shutdown). +- **The resources endpoint** — the one functional endpoint. It is given the identity of a `Composition` and of its `CompositionDefinition` (their names, namespaces, and GVRs), and returns the list of API resources the chart would touch. +- **The Swagger UI.** + +## What happens during a request + +```mermaid +sequenceDiagram + participant C as CDC caller + participant H as resources handler + participant K as Kubernetes API + participant HE as Helm engine + + C->>H: request, with the composition and definition identity + H->>K: fetch the Composition + H->>H: build chart values from the spec, inject global values + H->>H: attach a request-scoped tracer to the cluster connection + H->>K: fetch the CompositionDefinition, read its chart reference + H->>HE: server-side dry-run of the chart + HE->>K: lookups, CRD validation, capability discovery, all via the tracer + H->>C: JSON list of the resources the tracer captured +``` + +If the chart references credentials, the handler fetches the password from the referenced `Secret` before the dry-run. + +## What the result means + +The response is a flat list of entries, each identifying one API resource the dry-run touched: its group, version, resource, namespace, and name. It is **not** a values schema, **not** RBAC rules, and **not** rendered YAML — the caller (the CDC) turns these entries into RBAC rules itself. + +Two properties follow directly from *how* the list is produced (by observing traffic, see below), and any consumer must account for them: + +- **It reflects what was *touched*, not what was *rendered*.** An object the dry-run never looks up can be missing; an object that is only looked up (a read-only dependency) is included. +- **Duplicates are normal.** The same object can appear several times, because the dry-run may look it up more than once. Consumers should de-duplicate. + +## The tracer, conceptually + +The list isn't built by parsing the chart's output. Instead, a small interceptor sits on the dry-run's connection to the API server and records every request, turning each one into a resource entry by reading the API path (which encodes the group, version, resource, namespace, and name). Because it records every matching call and never de-duplicates, repeated lookups become repeated entries — hence the duplicates above. This is also why the result is "what was touched": only resources that actually generate API traffic during the dry-run show up. diff --git a/docs/developer-guide/03-extending.md b/docs/developer-guide/03-extending.md new file mode 100644 index 0000000..6214501 --- /dev/null +++ b/docs/developer-guide/03-extending.md @@ -0,0 +1,28 @@ +# Extending chart-inspector + +The three things you are most likely to change. + +## Add a new endpoint + +Endpoints follow a simple convention: a constructor that takes the shared dependency container and returns an HTTP handler. To add one: + +1. Write the handler, using the dependencies it needs from the shared container (the cluster clients, the Helm client, the pluralizer). +2. Add any new dependency to that container, where everything is assembled once at startup. +3. Register the route alongside the others. +4. Annotate the handler for Swagger and regenerate the API docs. + +## Extend what the tracer captures + +The detail in the result is bounded by what the tracer records and by the fields of a resource entry. To capture more — for example the HTTP verb, request bodies, subresources, or cluster-scoped list calls — extend the tracer's logic for turning an API request into an entry, and add any new fields to the resource entry so they flow through to the response. + +Keep in mind the "touched, not rendered" property: capturing more *detail per call* does not change *which* objects the dry-run touches. + +## Change the dry-run behavior + +The dry-run is configured where the handler builds the install request. Common changes: + +- **Dry-run mode** — server-side (the default) consults the live cluster for lookups, validation, and capability discovery; a client-only mode renders locally but loses everything the live lookups would surface. +- **CRD handling** — whether CRDs are included in the render. +- **TLS** — whether to skip verification for the chart source. + +Because the real machinery lives in the shared Helm library, deeper changes (caching, download behavior, the Helm action wiring) belong there, not in this service. diff --git a/docs/developer-guide/README.md b/docs/developer-guide/README.md new file mode 100644 index 0000000..0a1a44c --- /dev/null +++ b/docs/developer-guide/README.md @@ -0,0 +1,37 @@ +# chart-inspector — Developer Guide + +A contributor-facing guide to the stateless HTTP service that tells the rest of Krateo **which Kubernetes resources a chart would touch**, by running a server-side Helm dry-run. + +> Audience: engineers who **contribute to, extend, or debug `chart-inspector`** — not end users. This guide explains *ideas and flows*, not line-by-line code. For product concepts, see [docs.krateo.io](https://docs.krateo.io). + +## Role in KCO + +KCO turns Helm charts into Kubernetes-native APIs. **core-provider** generates a CRD from a chart's values schema and deploys, per `CompositionDefinition`, a **composition-dynamic-controller (CDC)**. The CDC renders the chart for each `Composition` instance and applies it as a Helm release. To scope its own **least-privilege RBAC**, the CDC needs to know exactly which API resources a chart manages — and that can only be answered by rendering the chart against the *live* cluster (Helm lookups and capability discovery are dynamic). `chart-inspector` answers that question: a single endpoint performs a **server-side Helm dry-run** and returns the set of API resources the chart touches. + +```mermaid +flowchart LR + CDC[composition-dynamic-controller] -->|asks which resources a chart touches| CI[chart-inspector] + CI -->|server-side dry-run| K8s[(Kubernetes API)] + CI -->|resources list| CDC + CDC -->|generate least-privilege RBAC| K8s +``` + +Two things about this relationship are easy to misread: + +- **The runtime caller is the CDC, not core-provider.** core-provider only injects the inspector's URL into the CDC's configuration; it never calls the service itself. +- **It is a separate service on purpose.** It holds a long-lived Helm client with a chart cache and a CRD watch that must live across requests, and server-side dry-runs need broad cluster read access — best isolated in its own pod and identity. See [`01-architecture.md`](./01-architecture.md). + +## Documents in this folder + +| Document | What it covers | +| --- | --- | +| [`01-architecture.md`](./01-architecture.md) | It's a thin HTTP service over the shared Helm engine: the main parts, how it boots, and the **tracer** idea (resources are discovered by *observing API traffic*, not by parsing rendered YAML). | +| [`02-api-and-request-lifecycle.md`](./02-api-and-request-lifecycle.md) | The endpoints, what happens during a request, what the result means, and how the tracer produces it. | +| [`03-extending.md`](./03-extending.md) | Adding an endpoint, extending the tracer, and changing the dry-run behavior. | + +## See also + +- **Ecosystem overview (canonical)** — the whole KCO pipeline lives in the **core-provider** repo: `core-provider/docs/developer-guide/00-ecosystem-overview.md`. +- **HTTP API (authoritative)** — the generated Swagger, served at `/swagger/`. +- **Logging contract** — `docs/logs-ingester-compatibility.md`. +- **Sibling guides** — **composition-dynamic-controller** (the caller) and **plumbing** (the Helm engine).