Skip to content

Conversation

@yasun1
Copy link
Contributor

@yasun1 yasun1 commented Nov 13, 2025

Initial HyperFleet API Implementation

Summary

This PR introduces the complete initial implementation of the HyperFleet API - a REST API service for cluster lifecycle management. The API provides CRUD operations for clusters, node pools, and adapter status resources with PostgreSQL integration.

Architecture Overview

HyperFleet API follows a simple CRUD-only architecture with no business logic or event creation. The service acts as a pure data layer that enables horizontal scaling through stateless design. All orchestration logic is handled by the Sentinel operator, while adapters manage the specifics of cluster specifications.

Key Features

Core Functionality

  • Cluster Management: Full CRUD operations for cluster resources
  • NodePool Management: Create and manage node pools within clusters
  • Adapter Status Reporting: Kubernetes-style condition-based status aggregation
  • Database Migrations: GORM-based migration system with versioning
  • Pagination & Search: List endpoints with pagination and search capabilities
  • OpenAPI Integration: TypeSpec-generated OpenAPI 3.0 specification with embedded spec

Technical Stack

  • Language: Go 1.24.9
  • API Definition: OpenAPI 3.0.3 (generated from TypeSpec)
  • Code Generation: openapi-generator-cli v7.16.0
  • Database: PostgreSQL with GORM ORM
  • Testing: Gomega + Resty for integration tests
  • Container Runtime: Podman

Project Structure

hyperfleet-api/
├── cmd/hyperfleet-api/              # Application entry point and CLI commands
│   ├── environments/           # Environment-specific configurations
│   ├── server/                  # HTTP server implementation
│   └── migrate/                 # Database migration command
├── pkg/
│   ├── api/                     # API models and OpenAPI generated code
│   ├── dao/                     # Data access layer (GORM)
│   ├── db/                      # Database setup and migrations
│   ├── handlers/                # HTTP request handlers
│   ├── services/                # Business logic layer
│   ├── auth/                    # Authentication and authorization middleware
│   └── config/                  # Configuration management
├── openapi/                     # OpenAPI specification source
├── test/
│   ├── integration/             # Integration test suite
│   └── factories/               # Test data factories
├── plugins/                     # Service plugin registry

API Endpoints

Clusters

  • GET /api/hyperfleet/v1/clusters - List clusters with pagination/search
  • POST /api/hyperfleet/v1/clusters - Create cluster
  • GET /api/hyperfleet/v1/clusters/{cluster_id} - Get cluster by ID
  • GET /api/hyperfleet/v1/clusters/{cluster_id}/statuses - List adapter statuses
  • POST /api/hyperfleet/v1/clusters/{cluster_id}/statuses - Create adapter status

NodePools

  • GET /api/hyperfleet/v1/nodepools - List all node pools
  • GET /api/hyperfleet/v1/clusters/{cluster_id}/nodepools - List node pools by cluster
  • POST /api/hyperfleet/v1/clusters/{cluster_id}/nodepools - Create node pool
  • GET /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id} - Get node pool
  • GET /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses - List all adapter statuses for nodepools
  • POST /api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses - Create or update adapter status

Database Schema

Clusters Table

  • Core fields: id, kind, name, spec (JSONB), labels (JSONB)
  • Version control: generation, status_observed_generation
  • Status fields: status_phase, status_last_transition_time, status_updated_at, status_adapters (JSONB)
  • Audit fields: created_at, updated_at, created_by, updated_by

NodePools Table

  • Core fields: id, kind, name, spec (JSONB), labels (JSONB)
  • Relationships: owner_id (cluster reference)
  • Status fields: status_phase, status_last_transition_time, status_updated_at
  • Audit fields: created_at, updated_at, created_by, updated_by

Adapter Status Table

  • Fields: id, cluster_id, adapter, metadata (JSONB), conditions (JSONB)
  • Audit fields: created_at, updated_at

Testing

Integration Tests

  • ✅ Cluster CRUD operations
  • ✅ NodePool CRUD operations
  • ✅ Adapter status reporting
  • ✅ Pagination and search functionality
  • ✅ OpenAPI specification validation
  • ✅ Metadata endpoint

Test Infrastructure

  • Testcontainers for PostgreSQL integration
  • Factory pattern for test data generation
  • Mock OCM client for authorization testing
  • JWT token generation for authentication

GitHub Actions

  • OpenAPI parser workflow for specification validation

Documentation

  • README.md: Comprehensive project documentation with API examples
  • PREREQUISITES.md: Setup requirements and dependencies
  • CLAUDE.md: AI assistant context and code generation templates
  • docs/: Additional documentation for DAO patterns, migrations, and testcontainers

Code Generation

The project includes templates for generating:

  • API handlers
  • DAO layer
  • Services
  • Migrations
  • Tests
  • Mocks
  • Presenters

Deployment

Local Development

make binary      # Build the binary
make run         # Run migrations and start server
make test        # Run unit tests
make test-integration  # Run integration tests

Container Build

  • Dockerfile: Main application container
  • Dockerfile.openapi: OpenAPI code generation container

Migration Notes

This is the initial implementation, so no migration from previous versions is required.

Checklist

  • All API endpoints implemented and tested
  • Database migrations created
  • Integration tests passing
  • OpenAPI specification validated
  • Documentation complete

Related Issues

Initial project implementation - no related issues.

@yasun1
Copy link
Contributor Author

yasun1 commented Nov 13, 2025

$ make test 
EMPTY pkg/api
EMPTY pkg/api/openapi
EMPTY pkg/api/presenters
EMPTY pkg/auth
EMPTY pkg/client/ocm
PASS pkg/config.TestConfigReadStringFile (0.00s)
PASS pkg/config.TestConfigReadIntFile (0.00s)
PASS pkg/config.TestConfigReadBoolFile (0.00s)
PASS pkg/config.TestConfigReadQuotedFile (0.00s)
PASS pkg/config (cached)
PASS pkg/controllers.TestControllerFramework (0.00s)
PASS pkg/controllers (cached)
EMPTY pkg/dao
EMPTY pkg/dao/mocks
EMPTY pkg/db
EMPTY pkg/db/db_context
EMPTY pkg/db/db_session
EMPTY pkg/db/migrations
EMPTY pkg/db/mocks
EMPTY pkg/db/transaction
PASS pkg/errors.TestErrorFormatting (0.00s)
PASS pkg/errors.TestErrorFind (0.00s)
PASS pkg/errors (cached)
PASS pkg/handlers.TestClusterNodePoolsHandler_Get/Success_-_Get_nodepool_by_cluster_and_nodepool_ID (0.00s)
PASS pkg/handlers.TestClusterNodePoolsHandler_Get/Error_-_Cluster_not_found (0.00s)
PASS pkg/handlers.TestClusterNodePoolsHandler_Get/Error_-_NodePool_not_found (0.00s)
PASS pkg/handlers.TestClusterNodePoolsHandler_Get/Error_-_NodePool_belongs_to_different_cluster (0.00s)
PASS pkg/handlers.TestClusterNodePoolsHandler_Get (0.00s)
PASS pkg/handlers (cached)
EMPTY pkg/logger
PASS pkg/services.TestSQLTranslation (0.00s)
PASS pkg/services (cached)
EMPTY pkg/util
EMPTY cmd/hyperfleet
EMPTY cmd/hyperfleet/clone
PASS cmd/hyperfleet/environments.TestLoadServices (0.03s)
PASS cmd/hyperfleet/environments
EMPTY cmd/hyperfleet/environments/registry
EMPTY cmd/hyperfleet/migrate
EMPTY cmd/hyperfleet/servecmd
EMPTY cmd/hyperfleet/server
EMPTY cmd/hyperfleet/server/logging

DONE 14 tests in 5.991s

@yasun1
Copy link
Contributor Author

yasun1 commented Nov 13, 2025

$ make test-integration TESTFLAGS="-count=1"
TESTCONTAINERS_RYUK_DISABLED=true OCM_ENV=integration_testing gotestsum --format short-verbose -- -p 1 -ldflags -s -v -timeout 1h -count=1 \
                        ./test/integration
I1113 10:58:11.879593   31870 framework.go:69] Initializing integration_testing environment
I1113 10:58:11.881425   31870 testcontainer.go:44] Starting PostgreSQL testcontainer...
2025/11/13 10:58:12 github.com/testcontainers/testcontainers-go - Connected to docker: 
  Server Version: 5.3.2
  API Version: 1.41
  Operating System: fedora
  Total Memory: 1947 MB
  Testcontainers for Go Version: v0.33.0
  Resolved Docker Host: unix:///var/run/docker.sock
  Resolved Docker Socket Path: /var/run/docker.sock
  Test SessionID: c9498e600dff90b79d1201fd7c24ae4811a277e963e23297c9a785cc4c1eda63
  Test ProcessID: 5c002a54-155f-471e-bfe7-2d19a5055cdf
2025/11/13 10:58:12 🐳 Creating container for image postgres:14.2
2025/11/13 10:58:12 ✅ Container created: 2ea193d5a3ab
2025/11/13 10:58:12 🐳 Starting container: 2ea193d5a3ab
2025/11/13 10:58:13 ✅ Container started: 2ea193d5a3ab
2025/11/13 10:58:13 ⏳ Waiting for container id 2ea193d5a3ab image: postgres:14.2. Waiting for: &{timeout:<nil> deadline:0x140004b4630 Strategies:[0x1400069e450]}
2025/11/13 10:58:14 🔔 Container is ready: 2ea193d5a3ab
I1113 10:58:14.440771   31870 testcontainer.go:69] PostgreSQL testcontainer started at: postgres://hyperfleet:foobar-bizz-buzz@localhost:44237/hyperfleet?sslmode=disable
I1113 10:58:14.450505   31870 testcontainer.go:101] Running database migrations on testcontainer...
I1113 10:58:14.467221   31870 testcontainer.go:106] Testcontainer database initialized successfully
I1113 10:58:14.467239   31870 framework.go:148] Using Mock OCM Authz Client
I1113 10:58:14.467247   31870 framework.go:173] Disabling Sentry error reporting
I1113 10:58:14.497076   31870 openapi.go:40] Loaded fully resolved OpenAPI specification from embedded pkg/api/openapi/api/openapi.yaml
I1113 10:58:14.497116   31870 openapi.go:50] Loaded OpenAPI UI HTML from embedded file
I1113 10:58:14.497938   31870 api_server.go:143] Serving without TLS at localhost:8000
I1113 10:58:14.498070   31870 healthcheck_server.go:56] Serving HealthCheck without TLS at localhost:8083
PASS test/integration.TestClusterStatusPost (0.02s)
PASS test/integration.TestClusterStatusGet (0.02s)
PASS test/integration.TestNodePoolStatusPost (0.01s)
PASS test/integration.TestNodePoolStatusGet (0.02s)
PASS test/integration.TestAdapterStatusPaging (0.04s)
PASS test/integration.TestAdapterStatusIdempotency (0.02s)
PASS test/integration.TestAdapterStatusPagingEdgeCases (0.05s)
PASS test/integration.TestClusterGet (0.01s)
PASS test/integration.TestClusterPost (0.01s)
PASS test/integration.TestClusterPaging (0.04s)
PASS test/integration.TestClusterListSearch (0.03s)
PASS test/integration.TestClusterSearchSQLInjection (0.02s)
PASS test/integration.TestClusterDuplicateNames (0.01s)
PASS test/integration.TestClusterBoundaryValues (0.02s)
PASS test/integration.TestCompatibilityGet (0.01s)
PASS test/integration.TestCompatibilityNoAuth (0.01s)
PASS test/integration.TestControllerBasicAuth (0.01s)
PASS test/integration.TestControllerClusterLifecycle (0.01s)
PASS test/integration.TestMetadataGet (0.01s)
PASS test/integration.TestNodePoolPost (0.01s)
PASS test/integration.TestNodePoolPaging (0.05s)
PASS test/integration.TestNodePoolListSearch (0.04s)
PASS test/integration.TestNodePoolsByClusterId (0.06s)
PASS test/integration.TestGetNodePoolByClusterIdAndNodePoolId (0.02s)
PASS test/integration.TestOpenAPIGet (0.01s)
PASS test/integration.TestOpenAPIUIGet (0.01s)
I1113 10:58:15.055039   31870 api_server.go:149] Web server terminated
I1113 10:58:15.055065   31870 testcontainer.go:141] Stopping PostgreSQL testcontainer...
2025/11/13 10:58:15 🐳 Terminating container: 2ea193d5a3ab
2025/11/13 10:58:15 🚫 Container terminated: 2ea193d5a3ab
I1113 10:58:15.211856   31870 testcontainer.go:145] PostgreSQL testcontainer stopped
PASS test/integration

DONE 26 tests in 7.168s

Copy link
Contributor

@rh-amarin rh-amarin left a comment

Choose a reason for hiding this comment

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

Trying to submit comments that appear as "pending" on my GitHub interface

This commit consolidates the migration to HyperFleet v2 architecture with the following changes:

- Remove v1 event-driven architecture components (events table, controllers, event DAOs)
- Cleanup unused CI/CD pipelines (.tekton, GitHub workflows)
- Remove legacy secrets, templates, and build scripts
- Standardize naming conventions (nodePool -> node_pool)
- Update documentation (CLAUDE.md, PREREQUISITES.md, README.md)
- Remove deprecated RHTAP integration files
- Simplify codebase structure for pure REST API approach

This migration removes the event-based orchestration in favor of Sentinel-driven
lifecycle management, aligning with the new HyperFleet architecture design.

fix: Update Dockerfile to use hyperfleet-api binary name

- Change COPY from hyperfleet to hyperfleet-api
- Update ENTRYPOINT to use /usr/local/bin/hyperfleet-api
- Update LABEL name to hyperfleet-api
- Update summary and description to HyperFleet API
- Add secrets directory to .gitignore
- Add /pkg/api/openapi/ to .gitignore
- Remove 61 generated files from git tracking
- Developers must run 'make generate' after cloning/pulling
- Update README with code generation requirements

This reduces PR noise and follows best practices for
managing auto-generated code.

docs: Update README with code generation requirements

- Add 'make generate' step to Initial Setup section
- Add important note about generated code not being tracked in git
- Update code generation workflow documentation
- Clarify that developers must run 'make generate' after cloning/pulling
}' | jq

# 3. Report adapter status
curl -X POST http://localhost:8000/api/hyperfleet/v1/clusters/$CLUSTER_ID/statuses \
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to add created_at and updated_at

curl -X POST http://localhost:8000/api/hyperfleet/v1/clusters/$CLUSTER_ID/statuses   -H "Content-Type: application/json"   -d '{
    "adapter": "hive-adapter",
    "observed_generation": 1,
    "conditions": [
      {
        "adapter": "hive-adapter",
        "type": "Ready",
        "status": "True",
        "observed_generation": 1,
        "reason": "ClusterProvisioned",
        "message": "Cluster successfully provisioned", 
        "created_at":"2025-11-17T15:04:05Z",
        "updated_at":"2025-11-17T15:04:05Z"
      }
    ]
  }' | jq

Copy link
Collaborator

Choose a reason for hiding this comment

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

I remember we talked about last_transition_time in the past, is it the created_at value? If yes, created_at is a bit of confusion for customers as I have no idea if created_at means my resources' created_at or current message's created_at. If it means the message's created_at, adapter have no idea if the message created or not as it sends POST all the time, unless it has to query the statues before it posting the status. It makes the logic more complex.

- Rename cmd/hyperfleet directory to cmd/hyperfleet-api
- Update go.mod module path to github.com/openshift-hyperfleet/hyperfleet-api
- Replace all 223 import path occurrences across 81 Go files
- Update Makefile ldflags, binary, and install targets
- Verify build compiles successfully with new module name

This ensures naming consistency across:
- Repository name: hyperfleet-api
- Binary name: hyperfleet-api
- Go module: github.com/openshift-hyperfleet/hyperfleet-api
- Command directory: cmd/hyperfleet-api

update

update
@rh-amarin rh-amarin merged commit d1dc4f2 into openshift-hyperfleet:main Nov 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants