Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ secrets
/pkg/api/openapi/
/data/generated/

# Ignore extracted OpenAPI spec (generated by make generate from hyperfleet-api-spec module)
/openapi/openapi.yaml

# Ignore generated mock files
*_mock.go

Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ make run-no-auth # Build, migrate, and run without auth

### Code Generation
```
make generate # Regenerate Go models from openapi/openapi.yaml (oapi-codegen)
make generate # Extract schema from hyperfleet-api-spec module, then run oapi-codegen
make generate-mocks # Regenerate mock implementations (go generate)
make generate-all # Both of the above
```
Expand Down Expand Up @@ -91,7 +91,8 @@ plugins/ # Plugin registration (init-based)
nodepools/plugin.go
generic/plugin.go
openapi/
openapi.yaml # SOURCE spec (TypeSpec output, 32KB, uses $ref)
README.md # Schema import, code generation, and validation details
openapi.yaml # Not in git — generated by make generate
oapi-codegen.yaml # Code generation config
test/
integration/ # Integration tests (testcontainers)
Expand Down Expand Up @@ -159,6 +160,5 @@ Create feature branches from `main`. PRs target `main`.
- **Never set** `status.phase` manually — calculated from adapter conditions
- **Never create** direct DB connections — use `SessionFactory.New(ctx)` for transaction participation
- **FIPS required**: build with `CGO_ENABLED=1 GOEXPERIMENT=boringcrypto`
- **Spec source of truth**: `openapi/openapi.yaml` (TypeSpec output); generated spec at `pkg/api/openapi/api/openapi.yaml` is never edited
- **TypeSpec** definitions live in a separate `hyperfleet-api-spec` repository
- **Spec source of truth**: `hyperfleet-api-spec` Go module; update `go.mod` to change spec versions — see [openapi/README.md](openapi/README.md) for full details on schema import, code generation, and validation
- **Tool versions** managed by Bingo (`.bingo/`) — don't manually install oapi-codegen or golangci-lint
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- OpenAPI schema is now consumed from the `hyperfleet-api-spec` Go module (`v1.0.12`) for code generation; `openapi/openapi.yaml` is extracted during `make generate` and is no longer tracked in git ([#155](https://github.com/openshift-hyperfleet/hyperfleet-api/pull/155))
- Replaced OCM SDK authentication handler with standalone JWT middleware, removing `ocm-sdk-go` dependency and its transitive dependencies (`glog`, `bluemonday`, `json-iterator`) ([#120](https://github.com/openshift-hyperfleet/hyperfleet-api/pull/120))
- Upgraded JWT library from `golang-jwt/jwt/v4` to `golang-jwt/jwt/v5` ([#120](https://github.com/openshift-hyperfleet/hyperfleet-api/pull/120))
- Refactored `AdapterStatusDao.Upsert()` to accept a pre-fetched existing record, moving lookup and `LastTransitionTime` preservation logic to the service layer ([#119](https://github.com/openshift-hyperfleet/hyperfleet-api/pull/119))
Expand Down
14 changes: 4 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ HyperFleet API is a **stateless REST API** serving as the pure CRUD data layer f

- **Language**: Go 1.24+ with FIPS crypto (`CGO_ENABLED=1 GOEXPERIMENT=boringcrypto`)
- **Database**: PostgreSQL 14.2 with GORM ORM
- **API Spec**: TypeSpec → OpenAPI 3.0.3 → oapi-codegen → Go models
- **API Spec**: TypeSpec → `hyperfleet-api-spec` Go module → oapi-codegen → Go models
- **Architecture**: Plugin-based route registration, transaction-per-request middleware

## Critical First Steps
Expand Down Expand Up @@ -100,24 +100,18 @@ Each resource registers via `init()` function:

- Transaction middleware creates GORM transactions for **write requests only** (POST/PUT/PATCH/DELETE): `pkg/db/transaction_middleware.go`
- Read requests (GET) skip transaction creation for performance
- OpenAPI source spec: `openapi/openapi.yaml` (TypeSpec-generated, uses `$ref`)
- Generated code: `pkg/api/openapi/` (models + embedded spec) — **never edit**
- Codegen config: `openapi/oapi-codegen.yaml` — uses oapi-codegen (not openapi-generator-cli)
- OpenAPI spec and code generation: see [openapi/README.md](openapi/README.md) — run `make generate` before building; generated files in `pkg/api/openapi/` are **never edited**
- Status aggregation: Service layer synthesizes `Available` and `Ready` conditions from adapter reports
- Plugin-based: each resource type registers routes/services in `plugins/*/plugin.go`

Two `openapi.yaml` files exist:
- `openapi/openapi.yaml` — source (32KB, has `$ref`)
- `pkg/api/openapi/api/openapi.yaml` — generated (44KB, fully resolved, embedded in binary)

## Boundaries

- **Never edit** files in `pkg/api/openapi/` — they are generated by `make generate`
- **Never edit** `*_mock.go` files — regenerate with `make generate-mocks`
- **Never set** `status.phase` manually — it is calculated from adapter conditions
- **Never create** direct DB connections — use `SessionFactory.New(ctx)` for transaction participation
- **FIPS required**: always build with `CGO_ENABLED=1 GOEXPERIMENT=boringcrypto`
- **Spec source of truth**: `openapi/openapi.yaml` (TypeSpec output); don't modify generated spec
- **OpenAPI spec**: not tracked in git — see [openapi/README.md](openapi/README.md) for spec versioning and generation details

## Related CLAUDE.md Files

Expand All @@ -131,4 +125,4 @@ Subdirectories contain context-specific guidance that loads when you work in tho
- `plugins/CLAUDE.md` — Plugin registration (init-based)
- `test/CLAUDE.md` — Test conventions, factories, and environment variables
- `charts/CLAUDE.md` — Helm chart testing and configuration
- `openapi/CLAUDE.md` — OpenAPI spec, code generation, and oapi-codegen config
- `openapi/README.md` — OpenAPI schema import, code generation, schema validation, and oapi-codegen config
3 changes: 0 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ WORKDIR /app
# ubi9-micro doesn't include CA certificates; copy from builder for TLS (e.g. Google Pub/Sub)
COPY --from=builder /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
COPY --from=builder /build/bin/hyperfleet-api /app/hyperfleet-api
COPY --from=builder /build/openapi/openapi.yaml /app/openapi/openapi.yaml

ENV HYPERFLEET_SERVER_OPENAPI_SCHEMA_PATH=/app/openapi/openapi.yaml

USER 65532:65532

Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@ verify-migrations: ## Verify migration files follow project conventions

.PHONY: generate
generate: $(OAPI_CODEGEN) ## Generate OpenAPI types using oapi-codegen
$(GO) mod download
rm -rf pkg/api/openapi
mkdir -p pkg/api/openapi
mkdir -p pkg/api/openapi openapi
@rm -f openapi/openapi.yaml
@cp "$$($(GO) list -m -f '{{.Dir}}' github.com/openshift-hyperfleet/hyperfleet-api-spec)/schemas/core/openapi.yaml" openapi/openapi.yaml
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Noticed you added embed.FS in schemas/schemas.go in the spec repo, but here we're copying via go list -m + cp instead of reading through it. The ticket also mentions hack/extract-schema.go reading from specschemas. Did something change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will update the Jira ticket, good point

$(OAPI_CODEGEN) --config openapi/oapi-codegen.yaml openapi/openapi.yaml

.PHONY: generate-mocks
Expand Down Expand Up @@ -159,7 +162,7 @@ run/docs: check-container-tool ## Run swagger and host the api spec
@echo "Please open http://localhost:8081/"
# Port 8081 instead of 80: ports <1024 are privileged and fail with rootless Podman.
# Port 8080 is avoided since it's used by the health endpoint server.
$(CONTAINER_TOOL) run -d -p 8081:8080 -e SWAGGER_JSON=/hyperfleet.yaml -v $(PWD)/openapi/hyperfleet.yaml:/hyperfleet.yaml swaggerapi/swagger-ui
$(CONTAINER_TOOL) run -d -p 8081:8080 -e SWAGGER_JSON=/openapi.yaml -v $(PWD)/openapi/openapi.yaml:/openapi.yaml swaggerapi/swagger-ui

.PHONY: cmds
cmds: ## Build all binaries under cmd/
Expand All @@ -180,6 +183,7 @@ clean: ## Delete temporary generated files
pkg/api/openapi \
data/generated/openapi/*.json \
secrets \
openapi/openapi.yaml \

.PHONY: secrets
secrets: ## Initialize secrets directory with default values
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ HyperFleet API - Simple REST API for cluster lifecycle management. Provides CRUD

- **Language**: Go 1.24+
- **API Definition**: OpenAPI 3.0
- **Code Generation**: openapi-generator-cli
- **Code Generation**: oapi-codegen
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- **Database**: PostgreSQL with GORM ORM
- **Container Runtime**: Podman
- **Testing**: Gomega + Resty
Expand All @@ -35,7 +35,7 @@ hyperfleet-api/
│ ├── db/ # Database setup and migrations
│ ├── handlers/ # HTTP request handlers
│ └── services/ # Business logic
├── openapi/ # API specification source
├── openapi/ # Generated artifacts from hyperfleet-api-spec module
├── test/ # Integration tests and factories
├── docs/ # Detailed documentation
└── Makefile # Build automation
Expand Down Expand Up @@ -171,6 +171,7 @@ This project uses [pre-commit](https://pre-commit.io/) for code quality checks.
- **[Deployment](docs/deployment.md)** - Container images, Kubernetes deployment, and configuration
- **[Authentication](docs/authentication.md)** - Development and production auth
- **[Logging](docs/logging.md)** - Structured logging, OpenTelemetry integration, and data masking
- **[Partner Schema Validation](openapi/README.md#partner-schema-validation)** - How to supply a partner-specific OpenAPI schema for runtime `spec` field validation

### Additional Resources

Expand Down
4 changes: 3 additions & 1 deletion docs/api-operator-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,8 @@ The API uses a two-step process to validate specs:
- **Schema missing or invalid**: API logs a warning and starts without validation. Specs are stored without schema validation.
- Startup is **non-blocking** — missing or invalid schema files do not prevent API startup

For details on how schemas are imported for code generation and which schema components map to each resource type, see [openapi/README.md](../openapi/README.md) in this repository.

---

## 4. Deployment Checklist
Expand Down Expand Up @@ -1053,7 +1055,7 @@ This section provides a **quick reference** for common API-specific issues and t
| **Pods stuck in init phase: `database does not exist`** | Database not created | Create database: `CREATE DATABASE hyperfleet;` |
| **Pods stuck in init phase: `connection timeout`** | Database connection retry settings too low | Increase database connection retry settings using `--db-conn-retry-attempts` and `--db-conn-retry-interval` flags in the init container command |
| **High API latency, slow responses** | Resource limits, database slow queries, or connection pool exhausted | Check metrics: `curl http://<api-service>:9090/metrics \| grep hyperfleet_api_request_duration_seconds`. Check resources: `kubectl top pods -n hyperfleet-system`. Check slow queries: `kubectl logs -n hyperfleet-system deployment/hyperfleet-api \| grep "slow query"`. Resolution: Increase resource limits/replicas, add database indexes, or increase `--db-max-open-connections` (default: 50). |
| **400 Bad Request** | Resource spec doesn't match OpenAPI schema | Check loaded schema: `kubectl logs -n hyperfleet-system deployment/hyperfleet-api \| grep "OPENAPI_SCHEMA_PATH"`. Retrieve schema: `kubectl exec -n hyperfleet-system deployment/hyperfleet-api -- cat /etc/hyperfleet/schemas/openapi.yaml`. Validate and fix spec. |
| **400 Bad Request** | Resource spec doesn't match OpenAPI schema | Check the loaded schema path: `kubectl logs -n hyperfleet-system deployment/hyperfleet-api \| grep "schema_path"`. Retrieve and inspect the schema: `kubectl exec -n hyperfleet-system deployment/hyperfleet-api -- cat $HYPERFLEET_SERVER_OPENAPI_SCHEMA_PATH`. Validate and fix spec. |
| **401 Unauthorized** | Missing or invalid JWT token | Verify authentication is enabled (`server.jwt.enabled=true`). If production, ensure valid JWT token is provided. Reference: [Authentication Guide](authentication.md). |
| **404 Not Found** | Resource doesn't exist | Verify resource ID is correct. Check if resource was deleted: `curl http://<api-service>:8000/api/hyperfleet/v1/clusters/$CLUSTER_ID`. |
| **409 Conflict** | Concurrent update or generation mismatch | Retry with exponential backoff. Ensure only one controller updates the same resource. |
Expand Down
3 changes: 1 addition & 2 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ HyperFleet API is configured via environment variables and configuration files.

The API validates cluster and nodepool `spec` fields against an OpenAPI schema. This allows different providers (GCP, AWS, Azure) to have different spec structures.

- **Configuration:** `server.openapi_schema_path` (supports config file, env var, or CLI flag)
- **Default:** `openapi/openapi.yaml` (provider-agnostic base schema)
Schema validation is optional and file-path based. Set `--server-openapi-schema-path` (or `HYPERFLEET_SERVER_OPENAPI_SCHEMA_PATH`) to a provider-specific OpenAPI schema file to enable it. If the path is missing or the file is unreadable, the API logs a warning and starts without validation — startup is non-blocking.

See [Configuration Guide](config.md) for all configuration options.

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103
github.com/oapi-codegen/runtime v1.2.0
github.com/onsi/gomega v1.27.1
github.com/openshift-hyperfleet/hyperfleet-api-spec v1.0.12
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/client_model v0.3.0
github.com/spf13/cobra v1.8.1
Expand All @@ -44,6 +45,7 @@ require (
)

require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.43.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.43.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.43.0 // indirect
Expand All @@ -58,7 +60,6 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/openshift-hyperfleet/hyperfleet-api-spec v1.0.12 h1:Eo0LvsU2lz2fJiy9LNiGSIqiOhHMTINK7P70VR+hkMA=
github.com/openshift-hyperfleet/hyperfleet-api-spec v1.0.12/go.mod h1:KITzIAd8HcMpH5lXdHFjgk45dvL6XLpP3wwz8iK+KCI=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
Expand Down
39 changes: 10 additions & 29 deletions openapi/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
# Claude Code Guidelines for OpenAPI

## Two openapi.yaml Files
See [README.md](README.md) for the full reference on how schemas are imported, generated, and used for validation.

1. **Source**: `openapi.yaml` in this directory (32KB, uses `$ref`) — generated by TypeSpec from a separate `hyperfleet-api-spec` repo. This is the source of truth for this repository.
2. **Generated**: `pkg/api/openapi/api/openapi.yaml` (44KB, fully resolved) — produced by `make generate`, embedded in binary via `//go:embed`
## Key Rules

**Never edit either file directly.** The source comes from TypeSpec; the generated one is overwritten by `make generate`.
- **Never edit** `openapi.yaml` or `pkg/api/openapi/openapi.gen.go` — both are overwritten by `make generate`.
- **Never copy** the spec file manually — `make generate` extracts it from the Go module cache automatically.
- **To change the schema**, update TypeSpec in `hyperfleet-api-spec`, publish a new release, bump the version in `go.mod`, then run `make generate-all`.

## Code Generation
## Quick Commands

Tool: **oapi-codegen** (not openapi-generator-cli)
Config: `oapi-codegen.yaml` in this directory

```
make generate # Regenerates pkg/api/openapi/ from openapi/openapi.yaml
make generate-all # generate + generate-mocks
```shell
make generate # Extract schema from spec module, then run oapi-codegen
make generate-mocks # Regenerate mock implementations
make generate-all # Both of the above
```

Generation produces:
- `pkg/api/openapi/openapi.gen.go` — Go model structs + client + embedded spec

## Config Details

From `oapi-codegen.yaml`:
- Package: `openapi`
- Output: `pkg/api/openapi/openapi.gen.go`
- Generates: models (yes), chi-server (no), client (yes), embedded-spec (yes)
- Compatibility: `old-merge-schemas: true` (inlines allOf), `old-aliasing: true` (type defs not aliases)

## Updating the API

1. Update TypeSpec definitions in `hyperfleet-api-spec` repo
2. Copy generated `openapi.yaml` to this directory
3. Run `make generate-all`
4. Update handlers/services/DAOs for any new or changed fields
Loading