From b20a8216b6378914d5def09e1be5f5882d52de65 Mon Sep 17 00:00:00 2001 From: Arun Maurya Date: Fri, 3 Jul 2026 16:15:39 +0530 Subject: [PATCH 1/2] Add agent-oriented documentation for contributors and AI tooling. Introduce AGENTS.md, ARCHITECTURE.md, and CONTRIBUTING.md as the component entry point, and ignore local QE output/coverage artifacts. Co-authored-by: Cursor --- .gitignore | 4 + AGENTS.md | 212 ++++++++++++++++++++++ ARCHITECTURE.md | 455 ++++++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 233 +++++++++++++++++++++++++ 4 files changed, 904 insertions(+) create mode 100644 AGENTS.md create mode 100644 ARCHITECTURE.md create mode 100644 CONTRIBUTING.md diff --git a/.gitignore b/.gitignore index d6495a05c..3732fa0e2 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ bin/ # Test binaries testbin/ + +# Local QE / coverage artifacts +output/ +e2e-cover-data/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..97ee30f54 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,212 @@ +# Cert Manager Operator — Agentic documentation + +**Component**: Cert Manager Operator (OpenShift) +**Repository**: [openshift/cert-manager-operator](https://github.com/openshift/cert-manager-operator) +**Documentation tier**: 2 (component-specific) + +> **Agent instruction**: When working in this repository, read **`README.md`** (install, upgrade, local run), **`docs/`** (proxy, metrics, cloud credentials), and the sections below. For **generic OpenShift operator patterns**, testing guidance, or security practices, use the **[Tier 1 hub](https://github.com/openshift/enhancements/tree/master/ai-docs)** under [openshift/enhancements](https://github.com/openshift/enhancements). + +> **Generic platform patterns**: [openshift/enhancements `ai-docs/`](https://github.com/openshift/enhancements/tree/master/ai-docs) + +--- + +## Why this file? + +`README.md` stays focused on **human** quick starts. **`AGENTS.md`** holds **agent-oriented** detail: Make targets, test tags, controller map, and PR hygiene—so tools and contributors have one predictable entry point. + +--- + +## What is cert-manager-operator? + +An **OpenShift operator** that installs and reconciles **upstream [cert-manager](https://github.com/cert-manager/cert-manager)** (controller, webhook, cainjector) and **optional** operands (Istio CSR, trust-manager), using OpenShift `operator.openshift.io` APIs and **`library-go`** patterns. This repo **does not** implement ACME or certificate issuance logic—that behavior is **upstream cert-manager**. + +--- + +## Core components + +- **Operator process**: `library-go` `controllercmd` entry → `pkg/operator/starter.go` wires informers, static/sync controllers, and **ClusterOperator-style** status via `pkg/operator/operatorclient/`. +- **Cert-manager operand**: Deployed into **`cert-manager`**; manifests and CRDs live under **`bindata/`** (regenerated from Makefile / `hack/`). +- **Addon controllers** (feature-gated): **Istio CSR** (`pkg/controller/istiocsr/`), **trust-manager** (`pkg/controller/trustmanager/`). +- **OLM / install artifacts**: `config/`, `bundle/`, `deploy/` — Kustomize and bundle generation (`make bundle`, `make deploy`). + +--- + +## Documentation structure + +This repository does **not** use a separate `ai-docs/` tree; context is distributed as follows: + +```text +README.md # Human quick start, install, upgrade cert-manager version +docs/ +├── operand_metrics.md # Metrics and monitoring for the operand +├── proxy.md # Proxy-related behavior +└── cloud_credentials.md # Ambient credentials / cloud secret wiring +api/operator/v1alpha1/ # CertManager, IstioCSR, TrustManager API + feature gates +pkg/ +├── cmd/operator/ # CLI entry (start, flags) +├── operator/ # Starter, setup_manager, generated clients, OperatorClient +└── controller/ + ├── certmanager/ # Core operand: deployments, network policy, credentials, overrides + ├── istiocsr/ # Istio CSR addon + ├── trustmanager/ # trust-manager addon + └── common/ # Shared constants (namespaces, trusted CA bundle names) +bindata/ # Generated CRDs and deployment YAML (do not hand-edit long-term) +hack/ # update-manifests, test-apis, CI helpers +test/ +├── apis/ # API / envtest suites +├── e2e/ # Ginkgo e2e (build tag: e2e); testdata/, optional plans/ +└── library/ # Shared test helpers +``` + +--- + +## Tier 1 links (ecosystem) + +| Topic | Location | +|-------|----------| +| Operator practices | [ai-docs/practices/operator-patterns.md](https://github.com/openshift/enhancements/blob/master/ai-docs/practices/operator-patterns.md) | +| Testing practices | [ai-docs/practices/testing.md](https://github.com/openshift/enhancements/blob/master/ai-docs/practices/testing.md) | +| Security practices | [ai-docs/practices/security.md](https://github.com/openshift/enhancements/blob/master/ai-docs/practices/security.md) | + +--- + +## Quick navigation + +| Topic | Location | Description | +|-------|----------|-------------| +| **CertManager API** | `api/operator/v1alpha1/certmanager_types.go` | Singleton `cluster`; deployment overrides, network policies, `OperatorSpec` inline | +| **IstioCSR API** | `api/operator/v1alpha1/istiocsr_types.go` | Addon CR for Istio + cert-manager | +| **TrustManager API** | `api/operator/v1alpha1/trustmanager_types.go` | Addon CR for trust-manager | +| **Feature gates** | `api/operator/v1alpha1/features.go` | `IstioCSR`, `TrustManager` + enhancement links | +| **Operator startup** | `pkg/operator/starter.go`, `pkg/operator/setup_manager.go` | Controller registration, informers | +| **Status / spec apply** | `pkg/operator/operatorclient/` | `TargetNamespace` = `cert-manager`; singleton `cluster` | +| **Core reconciliation** | `pkg/controller/certmanager/` | Controller, webhook, cainjector, network policy, credentials | +| **Operand versions** | `Makefile` | `CERT_MANAGER_VERSION`, `ISTIO_CSR_VERSION`, `TRUST_MANAGER_VERSION` | +| **E2E harness** | `test/e2e/suite_test.go` | Namespaces, clients, build tag `e2e` | +| **QE / plan notes** | `test/e2e/plans/` | Optional structured test plans (when present) | + +--- + +## Management state (`CertManager` / OpenShift `OperatorSpec`) + +`CertManager` embeds **`github.com/openshift/api/operator/v1`.OperatorSpec** (`managementState`, `unsupportedConfigOverrides`, etc.). Typical values: + +| State | Behavior (high level) | +|-------|------------------------| +| **Managed** | Operator owns install and upgrades of the operand for this component. | +| **Unmanaged** | Operator does not reconcile the operand; user owns lifecycle. | +| **Removed** | Operator tears down managed resources (see OpenShift docs for semantics). | + +Exact semantics follow OpenShift **operator API** conventions—when in doubt, cross-check [operator API](https://github.com/openshift/api) and Tier 1 operator docs above. + +--- + +## Key controller packages + +| Package / area | Purpose | +|----------------|---------| +| `pkg/controller/certmanager` | Main operand: **controller**, **webhook**, **cainjector** deployments, related images, network policies, **CredentialsRequest** integration, unsupported overrides validation | +| `pkg/controller/istiocsr` | **Istio CSR** deployment, RBAC, services, certificates (feature-gated) | +| `pkg/controller/trustmanager` | **trust-manager** install, webhooks, bundles (feature-gated) | +| `pkg/controller/common` | Shared **labels**, **operator namespace**, **trusted CA bundle** ConfigMap name/key | +| `pkg/operator/starter.go` | Composes **kube** / **operator** informers, **config** informers, registers workload loops | + +--- + +## Feature gates (addons) + +| Feature | API / controller | Notes | +|---------|------------------|--------| +| **IstioCSR** | `IstioCSR` CR, `pkg/controller/istiocsr` | Defaults and release level in `api/operator/v1alpha1/features.go` | +| **TrustManager** | `TrustManager` CR, `pkg/controller/trustmanager` | Defaults and release level in `features.go` | + +Always read **`features.go`** for current defaults and links to **OpenShift enhancements**. + +--- + +## Knowledge graph + +```text +CertManager (CR, name: cluster) + ├─> pkg/operator/operatorclient (spec/status, TargetNamespace = cert-manager) + ├─> pkg/controller/certmanager + │ ├─> cert-manager-controller / webhook / cainjector deployments + │ ├─> bindata manifests + RELATED_IMAGE_* / operand versions (Makefile) + │ ├─> optional default network policies + egress overrides + │ └─> cloud / platform integration (e.g. CredentialsRequest, trusted CA) + └─> ClusterOperator-style conditions (via library-go + operator API) + +IstioCSR (CR) ──> pkg/controller/istiocsr (feature gate) +TrustManager (CR) ──> pkg/controller/trustmanager (feature gate) + +Upstream cert-manager + └─> Certificate / Issuer / ACME behavior (not implemented in this repo) +``` + +--- + +## Ecosystem references + +- **Upstream cert-manager**: [cert-manager/cert-manager](https://github.com/cert-manager/cert-manager) — CRDs, controllers, issuance behavior. +- **OpenShift enhancements** (Istio CSR / trust-manager designs): linked from `api/operator/v1alpha1/features.go`. +- **OpenShift release / CI**: jobs often live in **[openshift/release](https://github.com/openshift/release)** (Prow); this repo may not ship `.github/workflows`. + +--- + +## Dev environment tips + +- **Work from the repository root** (directory containing `Makefile` and `go.mod`). `PROJECT_ROOT` is derived from `git rev-parse --show-toplevel` or `pwd`. +- **Go version**: Match **`go.mod`** (`go` directive). +- **Shell / Make**: `Makefile` uses `bash` with `-euo pipefail`; **`make help`** lists targets. +- **Cluster**: **`oc`** expected for `make deploy`, e2e waits, and debugging; see **`README.md`** for `make local-run` (scale in-cluster operator to 0 first). +- **After API edits**: **`make update`** or at least **`make manifests generate`** so CRDs and `pkg/operator` generated code stay consistent. +- **Do not hand-edit** **`vendor/`** or long-term **`bindata/`**; use **`make update`** / **`make update-vendor`** per `Makefile` and `hack/`. +- **Caches**: `XDG_CACHE_HOME` / `XDG_CONFIG_HOME` default under **`_output/`** when unset (CI-friendly). + +--- + +## Testing instructions + +- **CI**: Prefer matching **Prow** / **openshift/release** jobs locally with **`make verify`**, **`make lint`**, **`make test`** before merge. +- **Pre-merge loop**: + + ```sh + make verify + make lint + make test + ``` + +- **`make test`**: `manifests`, `generate`, `vet`, **`test-apis`** (`hack/test-apis.sh`, envtest + Ginkgo), **`test-unit`**. +- **`make test-unit`**: Excludes `test/e2e`, `test/apis`, `test/utils` (see `Makefile`). +- **E2E** (`test/e2e/`, tag **`e2e`**): cluster must already run the operator and stable operands; **`make test-e2e`** (uses **`make test-e2e-wait-for-stable-state`**). Tech Preview CI uses **`make test-e2e-tech-preview`** (`E2E_GINKGO_LABEL_FILTER_TECH_PREVIEW` in the Makefile). Narrow with **`TEST=...`** (`go test -run` regex) and **`E2E_GINKGO_LABEL_FILTER`** (`-ginkgo.label-filter`). Use the Make target—plain **`go test ./...`** does not cover e2e. +- **After refactors**: **`make verify`** and **`make lint`** (`.golangci.yaml`). +- **Add or update tests** for code you change (unit, `test/apis`, or e2e as appropriate). + +--- + +## PR instructions + +- **Title**: Clear, descriptive; follow repo / org template (**Jira** `OCPBUGS-…` or team key if required). +- **Before PR**: + + ```sh + make verify + make lint + make test + ``` + +- **API / bindata / manifests**: Commit outputs from **`make update`** (or minimal `make manifests generate`) so verify passes. +- **Scope**: Small diffs; follow existing **`library-go`** patterns. +- **User-visible behavior**: Update **`README.md`** or **`docs/`** when needed. + +--- + +## Quick triage + +| Symptom | Likely area | +|--------|-------------| +| Certificate / issuer not Ready | Operand + cluster config; **`Makefile`** / **`bindata`** cert-manager version | +| Operator Degraded / Progressing | `pkg/operator/`, `pkg/controller/certmanager/`, `pkg/operator/operatorclient/` | +| Istio / mesh | `pkg/controller/istiocsr/` + **`features.go`** | +| Trust bundles | `pkg/controller/trustmanager/` + **`features.go`** | +| Codegen / bindata failures | **`make update`**, **`hack/`** | diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..78527a3c9 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,455 @@ +# Architecture + +This document describes the high-level architecture of the Cert Manager Operator for Red Hat OpenShift. +If you want to familiarize yourself with the codebase, you are in the right place! + +For detailed guidelines on specific areas, see the files listed in `AGENTS.md`. +For install, upgrade, and local development, see `README.md`. + +## Bird's Eye View + +On the highest level, this is a Kubernetes operator that installs and manages the upstream [cert-manager](https://github.com/cert-manager/cert-manager) application on OpenShift clusters. The upstream project provides the actual certificate-issuance logic (Certificate, Issuer, ACME, etc.). This operator does **not** embed or fork that code. Instead, it manages upstream resources as static YAML manifests compiled into the binary (`bindata/`), decoded at runtime, mutated with operator-controlled configuration, and applied to the cluster. + +Optional **addon operands** — **Istio CSR** and **trust-manager** — follow the same bindata pattern but are reconciled by separate controller-runtime reconcilers behind a unified manager. + +```text + ┌─────────────────────────────────────────────┐ + │ CertManager CR (cluster) │ + │ singleton "cluster", user-facing API │ + │ embeds OpenShift OperatorSpec │ + └──────────────────┬──────────────────────────┘ + │ + ┌───────────────────────────┼───────────────────────────┐ + │ │ │ +┌────────▼─────────┐ ┌───────────▼──────────┐ ┌──────────▼──────────┐ +│ library-go │ │ DefaultCertManager │ │ controller-runtime │ +│ controllers (8) │ │ Controller │ │ manager (optional) │ +│ │ │ │ │ │ +│ staticresource + │ │ Auto-creates │ │ IstioCSR reconciler │ +│ deployment │ │ CertManager/cluster │ │ (feature-gated) │ +│ controllers for │ │ with ManagementState │ │ │ +│ controller, │ │ = Managed if missing │ │ TrustManager │ +│ webhook, │ │ │ │ reconciler │ +│ cainjector, │ │ │ │ (feature-gated) │ +│ network policies │ │ │ │ │ +└────────┬─────────┘ └──────────────────────┘ └──────────┬──────────┘ + │ │ + ▼ ▼ +┌────────────────────┐ ┌─────────────────────────┐ +│ Upstream operand │ │ Addon operands │ +│ (controller, │ │ IstioCSR (per-namespace │ +│ webhook, │ │ "default" CR) │ +│ cainjector in │ │ trust-manager │ +│ "cert-manager" │ │ (cluster "cluster" CR) │ +│ namespace) │ │ │ +└────────────────────┘ └─────────────────────────┘ +``` + +Three operator CRs drive the system: + +- **CertManager** (name: `cluster`, cluster-scoped): the primary user-facing CR. Controls operand installation, deployment overrides (args, env, resources, scheduling), default and user-defined network policies, and OpenShift `OperatorSpec` fields (`managementState`, `logLevel`, `unsupportedConfigOverrides`). +- **IstioCSR** (name: `default`, namespaced): addon CR for the Istio CSR agent. One instance per namespace; enables Istio workloads to obtain certificates via cert-manager. +- **TrustManager** (name: `cluster`, cluster-scoped): addon CR for trust-manager. Manages trust bundles and CA package distribution. + +The operator process runs in the **`cert-manager-operator`** namespace. Core and addon operands deploy into **`cert-manager`** (and trust-manager may reference a user-specified trust namespace). + +**Important:** this operator uses **two reconciliation frameworks** in one process — `openshift/library-go` factory controllers for the core cert-manager operand, and `controller-runtime` for feature-gated addons. Do not assume every reconciler follows the same pattern. + +## Code Map + +This section describes important directories and data structures. +Pay attention to the **Architecture Invariant** sections. + +### `api/operator/v1alpha1/` + +CRD type definitions. This is where `CertManager`, `IstioCSR`, and `TrustManager` structs live, along with shared types (`DeploymentConfig`, `ConditionalStatus`, `Mode`), condition constants, feature gate declarations, and deepcopy code. + +Validation is primarily **CEL-based** via `+kubebuilder:validation:XValidation` markers on the types. Runtime validation also occurs in deployment hooks (core operand) and controller-local `validate*Config()` functions (addons). There are no admission webhooks for the operator's own CRDs. + +`api/operator/v1alpha1/tests/` contains declarative YAML test suites (`.testsuite.yaml` files) exercised by the API integration test generator. + +**Architecture Invariant:** `TrustManager` and `IstioCSR` singleton names are CEL-enforced (`cluster` and `default` respectively). `CertManager` is cluster-scoped and conventionally named `cluster`; the `DefaultCertManagerController` auto-creates it if missing. + +**Architecture Invariant:** several fields are immutable once set, enforced by CEL — for example `defaultNetworkPolicy` cannot revert from `"true"` to `"false"`, `IstioCSR.spec.issuerRef` is immutable, and `TrustManager.spec.trustNamespace` is immutable once set. This prevents configuration drift that would leave the cluster in an inconsistent state. + +### `bindata/` + +Static YAML manifests for operands. These are the Deployments, Services, RBAC, NetworkPolicies, Certificates, and WebhookConfigurations that the operator creates in the cluster. + +| Subdirectory | Contents | +|--------------|----------| +| `cert-manager-deployment/` | Core cert-manager controller, webhook, and cainjector manifests | +| `istio-csr/` | Istio CSR operand manifests | +| `trust-manager/resources/` | trust-manager operand manifests | +| `networkpolicies/` | Default deny-all and allow policies for cert-manager and Istio CSR | + +These files are compiled into Go via go-bindata and live as `pkg/operator/assets/bindata.go` at build time. Sources are refreshed from upstream releases by `hack/update-*-manifests.sh`. + +**Architecture Invariant:** bindata manifests are templates, not final resources. Controllers decode them, mutate them (namespace, labels, annotations, images, env vars, scheduling, TLS profile), and then create or update them. Never deploy bindata YAML directly to a cluster. + +**Architecture Invariant:** do not hand-edit `pkg/operator/assets/bindata.go`. Run `make update` (or `make update-bindata`) to regenerate it from the YAML sources. + +### `main.go` and `pkg/cmd/operator/` + +The operator binary entrypoint. Uses `openshift/library-go` `controllercmd` — not a standalone controller-runtime `main`: + +```text +main.go → pkg/cmd/operator/cmd.go → controllercmd.NewControllerCommandConfig(...).NewCommandWithContext() + → pkg/operator/starter.go (RunOperator) +``` + +Runtime flags: + +- `--trusted-ca-configmap` — trusted CA bundle for operand containers +- `--cloud-credentials-secret` — ambient cloud credentials secret name +- `--unsupported-addon-features` — e.g. `IstioCSR=true`, `TrustManager=true` + +### `pkg/operator/` + +Manager setup, informer wiring, and controller registration. + +| File / package | Role | +|----------------|------| +| `starter.go` | Process entry: clients, informers, library-go controller goroutines, feature gate setup, optional controller-runtime manager | +| `setup_manager.go` | Unified controller-runtime manager for addon controllers; label-filtered cache | +| `operatorclient/` | `OperatorClient` implementing `v1helpers.OperatorClient` for `CertManager/cluster` | +| `assets/bindata.go` | Generated embed of `bindata/` | +| `clientset/`, `informers/`, `applyconfigurations/` | Codegen from `hack/update-clientgen.sh` | + +**`OperatorClient` responsibilities:** + +- `GetOperatorState()` — reads singleton `cluster` from informer lister +- `ApplyOperatorStatus()` — status updates with OpenShift condition canonicalization +- `EnsureFinalizer` / `RemoveFinalizer` +- `GetUnsupportedConfigOverrides()` — parses break-glass JSON from `OperatorSpec` + +Constants in `operatorclient/interfaces.go`: + +- `TargetNamespace` = `cert-manager` +- `OperatorNamespace` = `cert-manager-operator` + +### `pkg/controller/` + +The heart of the operator. Three controller packages plus shared infrastructure. + +#### `pkg/controller/certmanager/` + +The main reconciliation path for the core cert-manager operand. Uses **library-go** `staticresourcecontroller` and `deploymentcontroller` — not a single controller-runtime `Reconcile` loop. + +**`CertManagerControllerSet`** (`cert_manager_controller_set.go`) starts **eight controllers concurrently**: + +```text +1. controller static resources (RBAC, SA, namespace, etc.) +2. controller deployment +3. webhook static resources +4. webhook deployment +5. cainjector static resources +6. cainjector deployment +7. network policy static resources (conditional on defaultNetworkPolicy="true") +8. network policy user-defined (from CertManager.spec.networkPolicies) +``` + +Each static resource controller decodes bindata YAML and applies it via `resourceapply`. Each deployment controller reads a bindata Deployment template and runs a chain of **deployment hooks** (`generic_deployment_controller.go`): + +- Operand image override from `RELATED_IMAGE_*` env vars +- Log level from `OperatorSpec` +- Pod label, container arg, env, replica, resource, and scheduling overrides from `CertManager.spec` +- Proxy env injection (`operator-lib/proxy`) +- Trusted CA ConfigMap mount +- Service-account-bound token configuration +- Cloud credential secret mounting (when Infrastructure CR indicates AWS/GCP) +- Cluster TLS profile from APIServer CR (`tls_profile_hook.go`) + +Override values are validated at apply time by hooks in `deployment_overrides_validation.go` (allowed args, env keys, resource limits, scheduling fields). + +**Watches (library-go pattern):** + +- `operatorClient.Informer()` — `CertManager/cluster` +- Target namespace informers: Deployments, ConfigMaps, Secrets +- Optional: `Infrastructure`, `APIServer` informers (cloud credentials, TLS profile) + +**Drift correction:** Built into library-go `staticresourcecontroller` and `deploymentcontroller` via `resourceapply` / `resourcemerge`. Manual edits to managed ClusterRoles, Deployments, or NetworkPolicies are reverted on the next sync. + +**Note:** `certmanager_controller.go` is a **placeholder** kubebuilder RBAC stub with an empty `Reconcile()`. It exists for RBAC codegen only and is not the real reconciler. + +#### `pkg/controller/istiocsr/` + +Controller-runtime reconciler for the Istio CSR addon (feature-gated). + +**Primary watch:** `IstioCSR` (generation changes) + +**Secondary watches** (enqueue the namespaced `default` IstioCSR): + +- Managed resources (`app=cert-manager-istio-csr`): Deployment, Certificate, RBAC, Service, ServiceAccount, NetworkPolicy +- Watched (not managed): ConfigMaps with `istiocsr.openshift.operator.io/watched-by` label, Secret metadata, Issuer, ClusterIssuer + +**Reconcile order** (`install_istiocsr.go`): + +```text +validate config → network policies → services → service accounts → RBAC +→ certificates → deployments → processed annotation +``` + +**Singleton enforcement:** `disallowMultipleIstioCSRInstances()` — only one `IstioCSR` per namespace is supported. + +#### `pkg/controller/trustmanager/` + +Controller-runtime reconciler for the trust-manager addon (feature-gated, Tech Preview by default). + +**Primary watch:** `TrustManager/cluster` (generation changes) + +**Secondary watches** (all enqueue singleton `cluster`): + +- Managed resources (`app=cert-manager-trust-manager`): Deployment, SA, Service, ConfigMap, RBAC, Certificate, ValidatingWebhookConfiguration +- Special: CNO-injected `cert-manager-operator-trusted-ca-bundle` ConfigMap in the operator namespace (no managed label) + +**Reconcile order** (`install_trustmanager.go`): + +```text +validate config → validate trust namespace exists → default CA package ConfigMap +→ service accounts → RBAC → services → issuer → certificate → deployment +→ validating webhook → status observed state +``` + +#### `pkg/controller/common/` + +Shared utilities used by addon controllers (and some core hooks): + +| File | Purpose | +|------|---------| +| `constants.go` | `ManagedResourceLabelKey=app`, operator namespace, trusted CA ConfigMap name/key | +| `client.go` | `CtrlClient` wrapper over `mgr.GetClient()` with `UpdateWithRetry` | +| `errors.go` | `ReconcileError` with `IrrecoverableError`, `RetryRequiredError`, `MultipleInstanceError` | +| `reconcile_result.go` | `HandleReconcileResult()` — Ready/Degraded condition updates for addon CRs | +| `validation.go` | Kubernetes-native validation helpers for scheduling and metadata | +| `tls_profile_hook.go` | Cluster TLS profile from APIServer CR | +| `container_args.go` | Container arg merge utilities | + +**Architecture Invariant:** addon resource updates go through `UpdateWithRetry`, which wraps `retry.RetryOnConflict` with a read-modify-write pattern. + +### `config/` + +Kustomize manifests for CRDs, RBAC, manager deployment, samples, and OLM bundle inputs. + +| Path | Purpose | +|------|---------| +| `config/crd/bases/` | Generated CRDs (operator APIs + upstream cert-manager CRDs) | +| `config/rbac/` | Operator RBAC (`role.yaml` is generated) | +| `config/manager/` | Operator Deployment; trusted CA ConfigMap with `config.openshift.io/inject-trusted-cabundle` | +| `config/manifests/` | OLM CSV base for bundle generation | +| `config/samples/` | Sample CRs | +| `config/console/` | Console quick starts / YAML samples | + +CRDs in `config/crd/bases/` and RBAC in `config/rbac/role.yaml` are generated — do not hand-edit them. + +### `bundle/` + +OLM bundle artifacts generated by `make bundle`: + +- `bundle/manifests/cert-manager-operator.clusterserviceversion.yaml` — OLM CSV +- Operand CRDs, upstream cert-manager CRDs, trust-manager CRDs, RBAC, metrics Service, trusted CA ConfigMap +- `bundle/metadata/annotations.yaml` + +### `test/` + +A separate Go module (`./test` in `go.work`) containing: + +- `test/apis/` — API integration tests using Ginkgo + envtest. A generator auto-creates Ginkgo specs from the YAML test suites in `api/operator/v1alpha1/tests/`. +- `test/e2e/` — End-to-end tests using Ginkgo against a live cluster (build tag `e2e`). Covers deployment overrides, issuers (ACME, Vault, self-signed), Istio CSR, trust-manager, TLS profile, and observability. +- `test/library/` — Shared test helpers (kube client, cert utilities, dynamic resources, Istio CSR helpers). + +**Run:** `make test-e2e` (waits for stable operand state first). Tech Preview CI uses `make test-e2e-tech-preview`. Narrow with `TEST=...` and `E2E_GINKGO_LABEL_FILTER`. + +### `hack/` + +Shell scripts for codegen, verification, and CI. Notable scripts: + +| Script | Purpose | +|--------|---------| +| `update-cert-manager-manifests.sh` | Download upstream cert-manager release YAML → jsonnet patch → `bindata/` + CRDs | +| `update-istio-csr-manifests.sh` | Istio CSR operand manifests | +| `update-trust-manager-manifests.sh` | trust-manager operand manifests | +| `update-clientgen.sh` | Generate clientset, informers, apply configurations | +| `test-apis.sh` | Run API integration suite | +| `verify-*.sh` | deepcopy, clientgen, bundle, CRDs, deps, types | +| `e2e-coverage.sh` | E2E coverage image setup and collection | +| `local-run-config.yaml` | Config for `make local-run` | + +### `tools/` + +A separate Go module for build-time tool dependencies (controller-gen, golangci-lint, ginkgo, etc.). Tools are vendored and built from source. + +## Cross-Cutting Concerns + +### Dual Runtime Model + +The operator process runs two concurrent reconciliation models: + +| Path | Framework | CRs | Client pattern | +|------|-----------|-----|----------------| +| Core cert-manager | library-go `factory.Controller` | `CertManager/cluster` | Kubernetes client + informer listers | +| Addon operands | controller-runtime `manager` | `IstioCSR/default`, `TrustManager/cluster` | Cached `mgr.GetClient()` via `common.CtrlClient` | + +Both share the same process, logging (`klog` via `ctrl.SetLogger`), and startup context. Addon controllers start only when their feature gates are enabled. + +**Architecture Invariant:** do not add addon reconciliation logic to the library-go controller set, or core operand logic to the controller-runtime manager, without understanding this split. + +### Management State (`Managed` / `Unmanaged` / `Removed`) + +`CertManager` embeds `github.com/openshift/api/operator/v1.OperatorSpec`. Management state is enforced by library-go `staticresourcecontroller` and `deploymentcontroller`, which read `operatorClient.GetOperatorState()` on each sync. + +| State | Behavior (high level) | +|-------|------------------------| +| **Managed** | Operator owns install and upgrades of the core operand. | +| **Unmanaged** | Operator does not reconcile the operand; user owns lifecycle. | +| **Removed** | Operator tears down managed resources. | + +The `DefaultCertManagerController` auto-creates `CertManager/cluster` with `ManagementState: Managed` if the CR is missing. + +IstioCSR and TrustManager CRs do **not** embed `OperatorSpec`; they are feature-gated addons with their own lifecycle and `Ready`/`Degraded` conditions. + +### Feature Gates + +Declared in `api/operator/v1alpha1/features.go` and wired in `pkg/features/`: + +| Feature | Default | Release level | +|---------|---------|---------------| +| `IstioCSR` | `true` | GA | +| `TrustManager` | `false` | Tech Preview | + +Enabled via `--unsupported-addon-features` flag (e.g. `TrustManager=true`). At startup, `setupFeatureGates()` also reads `featuregates/cluster` when available. If neither IstioCSR nor TrustManager is enabled, the controller-runtime manager is not started. + +Always read `features.go` for current defaults and links to OpenShift enhancements. + +### Drift Detection + +| Area | Mechanism | +|------|-----------| +| Core operand | library-go `resourceapply` + `resourcemerge` in static/deployment controllers | +| Addon operands | Label/annotation drift checks, spec drift on Deployments and Certificates, secondary watches on managed resources with label predicates | +| User edits to managed resources | Re-enqueue via `Watches` + generation or label/annotation change predicates | + +**Architecture Invariant:** if someone manually modifies a managed ClusterRole, Deployment, or NetworkPolicy, the operator will detect the change and revert it. This is a critical security property for the core operand path. + +### Error Classification (Addon Controllers) + +Reconciliation errors in IstioCSR and TrustManager flow through `common.ReconcileError` and `HandleReconcileResult()`: + +| Reason | Requeue? | Status | +|--------|----------|--------| +| `IrrecoverableError` | No | Degraded=True, Ready=False | +| `RetryRequiredError` | Yes (30s) | Ready=False | +| `MultipleInstanceError` | No | Event recorded; reconcile skipped | + +`FromClientError()` auto-classifies Kubernetes API errors: `Unauthorized`, `Forbidden`, `Invalid`, and `BadRequest` become irrecoverable; most others become retry-required. + +Core library-go controllers use the event recorder and deployment hook errors directly; status flows through `CertManager.status` (OpenShift `OperatorStatus`: Available, Degraded, Progressing, Upgradeable). + +### Cache Strategy (Addon Manager) + +`setup_manager.go` configures a label-filtered controller-runtime cache for managed resources per addon controller. **ConfigMaps are intentionally excluded** from the label filter because: + +1. TrustManager watches both managed ConfigMaps and the CNO-injected `cert-manager-operator-trusted-ca-bundle` ConfigMap (no managed label). +2. IstioCSR watches managed ConfigMaps and user-created ConfigMaps with `istiocsr.openshift.operator.io/watched-by` (a different label key). + +ConfigMaps therefore use the default unfiltered informer; each controller applies predicate-level filtering. + +**Issuer** and **ClusterIssuer** are also unfiltered — IstioCSR reconciles user-created Issuers referenced from the spec. + +### TLS, Proxy, and Cloud Integration + +- **TLS profile:** Cluster minimum TLS version and cipher suites from the APIServer CR are applied to operand deployments via `tls_profile_hook.go`. +- **Proxy:** HTTP/HTTPS/NO_PROXY env vars injected via `operator-lib/proxy` based on cluster proxy configuration. +- **Cloud credentials:** On AWS/GCP platforms, the operator mounts a cloud credential secret (from `--cloud-credentials-secret`) into the cert-manager controller for ambient issuer authentication. +- **Trusted CA:** Operand containers mount the trusted CA bundle from the ConfigMap named by `--trusted-ca-configmap` (injected by OLM/CNO in production). + +### Code Generation + +Several artifacts are generated and must be committed: + +- `zz_generated.deepcopy.go` — from `make generate` +- `config/crd/bases/*.yaml` — from `make manifests` +- `pkg/operator/assets/bindata.go` — from `make update-bindata` +- `config/rbac/role.yaml` — from `make manifests` +- `pkg/operator/clientset/`, `informers/`, `applyconfigurations/` — from `hack/update-clientgen.sh` + +`make update` runs the full pipeline (`generate`, `update-manifests`, `update-bindata`). `make verify` checks that generated files are fresh. CI will reject PRs with stale generated files. + +**Architecture Invariant:** never edit generated files by hand. Always use `make update`. + +### Network Policy Architecture + +When `CertManager.spec.defaultNetworkPolicy` is `"true"`, the operator deploys a **deny-all** base NetworkPolicy, then layers specific allow-policies for API server egress, webhook ingress, metrics ingress, and DNS egress. User-defined policies from `CertManager.spec.networkPolicies` are reconciled separately (egress-focused). Istio CSR deploys its own network policies from `bindata/networkpolicies/`. + +**Architecture Invariant:** `defaultNetworkPolicy` cannot be changed from `"true"` back to `"false"` once enabled (CEL-enforced). + +### Operand Versions and Images + +Upstream operand versions are controlled by Makefile variables and refreshed via `hack/update-*-manifests.sh`: + +``` +CERT_MANAGER_VERSION ?= v1.20.2 +ISTIO_CSR_VERSION ?= v0.16.0 +TRUST_MANAGER_VERSION ?= v0.20.3 +``` + +At runtime, operand container images are pinned by OLM through `RELATED_IMAGE_*` environment variables: + +- `RELATED_IMAGE_CERT_MANAGER_CONTROLLER` +- `RELATED_IMAGE_CERT_MANAGER_WEBHOOK` +- `RELATED_IMAGE_CERT_MANAGER_CA_INJECTOR` +- `RELATED_IMAGE_CERT_MANAGER_ACMESOLVER` +- `RELATED_IMAGE_CERT_MANAGER_ISTIOCSR` +- `RELATED_IMAGE_CERT_MANAGER_TRUST_MANAGER` + +The operator has no compile-time dependency on upstream cert-manager Go code. + +### Go Workspace + +The repo uses `go.work` with three modules: `.`, `./test`, `./tools`. This means: + +- `GOFLAGS` is cleared for test and fmt targets to avoid `-mod=vendor` conflicting with `go.work`. +- Vendoring is workspace-level (`go work vendor`). +- All build-time tools are vendored and built from source. + +### Relationship to Upstream + +This operator manages — but does not contain — the upstream [cert-manager](https://github.com/cert-manager/cert-manager) project. The upstream project defines the CRDs that end users interact with (`Certificate`, `Issuer`, `ClusterIssuer`, `CertificateRequest`, etc.) and the controllers that perform issuance. This operator's job is to deploy, configure, and lifecycle-manage those upstream components on OpenShift, providing an opinionated, security-hardened, and OLM-integrated installation. + +Similarly, [Istio CSR](https://github.com/cert-manager/istio-csr) and [trust-manager](https://github.com/cert-manager/trust-manager) are separate upstream projects whose manifests are vendored into `bindata/` and managed by addon controllers. + +## Startup Sequence + +```text +1. controllercmd starts → RunOperator(ctx) +2. Build kube, operator, and apiextensions clients +3. Start informers (operator CRs, kube namespaces "", kube-system, cert-manager) +4. Optionally start Infrastructure informer (cloud credentials) +5. Start 8 library-go cert-manager controllers + DefaultCertManagerController (concurrent goroutines) +6. setupFeatureGates() — parse --unsupported-addon-features, read featuregates/cluster +7. If IstioCSR and/or TrustManager enabled → NewControllerManager() → manager.Start() in goroutine +8. Block on <-ctx.Done() +``` + +## Differences from a Typical Controller-Runtime Operator + +| Typical controller-runtime | This operator | +|-----------------------------|---------------| +| Single `Manager` + one reconciler per CR | Split: library-go factory controllers + separate ctrl manager for addons | +| Reconcile loop reads CR, applies manifests | Core: bindata + static/deployment controllers with hooks; addons: imperative per-resource reconcilers | +| Uniform client pattern | Informers/listers (core) vs cached ctrl client (addons) | +| Feature gates in manager setup | library-go startup + optional second manager | +| OpenShift `OperatorSpec` / management state | Yes — core path follows OpenShift operator conventions | +| Operand versions in code | Makefile + `hack/update-*-manifests.sh` from upstream releases | + +## Quick Triage + +| Symptom | Likely area | +|---------|-------------| +| Certificate / issuer not Ready | Operand + cluster config; `Makefile` / `bindata` cert-manager version | +| Operator Degraded / Progressing | `pkg/operator/`, `pkg/controller/certmanager/`, `pkg/operator/operatorclient/` | +| Istio / mesh | `pkg/controller/istiocsr/` + `features.go` | +| Trust bundles | `pkg/controller/trustmanager/` + `features.go` | +| Codegen / bindata failures | `make update`, `hack/` | +| Override rejected | `deployment_overrides_validation.go`, `CertManager.spec` fields | +| Addon not starting | `--unsupported-addon-features`, feature gate defaults in `features.go` | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..a7b8c7c36 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,233 @@ +# Contributing to Cert Manager Operator for Red Hat OpenShift + +Thank you for your interest in contributing! This guide covers the PR process, review expectations, coding conventions, and testing requirements. + +## Getting Started + +1. Fork the repository and clone your fork. +2. Create a feature branch from `master`. +3. Set up your environment: + - Go version matching `go.mod` (currently Go 1.26) + - `oc` CLI with access to an OpenShift cluster (required for `make deploy`, E2E tests, and local-run workflows) + - `kubectl` is useful for debugging but most Make targets and docs assume `oc` + +## Development Workflow + +### Build and Verify + +```sh +make build # Full build: codegen + fmt + vet + compile +make build-operator # Fast rebuild (binary only, no codegen) +``` + +After **any** code change, regenerate and verify before committing: + +```sh +make update && make verify +``` + +This regenerates deepcopy/clientgen code, upstream operand manifests, and bindata, then runs verification checks (bindata freshness, deepcopy, clientgen, bundle, deps, fmt, vet). CI will reject PRs with stale generated files. + +To regenerate CRDs and RBAC after API changes, `make test` also runs `manifests` and `generate`; commit those outputs along with `make update` results. + +### Linting + +```sh +make lint # Run golangci-lint with all configured linters +make lint-fix # Auto-fix linting issues +``` + +### Testing + +Run the full local test suite (no cluster required): + +```sh +make test # manifests + generate + vet + API + unit tests +``` + +Individual test targets: + +| Target | Description | +|--------|-------------| +| `make test-unit` | Unit tests (excludes `test/e2e`, `test/apis`, `test/library`) | +| `make test-apis` | API validation tests via envtest + Ginkgo | +| `make test-e2e` | End-to-end tests against a live OpenShift cluster | + +For E2E tests, the operator and stable operands must already be running on the cluster. `make test-e2e` waits for cert-manager deployments to become Available before running tests. + +Narrow E2E execution: + +```sh +# Ginkgo label filter (default excludes Service Mesh and non-Mint credential modes) +make test-e2e E2E_GINKGO_LABEL_FILTER="Platform:Generic" + +# Go test -run regex +make test-e2e TEST="Overrides" +``` + +Common Ginkgo labels in this repo: `Platform:Generic`, `Platform:AWS`, `Platform:GCP`, `Platform:Azure`, `Feature:IstioCSR`, `CredentialsMode:Mint`, `CredentialsMode:Manual`. + +See `README.md` for cluster setup (`make deploy`, `make local-run`) and `AGENTS.md` for the full E2E harness description. + +### Pre-Submission Checklist + +Before opening a PR, always run: + +```sh +make verify +make lint +make test +``` + +If you changed APIs, bindata sources, or codegen inputs, also run `make update` and commit the generated outputs before the steps above. + +## Coding Conventions + +### Import Order + +Imports must follow the order enforced by `gci` in [`.golangci.yaml`](.golangci.yaml): + +1. Standard library +2. Third-party packages +3. `github.com/openshift/cert-manager-operator` (project-local) +4. Blank imports, dot imports, aliases, local module + +### File Headers + +All `.go` files must include the Apache 2.0 license header from [`hack/boilerplate.go.txt`](hack/boilerplate.go.txt). + +### Naming Conventions + +- **Go packages**: Controller packages use lowercase names (`certmanager`, `istiocsr`, `trustmanager`, `common`). +- **Controller names**: kebab-case in log/event strings (e.g. `cert-manager-istio-csr-controller`, `cert-manager-controller-deployment`). +- **Finalizers** (addon controllers): `./` — e.g. `istiocsr.openshift.operator.io/cert-manager-istio-csr-controller`. +- **Managed resource label**: `app=` via `common.ManagedResourceLabelKey`. +- **Bindata YAML**: Upstream operand manifests live under `bindata/cert-manager-deployment/`, `bindata/istio-csr/`, `bindata/trust-manager/resources/`, and `bindata/networkpolicies/`. trust-manager resources follow `_.yml`; core cert-manager files use descriptive kebab-case names (e.g. `cert-manager-deployment.yaml`). + +### Linter Rules + +The repo uses golangci-lint v2 (see [`.golangci.yaml`](.golangci.yaml)). Key rules to be aware of: + +- **golines** enforces a max line length of 200 characters. +- **gofmt** rewrites `interface{}` to `any`. +- **wrapcheck** is configured with project-specific ignore patterns for Kubernetes client calls and controller-runtime builders. +- **depguard** is currently disabled due to golangci-lint v2 configuration issues — still avoid introducing `github.com/pkg/errors` or `github.com/sirupsen/logrus`; use standard `errors`/`fmt` and `klog`/`logr` instead. + +### Error Handling + +**Addon controllers** (Istio CSR, trust-manager): + +- Wrap API call errors with `common.FromClientError`. +- Use `common.NewIrrecoverableError` for configuration validation failures. +- Route results through `common.HandleReconcileResult` to set `Ready`/`Degraded` conditions. +- Never return both `RequeueAfter` and a non-nil error from `Reconcile`. + +**Core cert-manager controllers** (library-go): + +- Use deployment hook validators in `deployment_overrides_validation.go` for user-facing override errors. +- Management state and operand sync are handled by `staticresourcecontroller` and `deploymentcontroller` from `openshift/library-go` — follow existing hook patterns rather than adding a new controller-runtime reconciler for the core operand. + +### Generated Files + +Never edit these files by hand — always use `make update` (and `make manifests` when CRD/RBAC markers change): + +- `api/**/zz_generated.deepcopy.go` +- `config/crd/bases/*.yaml` +- `config/rbac/role.yaml` +- `pkg/operator/assets/bindata.go` +- `pkg/operator/clientset/`, `informers/`, `applyconfigurations/` +- `bundle/` (regenerate with `make bundle` when OLM metadata changes) + +## Testing Requirements + +### Unit Tests + +- Add unit tests for new reconciliation logic using table-driven tests. +- Addon controller tests use counterfeiter-generated fakes from `pkg/controller/common/fakes/`. +- Regenerate fakes after changing the `CtrlClient` interface: + + ```sh + make generate-fakes + # or: go generate ./pkg/controller/common/... + ``` + +- Core cert-manager deployment override and validation logic has extensive unit tests under `pkg/controller/certmanager/` — extend those when changing hooks or allowed override values. + +### API Tests + +- Add test cases in `api/operator/v1alpha1/tests//` for any new CRD field or CEL validation rule. +- Suites use `.testsuite.yaml` files and run via envtest + Ginkgo (`make test-apis`). + +### E2E Tests + +- Add E2E test cases with appropriate Ginkgo labels (`Platform:Generic` for portable tests; platform-specific labels for cloud credential or Service Mesh scenarios). +- E2E tests require a live OpenShift cluster with the operator deployed and operands in a stable state. +- Use the `e2e` build tag — run via `make test-e2e`, not plain `go test ./...`. +- Tech Preview addon tests (e.g. trust-manager) may require enabling the feature gate on the operator (`--unsupported-addon-features=TrustManager=true`) and an appropriate `E2E_GINKGO_LABEL_FILTER`. + +## Pull Request Process + +### Creating a PR + +1. Keep diffs small and focused. One logical change per PR. +2. Write a clear, descriptive PR title. +3. Reference the relevant Jira ticket in your commit messages (e.g. `OCPBUGS-12345: description` or your team's key if required). +4. Describe your changes and the motivation behind them in the PR body. + +### What to Include + +- **Code changes** with corresponding tests (unit, API, or E2E as appropriate). +- **Generated file updates** — run `make update` (and `make manifests` / `make bundle` when applicable) and commit the results. CI verifies generated artifacts via `make verify`. +- **Documentation updates** — update `README.md` or files under `docs/` when behavior changes are user-visible. + +### Review Process + +- PRs are reviewed and approved by maintainers listed in the [OWNERS](OWNERS) file. +- All CI checks must pass before merge. +- Reviewers will check for adherence to the architectural patterns documented in [ARCHITECTURE.md](ARCHITECTURE.md) and [AGENTS.md](AGENTS.md), including: + - Core operand changes use library-go static/deployment controllers and bindata — not ad-hoc controller-runtime reconcilers. + - Addon changes use controller-runtime with `common.CtrlClient` and the unified manager in `pkg/operator/setup_manager.go`. + - Proper use of deployment override validation for user-facing `CertManager` spec fields. + - Feature gates respected for Istio CSR and trust-manager. + +### CI Expectations + +CI runs checks equivalent to the local pre-merge loop: + +- `make verify` — bindata, deepcopy, clientgen, bundle, dependency verification, fmt, vet. +- `make lint` — full golangci-lint suite. +- `make test` — unit and API integration tests. + +Ensure all of these pass locally before pushing. + +## Go Workspace + +The repo uses `go.work` with three modules: `.`, `./test`, `./tools`. Key implications: + +- Do **not** use `-mod=vendor` for `go test`, `go fmt`, or E2E targets — the Makefile clears `GOFLAGS` for those targets to avoid conflicting with `go.work`. +- To add a dependency: `make update-dep PKG=pkg@version`, then `make update-vendor`. +- All build-time tools are vendored and built from source into `bin/`. Do not install tools globally. + +## Container Builds + +The default container engine is `podman`. Override with: + +```sh +CONTAINER_ENGINE=docker make image-build +``` + +See `README.md` for image registry, bundle, and catalog build/push workflows (`make image-build`, `make bundle`, `make catalog-build`). + +## Additional Resources + +- [ARCHITECTURE.md](ARCHITECTURE.md) — High-level architecture, dual-runtime model, reconciliation flows, and invariants +- [AGENTS.md](AGENTS.md) — Make targets, controller map, test tags, and PR hygiene +- [README.md](README.md) — Install, upgrade, local run, and cluster deployment +- [docs/operand_metrics.md](docs/operand_metrics.md) — Operand metrics and monitoring +- [docs/proxy.md](docs/proxy.md) — Proxy-related behavior +- [docs/cloud_credentials.md](docs/cloud_credentials.md) — Ambient credentials / cloud secret wiring +- [OpenShift Tier 1 ai-docs](https://github.com/openshift/enhancements/tree/master/ai-docs) — Generic operator, testing, and security practices + +## License + +By contributing, you agree that your contributions will be licensed under the [Apache License 2.0](LICENSE). From 70b5e59df2400eb339b72a4dc10a502ea7534033 Mon Sep 17 00:00:00 2001 From: Arun Maurya Date: Fri, 3 Jul 2026 19:58:24 +0530 Subject: [PATCH 2/2] create agentic docs for cert-manager-operator repo --- AGENTS.md | 166 ++++++++++++++++---- CLAUDE.md | 61 ++++++++ README.md | 246 +++++++++++------------------- docs/api-contracts-guidelines.md | 65 ++++++++ docs/cloud_credentials.md | 131 ---------------- docs/error-handling-guidelines.md | 54 +++++++ docs/integration-guidelines.md | 86 +++++++++++ docs/operand_metrics.md | 170 --------------------- docs/performance-guidelines.md | 41 +++++ docs/proxy.md | 52 ------- docs/security-guidelines.md | 58 +++++++ docs/testing-guidelines.md | 82 ++++++++++ 12 files changed, 672 insertions(+), 540 deletions(-) create mode 100644 CLAUDE.md create mode 100644 docs/api-contracts-guidelines.md delete mode 100644 docs/cloud_credentials.md create mode 100644 docs/error-handling-guidelines.md create mode 100644 docs/integration-guidelines.md delete mode 100644 docs/operand_metrics.md create mode 100644 docs/performance-guidelines.md delete mode 100644 docs/proxy.md create mode 100644 docs/security-guidelines.md create mode 100644 docs/testing-guidelines.md diff --git a/AGENTS.md b/AGENTS.md index 97ee30f54..9915d8d66 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ **Repository**: [openshift/cert-manager-operator](https://github.com/openshift/cert-manager-operator) **Documentation tier**: 2 (component-specific) -> **Agent instruction**: When working in this repository, read **`README.md`** (install, upgrade, local run), **`docs/`** (proxy, metrics, cloud credentials), and the sections below. For **generic OpenShift operator patterns**, testing guidance, or security practices, use the **[Tier 1 hub](https://github.com/openshift/enhancements/tree/master/ai-docs)** under [openshift/enhancements](https://github.com/openshift/enhancements). +> **Agent instruction**: When working in this repository, read **`README.md`** (install, upgrade, local run), **`docs/`** (guidelines and operational docs below), and the sections in this file. For **generic OpenShift operator patterns**, testing guidance, or security practices, use the **[Tier 1 hub](https://github.com/openshift/enhancements/tree/master/ai-docs)** under [openshift/enhancements](https://github.com/openshift/enhancements). > **Generic platform patterns**: [openshift/enhancements `ai-docs/`](https://github.com/openshift/enhancements/tree/master/ai-docs) @@ -12,7 +12,7 @@ ## Why this file? -`README.md` stays focused on **human** quick starts. **`AGENTS.md`** holds **agent-oriented** detail: Make targets, test tags, controller map, and PR hygiene—so tools and contributors have one predictable entry point. +`README.md` stays focused on **human** quick starts. **`AGENTS.md`** holds **agent-oriented** detail: Make targets, test tags, controller map, conventions, and PR hygiene—so tools and contributors have one predictable entry point. --- @@ -20,6 +20,10 @@ An **OpenShift operator** that installs and reconciles **upstream [cert-manager](https://github.com/cert-manager/cert-manager)** (controller, webhook, cainjector) and **optional** operands (Istio CSR, trust-manager), using OpenShift `operator.openshift.io` APIs and **`library-go`** patterns. This repo **does not** implement ACME or certificate issuance logic—that behavior is **upstream cert-manager**. +- **Module:** `github.com/openshift/cert-manager-operator` +- **Go workspace:** root + `test/` + `tools/` (`go.work`) +- **Frameworks:** openshift/library-go (core), controller-runtime v0.23 (operand controllers) + --- ## Core components @@ -27,35 +31,39 @@ An **OpenShift operator** that installs and reconciles **upstream [cert-manager] - **Operator process**: `library-go` `controllercmd` entry → `pkg/operator/starter.go` wires informers, static/sync controllers, and **ClusterOperator-style** status via `pkg/operator/operatorclient/`. - **Cert-manager operand**: Deployed into **`cert-manager`**; manifests and CRDs live under **`bindata/`** (regenerated from Makefile / `hack/`). - **Addon controllers** (feature-gated): **Istio CSR** (`pkg/controller/istiocsr/`), **trust-manager** (`pkg/controller/trustmanager/`). +- **Supporting packages**: `pkg/tlsprofile/` (APIServer TLS profile → cert-manager args), `pkg/features/` (feature gate discovery). - **OLM / install artifacts**: `config/`, `bundle/`, `deploy/` — Kustomize and bundle generation (`make bundle`, `make deploy`). --- -## Documentation structure - -This repository does **not** use a separate `ai-docs/` tree; context is distributed as follows: +## Repository layout ```text -README.md # Human quick start, install, upgrade cert-manager version +README.md # Human quick start, install, upgrade cert-manager version +AGENTS.md # This file — agent onboarding and conventions docs/ -├── operand_metrics.md # Metrics and monitoring for the operand -├── proxy.md # Proxy-related behavior -└── cloud_credentials.md # Ambient credentials / cloud secret wiring -api/operator/v1alpha1/ # CertManager, IstioCSR, TrustManager API + feature gates +├── *-guidelines.md # Agent-oriented deep guidelines (see index below) +├── proxy.md # Proxy-related behavior (when present) +├── cloud_credentials.md # Ambient credentials / cloud secret wiring (when present) +└── operand_metrics.md # Metrics and monitoring for the operand (when present) +api/operator/v1alpha1/ # CertManager, IstioCSR, TrustManager API + feature gates pkg/ -├── cmd/operator/ # CLI entry (start, flags) -├── operator/ # Starter, setup_manager, generated clients, OperatorClient -└── controller/ - ├── certmanager/ # Core operand: deployments, network policy, credentials, overrides - ├── istiocsr/ # Istio CSR addon - ├── trustmanager/ # trust-manager addon - └── common/ # Shared constants (namespaces, trusted CA bundle names) -bindata/ # Generated CRDs and deployment YAML (do not hand-edit long-term) -hack/ # update-manifests, test-apis, CI helpers +├── cmd/operator/ # CLI entry (start, flags) +├── operator/ # Starter, setup_manager, generated clients, operatorclient +├── controller/ +│ ├── certmanager/ # Core operand: deployments, network policy, credentials, overrides +│ ├── istiocsr/ # Istio CSR addon +│ ├── trustmanager/ # trust-manager addon +│ └── common/ # Shared errors, validation, TLS hooks, HandleReconcileResult +├── tlsprofile/ # APIServer TLS profile → cert-manager args +└── features/ # Feature gate discovery +bindata/ # Embedded operand YAML (do not hand-edit long-term) +config/ # Kustomize, CRDs, RBAC, OLM manifests +hack/ # update-manifests, test-apis, CI helpers test/ -├── apis/ # API / envtest suites -├── e2e/ # Ginkgo e2e (build tag: e2e); testdata/, optional plans/ -└── library/ # Shared test helpers +├── apis/ # API / envtest suites +├── e2e/ # Ginkgo e2e (build tag: e2e); testdata/ +└── library/ # Shared E2E helpers ``` --- @@ -83,7 +91,6 @@ test/ | **Core reconciliation** | `pkg/controller/certmanager/` | Controller, webhook, cainjector, network policy, credentials | | **Operand versions** | `Makefile` | `CERT_MANAGER_VERSION`, `ISTIO_CSR_VERSION`, `TRUST_MANAGER_VERSION` | | **E2E harness** | `test/e2e/suite_test.go` | Namespaces, clients, build tag `e2e` | -| **QE / plan notes** | `test/e2e/plans/` | Optional structured test plans (when present) | --- @@ -108,7 +115,7 @@ Exact semantics follow OpenShift **operator API** conventions—when in doubt, c | `pkg/controller/certmanager` | Main operand: **controller**, **webhook**, **cainjector** deployments, related images, network policies, **CredentialsRequest** integration, unsupported overrides validation | | `pkg/controller/istiocsr` | **Istio CSR** deployment, RBAC, services, certificates (feature-gated) | | `pkg/controller/trustmanager` | **trust-manager** install, webhooks, bundles (feature-gated) | -| `pkg/controller/common` | Shared **labels**, **operator namespace**, **trusted CA bundle** ConfigMap name/key | +| `pkg/controller/common` | Shared **labels**, **operator namespace**, **trusted CA bundle** ConfigMap name/key; `HandleReconcileResult` for status conditions | | `pkg/operator/starter.go` | Composes **kube** / **operator** informers, **config** informers, registers workload loops | --- @@ -145,11 +152,74 @@ Upstream cert-manager --- -## Ecosystem references +## Cross-cutting conventions -- **Upstream cert-manager**: [cert-manager/cert-manager](https://github.com/cert-manager/cert-manager) — CRDs, controllers, issuance behavior. -- **OpenShift enhancements** (Istio CSR / trust-manager designs): linked from `api/operator/v1alpha1/features.go`. -- **OpenShift release / CI**: jobs often live in **[openshift/release](https://github.com/openshift/release)** (Prow); this repo may not ship `.github/workflows`. +### Naming + +- Operand CR name is always `cluster` (CEL-enforced). +- Managed operand resources use label key `app` (`common.ManagedResourceLabelKey`). +- Package imports: prefer full paths; no dot imports except Ginkgo/Gomega in tests. + +### Code generation + +After changing API types or RBAC markers: + +```bash +make manifests generate verify +``` + +Never hand-edit generated files under `pkg/operator/clientset/`, `config/crd/bases/`, or deepcopy output. + +Operand version bumps: + +```bash +make update-manifests # after changing CERT_MANAGER_VERSION etc. in Makefile +``` + +### Controller patterns + +- controller-runtime reconcilers: use `common.HandleReconcileResult` for status conditions. +- library-go controllers: follow existing cert-manager controller patterns in `pkg/controller/certmanager/`. +- Add RBAC markers before implementing new API calls; regenerate with `make manifests`. + +### OLM safety + +- Patch Subscription for operator env changes — do not edit Deployments directly. +- Operand namespace: `cert-manager`; operator namespace: `cert-manager-operator`. + +### Linting + +- golangci-lint v2 (`.golangci.yaml`): gosec, wrapcheck, errorlint, contextcheck, musttag enabled. +- Run `make lint` and `make verify` before proposing changes. + +### Vendor + +- Dependencies are vendored. After module changes: `make update-vendor`. +- Build tools from vendor via Makefile `bin/` targets. +- Do not hand-edit `vendor/` or long-term `bindata/`; use `make update` / `make update-vendor` per `Makefile` and `hack/`. + +--- + +## Documentation index + +### Agent guidelines (in-repo) + +| Guideline | Scope | +|-----------|-------| +| [docs/security-guidelines.md](docs/security-guidelines.md) | RBAC, network policies, TLS, credentials, secrets | +| [docs/performance-guidelines.md](docs/performance-guidelines.md) | Workers, cache filtering, requeue, E2E parallelism | +| [docs/error-handling-guidelines.md](docs/error-handling-guidelines.md) | ReconcileError taxonomy, condition mapping | +| [docs/api-contracts-guidelines.md](docs/api-contracts-guidelines.md) | CRDs, CEL validation, codegen | +| [docs/testing-guidelines.md](docs/testing-guidelines.md) | Unit, envtest, E2E patterns and labels | +| [docs/integration-guidelines.md](docs/integration-guidelines.md) | OLM, OpenShift APIs, operand manifests | + +### Operational docs + +| Doc | Topic | +|-----|-------| +| [docs/proxy.md](docs/proxy.md) | Egress proxy and trusted CA | +| [docs/cloud_credentials.md](docs/cloud_credentials.md) | CCO ambient credentials | +| [docs/operand_metrics.md](docs/operand_metrics.md) | Prometheus metrics for operands | --- @@ -160,9 +230,16 @@ Upstream cert-manager - **Shell / Make**: `Makefile` uses `bash` with `-euo pipefail`; **`make help`** lists targets. - **Cluster**: **`oc`** expected for `make deploy`, e2e waits, and debugging; see **`README.md`** for `make local-run` (scale in-cluster operator to 0 first). - **After API edits**: **`make update`** or at least **`make manifests generate`** so CRDs and `pkg/operator` generated code stay consistent. -- **Do not hand-edit** **`vendor/`** or long-term **`bindata/`**; use **`make update`** / **`make update-vendor`** per `Makefile` and `hack/`. - **Caches**: `XDG_CACHE_HOME` / `XDG_CONFIG_HOME` default under **`_output/`** when unset (CI-friendly). +Local operator run: + +```bash +make local-run # against current kubeconfig +make deploy # kustomize deploy +make undeploy # remove +``` + --- ## Testing instructions @@ -180,7 +257,13 @@ Upstream cert-manager - **`make test-unit`**: Excludes `test/e2e`, `test/apis`, `test/utils` (see `Makefile`). - **E2E** (`test/e2e/`, tag **`e2e`**): cluster must already run the operator and stable operands; **`make test-e2e`** (uses **`make test-e2e-wait-for-stable-state`**). Tech Preview CI uses **`make test-e2e-tech-preview`** (`E2E_GINKGO_LABEL_FILTER_TECH_PREVIEW` in the Makefile). Narrow with **`TEST=...`** (`go test -run` regex) and **`E2E_GINKGO_LABEL_FILTER`** (`-ginkgo.label-filter`). Use the Make target—plain **`go test ./...`** does not cover e2e. - **After refactors**: **`make verify`** and **`make lint`** (`.golangci.yaml`). -- **Add or update tests** for code you change (unit, `test/apis`, or e2e as appropriate). +- **Add or update tests** for code you change (unit, `test/apis`, or e2e as appropriate). See [docs/testing-guidelines.md](docs/testing-guidelines.md) for patterns. + +Vulnerability scan before release-related changes: + +```bash +make govulncheck +``` --- @@ -197,7 +280,22 @@ Upstream cert-manager - **API / bindata / manifests**: Commit outputs from **`make update`** (or minimal `make manifests generate`) so verify passes. - **Scope**: Small diffs; follow existing **`library-go`** patterns. +- **API changes** include regenerated CRDs and clients. +- **New reconcile paths** include unit tests; user-facing behavior includes E2E when feasible. +- **RBAC changes** reflected in markers and `config/rbac/role.yaml`. - **User-visible behavior**: Update **`README.md`** or **`docs/`** when needed. +- **No secrets** or credential values in logs or test output. +- Do not commit `_output/`, `bin/` tool binaries, or coverage files. + +--- + +## Common pitfalls + +- Assuming direct Deployment control on OLM-managed operators — use Subscription patches. +- Adding cluster-wide watches without label filtering — follow `setup_manager.go` patterns. +- Skipping `make generate` after API changes — CI `make verify` will fail. +- Creating operand CRs with names other than `cluster` — rejected by CEL. +- Enabling `DefaultNetworkPolicy` without egress rules — operands get deny-all egress. --- @@ -210,3 +308,11 @@ Upstream cert-manager | Istio / mesh | `pkg/controller/istiocsr/` + **`features.go`** | | Trust bundles | `pkg/controller/trustmanager/` + **`features.go`** | | Codegen / bindata failures | **`make update`**, **`hack/`** | + +--- + +## Ecosystem references + +- **Upstream cert-manager**: [cert-manager/cert-manager](https://github.com/cert-manager/cert-manager) — CRDs, controllers, issuance behavior. +- **OpenShift enhancements** (Istio CSR / trust-manager designs): linked from `api/operator/v1alpha1/features.go`. +- **OpenShift release / CI**: jobs often live in **[openshift/release](https://github.com/openshift/release)** (Prow); this repo may not ship `.github/workflows`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..d6d9464df --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,61 @@ +@AGENTS.md + +## Build and test commands + +Run from the repository root: + +```bash +make verify # fmt, vet, script checks, dep verification +make test # generate, vet, test-apis, test-unit +make lint # golangci-lint +make build # generate, fmt, vet, build binary +``` + +Before proposing API or RBAC changes: + +```bash +make manifests generate verify +``` + +E2E (requires live OpenShift cluster): + +```bash +make test-e2e +# Filter: E2E_GINKGO_LABEL_FILTER='Platform:Generic && Feature:IstioCSR' +``` + +## Codegen workflow + +When modifying `api/operator/v1alpha1/` types or `+kubebuilder:rbac` markers: + +1. `make manifests` — regenerate CRDs and RBAC +2. `make generate` — deepcopy, client-gen, fakes +3. `make verify` — confirm no drift + +Operand version bumps: + +```bash +make update-manifests # after changing CERT_MANAGER_VERSION etc. in Makefile +``` + +## Pre-commit expectations + +- Run `make verify` and `make test` before suggesting a PR is ready. +- Run `make lint` when changing Go code; use `make lint-fix` for auto-fixable issues. +- Do not commit `_output/`, `bin/` tool binaries, or coverage files. + +## Local operator run + +```bash +make local-run # against current kubeconfig +make deploy # kustomize deploy +make undeploy # remove +``` + +## Vulnerability scan + +```bash +make govulncheck +``` + +Run before release-related changes. diff --git a/README.md b/README.md index 74c3a8102..c7deb940c 100644 --- a/README.md +++ b/README.md @@ -1,196 +1,128 @@ -# Cert Manager Operator for OpenShift +# cert-manager-operator -This repository contains Cert Manager Operator designed for OpenShift. The operator runs in `cert-manager-operator` namespace, whereas its operand in `cert-manager`. Both those namespaces are hardcoded. +OpenShift cluster operator that installs and manages [cert-manager](https://cert-manager.io/), [trust-manager](https://cert-manager.io/docs/trust/trust-manager/), and [Istio CSR](https://github.com/cert-manager/istio-csr) as platform operands. -## The operator architecture and design assumptions +## Components -The Operator uses the [upstream deployment manifests](https://github.com/cert-manager/cert-manager/releases). It divides them into separate files and deploys using 3 controllers: -- [cert_manager_cainjector_deployment.go](pkg/controller/certmanager/cert_manager_cainjector_deployment.go) -- [cert_manager_controller_deployment.go](pkg/controller/certmanager/cert_manager_controller_deployment.go) -- [cert_manager_webhook_deployment.go](pkg/controller/certmanager/cert_manager_webhook_deployment.go) +| Operand CR | Kind | Description | +|------------|------|-------------| +| `cluster` | CertManager | Core cert-manager deployment (controller, webhook, cainjector) | +| `cluster` | TrustManager | Bundle distribution for trust anchors (TechPreview) | +| `cluster` | IstioCSR | Certificate signing for Istio workloads (TechPreview) | -The Operator automatically deploys a cluster-scoped `CertManager` object named `cluster` if it's missing (with default values). +All CRs use API group `operator.openshift.io/v1alpha1` and must be named `cluster`. -### Directory structure +## Tech stack -``` -+- api - The API type -+- bindata - +- cert-manager-crds - CRDs for Cert Manager - +- cert-manager-deployment - Deployment Manifests for Cert Manager -+- bundle - +- cert-manager-operator - +- manifests - This operator's CRDs -+- cmd -+- deploy - +- examples - Examples to make testing easier -+- hack - All sorts of scripts -+- images - +- ci - Dockerfile -+- config - Template for generating OLM bundle -+- pkg -+- tools -+- vendor -``` - -## Running the operator locally (development) +- Go 1.26 with vendored dependencies (`go.work` spans root, `test/`, `tools/`) +- openshift/library-go for core operator pattern +- controller-runtime for optional operand controllers +- kubebuilder / operator-sdk for CRD and OLM bundle generation +- Ginkgo v2 + envtest for testing -Connect to your OpenShift cluster and run the following command: +## Project structure -```sh -make deploy -oc scale --replicas=0 deploy --all -n cert-manager-operator -make local-run ``` - -This command will install all the necessary Operator manifests as well as all necessary CRDs. After this part is complete, it will run the Operator locally. - -## Running the operator in the cluster - -### Preparing the environment -Prepare your environment for the installation commands. - -- Select the container runtime you want to build the images with (`podman` or `docker`): - ```sh - export CONTAINER_ENGINE=podman - ``` -- Select the name settings of the image: - ```sh - export REGISTRY=quay.io - export REPOSITORY=myuser - export IMAGE_VERSION=1.0.0 - ``` -- Login to the image registry: - ```sh - ${CONTAINER_ENGINE} login ${REGISTRY} -u ${REPOSITORY} - ``` - -### Installing the Cert Manager Operator by building and pushing the Operator image to a registry -1. Build and push the Operator image to a registry: - ```sh - export IMG=${REGISTRY}/${REPOSITORY}/cert-manager-operator:${IMAGE_VERSION} - make image-build image-push - ``` - - Note: If you're on a non-x86 arch like arm64, you may need to use commands like `docker buildx build --platform linux/amd64` or `podman build --platform linux/amd64` to specific the target platforms in the Makefile. (Docs link: [Docker](https://docs.docker.com/engine/reference/commandline/buildx_build/#platform), [Podman](https://docs.podman.io/en/stable/markdown/podman-build.1.html#platform-os-arch-variant)) - -2. _Optional_: you may need to link the registry secret to `cert-manager-operator` service account if the image is not public ([Doc link](https://docs.openshift.com/container-platform/4.10/openshift_images/managing_images/using-image-pull-secrets.html#images-allow-pods-to-reference-images-from-secure-registries_using-image-pull-secrets)): - - a. Create a secret with authentication details of your image registry: - ```sh - oc -n cert-manager-operator create secret generic certmanager-pull-secret --type=kubernetes.io/dockercfg --from-file=.dockercfg=${XDG_RUNTIME_DIR}/containers/auth.json - ``` - b. Link the secret to `cert-manager-operator` service account: - ```sh - oc -n cert-manager-operator secrets link cert-manager-operator certmanager-pull-secret --for=pull - ```` - -3. Run the following command to deploy the Cert Manager Operator: - ```sh - make deploy - ``` - -### Cleaning up the deployment - -To remove the Cert Manager Operator and its associated resources from the cluster, run the following command: - -```sh -make undeploy +api/operator/v1alpha1/ Operator CRD types +pkg/controller/ Reconciliation logic per operand +pkg/operator/ Operator bootstrap and manager setup +bindata/ Embedded operand manifests +config/ Kustomize overlays, CRDs, RBAC +test/e2e/ Cluster end-to-end tests +test/apis/ CRD validation integration tests +docs/ Operational docs and agent guidelines ``` -This will delete all resources created during the deployment process, including the operator and operand resources. - -## Updating resources +See [AGENTS.md](AGENTS.md) for AI agent conventions and the full documentation index. -Use the following command to update all generated resources: +## Getting started - make update +### Prerequisites -## Upgrading cert-manager +- Go 1.26+ +- `make`, `git` +- For E2E: OpenShift cluster with cert-manager operator installed via OLM +- Container engine (podman or docker) for image builds -Update the version of cert-manager in the `Makefile`: +### Build -```shell - $ git diff Makefile - diff --git a/Makefile b/Makefile - index e414cc7..d04d45d 100644 - --- a/Makefile - +++ b/Makefile - @@ -13,7 +13,7 @@ BUNDLE_IMAGE_TAG?=latest - - TEST_OPERATOR_NAMESPACE?=cert-manager-operator - - -MANIFEST_SOURCE = https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml - +MANIFEST_SOURCE = https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml - - OPERATOR_SDK_VERSION?=v1.12.0 - OPERATOR_SDK?=$(PERMANENT_TMP_GOPATH)/bin/operator-sdk-$(OPERATOR_SDK_VERSION) +```bash +make build # generate, fmt, vet, compile binary +make build-operator # compile only (skip codegen checks) ``` -Execute the `update-manifests` target: +### Test -```shell -$ make update -hack/update-cert-manager-manifests.sh https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml ----- Downloading manifest file from https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml ---- ----- Installing tooling ---- ----- Patching manifest ---- -cert-manager-crds/certificaterequests.cert-manager.io-crd.yaml -... +```bash +make test # unit + API integration +make test-unit # pkg/ unit tests +make test-apis # envtest CRD/CEL validation +make test-e2e # cluster E2E (requires OpenShift) ``` -Check the changes in the `bindata/` folder and assert any inconsistencies or errors. +### Verify and lint -## Running tests locally +```bash +make verify # fmt, vet, script checks +make lint # golangci-lint +make govulncheck # vulnerability scan +``` -To run all unit tests locally, use the following command: +### Run locally - make test +```bash +make local-run # run operator against current kubeconfig +``` -This will execute all unit tests and generate a coverage report (`cover.out`). +### Code generation -## Running e2e tests locally +After API or RBAC marker changes: -The testsuite assumes, that Cert Manager Operator has been successfully deployed -in the cluster and it also successfully deployed Cert Manager (the operand). This -is exactly what Prow is doing in cooperation with +```bash +make manifests generate verify +``` -`make test-e2e-wait-for-stable-state`. +## Deployment -If you'd like to run all the tests locally, you need to ensure the same requirements -are met. The easiest way to do it follow steps from above. +The operator is deployed via OLM: -Then, let it run for a few minutes. Once the operands are deployed, just invoke: +- Operator namespace: `cert-manager-operator` +- Operand namespace: `cert-manager` - make test-e2e +```bash +make deploy # kustomize deploy (development) +make bundle # generate OLM bundle +make image-build # build operator container image +``` -## Linting the code +## Configuration -To ensure the code adheres to the project's linting rules, run: +| Makefile variable | Default | Purpose | +|-------------------|---------|---------| +| `CERT_MANAGER_VERSION` | v1.20.2 | cert-manager operand version | +| `TRUST_MANAGER_VERSION` | v0.20.3 | trust-manager operand version | +| `ISTIO_CSR_VERSION` | v0.16.0 | istio-csr operand version | +| `BUNDLE_VERSION` | 1.20.0 | OLM bundle semver | +| `E2E_GINKGO_LABEL_FILTER` | Platform/credential filter | E2E test selection | - make lint +## Documentation -This will use `golangci-lint` to check for any linting issues in the codebase. +### Agent guidelines -## Using unsupported config overrides options +- [AGENTS.md](AGENTS.md) — cross-cutting conventions and docs index +- [docs/security-guidelines.md](docs/security-guidelines.md) +- [docs/testing-guidelines.md](docs/testing-guidelines.md) +- [docs/api-contracts-guidelines.md](docs/api-contracts-guidelines.md) +- [docs/error-handling-guidelines.md](docs/error-handling-guidelines.md) +- [docs/integration-guidelines.md](docs/integration-guidelines.md) +- [docs/performance-guidelines.md](docs/performance-guidelines.md) -It is possible (although not supported) to specify custom settings to each Cert Manager image. In order to do it, -you need to modify the `certmanager.operator/cluster` object: +### Operational -```asciidoc -apiVersion: operator.openshift.io/v1alpha1 -kind: CertManager -metadata: - name: cluster -spec: - managementState: "Managed" - unsupportedConfigOverrides: - # Here's an example to supply custom DNS settings. - controller: - args: - - "--dns01-recursive-nameservers=1.1.1.1:53" - - "--dns01-recursive-nameservers-only" -``` -## Metrics and Monitoring +- [docs/proxy.md](docs/proxy.md) — egress proxy and trusted CA configuration +- [docs/cloud_credentials.md](docs/cloud_credentials.md) — Cloud Credential Operator integration +- [docs/operand_metrics.md](docs/operand_metrics.md) — Prometheus metrics setup + +## License -The guide to [enable the cert-manager metrics and monitoring](https://github.com/openshift/cert-manager-operator/tree/master/docs/operand_metrics.md) will help you get started. +Apache License 2.0 — see [LICENSE](LICENSE). diff --git a/docs/api-contracts-guidelines.md b/docs/api-contracts-guidelines.md new file mode 100644 index 000000000..eb259f4d5 --- /dev/null +++ b/docs/api-contracts-guidelines.md @@ -0,0 +1,65 @@ +# API Contracts Guidelines + +CRD types, validation, and codegen conventions. + +## API group and versions + +- Group: `operator.openshift.io` +- Version: `v1alpha1` +- Package: `api/operator/v1alpha1/` +- Operand CRs: `CertManager`, `TrustManager`, `IstioCSR` + +## Singleton CR naming + +- Operand CRs must be named `cluster` (enforced by CEL validation on types). +- E2E tests assert rejection of non-`cluster` names (e.g. `test/e2e/istio_csr_operand_test.go`). + +## Kubebuilder markers + +- Use `+kubebuilder:object:root=true` on top-level types. +- Embed `github.com/openshift/api/operator/v1.OperatorSpec` and `OperatorStatus` for OpenShift operator consistency. +- Mark optional fields with `+optional` and `+kubebuilder:validation:Optional`. + +## CEL validation + +- Use `+kubebuilder:validation:XValidation` for immutability and cross-field rules (see `DefaultNetworkPolicy` in `certmanager_types.go`). +- API integration tests in `test/apis/` require Kubernetes ≥1.25 for CEL support — suite checks server version in `suite_test.go`. + +## Generated artifacts + +After API changes, run: + +```bash +make manifests # CRDs → config/crd/bases/ +make generate # deepcopy, client-gen, applyconfiguration +make verify # ensure no drift +``` + +- CRD YAML: `config/crd/bases/operator.openshift.io_*.yaml` +- Generated clients: `pkg/operator/clientset/`, `pkg/operator/informers/`, `pkg/operator/applyconfigurations/` +- Client-gen script: `hack/update-clientgen.sh` + +## DeploymentConfig pattern + +- `DeploymentConfig` embeds `OverrideArgs`, `OverrideEnvs`, and resource overrides for operand Deployments. +- Validation logic for overrides is in `pkg/controller/common/` and tested in `deployment_overrides_validation_test.go`. + +## Status conditions + +- Custom conditions use types in `api/operator/v1alpha1/` (`Degraded`, `Ready`, reason constants). +- `ConditionalStatus` helpers set conditions idempotently before status patch. + +## API test generation + +- `test/apis/generator.go` loads test specs from `api/` tree via `LoadTestSuiteSpecs`. +- Add new validation cases alongside type definitions; generator picks them up automatically. + +## Breaking changes + +- Prefer CEL immutability rules over admission webhooks for field-level constraints. +- Document new fields in kubebuilder comments — they appear in CRD OpenAPI schema. + +## OLM bundle + +- Bundle version uses semver (`BUNDLE_VERSION` in Makefile, default `1.20.0`). +- Run `make bundle` after CRD or CSV changes. diff --git a/docs/cloud_credentials.md b/docs/cloud_credentials.md deleted file mode 100644 index 299c9ee59..000000000 --- a/docs/cloud_credentials.md +++ /dev/null @@ -1,131 +0,0 @@ -# Ambient Credentials for cert-manager - -Cert-manager ACME dns-01 solvers for AWS Route53 and Google Cloud DNS can utilize ambient credentials available by default within the environment of the controller pod that is running on the cluster. The openshift-cert-manager-operator allows users to specify a cloud secret name for AWS and GCP clusters which would be used for the cert-manager ambient credentials. This is done by passing the name of the secret (which should mandatorily be present in the cert-manager namespace) containing the cloud credentials for authenticating with either AWS route53 or Google Cloud DNS. - -Note: ClusterIssuer(s) have support for ambient credential mode available by default, for Issuer(s) to utilize ambient credentials the `--issuer-ambient-credentials` has to be passed as an arg to the cert-manager controller pod. - -OpenShift clusters can utilize cluster cloud-credential operator to manage these secrets. For clusters with [credentials mode set as Manual (which is the case when using AWS STS or GCP Workload Identity based clusters)](https://docs.openshift.com/container-platform/latest/authentication/managing_cloud_provider_credentials/cco-mode-manual.html), then cluster administrators need to use ccoctl to generate the secret and manually apply the generated secret on the cluster. Otherwise, CredentialsRequest object once placed in the openshift-cloud-credential-operator namespace would automatically generate and place the k8s secret on the cluster. - -## AWS - -1. Create the following yaml file for credentials request object and `oc apply -f ` on the cluster. -```yaml -apiVersion: cloudcredential.openshift.io/v1 -kind: CredentialsRequest -metadata: - name: cert-manager - namespace: openshift-cloud-credential-operator -spec: - providerSpec: - apiVersion: cloudcredential.openshift.io/v1 - kind: AWSProviderSpec - statementEntries: - - action: - - route53:GetChange - effect: Allow - resource: arn:aws:route53:::change/* - - action: - - route53:ChangeResourceRecordSets - - route53:ListResourceRecordSets - effect: Allow - resource: arn:aws:route53:::hostedzone/* - - action: - - route53:ListHostedZonesByName - effect: Allow - resource: "*" - secretRef: - name: aws-creds - namespace: cert-manager - serviceAccountNames: - - cert-manager -``` -2. Create a new directory `` and place the `` in that directory. -3. If using STS and or credentials mode as Manual on the cluster, use [`ccoctl`](https://github.com/openshift/cloud-credential-operator/blob/master/docs/ccoctl.md) to generate the secrets and apply it on the cluster. - -If not using Manual credentials mode, skip steps 3, 4, 5, 6 and directly proceed to 7. - -```sh -ccoctl aws create-iam-roles --credentials-requests-dir --identity-provider-arn --name --output-dir --region -``` -The output of the previous ccoctl command would be similar to: - -```log -2023/05/15 18:10:34 Role arn:aws:iam::XXXXXXXXXXXX:role/-cert-manager-aws-creds created -2023/05/15 18:10:34 Saved credentials configuration to: /manifests/cert-manager-aws-creds-credentials.yaml -2023/05/15 18:10:35 Updated Role policy for Role -cert-manager-aws-creds -``` - -From the output copy the `` containing the arn role, eg. `arn:aws:iam::XXXXXXXXXXXX:role/-cert-manager-aws-creds` which would be used in next step. - -4. Annotate the cert-manager service account in the cert-manager namespace to use the correct AWS ARN role and other sts related annotations. This is required for [aws-pod-identity-webhook](https://github.com/openshift/aws-pod-identity-webhook) running on the cluster to correctly assign AWS roles to the pod(s) used by cert-manager. - -```sh -oc -n cert-manager annotate serviceaccount cert-manager eks.amazonaws.com/role-arn="" -oc -n cert-manager annotate serviceaccount cert-manager eks.amazonaws.com/audience="sts.amazonaws.com" -oc -n cert-manager annotate serviceaccount cert-manager eks.amazonaws.com/sts-regional-endpoints="true" -oc -n cert-manager annotate serviceaccount cert-manager eks.amazonaws.com/token-expiration="86400" -``` - -5. Apply the generated secrets on the cluster -```sh -ls /manifests/*-credentials.yaml | xargs -I{} oc apply -f {} -``` - -6. After the annotations and secrets have been applied, we need to delete the pods in the `cert-manager` namespace for the pod identity webhook to mutate the pods with the correct AWS cloud roles. -```sh -oc delete pods --all -n cert-manager -``` - -Wait for the pods to come up in running state. - -```sh -oc get pods -n cert-manager -w -``` - -7. Patch the subscription object on the cluster to inject the secret name in the operator deployment. -```sh -oc -n cert-manager-operator patch subscription --type='merge' -p '{"spec":{"config":{"env":[{"name":"CLOUD_CREDENTIALS_SECRET_NAME","value":"aws-creds"}]}}}' -``` - -## GCP - -1. Create the following yaml file for credentials request object and `oc apply -f ` on the cluster. -```yaml -apiVersion: cloudcredential.openshift.io/v1 -kind: CredentialsRequest -metadata: - labels: - app: cert-manager - app.kubernetes.io/component: controller - app.kubernetes.io/instance: cert-manager - app.kubernetes.io/name: cert-manager - name: cert-manager - namespace: openshift-cloud-credential-operator -spec: - providerSpec: - apiVersion: cloudcredential.openshift.io/v1 - kind: GCPProviderSpec - predefinedRoles: - - roles/dns.admin - secretRef: - name: gcp-credentials - namespace: cert-manager - serviceAccountNames: - - cert-manager -``` -2. Create a new directory `` and place the `` in that directory. -3. If using STS and or credentials mode as Manual on the cluster, use [`ccoctl`](https://github.com/openshift/cloud-credential-operator/blob/master/docs/ccoctl.md) to generate the secrets and apply it on the cluster. -```sh -ccoctl gcp create-service-accounts --credentials-requests-dir --name --output-dir --workload-identity-pool --workload-identity-provider --project -``` -If not using Manual credentials mode, skip steps 3, 4 and directly proceed to 5. - -4. Apply the generated secrets on the cluster -```sh -ls manifests/*-credentials.yaml | xargs -I{} oc apply -f {} -``` - -5. Patch the subscription object on the cluster to inject the secret name in the operator deployment. -```sh -oc -n cert-manager-operator patch subscription --type='merge' -p '{"spec":{"config":{"env":[{"name":"CLOUD_CREDENTIALS_SECRET_NAME","value":"gcp-credentials"}]}}}' -``` diff --git a/docs/error-handling-guidelines.md b/docs/error-handling-guidelines.md new file mode 100644 index 000000000..50b7dc18d --- /dev/null +++ b/docs/error-handling-guidelines.md @@ -0,0 +1,54 @@ +# Error Handling Guidelines + +Reconciliation error taxonomy and status update patterns. + +## ReconcileError types + +Use typed errors from `pkg/controller/common/errors.go`: + +| Type | Constructor | When to use | +|------|-------------|-------------| +| Irrecoverable | `NewIrrecoverableError` | RBAC denied, invalid spec, unrecoverable config | +| Retry required | `NewRetryRequiredError` | Transient API errors, operand not ready yet | +| Multiple instance | `NewMultipleInstanceError` | More than one singleton CR detected | + +Check error kind with `IsIrrecoverableError`, `IsRetryRequiredError`, `IsMultipleInstanceError`. + +## Kubernetes API errors + +- Use `FromClientError` to classify client errors: + - **Irrecoverable:** Unauthorized, Forbidden, Invalid, BadRequest, ServiceUnavailable + - **Retry:** NotFound, Conflict, Timeout, other transient errors +- Use `FromError` when wrapping an existing `ReconcileError` or unknown error. + +## Status condition mapping + +- Route all controller-runtime reconcilers through `HandleReconcileResult` (`pkg/controller/common/reconcile_result.go`): + - **Success:** `Degraded=False`, `Ready=True` + - **Retry:** `Degraded=False`, `Ready=False` (ReasonInProgress), `RequeueAfter` + - **Irrecoverable:** `Degraded=True`, `Ready=False`, no requeue +- Set both `Degraded` and `Ready` atomically before calling `updateConditionFn`. + +## Error wrapping + +- `wrapcheck` and `errorlint` are enabled in `.golangci.yaml`. +- Wrap errors with context: `fmt.Errorf("reconciling deployment %s: %w", name, err)`. +- Implement `Unwrap()` on custom error types (see `ReconcileError.Unwrap`). + +## Nil safety + +- All `New*Error` constructors return `nil` when input `err` is `nil` — safe to chain. + +## Library-go controllers + +- Core cert-manager controller uses library-go patterns; map errors to operator conditions via existing status helpers rather than inventing parallel error types. + +## Testing errors + +- Unit tests for error classification live in `pkg/controller/common/errors_test.go`. +- When adding new error paths, add table-driven tests covering both condition updates and requeue behavior. + +## Logging + +- Use `log.V(2).Info` for condition update details in `HandleReconcileResult`. +- Log error messages, not Secret contents or credential values. diff --git a/docs/integration-guidelines.md b/docs/integration-guidelines.md new file mode 100644 index 000000000..98fb063b2 --- /dev/null +++ b/docs/integration-guidelines.md @@ -0,0 +1,86 @@ +# Integration Guidelines + +OpenShift platform integration, OLM deployment, and operand lifecycle. + +## OLM deployment context + +| Property | Value | +|----------|-------| +| Operator namespace | `cert-manager-operator` | +| Operand namespace | `cert-manager` | +| Operator deployment | `cert-manager-operator-controller-manager` | +| CSV pattern | Bundle version from `BUNDLE_VERSION` (Makefile) | + +- Prefer patching Subscription for operator env/config — OLM reverts direct Deployment edits. +- See `docs/proxy.md` for subscription patch examples. + +## Dual runtime architecture + +1. **library-go path** (`pkg/operator/starter.go`): cert-manager core reconciliation, status controller, config observers. +2. **controller-runtime path** (`pkg/operator/setup_manager.go`): IstioCSR and TrustManager when feature gates allow. + +When adding platform integrations, place them in the controller path that owns the affected operand. + +## OpenShift API dependencies + +| API | Package | Usage | +|-----|---------|-------| +| FeatureGate | `pkg/features/` | TechPreview operand enablement | +| APIServer | `pkg/tlsprofile/` | TLS security profile | +| Infrastructure | cert-manager controller | Platform detection | +| Cloud Credential Operator | `credentials_request.go` | DNS-01 ambient creds | + +## Operand manifest pipeline + +- Embedded manifests: `bindata/` (cert-manager, trust-manager, istio-csr, network policies). +- Update via scripts: `hack/update-cert-manager-manifests.sh`, `hack/update-trust-manager-manifests.sh`, `hack/update-istio-csr-manifests.sh`. +- Run `make update-manifests` after bumping operand versions (`CERT_MANAGER_VERSION`, etc. in Makefile). + +## Upstream forks + +- cert-manager: `github.com/openshift/jetstack-cert-manager` (version in Makefile `CERT_MANAGER_VERSION`). +- trust-manager and istio-csr versions pinned in Makefile operand version section. + +## Feature gates + +- Runtime flag: `--unsupported-addon-features` → `UnsupportedAddonFeatures` in starter.go. +- Discovery: `pkg/features/features.go` reads cluster FeatureGate status. +- E2E labels `TechPreview` / `TechPreview:Inverted` align with gate behavior. + +## Metrics + +- Operand metrics setup documented in `docs/operand_metrics.md`. +- Operator metrics via library-go status/metrics patterns. + +## Bundle and catalog + +```bash +make bundle # Generate OLM bundle +make bundle-build # Build bundle image +make catalog-build # Build catalog/index image +``` + +- Bundle inputs: `config/manifests/`, CRDs, RBAC from `make manifests`. + +## Local development + +```bash +make local-run # Run operator locally against current kubeconfig +make deploy # Kustomize deploy to cluster +make undeploy # Remove deployment +``` + +## Jsonnet + +- Jsonnet configs in `jsonnet/` for manifest templating. +- Tool built from vendor: `$(BIN_DIR)/jsonnet`. + +## CI + +- OpenShift CI uses `.ci-operator.yaml` and `images/ci/` Dockerfiles. +- E2E detects CI via `OPENSHIFT_CI=true` in `test/e2e/suite_test.go` for path resolution. + +## Service Mesh (Istio CSR) + +- Service Mesh E2E uses testdata under `test/e2e/testdata/servicemesh/`. +- Version pins: `E2E_OSM_ISTIO_VERSION`, `E2E_OSM_OPERATOR_VERSION` in Makefile — keep in sync with `servicemesh_helpers_test.go` constants. diff --git a/docs/operand_metrics.md b/docs/operand_metrics.md deleted file mode 100644 index 5becaa7d0..000000000 --- a/docs/operand_metrics.md +++ /dev/null @@ -1,170 +0,0 @@ -## Monitoring cert-manager Metrics with OpenShift Monitoring - -cert-manager exposes metrics in the format expected by [Prometheus Operator](https://github.com/prometheus-operator/prometheus-operator) for all three of its core components: controller, cainjector, and webhook. - -You can configure OpenShift Monitoring to collect metrics from cert-manager operands by enabling the built-in user workload monitoring stack. This allows you to monitor user-defined projects in addition to the default platform monitoring. - -### Enable User Workload Monitoring - -Cluster administrators can enable monitoring for user-defined projects by setting the `enableUserWorkload: true` field in the cluster monitoring ConfigMap object. For more details, Please look at the detailed documentation to [Configuring user workload monitoring](https://docs.redhat.com/en/documentation/openshift_container_platform/4.20/html/monitoring/configuring-user-workload-monitoring). - -1. Create or edit the ConfigMap `cluster-monitoring-config` in namespace `openshift-monitoring`. - -``` -$ oc apply -f - < 9402/TCP 54s -cert-manager-cainjector ClusterIP 172.30.148.41 9402/TCP 63s -cert-manager-webhook ClusterIP 172.30.100.46 443/TCP,9402/TCP 62s -``` - -2. Apply a YAML manifest for the ServiceMonitor to look for services matching the specified labels within the `cert-manager` namespace and scrape metrics from their `/metrics` path on port 9402. - -``` -$ oc apply -f - < "Targets" page. - -### Query Metrics - -As a cluster administrator or as a user with view permissions for all projects, You can access these metrics using the command line or via the OpenShift web console. For more details, Please look at the detailed documentation to [Accessing metrics](https://docs.redhat.com/en/documentation/openshift_container_platform/4.20/html/monitoring/accessing-metrics). - -1. Retrieve a bearer token. You can use the following command to get a token for a specific service account. -``` -$ TOKEN=$(oc create token prometheus-k8s -n openshift-monitoring) -``` - -Alternatively, if you have cluster-admin access or view permissions for all projects, you might be able to use `$(oc whoami -t)` to get your own user token. - -2. Get the OpenShift API route for Thanos Querier. - -``` -$ URL=$(oc get route thanos-querier -n openshift-monitoring -o=jsonpath='{.status.ingress[0].host}') -``` - -3. Query the metrics using `curl`, authenticating with the bearer token. The query uses the `/api/v1/query endpoint`. The output will be in JSON format, using `| jq` for pretty JSON formatting. - -``` -$ curl -s -k -H "Authorization: Bearer $TOKEN" https://$URL/api/v1/query --data-urlencode 'query={job="cert-manager"}' | jq -``` - -Example output: - -``` -{ - "status": "success", - "data": { - "resultType": "vector", - "result": [ - { - "metric": { - "__name__": "certmanager_clock_time_seconds", - "container": "cert-manager-controller", - "endpoint": "9402", - "instance": "10.131.0.65:9402", - "job": "cert-manager", - "namespace": "cert-manager", - "pod": "cert-manager-b687bdddc-sv4xt", - "prometheus": "openshift-user-workload-monitoring/user-workload", - "service": "cert-manager" - }, - "value": [ - 1747897178.158, - "1747897156" - ] - }, - ... - { - "metric": { - "__name__": "up", - "container": "cert-manager-controller", - "endpoint": "9402", - "instance": "10.131.0.65:9402", - "job": "cert-manager", - "namespace": "cert-manager", - "pod": "cert-manager-b687bdddc-sv4xt", - "prometheus": "openshift-user-workload-monitoring/user-workload", - "service": "cert-manager" - }, - "value": [ - 1747897178.158, - "1" - ] - } - ] - } -} -``` - -In OpenShift web console, you can also view these metrics by navigating to the "Observe" -> "Metrics" page, and filter the metrics of each operands with `{job=""}`, `{instance=""}` or other advanced query expressions. diff --git a/docs/performance-guidelines.md b/docs/performance-guidelines.md new file mode 100644 index 000000000..6aa77bf94 --- /dev/null +++ b/docs/performance-guidelines.md @@ -0,0 +1,41 @@ +# Performance Guidelines + +Concurrency and resource-efficiency patterns for this operator. + +## Dual controller architecture + +- **Core cert-manager** uses openshift/library-go factory controllers started with a single worker: `go controller.Run(ctx, 1)` in `pkg/operator/starter.go`. +- **Optional operands** (IstioCSR, TrustManager) share one controller-runtime manager when feature gates enable them (`pkg/operator/setup_manager.go`). +- Do not add parallel workers to library-go controllers without evaluating library-go expectations. + +## Informer resync + +- Shared informer resync interval is `10 * time.Minute` (`resyncInterval` in `pkg/operator/starter.go`). +- Avoid lowering resync globally — it increases API server load across all watched resources. + +## Cache label filtering + +- Operand controllers configure controller-runtime cache with label selectors on `common.ManagedResourceLabelKey` (`app`) in `setup_manager.go`. +- When adding new watched types, prefer label-filtered caches over cluster-wide watches. +- ConfigMaps intentionally use unfiltered cache with event predicates — follow this pattern if adding similar exceptions. + +## Requeue behavior + +- Recoverable reconcile errors use `RequeueAfter` via `HandleReconcileResult` in `pkg/controller/common/reconcile_result.go`. +- Irrecoverable errors return without requeue — avoid tight retry loops on permanent failures. +- Pass explicit `requeueDuration` per controller; do not rely on controller-runtime default backoff for business-logic retries. + +## E2E parallelism + +- E2E tests run serially: Makefile uses `ginkgo -p 1` for `test-e2e`. +- Do not increase E2E parallelism without evaluating cluster resource contention and operand namespace isolation. + +## Codegen and build + +- Tools are built from vendor (`bin/` targets in Makefile) for reproducibility — avoid `go install` in CI recipes. +- `make generate` and `make manifests` can be expensive; run only when API or RBAC markers change. + +## Bindata and manifests + +- Operand manifests are embedded via bindata (`bindata/`). Large manifest changes affect binary size and startup decode time. +- Prefer Helm/Jsonnet update scripts (`hack/update-*-manifests.sh`) over hand-editing generated bindata YAML. diff --git a/docs/proxy.md b/docs/proxy.md deleted file mode 100644 index e6e3b28cf..000000000 --- a/docs/proxy.md +++ /dev/null @@ -1,52 +0,0 @@ -# Configuring egress proxy for Cert Manager Operator - -If a cluster wide egress proxy is configured on the OpenShift cluster, OLM automatically update all the operators' deployments with `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` environment variables. -Those variables are then propagated down to the cert-manager (operand) controllers by the cert manager operator. - -## Trusted Certificate Authority - -### Running operator - -Follow the instructions below to let Cert Manager Operator trust a custom Certificate Authority (CA). The operator's OLM subscription has to be already created. - -1. Create the configmap containing the CA bundle in `cert-manager` namespace. Run the following commands to [inject](https://docs.openshift.com/container-platform/4.12/networking/configuring-a-custom-pki.html#certificate-injection-using-operators_configuring-a-custom-pki) the CA bundle trusted by OpenShift into a configmap: - - ```bash - oc -n cert-manager create configmap trusted-ca - oc -n cert-manager label cm trusted-ca config.openshift.io/inject-trusted-cabundle=true - ``` - -2. Consume the created configmap in Cert Manager Operator's deployment by updating its subscription: - - ```bash - oc -n cert-manager-operator patch subscription --type='merge' -p '{"spec":{"config":{"env":[{"name":"TRUSTED_CA_CONFIGMAP_NAME","value":"trusted-ca"}]}}}' - ``` - - _Note_: Alternatively, you can also patch the `cert-manager-operator-controller-manager` deployment in the `cert-manager-operator` namespace. - `bash - oc set env deployment/cert-manager-operator-controller-manager TRUSTED_CA_CONFIGMAP_NAME=trusted-ca - ` - -3. Wait for the operator deployment to finish the rollout and verify that CA bundle is added to the existing controller: - - ```bash - oc get deployment -n cert-manager cert-manager -o=jsonpath={.spec.template.spec.'containers[0].volumeMounts'} | jq - [ - { - "mountPath": "/etc/pki/tls/certs/cert-manager-tls-ca-bundle.crt", - "name": "trusted-ca", - "subPath": "ca-bundle.crt" - } - ] - - oc get deployment -n cert-manager cert-manager -o=jsonpath={.spec.template.spec.volumes} | jq - [ - { - "configMap": { - "defaultMode": 420, - "name": "trusted-ca" - }, - "name": "trusted-ca" - } - ] - ``` diff --git a/docs/security-guidelines.md b/docs/security-guidelines.md new file mode 100644 index 000000000..a64ca26a7 --- /dev/null +++ b/docs/security-guidelines.md @@ -0,0 +1,58 @@ +# Security Guidelines + +Repo-specific security rules for the OpenShift cert-manager operator. + +## RBAC + +- Declare RBAC with `+kubebuilder:rbac` markers on controller files (e.g. `pkg/controller/certmanager/certmanager_controller.go`). Run `make manifests` to regenerate `config/rbac/role.yaml`. +- Prefer least privilege: add verbs only for resources the controller actually reconciles. +- Operand controllers (IstioCSR, TrustManager) use controller-runtime RBAC markers on `controller.go`; core cert-manager uses library-go factory controllers with the same marker pattern. + +## Managed resource labeling + +- Operand-owned resources use label key `app` (`common.ManagedResourceLabelKey`) with controller-specific values. +- Controllers filter watches via cache label selectors in `pkg/operator/setup_manager.go` to limit RBAC blast radius. +- ConfigMaps are an exception: unfiltered cache with predicate filtering (documented in setup_manager.go). + +## Network policies + +- `DefaultNetworkPolicy` on `CertManager` CR is immutable once set to `"true"` (CEL `XValidation` in `api/operator/v1alpha1/certmanager_types.go`). +- Default policies are deny-all egress; users must supply egress rules via `NetworkPolicies` field. +- Network policy YAML lives in `bindata/networkpolicies/`; reconciliation in `pkg/controller/certmanager/cert_manager_networkpolicy.go`. + +## TLS profiles + +- Cluster APIServer TLS profile is read from `config.openshift.io/APIServer` and propagated to cert-manager operand args via `pkg/tlsprofile/`. +- Do not hardcode cipher suites or min TLS version — use the TLS profile hook in `pkg/controller/common/tls_profile_hook.go`. + +## Cloud credentials + +- AWS/GCP ambient credentials use Cloud Credential Operator via `CredentialsRequest` (`pkg/controller/certmanager/credentials_request.go`). +- Cloud credential secret name is passed at runtime via `CloudCredentialSecret` flag (see `pkg/operator/starter.go`). +- See `docs/cloud_credentials.md` for operational setup. + +## Trusted CA / proxy + +- Trusted CA ConfigMap name is a runtime arg (`TrustedCAConfigMapName` in `pkg/operator/starter.go`). +- Prefer patching OLM Subscription env vars over editing Deployments directly (OLM reverts direct edits). +- See `docs/proxy.md` for proxy and CA injection patterns. + +## Secrets and logging + +- Never log Secret `.data`, private keys, or cloud credential contents in controllers or tests. +- Safe to log: resource names, namespaces, condition statuses, certificate subjects (not PEM bodies in CI logs). + +## Webhooks + +- TrustManager validating webhook logic is in `pkg/controller/trustmanager/webhooks.go`. +- Webhook tests use envtest/fake clients in `webhooks_test.go`. + +## Linting and scanning + +- `gosec` is enabled in `.golangci.yaml` — address findings or document justified exceptions. +- Run `make govulncheck` before release; vulnerability scan is part of the verify toolchain. + +## Feature gates + +- TechPreview operands (TrustManager, IstioCSR) are gated via `pkg/features/features.go` and `--unsupported-addon-features` runtime flag. +- Do not bypass feature gates in production code paths. diff --git a/docs/testing-guidelines.md b/docs/testing-guidelines.md new file mode 100644 index 000000000..568e206d8 --- /dev/null +++ b/docs/testing-guidelines.md @@ -0,0 +1,82 @@ +# Testing Guidelines + +Unit, API integration, and E2E test patterns. + +## Test tiers + +| Tier | Location | Command | Build tag | +|------|----------|---------|-----------| +| Unit | `pkg/**/*_test.go` | `make test-unit` | none | +| API integration | `test/apis/` | `make test-apis` | none (separate module) | +| E2E | `test/e2e/` | `make test-e2e` | `//go:build e2e` | + +Full CI path: `make test` (manifests, generate, vet, test-apis, test-unit). + +## Go workspace + +- Three modules in `go.work`: root, `test/`, `tools/`. +- E2E and API tests import the operator module; run tests from repo root via Makefile targets. + +## Unit tests + +- Place tests alongside code in `pkg/`. +- Use table-driven tests for validation and error classification. +- Counterfeiter fakes: run `make generate-fakes` when adding interfaces under `pkg/controller/`. +- Exclude e2e/apis from unit run: `-skip '(/apis|/e2e|/utils)'` in Makefile `test-unit` target. + +## API integration (envtest) + +- Suite bootstraps envtest with CRDs from `config/crd/bases/` (`test/apis/suite_test.go`). +- Uses Ginkgo + Gomega + komega matchers. +- Requires envtest assets: `make test-apis` downloads via `setup-envtest` (`ENVTEST_K8S_VERSION` default `1.32.0`). +- See `test/apis/README.md` for openshift/api test pattern reference. + +## E2E tests + +- Entry: `test/e2e/suite_test.go` with Ginkgo bootstrap. +- Shared helpers: `test/library/` (`kube_client.go`, `dynamic_resources.go`, `cert_utils.go`, `istiocsr.go`). +- Namespaces: operator `cert-manager-operator`, operand `cert-manager`. +- Operator deployment: `cert-manager-operator-controller-manager`. + +### Ginkgo labels + +Filter tests with `E2E_GINKGO_LABEL_FILTER` (Makefile default excludes ServiceMesh): + +| Label | Purpose | +|-------|---------| +| `Platform:Generic` | Runs on any supported platform | +| `Platform:AWS` | AWS-specific (DNS-01 credentials) | +| `Feature:TrustManager` | TrustManager operand | +| `Feature:IstioCSR` | Istio CSR operand | +| `Feature:IstioCSR-ServiceMesh` | OpenShift Service Mesh integration | +| `Feature:TLSProfile` | Cluster TLS profile propagation | +| `TechPreview` | Requires TechPreview feature gate | +| `TechPreview:Inverted` | Negative gate tests | +| `CredentialsMode:Mint` | CCO mint mode | + +- Individual tests use traceability labels like `ISTIOCSR-001`. +- Use `Ordered` contexts when tests depend on prior setup within a Describe block. + +### E2E conventions + +- Use `Eventually`/`Consistently` with explicit timeouts. +- Register cleanup via Ginkgo DeferCleanup or explicit teardown in Ordered suites. +- Testdata YAML lives under `test/e2e/testdata/`. +- Default timeout: `E2E_TIMEOUT` (2h in Makefile). + +## Coverage + +- Unit coverage: `make test-unit` with `-coverprofile cover.out`. +- E2E coverage image: `make image-build-coverage`, script `hack/e2e-coverage.sh`. + +## Lint before commit + +- `make lint` runs golangci-lint v2 (config in `.golangci.yaml`). +- `make verify` runs script checks, dep verification, fmt, vet. + +## Adding new E2E tests + +1. Check existing specs for reusable helpers in `test/library/`. +2. Add to an existing `Describe` with matching labels if behavior fits. +3. Use `//go:build e2e` at top of file. +4. Never log Secret data or private keys in test output.