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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .agentready-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# AgentReady configuration for ansible-operator-plugins
# See https://github.com/ambient-code/agentready for documentation
#
# openapi_specs is excluded because this project has no public HTTP API surface.
# See docs/decisions/adr-0004-openapi-not-applicable.md.
excluded_attributes:
- openapi_specs
15 changes: 15 additions & 0 deletions .claude/rules/controller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
paths:
- "internal/ansible/controller/**"
---

# Controller Module Rules

- Reconcile loop must update status conditions on every exit path (Running, Failure, or Successful).
- Use `APIReader` (direct API reads, bypassing cache) for status updates to prevent stale writes.
- Controllers are named `<kind>-<version>-controller` (lowercased), registered via `controller.New`.
- Finalizer lifecycle: added on first reconcile if configured, removed only after a successful finalizer run on deletion.
- The reconciler requires a `playbook_on_stats` event somewhere in the event stream (recorded as seen, then verified after the stream closes); if missing, reconciliation fails.
- `SetCondition` is a no-op when Type/Status/Reason are unchanged, except `FailureConditionType` which always updates.
- Per-CR reconcile period override via annotation: `ansible.sdk.operatorframework.io/reconcile-period: <duration>`.
- Validate at package level: `go test ./internal/ansible/controller/ -short`, `go vet ./internal/ansible/controller/`.
15 changes: 15 additions & 0 deletions .claude/rules/downstream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
paths:
- "openshift/**"
---

# Downstream (OpenShift) Module Rules

- The `openshift/` directory is an independent build overlay with its own `go.mod`, `vendor/`, `Makefile`, and `Dockerfile`.
- `openshift/` Go code does not import the root module's packages at build time (separate module).
- Commits during downstream rebases MUST use `UPSTREAM: <carry>:` or `UPSTREAM: <drop>:` prefix convention.
- DO NOT hand-edit `openshift/vendor/` or `openshift/release/ansible/ansible_collections/` -- these are generated.
- Downstream Make targets: `update-collections`, `generate-requirements`, `check-requirements`, `check-collections`.
- Dependency updates may need to happen in both `go.mod` and `openshift/go.mod`.
- Rebase workflow uses `openshift/hack/rebase_upstream.sh`.
- See `docs/references/downstream-sync.md` for the full rebase workflow.
16 changes: 16 additions & 0 deletions .claude/rules/proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
paths:
- "internal/ansible/proxy/**"
---

# Proxy Module Rules

- The proxy binds to `localhost` only. DO NOT change the bind address -- it has no authentication of its own.
- DO NOT remove either `Authorization` header stripping call in the handler chain. Both are required.
- Handler chain is assembled inside-out; ordering is load-bearing.
- Adding middleware that modifies request bodies must go between the authorization-stripping and owner-injection layers, never outside the cache handler.
- The cache handler implements a 6-second timeout with fallback to the API server.
- Owner references are injected via the proxy to track dependent resources for garbage collection.
- Metrics API binds to `localhost:5050`; do not expose externally.
- HTTP/2 is disabled by default (`--enable-http2=false`); `ReadHeaderTimeout: 5s` on all HTTP servers.
- Validate at package level: `go test ./internal/ansible/proxy/ -short`.
16 changes: 16 additions & 0 deletions .claude/rules/runner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
paths:
- "internal/ansible/runner/**"
---

# Runner Module Rules

- `ansible-runner` must be on `$PATH`. Detected via `exec.LookPath` at run time, not startup.
- Input directory layout: `/tmp/ansible-operator/runner/<group>/<version>/<kind>/<namespace>/<name>/`.
- Parameters in `env/extravars` are snake_cased from the CR spec. The `markUnsafe` feature wraps strings as `{"__ansible_unsafe": "<value>"}`.
- Each reconcile creates a Unix socket at `/tmp/ansibleoperator-<ident>` for the event API HTTP server.
- Events channel is buffered with capacity 1000 and a 10-second write timeout to prevent blocking.
- Status events (those without a UUID) are silently dropped; only JobEvents with a UUID are forwarded.
- After each run, a `latest` symlink is created under `artifacts/`.
- Use `sync.RWMutex` for concurrent data structures; never use `sync.Map`.
- Validate at package level: `go test ./internal/ansible/runner/ -short`.
14 changes: 14 additions & 0 deletions .claude/rules/scaffold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
paths:
- "pkg/plugins/ansible/v1/**"
---

# Scaffold/Plugin Module Rules

- Templates in `pkg/plugins/ansible/v1/scaffolds/internal/templates/` are the source of truth for scaffolded operator projects.
- After modifying any template, always run `make generate` to regenerate `testdata/`.
- DO NOT hand-edit files in `testdata/` -- they are generated artifacts.
- `pkg/` must never import `internal/`. The plugin scaffolding package is a public API consumed by downstream projects.
- Scaffold output includes: Dockerfile, watches.yaml, Makefile, roles directory, molecule tests.
- Pod security defaults: `runAsNonRoot`, `seccompProfile: RuntimeDefault`, drops all capabilities.
- Validate after template changes: run `make generate`, review the intended output with `git diff -- testdata/` (a diff here is expected for a real template change, not a failure), commit the template and generated-output changes together, then run `make verify`.
26 changes: 26 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "if [[ \"$CLAUDE_FILE_PATH\" == *.go ]]; then gofmt -w \"$CLAUDE_FILE_PATH\" 2>/dev/null; fi"
}
]
}
],
"PreToolUse": [
{
"matcher": "Shell",
"hooks": [
{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -qE '(rm -rf /|git push.*--force.*main|git reset --hard)' && echo 'BLOCK: destructive operation detected' && exit 1 || true"
}
]
}
]
}
}
58 changes: 58 additions & 0 deletions .claude/skills/add-controller-feature/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Add a Controller Reconcile Feature

## When to Use

When adding new behavior to the reconciliation loop, such as new status
conditions, event handling, or ansible-runner interaction patterns.

## Steps

1. **Identify the reconcile stage** where the feature belongs. The reconcile
loop in `internal/ansible/controller/reconcile.go` follows this flow:
- Read CR via `APIReader`
- Check finalizer lifecycle
- Start ansible-runner subprocess
- Consume events from the event API
- Update status conditions

2. **Implement the feature** in the appropriate file:
- `reconcile.go` -- main reconcile logic
- `status/` -- status condition management
- `controller.go` -- controller setup and watches

3. **Add tests** alongside source in the same package:

```bash
go test ./internal/ansible/controller/ -run TestMyFeature -short
```

## Key Rules

- Reconcile loop MUST update status conditions on every exit path.
- Use `APIReader` for status updates to prevent stale writes from cached data.
- Three condition types: `Running`, `Failure`, `Successful` -- transition between them.
- Use `sync.RWMutex` for concurrent data structures; never `sync.Map`.
- Log messages: Error/Fatal/Info/Warn must begin with uppercase. Error messages
(`errors.New`, `fmt.Errorf`) must begin with lowercase and not end with a period.
- Logging: use `logf.Log.WithName("...")`. V(0) for lifecycle, V(1) for events, V(2) for bodies.

## Reference Files

- Reconcile loop: `internal/ansible/controller/reconcile.go`
- Status management: `internal/ansible/controller/status/`
- Controller setup: `internal/ansible/controller/controller.go`

## Design Doc Enforcement

If this change affects reconcile flow, event handling, or component
boundaries, review and update the architecture doc in
`docs/architecture/components.md` in the same PR so the design stays
accurate for future agents.

## Validation

```bash
go vet ./internal/ansible/controller/
go test ./internal/ansible/controller/ -short
make verify
```
41 changes: 41 additions & 0 deletions .claude/skills/add-watch-entry/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Add a New GVK Watch Entry

## When to Use

When adding support for a new Kubernetes resource (Group/Version/Kind) to be
managed by an Ansible playbook or role.

## Steps

1. **Define the watch** in `watches.yaml`:

```yaml
- group: example.com
version: v1alpha1
kind: MyResource
role: roles/myresource
# or: playbook: playbooks/myresource.yml
```

2. **Create the Ansible role** under `roles/myresource/` with standard structure
(`tasks/main.yml`, `defaults/main.yml`, etc.).

3. **Reference files**:
- Watch schema and defaults: `internal/ansible/watches/watches.go`
- Example watch config: `testdata/ansible/memcached-operator/watches.yaml`
- Watch loading/validation: `internal/ansible/watches/watches_test.go`

## Key Rules

- A watch must specify exactly one of `playbook` or `role`, never both.
- Default values: `manageStatus: true`, `watchDependentResources: true`,
`snakeCaseParameters: true`, `maxRunnerArtifacts: 20`, `ansibleVerbosity: 2`.
- Per-GVK concurrency: set via `MAX_CONCURRENT_RECONCILES_<KIND>_<GROUP>` env var.
- Supports `${VAR}` environment variable interpolation via `os.Expand`.

## Validation

```bash
go test ./internal/ansible/watches/ -short
make verify
```
43 changes: 43 additions & 0 deletions .claude/skills/downstream-carry/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Downstream Carry Patch

## When to Use

When making a change that is specific to the OpenShift downstream fork and must
persist across upstream rebases.

## Steps

1. **Make changes** in the `openshift/` directory (it has its own `go.mod`,
`vendor/`, `Makefile`, and `Dockerfile`).

2. **Commit with the carry prefix**:

```bash
git commit -m "UPSTREAM: <carry>: description of the change"
```

3. **Validate downstream targets**:

```bash
cd openshift && make check-requirements check-collections
```

## Commit Prefix Convention

| Prefix | Meaning |
|---|---|
| `UPSTREAM: <carry>:` | Preserve this change across future rebases |
| `UPSTREAM: <drop>:` | Accept upstream version; discard this delta on next rebase |

## Key Rules

- `openshift/` Go code does not import the root module's packages at build time.
- DO NOT hand-edit `openshift/vendor/` or `openshift/release/ansible/ansible_collections/`.
- Rebase workflow: `openshift/hack/rebase_upstream.sh`.
- If a carry patch touches both root and `openshift/`, split into separate commits.

## Reference

- Full rebase workflow: `docs/references/downstream-sync.md`
- Downstream overview: `openshift/README.md`
- ADR: `docs/decisions/adr-0001-upstream-downstream-mirror.md`
54 changes: 54 additions & 0 deletions .claude/skills/scaffold-template/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Modify Scaffold Templates

## When to Use

When changing the generated output of `ansible-operator init` or
`ansible-operator create api` -- the files scaffolded for new operator projects.

## Steps

1. **Edit templates** in `pkg/plugins/ansible/v1/scaffolds/internal/templates/`.
These are Go template files that produce the scaffolded project structure.

2. **Rebuild the binary**:

```bash
make build
```

3. **Regenerate testdata** (this runs the scaffolder against sample inputs):

```bash
make generate
```

4. **Verify no unintended changes**:

```bash
git diff testdata/
make verify
```

## Key Rules

- DO NOT hand-edit files in `testdata/` -- they are generated from templates.
- `pkg/` must never import `internal/`. The plugin package is a public API.
- Scaffold output includes: Dockerfile, watches.yaml, Makefile, roles directory,
molecule tests, RBAC manifests.
- Pod security defaults in scaffolded output: `runAsNonRoot`,
`seccompProfile: RuntimeDefault`, drops all capabilities.
- Every new `.go` file must have an Apache 2.0 license header.

## Reference Files

- Template directory: `pkg/plugins/ansible/v1/scaffolds/internal/templates/`
- Plugin entry point: `pkg/plugins/ansible/v1/init.go`
- Generated samples: `testdata/memcached-molecule-operator/`

## Validation

```bash
make generate
git diff -- testdata/ # review the generated diff; expected for intended template changes
make verify
```
51 changes: 51 additions & 0 deletions .claude/skills/update-dependencies/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Update Go Dependencies

## When to Use

When bumping a Go module dependency version, adding a new dependency, or
responding to a Dependabot alert.

## Steps

1. **Update `go.mod`**:

```bash
go get <module>@<version>
```

2. **Tidy and vendor**:

```bash
go mod tidy
go mod vendor
```

3. **Run full validation**:

```bash
make verify
```

4. **Check for dirty tree** (CI will fail if vendor is stale):

```bash
git diff --exit-code vendor/
```

## Key Rules

- This project vendors all dependencies. Always commit the updated `vendor/`.
- `make fix` runs `go mod tidy` but does NOT run `go mod vendor`.
- Dependency updates may need to happen in both `go.mod` (root) and
`openshift/go.mod` (downstream overlay).
- Key dependencies to be careful with: `controller-runtime`, `client-go`,
`operator-lib`, `kubebuilder/v4`.
- Tool versions are managed by bingo in `.bingo/` -- update those separately.

## Validation

```bash
go mod tidy && go mod vendor
make verify
git diff --exit-code
```
10 changes: 10 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
# See https://docs.coderabbit.ai/reference/configuration for all fields and default values
knowledge_base:
code_guidelines:
filePatterns:
- "docs/domain/*.md"
- "docs/architecture/*.md"
- "docs/references/*.md"
- "docs/AOP_DEVELOPMENT.md"
- "docs/AOP_TESTING.md"
Loading