-
Notifications
You must be signed in to change notification settings - Fork 19
Adds agents.md for External-Secrets Operator #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,204 @@ | ||
| # AGENTS.md | ||
|
|
||
| ## Docs Index | ||
|
|
||
| Detailed domain-specific guidelines are in these files -- read them before working in the corresponding area: | ||
|
|
||
| - [docs/security-guidelines.md](docs/security-guidelines.md) -- Container security, RBAC, TLS, network policies, webhook security | ||
| - [docs/performance-guidelines.md](docs/performance-guidelines.md) -- Cache architecture, watch predicates, reconciliation patterns, drift detection | ||
| - [docs/error-handling-guidelines.md](docs/error-handling-guidelines.md) -- ReconcileError types, retry logic, status conditions, logging | ||
| - [docs/api-contracts-guidelines.md](docs/api-contracts-guidelines.md) -- CRD types, kubebuilder markers, CEL validation, code generation | ||
| - [docs/testing-guidelines.md](docs/testing-guidelines.md) -- Unit/API/E2E test tiers, frameworks, test helpers, CI integration | ||
| - [docs/integration-guidelines.md](docs/integration-guidelines.md) -- Bindata pattern, cert-manager, OpenShift platform, Bitwarden plugin | ||
|
|
||
| ## Project Overview | ||
|
|
||
| This is a Kubernetes operator (built with kubebuilder/controller-runtime) that deploys and manages the upstream [external-secrets](https://github.com/openshift/external-secrets) application on OpenShift clusters. The operator does not embed upstream code -- it manages upstream resources as static YAML manifests (bindata) applied imperatively. | ||
|
|
||
| Three controllers run in a single binary: | ||
|
|
||
| | Controller | Package | Watches | Purpose | | ||
| |---|---|---|---| | ||
| | `external-secrets-controller` | `pkg/controller/external_secrets/` | `ExternalSecretsConfig` CR + all managed resources | Installs/reconciles operand deployments, RBAC, services, webhooks, network policies | | ||
| | `external-secrets-manager` | `pkg/controller/external_secrets_manager/` | `ExternalSecretsManager` CR + `ExternalSecretsConfig` status | Aggregates controller statuses into a global status CR | | ||
| | `crd-annotator` | `pkg/controller/crd_annotator/` | ESO CRDs + `ExternalSecretsConfig` | Adds cert-manager CA injection annotations to operand CRDs (conditional) | | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ``` | ||
| api/v1alpha1/ CRD type definitions, conditions, shared types, deepcopy | ||
| api/v1alpha1/tests/ Declarative YAML test suites for CRD validation | ||
| bindata/ Static operand YAML manifests (compiled into Go via go-bindata) | ||
| cmd/ Operator entrypoint (main.go) | ||
| config/ Kustomize manifests (CRDs, RBAC, manager, samples, bundle) | ||
| hack/ Shell scripts (codegen, verification, CI helpers) | ||
| images/ci/ CI Dockerfiles (coverage-instrumented builds) | ||
| pkg/controller/ Controller implementations | ||
| client/ CtrlClient interface + counterfeiter fakes | ||
| common/ Shared utilities (errors, constants, decode helpers, drift detection) | ||
| commontest/ Shared test fixtures (TestExternalSecretsConfig, TestExternalSecretsManager) | ||
| crd_annotator/ CRD annotation controller | ||
| external_secrets/ Main operand reconciliation controller | ||
| external_secrets_manager/ Status aggregation controller | ||
| pkg/operator/ Manager setup, controller registration | ||
| pkg/version/ Build-time version info (ldflags) | ||
| test/apis/ API integration tests (Ginkgo + envtest) | ||
| test/e2e/ End-to-end tests (Ginkgo + live cluster) | ||
| test/utils/ E2E test helpers | ||
| tools/ Go module for build-time tool dependencies | ||
| ``` | ||
|
|
||
| ## Go Workspace and Module Layout | ||
|
|
||
| The repo uses `go.work` with four modules: `.`, `./cmd/external-secrets-operator`, `./test`, `./tools`. This means: | ||
|
|
||
| - `GOFLAGS` is cleared for test and fmt targets to avoid `-mod=vendor` conflicting with `go.work`. | ||
| - When adding a dependency, use `make update-dep PKG=pkg@version` to update across all modules, then `make update-vendor`. | ||
| - Vendoring is workspace-level (`go work vendor`), not per-module. | ||
| - All build-time tools (controller-gen, golangci-lint, ginkgo, etc.) are vendored and built from source via `go build -mod=vendor`. | ||
|
|
||
| ## Build System (Key Makefile Targets) | ||
|
|
||
| | Target | What it does | | ||
| |---|---| | ||
| | `make build` | Full build: generate + manifests + fmt + vet + compile binary | | ||
| | `make build-operator` | Compile binary only (no codegen, fastest iteration) | | ||
| | `make test` | Run unit + API integration tests (no cluster needed) | | ||
| | `make test-unit` | Unit tests only | | ||
| | `make test-apis` | API validation tests via envtest | | ||
| | `make test-e2e` | E2E tests against a live cluster | | ||
| | `make lint` | Run golangci-lint with all configured linters | | ||
| | `make lint-fix` | Run golangci-lint with auto-fix | | ||
| | `make update` | Full regeneration: codegen + manifests + operand manifests + bindata + bundle + docs | | ||
| | `make verify` | Run vet + fmt + verify-deps + verify-bindata + verify-generated + govulncheck + check-git-diff | | ||
| | `make update-vendor` | Update vendor directory across all workspace modules | | ||
| | `make update-dep PKG=x@v` | Update a single dependency across all modules | | ||
| | `make manifests` | Regenerate CRD YAML, RBAC, webhook configs from kubebuilder markers | | ||
| | `make generate` | Regenerate DeepCopy methods | | ||
| | `make docs` | Regenerate API reference docs | | ||
| | `make clean` | Remove bin/, _output/, cover.out | | ||
|
|
||
| After any code change, run `make update && make verify` to ensure all generated files are consistent. CI runs `check-git-diff` which fails if generated files are stale. | ||
|
|
||
| ## Code Style and Formatting | ||
|
|
||
| ### Import Order | ||
|
|
||
| Imports must follow the order enforced by `gci` in `.golangci.yml`: | ||
|
|
||
| 1. Standard library | ||
| 2. Third-party packages | ||
| 3. `github.com/openshift/external-secrets-operator` (project-local) | ||
| 4. Blank imports, dot imports, aliases, local module | ||
|
|
||
| ### Linting | ||
|
|
||
| The repo uses golangci-lint v2 with a comprehensive set of linters (see `.golangci.yml`). Key rules: | ||
|
|
||
| - **depguard** blocks `math/rand` (use `math/rand/v2`), `github.com/sirupsen/logrus`, and `github.com/pkg/errors` (use `errors`/`fmt`). | ||
| - **kubeapilinter** runs only on `api/v1alpha1/*` files. Use `//nolint:kubeapilinter` with a comment for intentional deviations. | ||
| - **golines** max line length is 200 characters. | ||
| - **gofmt** rewrites `interface{}` to `any` and `a[b:len(a)]` to `a[b:]`. | ||
| - Generated files are excluded in `lax` mode. | ||
| - Test files have relaxed rules for `gocyclo`, `errcheck`, `gosec`, `forcetypeassert`, and others. | ||
|
|
||
| ### File Headers | ||
|
|
||
| All `.go` files must include the Apache 2.0 license header from `hack/boilerplate.go.txt` (year 2025). | ||
|
|
||
| ### FIPS Build | ||
|
|
||
| Production builds use `hack/go-fips.sh` which enables `GOEXPERIMENT=strictfipsruntime` and build tags `strictfipsruntime,openssl` when the Go compiler supports it. Local dev builds without FIPS work but cannot be used in CI/production. | ||
|
|
||
| ## Naming Conventions | ||
|
|
||
| ### Go Packages | ||
|
|
||
| Controller packages use `snake_case`: `external_secrets`, `external_secrets_manager`, `crd_annotator`. This matches kubebuilder conventions. | ||
|
|
||
| ### Constants | ||
|
|
||
| - Controller names: `"external-secrets-controller"`, `"external-secrets-manager"`, `"crd-annotator"` (kebab-case in strings). | ||
| - Finalizer format: `<crd-plural>.<api-group>/<controller-name>` (e.g., `externalsecretsconfigs.operator.openshift.io/external-secrets-controller`). | ||
| - Asset name constants: `<resourceKind>_<resourceName>AssetName` in camelCase (e.g., `controllerDeploymentAssetName`). | ||
| - Environment variable constants: all-caps with suffix `EnvVarName` (e.g., `externalSecretsImageEnvVarName`). | ||
|
|
||
| ### Bindata YAML Files | ||
|
|
||
| Files in `bindata/external-secrets/resources/` follow the pattern: `<kind-lowercase>_<resource-name>.yml` (e.g., `deployment_external-secrets.yml`, `clusterrole_external-secrets-controller.yml`). Network policies in `bindata/external-secrets/` use `.yaml` extension. | ||
|
|
||
| ### CRD Object Names | ||
|
|
||
| Both `ExternalSecretsConfig` and `ExternalSecretsManager` are singletons named `"cluster"` (enforced by CEL). Constants `ExternalSecretsConfigObjectName` and `ExternalSecretsManagerObjectName` in `pkg/controller/common/constants.go` hold this value. | ||
|
|
||
| ## Architectural Patterns | ||
|
|
||
| ### Reconciler Structure | ||
|
|
||
| Every controller follows the same pattern: | ||
|
|
||
| 1. A `Reconciler` struct embedding `operatorclient.CtrlClient`. | ||
| 2. A `New(ctx, mgr)` constructor that builds the reconciler and client(s). | ||
| 3. A `SetupWithManager(mgr)` method that wires watches, predicates, and map functions. | ||
| 4. A `Reconcile(ctx, req)` method that fetches the primary CR and delegates to `processReconcileRequest`. | ||
| 5. An `updateStatus(ctx, obj)` method using `retry.RetryOnConflict`. | ||
|
|
||
| ### CtrlClient Interface | ||
|
|
||
| All controllers interact with Kubernetes through `pkg/controller/client.CtrlClient`, not the raw `controller-runtime client.Client`. This interface adds `UpdateWithRetry`, `StatusUpdate`, and `Exists` methods. Unit tests use counterfeiter-generated fakes (`pkg/controller/client/fakes/`). | ||
|
|
||
| To regenerate fakes after changing the interface: `go generate ./pkg/controller/client/...` | ||
|
|
||
| ### Resource Reconciliation Pattern | ||
|
|
||
| For each resource type (deployments, services, RBAC, etc.), the same flow is used: | ||
|
|
||
| 1. Decode static YAML from bindata (`common.Decode*ObjBytes(assets.MustAsset(...))`). | ||
| 2. Mutate the decoded object (set namespace, labels, annotations, images, env vars, security context). | ||
| 3. Check existence with `r.Exists()`. | ||
| 4. If new: `r.Create()`. If existing: compare with `common.HasObjectChanged()`, then `r.UpdateWithRetry()` if drifted. | ||
| 5. Record Kubernetes events for create/update operations. | ||
| 6. Wrap errors with `common.FromClientError()`. | ||
|
|
||
| ### Conditional Resources | ||
|
|
||
| Resources that depend on CR configuration use a slice of `{assetName string, condition bool}` structs. Only assets with `condition: true` are applied. Follow this pattern when adding new conditionally-created resources. | ||
|
|
||
| ## Common Pitfalls | ||
|
|
||
| 1. **Never return both `RequeueAfter` and a non-nil error** from `Reconcile`. Return one or the other. | ||
| 2. **Never edit generated files by hand**: `zz_generated.deepcopy.go`, `config/crd/bases/*.yaml`, `pkg/operator/assets/bindata.go`, `config/rbac/role.yaml`, `docs/api_reference.md`. Always use `make update`. | ||
| 3. **Always run `make update && make verify`** after any code change. CI will reject PRs with stale generated files. | ||
| 4. **Use the cached client for managed resources** (those with `app: external-secrets` label). Use the uncached client only for external resources like cert-manager Issuers or user-provided Secrets. | ||
| 5. **Add new watched resources to both `controllerManagedResources` and `buildCacheObjectList()`** in `pkg/controller/external_secrets/controller.go`. | ||
| 6. **Add new resource types to `HasObjectChanged`** in `pkg/controller/common/utils.go` with field-level comparison, not full `DeepEqual`. | ||
| 7. **Decode helpers panic on failure** -- this is intentional for build-time-constant assets. Do not add error handling around `Decode*ObjBytes` calls. | ||
| 8. **RBAC markers** (`+kubebuilder:rbac`) go in `pkg/controller/external_secrets/controller.go` for the operator's own permissions. Operand RBAC is in static YAML under `bindata/`. | ||
| 9. **The `go.work` workspace** means you cannot use `-mod=vendor` for `go test` or `go fmt`. The Makefile already clears `GOFLAGS` for affected targets. | ||
| 10. **Container tool defaults to `podman`** (`CONTAINER_TOOL ?= podman`), not Docker. Override with `CONTAINER_TOOL=docker` if needed. | ||
|
|
||
| ## PR and Contribution Expectations | ||
|
|
||
| - Run `make lint` and `make test` locally before submitting. | ||
| - Run `make verify` to ensure generated files are in sync. | ||
| - Add unit tests for new reconciliation logic using table-driven tests and `FakeCtrlClient`. | ||
| - Add API test cases in `api/v1alpha1/tests/<crd>/` for any new CRD field or validation rule. | ||
| - Add E2E test cases with appropriate Ginkgo labels for platform-specific tests. | ||
| - Follow existing error wrapping patterns: `common.FromClientError` for API calls, `common.NewIrrecoverableError` for config validation failures. | ||
| - Commit messages should reference the relevant Jira ticket (e.g., `OCPBUGS-12345: description`). | ||
| - PR reviewers/approvers are listed in `OWNERS`. | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| The operator reads these at runtime (typically set by OLM/CSV): | ||
|
|
||
| | Variable | Purpose | | ||
| |---|---| | ||
| | `RELATED_IMAGE_EXTERNAL_SECRETS` | Container image for the external-secrets operand | | ||
| | `RELATED_IMAGE_BITWARDEN_SDK_SERVER` | Container image for the Bitwarden SDK server | | ||
| | `OPERAND_EXTERNAL_SECRETS_IMAGE_VERSION` | Version label for operand resources | | ||
| | `BITWARDEN_SDK_SERVER_IMAGE_VERSION` | Version label for Bitwarden resources | | ||
| | `OPERATOR_IMAGE_VERSION` | Operator version string | | ||
| | `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` | Proxy fallback from OLM environment | | ||
|
|
||
| For local development, set at minimum `RELATED_IMAGE_EXTERNAL_SECRETS` via `t.Setenv()` in tests or shell export for `make run`. | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,51 @@ | ||||||||||
| # CLAUDE.md | ||||||||||
|
|
||||||||||
| @AGENTS.md | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clarify or remove the The 🔗 Proposed fix # CLAUDE.md
-@AGENTS.md
+See [AGENTS.md](AGENTS.md) for comprehensive architectural details.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| ## Build and Test Commands | ||||||||||
|
|
||||||||||
| When working in this repository, use these commands: | ||||||||||
|
|
||||||||||
| ### Before Committing | ||||||||||
| ```bash | ||||||||||
| make update && make verify | ||||||||||
| ``` | ||||||||||
| This regenerates all code, manifests, and bindata, then runs verification checks. CI will reject PRs with stale generated files. | ||||||||||
|
|
||||||||||
| ### Development Workflow | ||||||||||
| ```bash | ||||||||||
| make build # Full build with codegen | ||||||||||
| make build-operator # Fast rebuild (binary only, no codegen) | ||||||||||
| make test # Run unit + API tests (no cluster needed) | ||||||||||
| make lint # Run golangci-lint | ||||||||||
| make lint-fix # Auto-fix linting issues | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ### Testing | ||||||||||
| ```bash | ||||||||||
| make test-unit # Unit tests only | ||||||||||
| make test-apis # API validation tests (envtest) | ||||||||||
| make test-e2e # E2E tests (requires cluster) | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ### Dependency Management | ||||||||||
| ```bash | ||||||||||
| make update-vendor # Sync vendor across all workspace modules | ||||||||||
| make update-dep PKG=package@version # Update a dependency in all modules | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ## Claude Code Preferences | ||||||||||
|
|
||||||||||
| - Always run `make update && make verify` after code changes that affect: | ||||||||||
| - CRD definitions (`api/v1alpha1/`) | ||||||||||
| - Kubebuilder markers (`+kubebuilder:*`) | ||||||||||
| - Bindata YAML (`bindata/`) | ||||||||||
| - Generated code triggers | ||||||||||
|
|
||||||||||
| - Use `make lint-fix` to automatically fix formatting and linting issues before suggesting manual fixes | ||||||||||
|
|
||||||||||
| - The repository uses a Go workspace (`go.work`) with 4 modules. Never manually edit `GOFLAGS` or use `-mod=vendor` in test/fmt commands — the Makefile handles this | ||||||||||
|
|
||||||||||
| - Container builds default to `podman`. Override with `CONTAINER_TOOL=docker` if needed | ||||||||||
|
|
||||||||||
| - All build-time tools are vendored. Do not suggest installing tools globally | ||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add language identifier to the code fence.
The directory structure code block should specify a language identifier (e.g.,
textorplaintext) for proper rendering and linting compliance.📝 Proposed fix
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 28-28: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents