Skip to content

Latest commit

 

History

History
executable file
·
91 lines (71 loc) · 5.97 KB

File metadata and controls

executable file
·
91 lines (71 loc) · 5.97 KB

AGENTS.md

Project Identity

HyperFleet API is a stateless REST API serving as the pure CRUD data layer for HyperFleet cluster lifecycle management. It persists clusters, node pools, and adapter statuses to PostgreSQL - no business logic, no events. Sentinel handles orchestration; adapters execute and report back.

  • Language: Go 1.26+ with FIPS crypto (CGO_ENABLED=1 GOEXPERIMENT=boringcrypto)
  • Database: PostgreSQL 14.2 with GORM ORM
  • API Spec: TypeSpec -> hyperfleet-api-spec Go module -> oapi-codegen -> Go models
  • Architecture: Container-based dependency injection, config-driven route registration, service-owned mutation transactions

Critical First Steps

Generated code is not checked into git. Before building, testing, or even running go mod download:

make generate-all    # Generates OpenAPI types + mock implementations

Setup sequence for a fresh clone:

  1. make generate-all - generate OpenAPI models and mocks
  2. go mod download - fetch dependencies
  3. make install-hooks - install pre-commit hooks
  4. make db/setup - start local PostgreSQL container
  5. make build - build binary
  6. ./bin/hyperfleet-api migrate - apply database migrations
  7. make run-no-auth - start server without authentication

Tool versions are pinned in tools/go.mod and invoked via go tool -modfile=tools/go.mod <name>.

Verification

Command What it does Requires DB?
make verify go vet + gofmt check No
make lint golangci-lint No
make test Unit tests No
make test-integration Integration tests (testcontainers) No (auto-creates)
make test-helm Helm chart lint + template validation No
make verify-all verify + lint + test (single command) No
make test-all lint + test + test-integration + test-helm Auto-creates

Quick feedback: make verify-all. Full pre-push: make test-all.

Source of Truth

Topic Where to look
OpenAPI spec & code generation openapi/README.md
Handler pipeline & validation pkg/handlers/CLAUDE.md
Service interface & status aggregation pkg/services/CLAUDE.md
DAO patterns & session access pkg/dao/CLAUDE.md
SessionFactory & transactions pkg/db/CLAUDE.md
Error constructors & RFC 9457 pkg/errors/CLAUDE.md
Test conventions & factories test/CLAUDE.md
Helm chart testing charts/CLAUDE.md
Development setup docs/development.md
Deployment docs/deployment.md
Authentication docs/authentication.md
Contributing CONTRIBUTING.md

Architecture Context

Request flow: Router -> Middleware (logging, auth, timeout) -> Handler -> Service transaction -> DAO -> GORM -> PostgreSQL

  • Startup wiring: servecmd.runServe loads config -> container.NewContainer(cfg, closer) -> BuildAPIServer(...) -> server.NewRouterFromConfig + server.NewAPIServer. Shutdown uses pkg/closer (LIFO order): readiness probe -> health drain -> metrics drain -> API drain -> JWT close -> OTel flush -> DB pool close
  • Entity routes are config-driven: declared in config.yaml under entities:, registered at startup via registry.LoadDescriptors(), routes auto-generated by RegisterEntityRoutes. No per-entity Go code needed.
  • Service transactions: each mutation service opens one db.TxRunner.Do boundary and commits before returning success; reads skip explicit transactions for performance. Request middleware applies timeouts only.
  • Status aggregation: Service layer synthesizes Available, Reconciled, and LastKnownReconciled conditions from adapter reports
  • Public routes (/openapi, /openapi.html, metadata) bypass auth and schema validation; everything else is gated by both, auth outermost
  • Container (cmd/hyperfleet-api/container) lazily constructs and caches dependencies. Holds DAOs, services, schema validator, JWT handler - but does NOT own their lifecycle. Shutdown ordering is handled by pkg/closer in the composition root.
  • Server package (cmd/hyperfleet-api/server) deliberately does not import pkg/config - takes narrow cfg interfaces instead. Put anything needing *config.ApplicationConfig in the composition root.

Boundaries

  • Never edit files in pkg/api/openapi/ or *_mock.go - regenerate with make generate-all
  • 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: hyperfleet-api-spec Go module; update go.mod to change spec versions - see openapi/README.md
  • Tool versions pinned in tools/go.mod - don't manually install oapi-codegen or golangci-lint

Gotchas

  • make generate-all is mandatory - build and tests fail without it; generated code is gitignored
  • pkg/api/openapi/ is read-only - never hand-edit, always regenerate
  • Service methods return *errors.ServiceError, not stdlib error - use constructor functions from pkg/errors/errors.go; see pkg/errors/CLAUDE.md for the full reference
  • apiServer.Close() severs in-flight requests - always use Shutdown(ctx) with a drain budget; Close() is only the force-close fallback after Shutdown times out
  • Container has no Close() - lifecycle (JWT handler, session factory, OTel) is managed by pkg/closer in the composition root, not on Container
  • Integration tests share a single testcontainer - test.NewHelper(t) initializes once per process via sync.Once; the PostgreSQL container, API server, and JWK mock are shared across all tests in the suite
  • Schema validation requires the OpenAPI spec file - if HYPERFLEET_SERVER_OPENAPI_SCHEMA_PATH is unset, validation is skipped; TestMain in integration tests auto-sets it to test/validation-schema.yaml