Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions docs/developer-guide/01-architecture.md
Original file line number Diff line number Diff line change
@@ -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.
45 changes: 45 additions & 0 deletions docs/developer-guide/02-api-and-request-lifecycle.md
Original file line number Diff line number Diff line change
@@ -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.
28 changes: 28 additions & 0 deletions docs/developer-guide/03-extending.md
Original file line number Diff line number Diff line change
@@ -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.
37 changes: 37 additions & 0 deletions docs/developer-guide/README.md
Original file line number Diff line number Diff line change
@@ -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).