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
9 changes: 9 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,18 @@ formatters:
enable:
- gofmt
- goimports
- gci
settings:
gofmt:
simplify: true
# Local addition on top of org standard: repo-specific import grouping
gci:
custom-order: true
sections:
- standard
- default
- prefix(github.com/openshift-hyperfleet)
- prefix(github.com/openshift-hyperfleet/hyperfleet-adapter)
exclusions:
generated: lax
paths:
Expand Down
11 changes: 11 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
default_install_hook_types: [pre-commit, commit-msg]

repos:
- repo: https://github.com/openshift-hyperfleet/hyperfleet-hooks
rev: v0.2.1
hooks:
- id: hyperfleet-commitlint
stages: [commit-msg]
- id: hyperfleet-gofmt
- id: hyperfleet-golangci-lint
- id: hyperfleet-go-vet
77 changes: 77 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# HyperFleet Adapter

Event-driven Kubernetes resource manager. Consumes CloudEvents from a message broker, executes configured actions (K8s resource apply, HyperFleet API calls, Maestro ManifestWork), and reports status back.

Go 1.25.0 · Cobra CLI · Viper config · golangci-lint (bingo-managed) · Tekton CI (Konflux)

## Verification Checklist

```bash
make fmt # Format code + imports (golangci-lint fmt with gci)
make lint # golangci-lint (config: .golangci.yml)
make test # Unit tests with race detection (excludes test/ dir)
make test-integration # Integration tests via testcontainers (needs Docker/Podman)
make build # Build binary → bin/hyperfleet-adapter
```

`make test-all` runs all of the above plus `make test-helm`. Pre-commit hooks: `make install-hooks`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider documenting pre-commit hook caveats.

Based on the linked hyperfleet-api repository, .pre-commit-config.yaml may include internal hooks (e.g., rh-pre-commit) that external contributors cannot access and should skip or comment out. Consider adding a note to help external contributors avoid setup friction.

📝 Suggested documentation addition
-`make test-all` runs all of the above plus `make test-helm`. Pre-commit hooks: `make install-hooks`.
+`make test-all` runs all of the above plus `make test-helm`. Pre-commit hooks: `make install-hooks`. (External contributors: if hooks reference internal Red Hat repos, comment those entries out in `.pre-commit-config.yaml`.)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`make test-all` runs all of the above plus `make test-helm`. Pre-commit hooks: `make install-hooks`.
`make test-all` runs all of the above plus `make test-helm`. Pre-commit hooks: `make install-hooks`. (External contributors: if hooks reference internal Red Hat repos, comment those entries out in `.pre-commit-config.yaml`.)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` at line 17, Add a short note to AGENTS.md near the
development/setup section (around the `make install-hooks` mention) explaining
that the repository's `.pre-commit-config.yaml` may reference internal hooks
(for example `rh-pre-commit`) that external contributors cannot access, and
instruct them to either comment out or remove those internal hook entries or run
`pre-commit install --forbid-protected-refs`/use `SKIP=` env var before
installation; reference the exact files/entries `.pre-commit-config.yaml` and
the internal hook name `rh-pre-commit` so readers can locate and edit the
config.


## CLI

Subcommands: `adapter serve`, `adapter config-dump`, `adapter version`. Config paths via `-c`/`HYPERFLEET_ADAPTER_CONFIG` and `-t`/`HYPERFLEET_TASK_CONFIG`. All flags have env var equivalents — run `adapter serve --help`.

Dry-run mode: `adapter serve --dry-run-event event.json` processes a single event with mock clients, no broker or cluster needed.

## Two Config Files

Adapter loads two configs merged at startup: deployment config (`adapter-config.yaml` — infra, clients, logging) and task config (`adapter-task-config.yaml` — params, preconditions, resources, post-actions). Override rules differ — see Gotchas. Templates in `configs/`.

## Source of Truth

| Topic | Location |
|-------|----------|
| Configuration reference | `docs/configuration.md` |
| Adapter authoring guide | `docs/adapter-authoring-guide.md` |
| Metrics & Prometheus queries | `docs/metrics.md` |
| Alerts | `docs/alerts.md` |
| Runbook | `docs/runbook.md` |
| Helm chart | `charts/` |
| CI pipelines | `.tekton/` (Konflux/Tekton PipelineRuns) |

## Code Conventions

@docs/conventions/logging.md
@docs/conventions/cel.md

### Error Handling

`pkg/errors` provides ServiceError constructors for API-style errors with numeric codes and HTTP status:

```go
errors.NotFound("cluster %s not found", clusterID) // → *ServiceError
errors.KubernetesError("failed to get resource: %v", err)
```

IMPORTANT: These return `*ServiceError`, not `error`. Use `.AsError()` to convert.

## Boundaries

- Every CLI flag must have a corresponding env var (Viper convention)

## Gotchas

- IMPORTANT: **Two config files, different override rules.** Deployment config supports env/flag overrides via Viper. Task config is pure YAML — env vars do nothing there. Mixing them up wastes debugging time.
- IMPORTANT: **Naming conventions differ by layer.** Config YAML: `snake_case` (`subscription_id`). Go code: `CamelCase` (`SubscriptionID`). Helm values: `camelCase` (`subscriptionId`). Wrong casing silently drops values.
- **Tracing default mismatch.** Binary defaults to tracing ON. Helm chart defaults to OFF. Local `adapter serve` will attempt OTLP export unless you set `HYPERFLEET_TRACING_ENABLED=false`.
- **Integration tests build a container on first run.** `make test-integration` calls `make image-integration-test` if `INTEGRATION_ENVTEST_IMAGE` is unset. First run takes minutes.

## Non-Obvious Packages

- `internal/executor/` — event execution pipeline (params → preconditions → resources → post-actions)
- `internal/transportclient/` — unified apply interface abstracting K8s direct and Maestro ManifestWork

## Links

- [Architecture Docs](https://github.com/openshift-hyperfleet/architecture)
- [HyperFleet API Spec](https://github.com/openshift-hyperfleet/hyperfleet-api-spec)
- [Broker Library](https://github.com/openshift-hyperfleet/hyperfleet-broker)
264 changes: 1 addition & 263 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,263 +1 @@
# Claude Code Context for HyperFleet Adapter

This file provides AI agent context for working with the HyperFleet Adapter codebase.

## Quick Reference

**Language:** Go 1.25.0+
**Build System:** Make
**Test Framework:** Go testing + Testcontainers
**Linter:** golangci-lint (managed via bingo)

## Essential Commands

### Build and Verify

```bash
make build # Build binary → bin/hyperfleet-adapter
make fmt # Format code with goimports
make lint # Run golangci-lint
make tidy # Tidy dependencies
make verify # Run fmt-check and vet
```

### Testing

```bash
make test # Unit tests only (fast)
make test-integration # Integration tests with envtest (unprivileged, CI-safe)
make test-all # All checks: lint, unit, integration, and helm tests
make test-coverage # Generate coverage report
```

### Container Images

```bash
make image # Build image
make image-push # Build and push to quay.io/openshift-hyperfleet/hyperfleet-adapter
QUAY_USER=myuser make image-dev # Build and push to personal Quay
```

## Validation Checklist

Before committing code, run these in order:

1. `make fmt` - Format code
2. `make lint` - Check linting
3. `make test` - Unit tests
4. `make test-integration` - Integration tests (or `make test-all`)
5. `make build` - Ensure binary builds

## Project Structure

```
pkg/ # Exported packages (can be imported by other projects)
├── constants/ # Shared constants (annotations, labels)
├── errors/ # Error handling utilities with codes
├── health/ # Health and metrics HTTP servers
├── logger/ # Structured logging with context
├── metrics/ # Prometheus metrics recorder
├── otel/ # OpenTelemetry tracing
├── utils/ # General utilities
└── version/ # Version info

internal/ # Internal packages (not importable)
├── configloader/ # YAML config loading + validation
├── criteria/ # Precondition and CEL evaluation
├── dryrun/ # Dry-run API client and recording transport
├── executor/ # Event execution pipeline (phases)
├── generation/ # Generation tracking
├── hyperfleetapi/ # HyperFleet API client
├── k8sclient/ # Kubernetes client wrapper
├── maestroclient/ # Maestro ManifestWork client
├── manifest/ # Manifest generation and rendering
└── transportclient/ # Unified apply interface

cmd/adapter/ # Main entry point
test/integration/ # Integration tests
charts/ # Helm chart
configs/ # Config templates and examples
scripts/ # Build scripts
```

## Code Conventions

### Logging

Always use structured logging with context:

```go
logger.Info(ctx, "message")
logger.Error(ctx, "error occurred")

// Add structured fields via context or logger chaining
ctx = logger.WithLogField(ctx, "cluster_id", clusterID)
log := logger.With("resource", name).With("error", err)
```

Never use `fmt.Printf` or `log.Println` - use the logger package.

### Error Handling

Use typed error constructors from `pkg/errors`:

```go
return errors.NotFound("cluster %s not found", clusterID)
return errors.KubernetesError("failed to get resource: %v", err)
return errors.ConfigurationError("invalid config: %v", err)
```

Always propagate errors up with context, don't silently ignore them.

### Context Propagation

All long-running operations must accept `context.Context` as first parameter:

```go
func ProcessEvent(ctx context.Context, event cloudevents.Event) error
```

Use `logger.WithLogField(ctx, key, value)` or `logger.WithLogFields(ctx, fields)` to attach structured fields to the context for logging.

### CEL Expressions

CEL expressions in config must use exact field names from the CEL environment:

- `<param_name>` - extracted parameters are injected as top-level variables (e.g., `clusterID`, not `params.clusterID`)
- `resources.*` - discovered resources (post-phase), e.g., `resources.myCluster`
- `adapter.*` - adapter metadata (name, status, etc.)
- Custom functions: `now()` (RFC3339 timestamp), `toJson(val)`, `dig(map, "dot.path")`

### Resource Names

Configuration uses **snake_case** (Viper convention):

- `adapter.name`, `clients.hyperfleet_api.base_url`

Code uses **camelCase** (Go convention):

- `AdapterName`, `HyperFleetAPIBaseURL`

Helm uses camelCase (Helm convention)

- `adapterConfig`, `baseUrl`

## Boundaries - Do NOT Do This

### Generated Files

- **Do not modify** files in `.bingo/` - these are managed by bingo
- **Do not edit** `go.sum` manually - use `make tidy`

### Testing

- **Do not skip integration tests** in PRs - they run in CI and catch real issues
- **Do not mock Kubernetes clients** in integration tests - use envtest/K3s
- **Do not use time.Sleep** in tests - use context timeouts or test utilities

### Configuration

- **Do not add hardcoded values** - use configuration or environment variables
- **Do not add CLI flags** without also supporting env vars (Viper convention)
- **Do not change config field names** without migration path (breaking change)

### Dependencies

- **Do not add dependencies** without license check (Apache 2.0 compatible only)
- **Do not update hyperfleet-broker** without verifying metric compatibility
- **Do not vendor dependencies** - this project uses Go modules

### Git

- **Do not force push** to main or release branches
- **Do not amend published commits** - create new commits
- **Do not commit** `.env` files, credentials, or sensitive data

### API Changes

- **Do not break backward compatibility** in config schema without version bump
- **Do not change CloudEvent types** without coordinating with HyperFleet API team
- **Do not modify status payload schema** without API spec update

## Testing Patterns

### Table-Driven Tests

Use subtests for multiple scenarios:

```go
func TestFunction(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
{"valid", "input", "output", false},
{"invalid", "", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Function(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("unexpected error: %v", err)
}
if got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}
```

### Integration Test Setup

Use testcontainers for real dependencies:

```go
func TestIntegration(t *testing.T) {
ctx := context.Background()
env := testutil.SetupEnvTest(t, ctx) // Sets up K8s API
defer env.Teardown()
// Test with real K8s client
}
```

## Common Tasks

### Adding a New Parameter Extractor

1. Add extractor to `internal/executor/param_extractor.go`
2. Add tests in same package
3. Document in `docs/configuration.md`
4. Add example in `configs/adapter-task-config-template.yaml`

### Adding a New Precondition Type

1. Add evaluator to `internal/criteria/evaluator.go`
2. Add tests with mock clients
3. Update schema in `internal/configloader/types.go`
4. Document in adapter authoring guide

### Adding Metrics

1. Define metric in `pkg/metrics/recorder.go`
2. Instrument code with metric calls
3. Add Prometheus query to `docs/metrics.md`
4. Add recommended alert to `docs/alerts.md`

## Release Process

Version follows semver (MAJOR.MINOR.PATCH):

- Version is derived from `APP_VERSION` in `Makefile` (auto-set from git tags via `git describe`)
- Update `CHANGELOG.md` with release notes
- Tag: `git tag -a v0.2.0 -m "Release v0.2.0"`
- Push: `git push origin v0.2.0`
- CI builds and pushes image with version tag

## Links

- [Architecture Docs](https://github.com/openshift-hyperfleet/architecture)
- [HyperFleet API Spec](https://github.com/openshift-hyperfleet/hyperfleet-api-spec)
- [Broker Library](https://github.com/openshift-hyperfleet/hyperfleet-broker)
@AGENTS.md
Loading