Skip to content

Commit ec90657

Browse files
committed
Add agentic documentation for AI-assisted development
1 parent ae52714 commit ec90657

16 files changed

Lines changed: 1675 additions & 0 deletions

AGENTS.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Zero Trust Workload Identity Manager (ZTWIM) - Agentic Documentation
2+
3+
**Component**: Zero Trust Workload Identity Manager
4+
**Repository**: openshift/zero-trust-workload-identity-manager
5+
6+
> **Retrieval-first**: Before generating code, read the relevant `ai-docs/` files. Start with `domain/` for API types, then `architecture/components.md` for patterns, then `ZTWIM_DEVELOPMENT.md` for workflow.
7+
>
8+
> **Generic Platform Patterns**: See Platform documentation (openshift/enhancements/ai-docs/) for operator patterns, testing practices, security guidelines, and cross-repo ADRs.
9+
10+
## What is ZTWIM?
11+
12+
A controller-runtime operator (not library-go) that deploys and manages upstream SPIFFE/SPIRE components on OpenShift 4.18+ so workloads get dynamic, rotatable identities (JWT-SVID / X.509-SVID). GA since v1.0.0; published to `stable-v1` channel via `redhat-operators` CatalogSource.
13+
14+
**Key Principle**: The operator does not embed upstream code — it manages upstream resources as static YAML manifests (bindata) applied imperatively, deploying upstream container images via `RELATED_IMAGE_*` env vars.
15+
16+
## Core Components
17+
18+
| Controller | Purpose | Key Resources |
19+
|---|---|---|
20+
| **ZTWIM** | Status aggregator | Watches all operand CRs + OperatorCondition, syncs Upgradeable to OLM |
21+
| **SpireServer** | SPIRE server lifecycle | StatefulSet, ConfigMaps, Routes, webhooks, federation, SPIFFE CRs |
22+
| **SpireAgent** | SPIRE agent lifecycle | DaemonSet, custom SCC, ConfigMap, ClusterRole |
23+
| **SpiffeCSIDriver** | CSI driver lifecycle | DaemonSet, CSIDriver, privileged SCC RoleBinding |
24+
| **SpireOIDCDiscoveryProvider** | OIDC discovery | Deployment, Route, ClusterSPIFFEID, ConfigMap |
25+
26+
**Quick Start**: `oc get zerotrustworkloadidentitymanagers cluster -o yaml` | `oc get spireservers,spireagents,spiffecsidrivers,spireoidcdiscoveryproviders`
27+
28+
## Critical Patterns
29+
30+
**1. Never hand-edit generated files**`zz_generated.deepcopy.go`, `config/crd/bases/*.yaml`, `pkg/operator/assets/bindata.go`. Use `make generate`, `make manifests`, `make update-bindata`. Run `make verify` before pushing.
31+
32+
**2. All CRDs are cluster-scoped singletons named `cluster`** — enforced by CEL XValidation. Controller names use the full prefix: `zero-trust-workload-identity-manager-<component>-controller` (constants in `pkg/controller/utils/constants.go`).
33+
34+
**3. Operand controllers watch the parent ZTWIM CR** — all four operand controllers watch `ZeroTrustWorkloadIdentityManager` with `ZTWIMSpecChangedPredicate`, and managed resources with component-specific label predicates (`ControllerManagedResourcesForComponent`). The ZTWIM controller does NOT create operand CRs — it only aggregates status.
35+
36+
**4. Vendor is tracked** — after dependency changes, always `make vendor` and commit the vendor directory. The build uses `-mod=vendor`.
37+
38+
## Key Build Commands
39+
40+
| Command | Purpose |
41+
|---|---|
42+
| `make build` | Full build (manifests + generate + fmt + vet + binary) |
43+
| `make test` | Unit tests with envtest |
44+
| `make verify` | vet + fmt + golangci-lint |
45+
| `make manifests generate update-bindata` | Regenerate all generated files |
46+
47+
## Documentation Structure
48+
49+
```text
50+
ai-docs/
51+
├── domain/ # CRD type docs (ZTWIM, SpireServer, SpireAgent, etc.)
52+
├── architecture/ # Repo layout, controller table, reconciliation flow
53+
│ └── components.md
54+
├── decisions/ # Component ADRs (controller-runtime choice, bindata)
55+
│ ├── adr-0001-*.md
56+
│ └── adr-template.md
57+
├── exec-plans/
58+
│ └── README.md # Pointer to Platform guidance
59+
├── references/
60+
│ ├── ecosystem.md # Links to Platform
61+
│ └── enhancements.md # Enhancement proposals & upstream refs
62+
├── ZTWIM_DEVELOPMENT.md # Build, workflow, common tasks, env vars
63+
└── ZTWIM_TESTING.md # Unit (FakeCustomCtrlClient) + E2E (Ginkgo)
64+
```
65+
66+
**AI Agent Path**: domain/ → architecture/ → decisions/ → ZTWIM_DEVELOPMENT.md
67+
68+
**Platform Patterns**: [Operator](https://github.com/openshift/enhancements/tree/master/ai-docs) | [Testing](https://github.com/openshift/enhancements/tree/master/ai-docs) | [Security](https://github.com/openshift/enhancements/tree/master/ai-docs)
69+
70+
## Upstream Operands
71+
72+
| Upstream | Integration |
73+
|---|---|
74+
| [spire](https://github.com/spiffe/spire) | Server (StatefulSet), agent (DaemonSet), OIDC (Deployment) images |
75+
| [spire-controller-manager](https://github.com/spiffe/spire-controller-manager) | Go module dep for API types; sidecar with SPIRE server |
76+
| [spiffe-csi](https://github.com/spiffe/spiffe-csi) | CSI driver DaemonSet image |
77+
78+
**Release**: Managed via [zero-trust-workload-identity-manager-release](https://github.com/openshift/zero-trust-workload-identity-manager-release) (Konflux, dual-branch model: `release-*` for images, `main` for FBC catalogs).
79+
80+
## External References
81+
82+
- [SPIFFE Specification](https://github.com/spiffe/spiffe) | [SPIRE Docs](https://spiffe.io/docs/latest/spire-about/) | [OpenShift Docs](https://docs.openshift.com/)
83+
84+
---
85+
86+
**Platform Documentation**: openshift/enhancements/ai-docs/

ai-docs/ZTWIM_DEVELOPMENT.md

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# ZTWIM Development Guide
2+
3+
> For generic Go/operator practices, see the Platform Development Guide.
4+
5+
## Quick Start
6+
7+
### Prerequisites
8+
9+
| Tool | Version | Notes |
10+
|------|---------|-------|
11+
| Go | 1.25+ | See `go.mod` for exact version (1.25.7) |
12+
| Docker or Podman | Latest | Set `CONTAINER_TOOL=podman` if using Podman |
13+
| oc / kubectl | Latest | For cluster interaction |
14+
| OpenShift | 4.18+ | E2E tests require a live cluster |
15+
| operator-sdk | v1.39.0 | Auto-downloaded by Makefile |
16+
17+
### Build Commands
18+
19+
```bash
20+
make build # Full build: manifests + generate + fmt + vet + binary
21+
make build-operator # Binary only (no codegen, fastest iteration)
22+
make test # Unit tests with coverage
23+
make verify # vet + fmt + lint
24+
make lint # golangci-lint (v1.59.1)
25+
```
26+
27+
## Code Style
28+
29+
### Import Order
30+
31+
Imports follow this grouping convention (checked by `goimports` linter in CI):
32+
33+
1. Standard library
34+
2. `k8s.io/*`, `sigs.k8s.io/*`
35+
3. Third-party (`github.com/go-logr/logr`, `github.com/operator-framework/api`, etc.)
36+
4. OpenShift (`github.com/openshift/api`, `github.com/openshift/client-go`)
37+
5. This project (`github.com/openshift/zero-trust-workload-identity-manager/...`)
38+
39+
### Standard Import Aliases
40+
41+
| Alias | Package |
42+
|---|---|
43+
| `ctrl` | `sigs.k8s.io/controller-runtime` |
44+
| `kerrors` | `k8s.io/apimachinery/pkg/api/errors` |
45+
| `apimeta` | `k8s.io/apimachinery/pkg/api/meta` |
46+
| `customClient` | `github.com/openshift/zero-trust-workload-identity-manager/pkg/client` |
47+
| `routev1` | `github.com/openshift/api/route/v1` |
48+
49+
### File Headers
50+
51+
All `.go` files must include the Apache 2.0 license header from `hack/boilerplate.go.txt`.
52+
53+
## Development Workflow
54+
55+
### Local Build
56+
57+
```bash
58+
export OPERATOR_NAMESPACE=zero-trust-workload-identity-manager
59+
make build-operator
60+
./bin/zero-trust-workload-identity-manager --v=5 --metrics-secure=false
61+
```
62+
63+
The binary is FIPS-aware — `hack/go-fips.sh` sets `GOEXPERIMENT=strictfipsruntime` when the compiler supports it. Local builds without the FIPS-capable toolchain still succeed but emit a warning.
64+
65+
### Testing on Cluster
66+
67+
```bash
68+
make docker-build IMG=<registry>/ztwim:dev
69+
make docker-push IMG=<registry>/ztwim:dev
70+
make deploy IMG=<registry>/ztwim:dev
71+
```
72+
73+
Or the all-in-one:
74+
75+
```bash
76+
make generate-deploy IMG=<registry>/ztwim:dev
77+
```
78+
79+
### Debugging
80+
81+
Run locally against a remote cluster:
82+
83+
```bash
84+
make run # starts controller with --v=5 against current kubeconfig
85+
```
86+
87+
Set `OPERATOR_NAMESPACE=zero-trust-workload-identity-manager` — the operator exits immediately if this is empty.
88+
89+
## Common Tasks
90+
91+
### Adding a New Operand
92+
93+
1. Define the CRD type in `api/v1alpha1/<operand>_types.go` with `+kubebuilder:resource:scope=Cluster`, singleton CEL validation, `ConditionalStatus` embedding, and `GetConditionalStatus()` method.
94+
2. Create the controller package under `pkg/controller/<operand>/` following the reconciler structure: `Reconciler` struct, `New(mgr)`, `SetupWithManager(mgr)`, `Reconcile(ctx, req)`.
95+
3. Register the scheme and wire the controller in `cmd/zero-trust-workload-identity-manager/main.go`.
96+
4. Run `make manifests generate`.
97+
5. Add the new CR type to `cacheResourceWithoutReqSelectors` and `informerResources` in `pkg/client/client.go`.
98+
6. Add a `get<Operand>Status()` method to the ZTWIM controller and include it in `aggregateOperandStatus()`.
99+
7. Add a `Watches` entry for the new operand CR in the ZTWIM controller's `SetupWithManager()` with `operandStatusChangedPredicate`.
100+
8. Add RBAC markers on the ZTWIM controller for the new CRD, then `make manifests`.
101+
9. Add component label constant and label generator function in `pkg/controller/utils/labels.go`.
102+
10. Add image env var constant and getter in `pkg/controller/utils/constants.go` and `relatedImages.go`.
103+
11. Add bindata YAML under `bindata/<operand>/`, add constants, and run `make update-bindata`.
104+
12. Run `make manifests generate update-bindata && make verify && make test`.
105+
106+
### Adding a New Bindata Resource
107+
108+
1. Create the YAML manifest in `bindata/<component>/` with `app.kubernetes.io/managed-by: zero-trust-workload-identity-manager` label.
109+
2. Add an asset path constant in `pkg/controller/utils/constants.go`.
110+
3. Run `make update-bindata` to regenerate `pkg/operator/assets/bindata.go`.
111+
4. Add a `reconcileNewResource()` method following the decode → mutate → SetControllerReference → Get → Create/Update pattern.
112+
5. Call the new reconcile method in `Reconcile()` at the correct position in the ordering.
113+
6. Add a status condition constant for the new resource.
114+
7. Add a `Watches` entry in `SetupWithManager()` if it's a new GVK.
115+
8. Register the resource type in `NewCacheBuilder()` under `cacheResources` in `pkg/client/client.go`.
116+
9. Add the resource type to `ResourceNeedsUpdate()` in `pkg/controller/utils/` with field-level comparison.
117+
10. Run `make manifests generate update-bindata && make verify`.
118+
119+
### Adding a New CRD Field
120+
121+
1. Add the field to the appropriate `*Spec` or `*Status` struct in `api/v1alpha1/`.
122+
2. Add kubebuilder validation markers (Required/Optional, Enum, Pattern, Min/Max, Default).
123+
3. If immutable, add a CEL `XValidation` rule with `self == oldSelf`.
124+
4. Run `make generate` (deepcopy) then `make manifests` (CRD YAML).
125+
5. Update controller logic to read/use the new field.
126+
6. Update bindata templates if the field affects rendered YAML; run `make update-bindata`.
127+
7. Add unit tests covering validation (valid values, invalid values, immutability).
128+
8. Run `make verify` to confirm lint/fmt pass.
129+
130+
### Updating Dependencies
131+
132+
```bash
133+
go get <package>@<version>
134+
make vendor # runs go mod tidy + go mod vendor
135+
make verify # ensure lint and vet still pass
136+
```
137+
138+
## Build & Release
139+
140+
### Local Build
141+
142+
```bash
143+
make docker-build IMG=<registry>/ztwim:latest
144+
```
145+
146+
The `Dockerfile` uses a two-stage build: `registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.21` for compilation and `ubi9-minimal:9.4` as the runtime base.
147+
148+
### CI Build
149+
150+
`.ci-operator.yaml` defines the build root:
151+
152+
```yaml
153+
build_root_image:
154+
name: builder
155+
namespace: ocp
156+
tag: rhel-9-golang-1.25-openshift-4.21
157+
```
158+
159+
CI runs `make test` (unit) and `make test-e2e` (E2E on a live cluster).
160+
161+
### Release Process
162+
163+
Release images are managed in the separate `zero-trust-workload-identity-manager-release` repository. The OLM bundle is generated via:
164+
165+
```bash
166+
make bundle VERSION=<version>
167+
make bundle-build BUNDLE_IMG=<registry>/ztwim-bundle:v<version>
168+
```
169+
170+
## Common Mistakes
171+
172+
1. **DO NOT** forget to set `OPERATOR_NAMESPACE` env var — the operator will exit(1) immediately.
173+
2. **DO NOT** add bindata YAML without running `make update-bindata` — the embedded assets won't include your changes.
174+
3. **DO NOT** run `make test` without `OPERATOR_NAMESPACE=zero-trust-workload-identity-manager` — tests rely on it for namespace resolution.
175+
4. **DO NOT** skip `make manifests generate` after changing `api/v1alpha1/` types — CRDs and DeepCopy will be stale.
176+
5. **DO NOT** use `controllerutil.SetControllerReference` without registering the owner type in the scheme — it will fail silently.
177+
6. **DO NOT** return `nil` from a reconciler when a sub-function errors — always propagate errors so controller-runtime can requeue.
178+
7. **DO NOT** create resources without setting owner references — orphaned resources won't be garbage-collected.
179+
8. **DO NOT** bypass the `FakeCustomCtrlClient` interface in tests — use counterfeiter fakes for consistent stubbing.
180+
181+
## Logging Conventions
182+
183+
| Scenario | Pattern |
184+
|---|---|
185+
| Error with requeue | `r.log.Error(err, "failed to <action>")` then `return (Result{}, err)` |
186+
| Error without requeue (validation) | `r.log.Error(err, "descriptive message", "key", value)` then set condition |
187+
| Informational not-found | `r.log.Info("resource not found, ignoring")` — never log at Error level |
188+
| Debug-level detail | `r.log.V(1).Info("skipping update", "reason", "...")` |
189+
| Status update failure in defer | `r.log.Error(err, "failed to update status")` — log only, do not propagate |
190+
191+
Never use `fmt.Printf` or `klog` directly.
192+
193+
## Event Recording
194+
195+
Events are reserved for **user-actionable warnings** — not routine reconciliation. Rules:
196+
- Use `corev1.EventTypeWarning` for problems the user should address.
197+
- Do not emit events for transient API errors or routine reconciliation.
198+
- Event reason should be PascalCase (e.g., `TTLConfigurationWarning`).
199+
200+
## Environment Variables
201+
202+
| Variable | Purpose | Default |
203+
|----------|---------|---------|
204+
| `OPERATOR_NAMESPACE` | Namespace for operator resources | (required) |
205+
| `OPERATOR_CONDITION_NAME` | OLM OperatorCondition name for Upgradeable sync | (required) |
206+
| `CREATE_ONLY_MODE` | Skip updates, only create resources | `""` (disabled) |
207+
| `RELATED_IMAGE_SPIRE_SERVER` | Spire Server image override | Set by CSV |
208+
| `RELATED_IMAGE_SPIRE_AGENT` | Spire Agent image override | Set by CSV |
209+
| `RELATED_IMAGE_SPIFFE_CSI_DRIVER` | SPIFFE CSI Driver image override | Set by CSV |
210+
| `RELATED_IMAGE_SPIRE_OIDC_DISCOVERY_PROVIDER` | OIDC Discovery Provider image override | Set by CSV |
211+
| `RELATED_IMAGE_SPIRE_CONTROLLER_MANAGER` | Controller Manager image override | Set by CSV |
212+
| `RELATED_IMAGE_NODE_DRIVER_REGISTRAR` | Node Driver Registrar image override | Set by CSV |
213+
| `RELATED_IMAGE_SPIFFE_CSI_INIT_CONTAINER` | CSI init container image override | Set by CSV |
214+
| `HTTP_PROXY` | HTTP proxy for operand pods | `""` |
215+
| `HTTPS_PROXY` | HTTPS proxy for operand pods | `""` |
216+
| `TRUSTED_CA_BUNDLE_CONFIGMAP` | CA bundle ConfigMap (required when proxy set) | `""` |
217+
```
218+
219+
---

0 commit comments

Comments
 (0)