Skip to content

Conversation

@yingzhanredhat
Copy link
Contributor

@yingzhanredhat yingzhanredhat commented Dec 1, 2025

  1. Fix the error from presubmit jobs: lint/unit/integration
  • Fix some error from "make lint"
  • Remove $(GOLANGCI_LINT_BIN) to use golangci-lint directly to avoid it can't find the tool under the configured path
  1. pkg/db/db_session/testcontainer.go: fix the test-integration error

  2. Makefile: Prow only supports podman tool, it will meet error when run "make generate".So add -o APT::Sandbox::User=root to fix it

 make lint                    
golangci-lint run -D unused \
                ./pkg/api/openapi...
0 issues.
 yingzhan@yingzhan-mac  ~/ying-work/code/hyperfleet-api   ying-test ±✚  make test
CGO_ENABLED=1 GOEXPERIMENT=boringcrypto go install -ldflags="-X github.com/openshift-hyperfleet/hyperfleet-api/pkg/api.Version=87558ae-modified -X 'github.com/openshift-hyperfleet/hyperfleet-api/pkg/api.BuildTime=2025-12-01 11:30:35 UTC'" ./cmd/hyperfleet-api
# github.com/shoenig/go-m1cpu
../../../go/pkg/mod/github.com/shoenig/go-m1cpu@v0.1.6/cpu.go:75:17: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]
../../../go/pkg/mod/github.com/shoenig/go-m1cpu@v0.1.6/cpu.go:77:16: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]

***********************************************
* Your go version is not the expected go1.24. *
***********************************************
Secrets directory initialized with default values
OCM_ENV=unit_testing gotestsum --format short-verbose -- -p 1 -v  \
                ./pkg/... \
                ./cmd/...
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)
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
EMPTY pkg/logger
PASS pkg/services.TestSQLTranslation (0.00s)
PASS pkg/services.TestMapAdapterToConditionType (0.00s)
PASS pkg/services.TestMapAdapterToConditionType_CustomSuffix (0.00s)
PASS pkg/services.TestMapAdapterToConditionType_DefaultAfterCustom (0.00s)
PASS pkg/services
EMPTY pkg/util
EMPTY cmd/hyperfleet-api
PASS cmd/hyperfleet-api/environments.TestLoadServices (0.00s)
PASS cmd/hyperfleet-api/environments
EMPTY cmd/hyperfleet-api/environments/registry
EMPTY cmd/hyperfleet-api/migrate
EMPTY cmd/hyperfleet-api/servecmd
EMPTY cmd/hyperfleet-api/server
EMPTY cmd/hyperfleet-api/server/logging

DONE 16 tests in 6.324s
 yingzhan@yingzhan-mac  ~/ying-work/code/hyperfleet-api   ying-test ±✚  make test-integration
CGO_ENABLED=1 GOEXPERIMENT=boringcrypto go install -ldflags="-X github.com/openshift-hyperfleet/hyperfleet-api/pkg/api.Version=87558ae-modified -X 'github.com/openshift-hyperfleet/hyperfleet-api/pkg/api.BuildTime=2025-12-01 11:30:57 UTC'" ./cmd/hyperfleet-api
# github.com/shoenig/go-m1cpu
../../../go/pkg/mod/github.com/shoenig/go-m1cpu@v0.1.6/cpu.go:75:17: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]
../../../go/pkg/mod/github.com/shoenig/go-m1cpu@v0.1.6/cpu.go:77:16: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]

***********************************************
* Your go version is not the expected go1.24. *
***********************************************
Secrets directory initialized with default values
TESTCONTAINERS_RYUK_DISABLED=true OCM_ENV=integration_testing gotestsum --format short-verbose -- -p 1 -ldflags -s -v -timeout 1h  \
                        ./test/integration
I1201 19:31:05.631607   58720 framework.go:67] Initializing integration_testing environment
I1201 19:31:05.631863   58720 testcontainer.go:44] Starting PostgreSQL testcontainer...
2025/12/01 19:31:05 github.com/testcontainers/testcontainers-go - Connected to docker: 
  Server Version: 5.3.2
  API Version: 1.41
  Operating System: fedora
  Total Memory: 3893 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: c45b3b976ed5d8ccc94bd9662326dfe865e40da5e0d750a27a25a56f19536bd9
  Test ProcessID: 5fbcb491-c649-4612-98bb-b571fecbabe7
2025/12/01 19:31:05 🐳 Creating container for image postgres:14.2
2025/12/01 19:31:05 ✅ Container created: b2a4f1058391
2025/12/01 19:31:05 🐳 Starting container: b2a4f1058391
2025/12/01 19:31:05 ✅ Container started: b2a4f1058391
2025/12/01 19:31:05 ⏳ Waiting for container id b2a4f1058391 image: postgres:14.2. Waiting for: &{timeout:<nil> deadline:0x1400038d080 Strategies:[0x140004b1140]}
2025/12/01 19:31:06 🔔 Container is ready: b2a4f1058391
I1201 19:31:06.656754   58720 testcontainer.go:68] PostgreSQL testcontainer started at: postgres://hyperfleet:foobar-bizz-buzz@localhost:38089/hyperfleet?sslmode=disable
I1201 19:31:06.661839   58720 testcontainer.go:100] Running database migrations on testcontainer...
I1201 19:31:06.674623   58720 testcontainer.go:105] Testcontainer database initialized successfully
I1201 19:31:06.674636   58720 framework.go:139] Using Mock OCM Authz Client
I1201 19:31:06.677264   58720 openapi.go:40] Loaded fully resolved OpenAPI specification from embedded pkg/api/openapi/api/openapi.yaml
I1201 19:31:06.677300   58720 openapi.go:50] Loaded OpenAPI UI HTML from embedded file
I1201 19:31:06.677962   58720 api_server.go:123] Serving without TLS at localhost:8000
I1201 19:31:06.678026   58720 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.05s)
PASS test/integration.TestAdapterStatusIdempotency (0.02s)
PASS test/integration.TestAdapterStatusPagingEdgeCases (0.06s)
PASS test/integration.TestClusterGet (0.01s)
PASS test/integration.TestClusterPost (0.01s)
PASS test/integration.TestClusterPaging (0.02s)
PASS test/integration.TestClusterListSearch (0.02s)
PASS test/integration.TestClusterSearchSQLInjection (0.01s)
PASS test/integration.TestClusterDuplicateNames (0.01s)
PASS test/integration.TestClusterBoundaryValues (0.01s)
PASS test/integration.TestCompatibilityGet (0.01s)
PASS test/integration.TestCompatibilityNoAuth (0.00s)
PASS test/integration.TestMetadataGet (0.00s)
PASS test/integration.TestNodePoolPost (0.01s)
PASS test/integration.TestNodePoolPaging (0.03s)
PASS test/integration.TestNodePoolListSearch (0.02s)
PASS test/integration.TestNodePoolsByClusterId (0.01s)
PASS test/integration.TestGetNodePoolByClusterIdAndNodePoolId (0.01s)
PASS test/integration.TestOpenAPIGet (0.01s)
PASS test/integration.TestOpenAPIUIGet (0.00s)
I1201 19:31:07.076588   58720 api_server.go:129] Web server terminated
I1201 19:31:07.076626   58720 testcontainer.go:140] Stopping PostgreSQL testcontainer...
2025/12/01 19:31:07 🐳 Terminating container: b2a4f1058391
2025/12/01 19:31:07 🚫 Container terminated: b2a4f1058391
I1201 19:31:07.186793   58720 testcontainer.go:144] PostgreSQL testcontainer stopped
PASS test/integration

DONE 24 tests in 2.512s

Summary by CodeRabbit

  • Bug Fixes

    • More robust error handling for JSON marshal/unmarshal, HTTP writes, DB pings and closes; safer shutdown/close handling and logged failures.
    • Improved logging for status aggregation and update errors to surface issues instead of silent failures.
  • Chores

    • Tooling and build updates (OpenAPI/golangci-lint/go toolchain); Docker build commands hardened for apt sandboxing.
    • Hardened tests and container readiness checks; safer test error reporting.
  • Refactor

    • Simplified middleware/control flow and some public method signatures for clearer error propagation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Walkthrough

This PR adds defensive error handling and safer type assertions across many packages, updates method signatures and signatures usage (notably pkg/client/ocm.Client.Close and treeWalkForSqlizer), refines HTTP response writing to handle marshal/write errors, tightens JSON marshal/unmarshal fallbacks in several API types, updates test code and mocks, switches testcontainer readiness to port checks, replaces active authz init with a mock, and updates tooling (Dockerfile APT sandbox flag, openapitools.json, golangci-lint/.bingo/.mod, and .golangci.yml).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review callers and tests affected by the treeWalkForSqlizer signature change (pkg/services and tests).
  • Verify all call sites handle the new error return from pkg/client/ocm.Client.Close (Env.Teardown, server shutdowns).
  • Inspect marshaling/unmarshaling fallbacks in pkg/api/*_types.go to ensure semantics are preserved and important errors aren't silently masked.
  • Audit response-writing changes (writeJSONResponse and handler writes) to confirm header/body behavior when marshaling or write errors occur.
  • Validate CI/tooling updates: Dockerfile APT flag, openapitools.json bump, .bingo/.mod golangci-lint changes, and .golangci.yml exclusion.
  • Check test updates for correctness: changed receivers in test/mocks/ocm.go, removed mockResponseWriter, added nolint directives, and altered integration assertions.

Possibly related PRs

Suggested labels

lgtm

Suggested reviewers

  • rafabene

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main objective: fixing errors in presubmit jobs (lint/unit/integration testing), with the ticket reference HYPERFLEET-295 providing context.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/config/config_test.go (1)

11-24: Move os.Remove defers after error checks to avoid possible nil deref

Using //nolint:errcheck on the os.Remove calls is reasonable in tests, but placing the defer before checking err means stringFile.Name()/intFile.Name()/boolFile.Name()/stringFile.Name() are evaluated even when createConfigFile returns (nil, err), which would panic on a rare filesystem failure.

Reordering to check err first removes that edge‑case panic while preserving current behavior:

 func TestConfigReadStringFile(t *testing.T) {
 	RegisterTestingT(t)

-	stringFile, err := createConfigFile("string", "example\n")
-	defer os.Remove(stringFile.Name()) //nolint:errcheck
-	if err != nil {
-		log.Fatal(err)
-	}
+	stringFile, err := createConfigFile("string", "example\n")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.Remove(stringFile.Name()) //nolint:errcheck
@@
 func TestConfigReadIntFile(t *testing.T) {
@@
-	intFile, err := createConfigFile("int", "123")
-	defer os.Remove(intFile.Name()) //nolint:errcheck
-	if err != nil {
-		log.Fatal(err)
-	}
+	intFile, err := createConfigFile("int", "123")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.Remove(intFile.Name()) //nolint:errcheck
@@
 func TestConfigReadBoolFile(t *testing.T) {
@@
-	boolFile, err := createConfigFile("bool", "true")
-	defer os.Remove(boolFile.Name()) //nolint:errcheck
-	if err != nil {
-		log.Fatal(err)
-	}
+	boolFile, err := createConfigFile("bool", "true")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.Remove(boolFile.Name()) //nolint:errcheck
@@
 func TestConfigReadQuotedFile(t *testing.T) {
@@
-	stringFile, err := createConfigFile("string", "example")
-	defer os.Remove(stringFile.Name()) //nolint:errcheck
-	if err != nil {
-		log.Fatal(err)
-	}
+	stringFile, err := createConfigFile("string", "example")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.Remove(stringFile.Name()) //nolint:errcheck

Also applies to: 26-39, 41-54, 56-69

🧹 Nitpick comments (15)
pkg/services/types.go (1)

31-45: Guarded parsing for page/size looks correct and preserves defaults

The new err == nil guards around strconv.Atoi/strconv.ParseInt mean invalid numeric query values no longer overwrite the default Page=1 and Size=100, while still respecting valid values for both pageSize and legacy size. The MaxListSize clamp below continues to provide a hard upper bound, so behavior remains sane even for large inputs.

If you ever want to tighten semantics further, you could optionally normalize page <= 0 back to 1, but that’s not required for this PR.

Based on learnings, this keeps ListArguments-based pagination/search behavior aligned with the repo’s conventions.

pkg/api/presenters/slice_filter.go (1)

147-153: Escape name to avoid regex metacharacter issues.

If name contains regex metacharacters (e.g., ., *, +), the pattern may not match as intended. Use regexp.QuoteMeta() to escape the name.

 func removeStar(in map[string]bool, name string) map[string]bool {
-	pattern := `(` + name + `\..*)`
+	pattern := `(` + regexp.QuoteMeta(name) + `\..*)`
 	pat, err := regexp.Compile(pattern)
 	if err != nil {
 		// Invalid pattern, return unchanged
 		return in
 	}
pkg/services/cluster.go (1)

132-133: Comment is misleading.

The comment says "Build the list of ConditionAvailable" but the code builds a list of ResourceCondition objects (one per adapter, derived from each adapter's "Available" condition). Consider updating to something like "Build the list of ResourceConditions from adapter Available conditions".

pkg/api/cluster_types.go (1)

138-157: Consider logging marshal/unmarshal failures for observability.

While the fallback behavior is correct, silent failures could mask data corruption or programming errors. Consider adding debug-level logging when these fallbacks trigger, to aid troubleshooting in production.

Note: The marshal of an empty slice at lines 154-157 cannot realistically fail, so that particular error check is purely defensive.

pkg/api/adapter_status_types.go (1)

71-74: Inconsistent error handling for Metadata unmarshal.

Other unmarshal calls (Conditions at line 55, Data at line 64) now have explicit error handling with fallback defaults, but Metadata still uses _ = to ignore errors. Consider handling this consistently for uniformity.

 	// Unmarshal Metadata
 	var metadata *openapi.AdapterStatusBaseMetadata
 	if len(as.Metadata) > 0 {
-		_ = json.Unmarshal(as.Metadata, &metadata)
+		if err := json.Unmarshal(as.Metadata, &metadata); err != nil {
+			metadata = nil
+		}
 	}
pkg/auth/context.go (1)

82-96: JWT claim extraction is now safe; consider an optional helper to reduce repetition

The checked string assertions for username, first_name, last_name, email, and clientId correctly avoid panics on unexpected types and keep behavior clear.

If you want to DRY this up, you could optionally introduce a small helper and use it here:

-	if username, ok := claims["username"].(string); ok {
-		payload.Username = username
-	}
-	if firstName, ok := claims["first_name"].(string); ok {
-		payload.FirstName = firstName
-	}
-	if lastName, ok := claims["last_name"].(string); ok {
-		payload.LastName = lastName
-	}
-	if email, ok := claims["email"].(string); ok {
-		payload.Email = email
-	}
-	if clientID, ok := claims["clientId"].(string); ok {
-		payload.ClientID = clientID
-	}
+	payload.Username = getStringClaim(claims, "username")
+	payload.FirstName = getStringClaim(claims, "first_name")
+	payload.LastName = getStringClaim(claims, "last_name")
+	payload.Email = getStringClaim(claims, "email")
+	payload.ClientID = getStringClaim(claims, "clientId")

And define (outside this range) something like:

func getStringClaim(claims jwt.MapClaims, key string) string {
	if v, ok := claims[key].(string); ok {
		return v
	}
	return ""
}

Purely optional; current code is correct and clear.

pkg/db/db_session/testcontainer.go (3)

47-55: Port-based wait strategy is correct; consider delegating to postgres.BasicWaitStrategies.

Using wait.ForListeningPort("5432/tcp").WithStartupTimeout(60*time.Second) via testcontainers.WithWaitStrategy is idiomatic for testcontainers-go v0.33.0 and matches the docs for the HostPort wait strategy (string "5432/tcp" is the expected form, and 60s is the default timeout anyway). (golang.testcontainers.org)

For future-proofing and cross‑platform stability (especially for non‑Linux developers), you might consider letting the Postgres module apply its own recommended combination of log+port wait strategies instead of hand-rolling:

-    testcontainers.WithWaitStrategy(
-        wait.ForListeningPort("5432/tcp").
-            WithStartupTimeout(60*time.Second)),
+    postgres.BasicWaitStrategies(),

postgres.BasicWaitStrategies() composes the documented pattern of waiting for the "database system is ready to accept connections" log twice plus a host‑side port readiness check, which the module authors call out as the reliable default. (golang.testcontainers.org)


76-78: Optionally tune MaxIdleConns alongside MaxOpenConns for the test DB pool.

You’re setting SetMaxOpenConns, but leaving MaxIdleConns at the default. For integration workloads that open and close many short‑lived connections, explicitly setting a sensible MaxIdleConns (often equal to or slightly below MaxOpenConns) can reduce churn and make behavior more predictable:

 f.sqlDB.SetMaxOpenConns(config.MaxOpenConnections)
+if config.MaxIdleConnections > 0 {
+    f.sqlDB.SetMaxIdleConns(config.MaxIdleConnections)
+}

(Adjust the field name if your DatabaseConfig uses a different one.)

Based on learnings, this aligns with the repo’s guidance for configuring DB connection pooling under pkg/db.


9-20: Logging in this helper still uses glog; consider migrating to the structured OCM logger.

This file imports and uses github.com/golang/glog for all its messages. Repo guidelines for Go code prefer logger.NewOCMLogger(ctx) with contextual fields (opid, accountID, tx_id) for structured logging; even though this is a test helper and the logging style predates this PR, it’s a good candidate for a future cleanup to keep logging consistent across the codebase.

You could, for example, initialize an OCM logger in Init and thread it through the methods instead of using global glog.

As per coding guidelines, structured logging should be used in **/*.go.

pkg/services/generic_test.go (1)

23-23: Consider asserting on dbFactory.Close() instead of suppressing errcheck

Ignoring the Close() error is safe enough for a mock in tests, but you could strengthen the test and drop the nolint by checking it explicitly, e.g.:

-	defer dbFactory.Close() //nolint:errcheck
+	defer func() {
+		err := dbFactory.Close()
+		Expect(err).ToNot(HaveOccurred())
+	}()

This keeps the linter happy and will flag unexpected teardown failures in the future.

pkg/services/node_pool.go (1)

132-140: Clearer local error naming in unmarshal loop

Renaming the inner error to unmarshalErr removes any ambiguity with the outer err used later in the function; behavior is unchanged and the intent is clearer.

Makefile (1)

111-118: Direct golangci-lint invocation simplifies the lint workflow

Switching to golangci-lint run -D unused removes the indirection through a custom binary path and should resolve the presubmit path issues, assuming golangci-lint is on $PATH in CI/Prow images.

Separately from this change, you may want to align the binary target with the install/cmds targets by explicitly setting CGO_ENABLED=$(CGO_ENABLED) and GOEXPERIMENT=boringcrypto there as well for consistent FIPS‑compliant builds. As per coding guidelines, this keeps all build paths using the same crypto settings.

Dockerfile.openapi (1)

3-5: Sandbox flag on apt-get is a pragmatic Prow/rootless workaround

Using -o APT::Sandbox::User=root for both apt-get update and apt-get install matches the documented workaround for rootless Podman setgroups issues in CI and keeps the package set identical to before.

pkg/db/sql_helpers.go (1)

83-87: Consider using a named constant for the magic number 11.

The length check len(leftStr) < 11 corresponds to len("properties."), which is correct. However, this magic number could be clearer.

+const propertiesPrefix = "properties."
+
 func propertiesNodeConverter(n tsl.Node) tsl.Node {
 	// Get the left side operator.
 	l, ok := n.Left.(tsl.Node)
 	if !ok {
 		return n
 	}
 
 	// Get the property name.
 	leftStr, ok := l.Left.(string)
-	if !ok || len(leftStr) < 11 {
+	if !ok || len(leftStr) < len(propertiesPrefix) {
 		return n
 	}
-	propetyName := leftStr[11:]
+	propetyName := leftStr[len(propertiesPrefix):]

Also note: there's a typo in propetyName (missing 'r').

pkg/db/transaction_middleware.go (1)

44-52: Consider logging errors for observability.

The error handling is correct—headers are already sent, so status code changes aren't possible. However, these silent failures could hinder debugging in production. Based on learnings, this project uses structured logging via logger.NewOCMLogger(ctx).

Since this function lacks context access, you could either:

  1. Pass context as a parameter for logging capability
  2. Accept the silent failure for this low-level helper (reasonable given the caller already logged the original error)

The current approach is acceptable if the calling code logs the original error before calling writeJSONResponse. Verify that callers handle logging appropriately.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87558ae and b7202fe.

📒 Files selected for processing (30)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/types.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/pkg/handlers/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/handlers/cluster.go
  • pkg/handlers/openapi.go
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Files:

  • pkg/handlers/node_pool.go
  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/api/node_pool_types.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/helpers.go
  • pkg/handlers/cluster.go
  • pkg/db/db_session/default.go
  • pkg/api/adapter_status_types.go
  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/main.go
  • pkg/client/ocm/client.go
  • pkg/services/generic_test.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/api/cluster_types.go
  • pkg/config/config_test.go
  • pkg/services/types.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/api/presenters/slice_filter.go
  • pkg/auth/context.go
**/pkg/services/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Files:

  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/services/generic_test.go
  • pkg/services/types.go
  • pkg/services/generic.go
**/pkg/{services,dao}/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Files:

  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/services/generic_test.go
  • pkg/services/types.go
  • pkg/services/generic.go
**/pkg/db/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/pkg/db/*.go: All database connections must respect db-sslmode parameter (disable | require | verify-ca | verify-full) with production defaulting to verify-full
Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Files:

  • pkg/db/sql_helpers.go
  • pkg/db/transaction_middleware.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

All unit tests must run with OCM_ENV=unit_testing environment variable for proper test isolation

Files:

  • pkg/services/generic_test.go
  • pkg/config/config_test.go
  • cmd/hyperfleet-api/environments/framework_test.go
**/pkg/db/transaction_middleware.go

📄 CodeRabbit inference engine (CLAUDE.md)

All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Files:

  • pkg/db/transaction_middleware.go
{Makefile,*.mk,**/Makefile,**/*.mk}

📄 CodeRabbit inference engine (CLAUDE.md)

Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Files:

  • Makefile
{Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Podman for containerized development and build consistency (database, code generation)

Files:

  • Makefile
🧠 Learnings (23)
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : NodePool resources must enforce owner_references.id (parent cluster ID) via foreign key constraint in database schema

Applied to files:

  • pkg/services/node_pool.go
  • pkg/api/node_pool_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination

Applied to files:

  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/api/node_pool_types.go
  • pkg/api/adapter_status_types.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/services/*.go : Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Applied to files:

  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Applied to files:

  • pkg/services/cluster.go
  • pkg/db/db_session/default.go
  • pkg/api/adapter_status_types.go
  • pkg/services/generic_test.go
  • pkg/services/types.go
  • pkg/services/generic.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/handlers/openapi.go
  • pkg/api/cluster_types.go
  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use TypeSpec to define API specifications, which compile to OpenAPI 3.0.3, then generate Go models using openapi-generator-cli v7.16.0

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*service*,*handler*}.go : Retrieve database sessions from context via db.NewContext(ctx) rather than creating new GORM connections

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/*.go : Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/test/integration/**/*.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure state isolation

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • pkg/services/generic_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/test/integration/**/*_test.go : All integration tests must run with OCM_ENV=integration_testing environment variable and fresh PostgreSQL database per test

Applied to files:

  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses

Applied to files:

  • pkg/handlers/helpers.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/handlers/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Applied to files:

  • pkg/handlers/helpers.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*db*}.go : All database operations must accept context.Context parameter to enable proper transaction propagation from middleware

Applied to files:

  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/transaction_middleware.go : All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Applied to files:

  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns to avoid separate status tables for each resource type

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/environments/*.go : Use environment-based configuration via OCM_ENV variable (development, unit_testing, integration_testing, production) for environment-specific behavior

Applied to files:

  • .golangci.yml
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/environments/framework_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary

Applied to files:

  • .golangci.yml
  • pkg/handlers/openapi.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to pkg/api/openapi/api/openapi.yaml : Only edit openapi/openapi.yaml as the source; the generated pkg/api/openapi/api/openapi.yaml will be overwritten by 'make generate'

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Store resource specifications in JSONB columns (spec field) to enable flexible cloud provider configuration

Applied to files:

  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*handler*}.go : List API endpoints must support pagination and search via ListArgs with limit, offset, and filter parameters

Applied to files:

  • pkg/services/types.go
  • pkg/services/generic.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk} : Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Applied to files:

  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility

Applied to files:

  • Makefile
  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile} : Use Podman for containerized development and build consistency (database, code generation)

Applied to files:

  • Dockerfile.openapi
🧬 Code graph analysis (8)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/db/sql_helpers.go (1)
pkg/errors/errors.go (1)
  • BadRequest (196-198)
pkg/handlers/cluster.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
cmd/hyperfleet-api/server/api_server.go (2)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
cmd/hyperfleet-api/environments/framework_test.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Services (44-47)
🔇 Additional comments (27)
pkg/api/presenters/slice_filter.go (2)

114-116: LGTM! Proper error propagation from recursive validation.

This fix correctly surfaces validation errors from nested struct fields, preventing silent failures.


194-196: LGTM! Safe handling of nil time pointers.

The added nil check and guarded type assertion correctly prevent panics when encountering nil *time.Time fields.

pkg/services/cluster.go (1)

139-141: LGTM!

Renaming err to unmarshalErr avoids shadowing the outer err variable and improves clarity. The silent continue on unmarshal failure is appropriate here since corrupted adapter data shouldn't block aggregation of other adapters.

pkg/api/cluster_types.go (1)

72-75: Defensive error handling looks good.

Falling back to empty structures when unmarshalling fails prevents panics and ensures the API returns valid responses even with corrupted database data.

pkg/api/node_pool_types.go (2)

82-100: LGTM!

Defensive error handling for Spec, Labels, and StatusConditions unmarshalling is consistent with the pattern in cluster_types.go. Fallback to empty structures ensures robust API responses.


159-178: LGTM!

Marshal error handling with fallbacks is consistent with cluster_types.go. The sequential err reuse is safe here since each error is checked immediately after its assignment.

pkg/auth/context.go (3)

45-48: Safer username extraction from context

Guarding the type assertion with ok and falling back to "" removes the risk of panics if the value is missing or not a string, while preserving expected behavior for SetUsernameContext.


100-115: Alternative claim fallbacks are robust and nil-safe

The fallback reads from preferred_username, given_name, and family_name now also use checked string assertions, which safely handle missing or non-string claims without panicking while keeping the previous behavior of gradually filling in missing fields.


119-126: name-based fallback is safely guarded and avoids panics

The additional ok check on claims["name"] combined with the length check on names ensures this fallback path can’t panic on non-string or empty values, and preserves the original intent of deriving first/last name from a single name claim.

pkg/services/generic.go (3)

43-56: listContext expansion is coherent with downstream usage

The added fields (pagingMeta, ulog, resourceList, disallowedFields, resourceType, joins, groupBy, set) are all correctly initialized or safely zero-valued and are used consistently in List, buildPreload, addJoins, and loadList. No issues from this refactor as written.


151-176: buildSearchValues correctly adapts to the simplified treeWalkForSqlizer API

Swapping to sqlizer, serviceErr := s.treeWalkForSqlizer(listCtx, tslTree) and dropping the intermediate TSL node return fits the actual usage pattern here (the transformed tree isn’t needed outside SQL generation). Error propagation remains clear and consistent.


332-346: treeWalkForSqlizer refactor looks correct and keeps semantics

The new signature returning only squirrel.Sqlizer simplifies callers without changing behavior:

  • db.FieldNameWalk is still applied first to validate/normalize fields using disallowedFields.
  • Failures from either FieldNameWalk or sqlFilter.Walk are converted into *errors.ServiceError with ErrorBadRequest, and on success you now return just the sqlizer.

This is a clean internal API tightening with no obvious edge‑case regressions.

pkg/services/generic_test.go (1)

62-76: Tests correctly updated for new treeWalkForSqlizer return signature

The call to treeWalkForSqlizer now binds only sqlizer and serviceErr, and the subsequent ToSql() assertions still validate both SQL and bound values. This matches the service’s refactored API and preserves the original test intent.

cmd/hyperfleet-api/server/api_server.go (1)

146-151: Good addition to surface DB close failures

Checking and logging the error from SessionFactory.Close() avoids silently ignoring shutdown problems while still keeping shutdown flow simple. This aligns well with the documented “call exactly once at exit” contract on the session factory.

openapitools.json (1)

1-6: OpenAPI Generator version bump looks consistent

Setting the CLI "version": "7.16.0" is consistent with the openapitools/openapi-generator-cli:v7.16.0 base image in Dockerfile.openapi, so the tooling versions line up. You may want to double‑check any external scripts or local workflows that still assume 5.4.0.

pkg/db/db_session/default.go (1)

110-113: Now logging listener Ping failures is a nice observability win

Capturing and logging l.Ping() errors (even at V(10)) gives you visibility into notification channel issues without changing the behavior of the retry loop.

pkg/handlers/cluster.go (1)

72-77: Defensive marshal error handling in Patch is appropriate

Adding the json.Marshal error check and returning a GeneralError ensures bad or unexpected spec payloads are surfaced as structured API errors instead of causing unhandled failures, while leaving the happy path unchanged.

pkg/db/sql_helpers.go (2)

28-31: LGTM! Defensive type assertion prevents potential panic.

The safe type assertion with ok check before using leftStr is the correct approach. This prevents a panic if l.Left is not a string.


145-150: LGTM! Proper error handling for invalid node structure.

The explicit type-assertion check with a descriptive BadRequest error aligns with the pattern used elsewhere in this file and follows the project's error handling conventions from pkg/errors.

pkg/handlers/helpers.go (1)

17-27: LGTM! Consistent defensive error handling.

This pattern matches the error handling in pkg/db/transaction_middleware.go and pkg/auth/helpers.go, creating a consistent approach across the codebase. Since headers are written before the payload (line 15), early returns on error are the correct behavior.

pkg/auth/helpers.go (1)

29-39: LGTM! Consistent with project-wide error handling pattern.

The defensive error handling aligns with the changes in pkg/handlers/helpers.go and pkg/db/transaction_middleware.go. Since handleError (the caller at line 22) already logs the original error before calling writeJSONResponse, the silent early return here is acceptable.

pkg/handlers/openapi.go (2)

58-65: LGTM! Defensive write error handling for OpenAPI endpoint.

Properly handles the case where response writing fails after headers are committed. The early return is appropriate since no recovery is possible at this point.


67-74: LGTM! Consistent with GetOpenAPI error handling.

Same defensive pattern applied to the OpenAPI UI endpoint.

cmd/hyperfleet-api/main.go (1)

28-30: LGTM! Explicit error handling for flag parsing.

Converting silent flag parsing to explicit error handling with glog.Fatalf is the right approach—a flag parsing failure at startup should halt the program. This aligns with the PR's broader goal of adding defensive error handling.

pkg/handlers/node_pool.go (1)

73-77: LGTM! Proper error handling for JSON marshalling.

The defensive check prevents silent failures when marshalling patch.Spec fails. This follows the handler pattern of handling errors with appropriate error responses.

pkg/client/ocm/client.go (1)

78-83: LGTM! Error propagation from Close() improves observability.

The updated signature allows callers to handle and log close failures appropriately, as demonstrated in the corresponding Teardown() changes in framework.go.

cmd/hyperfleet-api/environments/framework_test.go (1)

25-56: Reflection fix is correct. Verify test runs with OCM_ENV=unit_testing.

The change to reflect.ValueOf(&env.Services).Elem() is the correct fix to obtain an addressable reflect.Value, which is necessary for proper field iteration.

As per coding guidelines, unit tests should run with OCM_ENV=unit_testing for proper test isolation. Verify this test is executed with the correct environment variable set.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (7)
Dockerfile.openapi (1)

8-8: Upgrade Go version to 1.24.9 or later.

The Dockerfile installs go1.24.0, but per coding guidelines, all builds should use Go 1.24.9 or later for full compatibility. The PR build log also reports "non-expected Go version" warnings. Consider upgrading to a compatible patch version.

Apply this change to upgrade the Go version:

-RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz && \
-    tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz && \
-    rm go1.24.0.linux-amd64.tar.gz
+RUN wget https://go.dev/dl/go1.24.9.linux-amd64.tar.gz && \
+    tar -C /usr/local -xzf go1.24.9.linux-amd64.tar.gz && \
+    rm go1.24.9.linux-amd64.tar.gz
pkg/db/sql_helpers.go (2)

84-84: Extract magic number to a named constant.

The magic number 11 represents the length of the "properties." prefix. Consider extracting it to a named constant for clarity and maintainability.

Apply this diff to improve code clarity:

+const propertiesPrefix = "properties."
+const propertiesPrefixLen = len(propertiesPrefix)
+
 func propertiesNodeConverter(n tsl.Node) tsl.Node {
 
 	// Get the left side operator.
 	l, ok := n.Left.(tsl.Node)
 	if !ok {
 		return n
 	}
 
 	// Get the property name.
 	leftStr, ok := l.Left.(string)
-	if !ok || len(leftStr) < 11 {
+	if !ok || len(leftStr) < propertiesPrefixLen {
 		return n
 	}
-	propetyName := leftStr[11:]
+	propetyName := leftStr[propertiesPrefixLen:]

87-87: Fix typo in variable name.

The variable propetyName should be propertyName for correct spelling.

Apply this diff:

-	propetyName := leftStr[11:]
+	propertyName := leftStr[11:]
 
 	// Build a new node that converts:
 	// ( properties.<name> = <value> ) to
 	// ( properties ->> <name> = <value> )
 	propertyNode := tsl.Node{
 		Func: n.Func,
 		Left: tsl.Node{
 			Func: tsl.IdentOp,
-			Left: fmt.Sprintf("properties ->> '%s'", propetyName),
+			Left: fmt.Sprintf("properties ->> '%s'", propertyName),
 		},
 		Right: n.Right,
 	}
pkg/api/adapter_status_types.go (2)

54-68: Good defensive error handling for JSON unmarshalling.

The explicit error handling with fallbacks to empty slice/map for Conditions and Data is a solid improvement over silently ignoring errors.

Note: Line 73 still uses _ = json.Unmarshal(...) for Metadata, which is inconsistent with the new pattern. Consider applying the same error handling there for consistency.


51-96: Consider adding logging for marshal/unmarshal fallbacks.

Per the coding guidelines, structured logging via logger.NewOCMLogger(ctx) should be used. The silent fallbacks here could make debugging difficult when JSON operations fail in production. Consider accepting a context.Context parameter and logging warnings when fallbacks are triggered.

This is a low-priority enhancement that could be addressed in a follow-up.

Also applies to: 98-163

pkg/db/transaction_middleware.go (1)

44-52: Marshal/write errors are silently swallowed and the comment is misleading

Right now both json.Marshal and w.Write errors cause an early return with no logging, while the comment on Line 46 says “Log error”. That’s safe but makes these failures hard to diagnose and the comment inaccurate.

Consider either:

  • Plumb a logger/context into writeJSONResponse (or accept a logger) and actually log the marshal/write errors, or
  • If you intentionally don’t want logging here, update the comment to reflect that the error is intentionally ignored after headers are sent.

This is non-blocking but will make behavior clearer and debugging easier.

pkg/services/node_pool.go (1)

132-140: Consider logging adapter condition unmarshal failures for observability

Renaming the inner error to unmarshalErr is clearer, but these failures are still silently skipped. To make bad adapter payloads debuggable and align with the structured logging guidance, consider logging at warn/debug level before continuing:

 func (s *sqlNodePoolService) UpdateNodePoolStatusFromAdapters(ctx context.Context, nodePoolID string) (*api.NodePool, *errors.ServiceError) {
-	// Build the list of ConditionAvailable
-	adapters := []openapi.ResourceCondition{}
+	logger := logger.NewOCMLogger(ctx)
+	// Build the list of ConditionAvailable
+	adapters := []openapi.ResourceCondition{}
@@
-		if unmarshalErr := json.Unmarshal(adapterStatus.Conditions, &conditions); unmarshalErr != nil {
-			continue // Skip if can't unmarshal
-		}
+		if unmarshalErr := json.Unmarshal(adapterStatus.Conditions, &conditions); unmarshalErr != nil {
+			logger.Warnf("Failed to unmarshal adapter conditions for adapter %s on NodePool %s: %v",
+				adapterStatus.Adapter, nodePoolID, unmarshalErr)
+			continue // Skip if can't unmarshal
+		}

As per coding guidelines, this also helps keep logging context-aware via logger.NewOCMLogger(ctx).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7202fe and e45d034.

📒 Files selected for processing (30)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/types.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • pkg/config/config_test.go
🚧 Files skipped from review as they are similar to previous changes (16)
  • cmd/hyperfleet-api/environments/framework_test.go
  • cmd/hyperfleet-api/main.go
  • pkg/services/cluster.go
  • pkg/api/cluster_types.go
  • pkg/db/db_session/default.go
  • pkg/auth/helpers.go
  • Makefile
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/client/ocm/client.go
  • pkg/auth/context.go
  • .golangci.yml
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/helpers.go
  • openapitools.json
  • pkg/services/types.go
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Files:

  • pkg/api/presenters/slice_filter.go
  • pkg/api/adapter_status_types.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/db/db_session/test.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/cluster.go
  • pkg/services/node_pool.go
  • pkg/db/transaction_middleware.go
  • pkg/api/node_pool_types.go
  • pkg/handlers/node_pool.go
  • cmd/hyperfleet-api/environments/framework.go
**/pkg/services/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
**/pkg/{services,dao}/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

All unit tests must run with OCM_ENV=unit_testing environment variable for proper test isolation

Files:

  • pkg/services/generic_test.go
**/pkg/db/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/pkg/db/*.go: All database connections must respect db-sslmode parameter (disable | require | verify-ca | verify-full) with production defaulting to verify-full
Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Files:

  • pkg/db/sql_helpers.go
  • pkg/db/transaction_middleware.go
**/pkg/handlers/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Files:

  • pkg/handlers/cluster.go
  • pkg/handlers/node_pool.go
**/pkg/db/transaction_middleware.go

📄 CodeRabbit inference engine (CLAUDE.md)

All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Files:

  • pkg/db/transaction_middleware.go
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/node_pool.go
  • pkg/api/node_pool_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/services/*.go : Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/node_pool.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns to avoid separate status tables for each resource type

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to pkg/api/openapi/api/openapi.yaml : Only edit openapi/openapi.yaml as the source; the generated pkg/api/openapi/api/openapi.yaml will be overwritten by 'make generate'

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/test/factories/**/*.go : Use test factories in test/factories/ for consistent test data generation with builder pattern

Applied to files:

  • pkg/services/generic_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/test/integration/**/*.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure state isolation

Applied to files:

  • pkg/services/generic_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*handler*}.go : List API endpoints must support pagination and search via ListArgs with limit, offset, and filter parameters

Applied to files:

  • pkg/services/generic.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman

Applied to files:

  • Dockerfile.openapi
  • pkg/api/node_pool_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile} : Use Podman for containerized development and build consistency (database, code generation)

Applied to files:

  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility

Applied to files:

  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/transaction_middleware.go : All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/handlers/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : NodePool resources must enforce owner_references.id (parent cluster ID) via foreign key constraint in database schema

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/handlers/node_pool.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Store resource specifications in JSONB columns (spec field) to enable flexible cloud provider configuration

Applied to files:

  • pkg/api/node_pool_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/environments/*.go : Use environment-based configuration via OCM_ENV variable (development, unit_testing, integration_testing, production) for environment-specific behavior

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
🧬 Code graph analysis (6)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
pkg/db/sql_helpers.go (1)
pkg/errors/errors.go (1)
  • BadRequest (196-198)
pkg/handlers/cluster.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
🔇 Additional comments (17)
Dockerfile.openapi (1)

3-5: Sandbox workaround for Prow rootless podman environment looks good.

The addition of -o APT::Sandbox::User=root with the explanatory comment correctly addresses the rootless podman setgroups error in the Prow environment, aligning with the PR objectives.

cmd/hyperfleet-api/environments/framework.go (1)

158-162: LGTM! Properly addresses the previous review concern.

The nil check prevents potential panic if LoadClients() fails or is skipped, and the error handling ensures close failures are logged. This implementation matches the defensive pattern used for Database.SessionFactory cleanup above.

pkg/db/sql_helpers.go (3)

28-29: LGTM! Defensive type assertion prevents panic.

The checked type assertion properly guards against potential panics when l.Left is not a string, gracefully returning false instead.


83-87: LGTM! Defensive checks prevent panics.

The type assertion and length validation properly guard against:

  • Panic from unchecked type assertion
  • Out-of-bounds panic when slicing strings shorter than 11 characters

145-150: LGTM! Proper error handling for invalid node structure.

The checked type assertion properly guards against panics and returns a clear BadRequest error when the node structure is invalid. This is consistent with the error handling pattern used elsewhere in the function (lines 127-128, 188).

pkg/api/presenters/slice_filter.go (3)

114-116: LGTM! Essential fix to propagate nested validation errors.

Previously, validation errors from nested struct fields were silently ignored. This change ensures they bubble up correctly, preventing silent failures.


149-153: Good defensive error handling.

The pattern is constructed from struct field JSON tags and should almost never fail to compile. Returning the input unchanged on error is a reasonable fallback that prevents panics while preserving existing entries.


194-196: LGTM! Prevents nil pointer dereference.

The nil check ensures that time.Time pointers are validated before calling Format(), preventing runtime panics on optional/nil time fields.

pkg/db/db_session/test.go (1)

216-222: Inconsistent error handling: choose either panic or error return.

The function signature declares error as a return type but will never actually return an error—it either panics (line 218) or returns nil (line 221). This creates a misleading API for callers who expect to handle returned errors.

Consider one of the following consistent approaches:

  1. Remove the error return and keep panic (appropriate for test helpers):
-func (f *Test) ResetDB() error {
+func (f *Test) ResetDB() {
 	if err := resetDB(f.config); err != nil {
 		panic(fmt.Sprintf("failed to reset test database: %v", err))
 	}
 	f.wasDisconnected = true
-	return nil
 }
  1. Return the error instead of panicking (allows callers to handle failures):
 func (f *Test) ResetDB() error {
 	if err := resetDB(f.config); err != nil {
-		panic(fmt.Sprintf("failed to reset test database: %v", err))
+		return fmt.Errorf("failed to reset test database: %w", err)
 	}
 	f.wasDisconnected = true
 	return nil
 }

Option 1 is simpler for test helpers where failures should abort the test. Option 2 provides more flexibility if some callers need graceful error handling. Verify all callers of ResetDB() handle the returned error appropriately if option 2 is chosen.

pkg/api/adapter_status_types.go (3)

122-127: LGTM: Defensive error handling for conditions marshalling.

The fallback to an empty JSON array prevents failures from propagating while maintaining valid JSON structure.


130-140: Fix correctly addresses the scoping issue from past review.

Using a dedicated dataErr variable inside the if req.Data != nil block ensures the error check only applies when json.Marshal was actually called, preventing false fallbacks when req.Data is nil.


144-150: LGTM: Proper error handling for optional metadata.

The error check is correctly scoped immediately after the assignment, and falling back to nil is appropriate for optional metadata.

pkg/services/generic.go (1)

173-176: treeWalkForSqlizer signature & call-site update look correct

The refactor on Line 332 to return only (squirrel.Sqlizer, *errors.ServiceError) is wired correctly into buildSearchValues (Line 173):

  • Errors from db.FieldNameWalk and sqlFilter.Walk now consistently return nil for the Sqlizer plus a non‑nil *ServiceError.
  • All visible call sites (buildSearchValues here and the tests in pkg/services/generic_test.go) check serviceErr before calling ToSql(), so the nil return on error is safe and won’t lead to panics.
  • Behavior and error semantics (BadRequest vs general error) are preserved while simplifying the API.

No changes needed from my side.

Also applies to: 332-346

pkg/services/generic_test.go (1)

23-24: Test updates for treeWalkForSqlizer and dbFactory cleanup look good; verify OCM_ENV setup

  • Line 23: Deferring dbFactory.Close() with //nolint:errcheck is acceptable for cleanup in tests. If Close() can fail meaningfully, consider asserting on the error instead of suppressing it.
  • Lines 70–73: The test correctly uses the two-value (sqlizer, serviceErr) return from treeWalkForSqlizer and guards on serviceErr before calling ToSql(), which aligns with the new signature.

Per coding guidelines, all unit tests must run with OCM_ENV=unit_testing for proper test isolation. Verify that this environment variable is configured in your test runner, Makefile, or shared test setup so this test inherits the correct isolation environment.

pkg/handlers/cluster.go (1)

72-75: Marshal error handling on patch.Spec looks correct

Checking json.Marshal(*patch.Spec) and returning a errors.GeneralError on failure prevents silently persisting an invalid spec and integrates cleanly with the existing handlerConfig/handleError flow. No issues from a handler-pattern or error-propagation standpoint.

pkg/handlers/node_pool.go (1)

73-76: Consistent, safer handling of patch.Spec marshaling

Adding json.Marshal(*patch.Spec) with an explicit error check and returning errors.GeneralError on failure avoids persisting malformed specs and keeps Cluster and NodePool patch behavior consistent. This fits the expected handler pattern.

pkg/api/node_pool_types.go (1)

80-101: Defensive JSON defaults for Spec/Labels/StatusConditions are reasonable

The new patterns that default Spec/Labels/StatusConditions to {} / [] on marshal/unmarshal failure make the NodePool model more robust against unexpected or corrupt JSON while keeping the DB JSONB fields non-null and the OpenAPI view predictable. The nil→empty transitions are a minor semantic shift but should be safe and improve API stability.

Also applies to: 159-178

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
.golangci.yml (1)

56-62: Redundant and incorrect exclude-files entry needs cleanup.

The exclude-files entry on line 61 uses a directory path (pkg/api/openapi/) instead of a glob pattern, and it duplicates the exclusion already provided by exclude-dirs on line 57. Remove this redundant entry or replace it with a file-specific glob pattern if targeting generated files by pattern (e.g., .*_generated\.go$).

Apply this diff to remove the redundant entry:

  exclude-dirs:
    - pkg/api/openapi

  # Exclude files that require generated code
  exclude-files:
-    - pkg/api/openapi/ # Generated code

Alternatively, if you need file-specific exclusion:

  exclude-files:
+    - ".*_generated\\.go$"  # Generated files by naming convention
🧹 Nitpick comments (6)
cmd/hyperfleet-api/server/routes.go (1)

53-57: Missing var err error declaration in commented-out block.

The TODO block is reasonable for temporarily disabling authz. However, note that if this code is uncommented later, it will fail to compile because err is not declared. The JWT middleware pattern (lines 45-47) correctly uses var err error before the assignment.

For future re-enablement, the block should be:

// TODO: if env().Config.Server.EnableAuthz {
//	var err error
//	authzMiddleware, err = auth.NewAuthzMiddleware()
//	check(err, "Unable to create authz middleware")
// }
cmd/hyperfleet-api/server/logging/writer.go (1)

21-29: Consider capturing full body and default status for more accurate response logs

Unrelated to this PR’s specific change, but while you’re touching this wrapper:

  • Write only keeps the last chunk (writer.responseBody = body), so multi-chunk responses are logged incompletely.
  • If a handler never calls WriteHeader explicitly, responseStatus stays at its zero value (0) even though the underlying ResponseWriter will send 200, so logs can show an incorrect status.

You could optionally improve logging fidelity like this:

 func (writer *loggingWriter) Write(body []byte) (int, error) {
-	writer.responseBody = body
-	return writer.ResponseWriter.Write(body)
+	if writer.responseStatus == 0 {
+		// Match net/http default status code when Write is called without WriteHeader.
+		writer.responseStatus = http.StatusOK
+	}
+	n, err := writer.ResponseWriter.Write(body)
+	// Append to preserve the full body across multiple writes.
+	writer.responseBody = append(writer.responseBody, body[:n]...)
+	return n, err
 }

This is optional and only affects log accuracy, not handler behavior.

pkg/handlers/helpers.go (1)

18-26: Safer JSON response handling; consider future restructuring if you need 5xx on marshal errors

Checking both json.Marshal and w.Write errors and returning early avoids silent failures and is an improvement over ignoring them. If you ever need to change the status code on marshal failure, you might later refactor to marshal before calling WriteHeader, but that’s not required for this PR.

pkg/auth/helpers.go (1)

30-38: Consistent JSON response error handling in auth helpers

The added checks on json.Marshal and w.Write make auth responses safer and consistent with the handler/db helpers. If duplication between these writeJSONResponse variants becomes a maintenance burden, you could later centralize this into a shared helper, but it’s fine as-is for this PR.

pkg/db/transaction_middleware.go (1)

44-52: Clarify comment vs behavior in db writeJSONResponse

Handling json.Marshal and w.Write errors with early returns is good. The comment “Log error but don't expose to client” is a bit misleading since no log is actually emitted in this helper (and there’s no context/logger available here). Consider either adjusting the comment to reflect the current behavior or, if you later thread a logger/context into this function, actually logging the marshal error there.

pkg/api/cluster_types.go (1)

138-141: Defensive marshal fallbacks are fine; consider logging unexpected errors

Handling marshal errors for Spec, Labels, and the initial empty StatusConditions by falling back to "{}" / "[]" keeps inserts robust even if something unexpected happens during JSON encoding, and it avoids violating the JSONB NOT NULL intent. Since marshal failures here should be highly unusual (given input came from JSON already), you might optionally log these errors before substituting the default payloads to aid debugging if they ever occur, but the current behavior is functionally safe.

Also applies to: 148-151, 154-157

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e45d034 and e76006d.

📒 Files selected for processing (37)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/config/config_test.go
🚧 Files skipped from review as they are similar to previous changes (17)
  • cmd/hyperfleet-api/main.go
  • pkg/services/types.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/api/presenters/slice_filter.go
  • pkg/api/node_pool_types.go
  • pkg/api/adapter_status_types.go
  • pkg/auth/context.go
  • pkg/handlers/node_pool.go
  • pkg/db/sql_helpers.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/db_session/test.go
  • pkg/services/node_pool.go
  • Makefile
  • cmd/hyperfleet-api/environments/framework_test.go
  • openapitools.json
  • pkg/services/generic_test.go
🧰 Additional context used
📓 Path-based instructions (6)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Files:

  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/db/transaction_middleware.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/framework.go
  • pkg/services/cluster.go
  • pkg/handlers/cluster.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/client/ocm/client.go
  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/handlers/helpers.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/handlers/cluster_status.go
  • pkg/api/cluster_types.go
  • pkg/services/generic.go
**/pkg/handlers/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Files:

  • pkg/handlers/openapi.go
  • pkg/handlers/framework.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
  • pkg/handlers/cluster_status.go
**/pkg/db/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/pkg/db/*.go: All database connections must respect db-sslmode parameter (disable | require | verify-ca | verify-full) with production defaulting to verify-full
Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Files:

  • pkg/db/transaction_middleware.go
**/pkg/db/transaction_middleware.go

📄 CodeRabbit inference engine (CLAUDE.md)

All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Files:

  • pkg/db/transaction_middleware.go
**/pkg/services/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Files:

  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
**/pkg/{services,dao}/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Files:

  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses

Applied to files:

  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/handlers/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Applied to files:

  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • .golangci.yml
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman

Applied to files:

  • pkg/handlers/openapi.go
  • .golangci.yml
  • pkg/api/cluster_types.go
  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to pkg/api/openapi/api/openapi.yaml : Only edit openapi/openapi.yaml as the source; the generated pkg/api/openapi/api/openapi.yaml will be overwritten by 'make generate'

Applied to files:

  • pkg/handlers/openapi.go
  • .golangci.yml
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/transaction_middleware.go : All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination

Applied to files:

  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/services/*.go : Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Applied to files:

  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns to avoid separate status tables for each resource type

Applied to files:

  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Store resource specifications in JSONB columns (spec field) to enable flexible cloud provider configuration

Applied to files:

  • pkg/services/cluster.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/environments/*.go : Use environment-based configuration via OCM_ENV variable (development, unit_testing, integration_testing, production) for environment-specific behavior

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
  • .golangci.yml
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/server/*.go : Use JWT authentication from Red Hat SSO (via --jwk-cert-url flag) with account ID and username extraction from claims

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use TypeSpec to define API specifications, which compile to OpenAPI 3.0.3, then generate Go models using openapi-generator-cli v7.16.0

Applied to files:

  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile} : Use Podman for containerized development and build consistency (database, code generation)

Applied to files:

  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility

Applied to files:

  • Dockerfile.openapi
🧬 Code graph analysis (5)
pkg/handlers/cluster.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/handlers/cluster_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
🔇 Additional comments (16)
Dockerfile.openapi (1)

3-5: Correct and well-documented APT sandbox workaround for rootless Podman in Prow.

The changes appropriately apply the -o APT::Sandbox::User=root flag to both apt-get update and apt-get install commands, resolving the setgroups error encountered in rootless Podman environments. The explanatory comment on line 3 makes the intent clear. This aligns with the PR objective and is a minimal, targeted fix.

.golangci.yml (2)

49-52: ✅ Staticcheck linter re-enabled with proper justification.

The previous concern about staticcheck being disabled has been resolved—it is now properly enabled on line 46. The current disable list (unused, gosec, unparam) is well-justified with clear explanations for each choice (false positives, known CVE issues, strictness concerns).


1-76: Configuration looks solid and well-documented.

The configuration is well-structured with clear rationale for linter selections and customizations. The 5-minute timeout, enabled test linting, and targeted linter settings align well with the PR objectives (zero lint issues post-make lint). The note at the top (lines 4–5) appropriately reminds developers to run make generate before linting, which prevents generated code from causing lint failures.

cmd/hyperfleet-api/server/logging/writer.go (1)

45-51: Change to writer.Header() is correct and slightly more idiomatic

Using the promoted Header() method (writer.Header()) instead of writer.ResponseWriter.Header() is behaviorally equivalent here and marginally improves readability/future-proofing (e.g., if loggingWriter ever overrides Header). No issues from this change.

pkg/services/cluster.go (1)

139-141: LGTM! Clear error handling.

The rename to unmarshalErr with an explicit nil check improves code clarity and aligns with the PR's pattern of defensive error handling for JSON operations.

pkg/services/generic.go (2)

173-173: LGTM!

The call site correctly updated to match the new treeWalkForSqlizer signature, dropping the unused tslTree return value.


332-345: LGTM!

The refactored function signature correctly removes the unused tsl.Node return value. The modified tree from FieldNameWalk is only needed internally to generate the sqlizer, so returning it was unnecessary. All return statements properly match the new signature.

cmd/hyperfleet-api/environments/framework.go (1)

158-162: LGTM! Nil guard addresses previous review concern.

The nil check before calling Close() prevents the potential panic that was flagged in the previous review. Error handling is consistent with the database teardown pattern above.

pkg/client/ocm/client.go (1)

78-83: Verify all callers of Close() handle the new error return and confirm structured logging compliance.

The signature change from Close() to Close() error enables proper error propagation and the nil check is defensive. However, all call sites must be updated to handle the returned error, and the implementation should include structured logging via logger.NewOCMLogger(ctx) per coding guidelines for Go files.

pkg/services/status_aggregation.go (1)

155-163: Switch on adapter availability is correct and clearer

Using a switch on adapterInfo.available preserves the existing True/False behavior and now explicitly logs unexpected values while still treating them as NotReady, which matches the documented Ready/Failed/NotReady semantics. Based on learnings, this aligns with the Kubernetes-style condition aggregation guidance.

pkg/handlers/openapi.go (1)

61-64: Write error checks in OpenAPI handlers look good

Adding error checks around w.Write in both GetOpenAPI and GetOpenAPIUI and returning once a write fails is correct given the response is already committed and avoids silent IO errors.

Also applies to: 70-73

pkg/handlers/framework.go (1)

109-114: Idiomatic switch on serviceErr in handleGet

Switching directly on serviceErr with a nil case keeps the same behavior as the previous boolean switch and reads more idiomatically without changing status or error handling.

pkg/handlers/nodepool_status.go (1)

11-13: Good structured logging for non-fatal nodepool aggregation errors

Importing the logger and emitting a Warning with nodepool_id and error when UpdateNodePoolStatusFromAdapters fails makes these soft failures observable without breaking the request, and uses logger.NewOCMLogger(ctx) in line with the structured logging guidance. As per coding guidelines, this aligns with the recommended logging practice.

Also applies to: 92-96

pkg/handlers/cluster_status.go (1)

11-13: Consistent clustered status aggregation logging

The added logger import and warning log on UpdateClusterStatusFromAdapters failure provide visibility into aggregation issues while keeping the Create operation non-fatal, and use logger.NewOCMLogger(ctx) with structured fields (cluster_id, error) consistently with the nodepool handler. As per coding guidelines, this is aligned with the structured logging requirement.

Also applies to: 92-96

pkg/handlers/cluster.go (1)

72-75: Solid JSON marshal error handling in PATCH path

Adding explicit json.Marshal error handling and returning errors.GeneralError keeps invalid specs from being persisted and aligns with the structured error pattern used elsewhere in handlers. No further changes needed here.

pkg/api/cluster_types.go (1)

72-75: Improved resilience when unmarshalling JSON fields

The new error-handling around Spec, Labels, and StatusConditions JSON unmarshalling (falling back to empty maps/slices on error) is a good defensive improvement and prevents partially decoded or corrupt JSON from leaking into the OpenAPI representation. The explicit note that StatusConditions are stored as ResourceCondition in the DB also clarifies the model mapping and is consistent with the conditions guidance for status fields.

Also applies to: 80-83, 85-90

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Makefile (2)

120-125: Apply FIPS-compliant flags to the binary target for consistency.

The install target (line 129) correctly uses CGO_ENABLED=$(CGO_ENABLED) GOEXPERIMENT=boringcrypto, but the binary target (line 124) does not set these flags. This creates an inconsistency: binaries built with make binary will not be FIPS-compliant, while those built with make install will be.

Based on learnings, FIPS-compliant crypto is required for enterprise/government deployments.

Apply this diff to add FIPS flags to the binary target:

 binary: check-gopath
 	echo "Building version: ${build_version}"
-	${GO} build -ldflags="$(ldflags)" -o hyperfleet-api ./cmd/hyperfleet-api
+	CGO_ENABLED=$(CGO_ENABLED) GOEXPERIMENT=boringcrypto ${GO} build -ldflags="$(ldflags)" -o hyperfleet-api ./cmd/hyperfleet-api

254-261: Add FIPS-compliant flag GOEXPERIMENT=boringcrypto to the cmds target.

The cmds target sets CGO_ENABLED but omits GOEXPERIMENT=boringcrypto. For consistency with the install target and to ensure FIPS compliance across all built binaries, this flag should be added.

Apply this diff to add the missing FIPS flag:

 .PHONY: cmds
 cmds:
 	for cmd in $$(ls cmd); do \
-		CGO_ENABLED=$(CGO_ENABLED) ${GO} build \
+		CGO_ENABLED=$(CGO_ENABLED) GOEXPERIMENT=boringcrypto ${GO} build \
 			-ldflags="$(ldflags)" \
 			-o "$${cmd}" \
 			"./cmd/$${cmd}" \
♻️ Duplicate comments (1)
pkg/services/cluster.go (1)

132-132: Comment improvement addresses past review feedback.

The updated comment now accurately describes building a list of ResourceCondition from adapter Available conditions, addressing the previous review concern.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e76006d and 5ce95ab.

📒 Files selected for processing (37)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • pkg/handlers/node_pool.go
  • pkg/api/node_pool_types.go
  • pkg/handlers/cluster_status.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/api/presenters/slice_filter.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/cluster.go
  • pkg/handlers/framework.go
  • pkg/services/status_aggregation.go
  • .golangci.yml
  • pkg/services/node_pool.go
  • pkg/services/types.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/main.go
  • pkg/config/config_test.go
  • pkg/services/generic_test.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Files:

  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/api/adapter_status_types.go
  • pkg/handlers/helpers.go
  • pkg/api/cluster_types.go
  • pkg/db/transaction_middleware.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/db/db_session/default.go
  • pkg/client/ocm/client.go
  • pkg/handlers/nodepool_status.go
  • pkg/auth/context.go
  • cmd/hyperfleet-api/server/routes.go
**/pkg/handlers/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Files:

  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

All unit tests must run with OCM_ENV=unit_testing environment variable for proper test isolation

Files:

  • cmd/hyperfleet-api/environments/framework_test.go
**/pkg/db/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/pkg/db/*.go: All database connections must respect db-sslmode parameter (disable | require | verify-ca | verify-full) with production defaulting to verify-full
Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Files:

  • pkg/db/transaction_middleware.go
**/pkg/db/transaction_middleware.go

📄 CodeRabbit inference engine (CLAUDE.md)

All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Files:

  • pkg/db/transaction_middleware.go
**/pkg/services/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Files:

  • pkg/services/cluster.go
  • pkg/services/generic.go
**/pkg/{services,dao}/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Files:

  • pkg/services/cluster.go
  • pkg/services/generic.go
{Makefile,*.mk,**/Makefile,**/*.mk}

📄 CodeRabbit inference engine (CLAUDE.md)

Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Files:

  • Makefile
{Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Podman for containerized development and build consistency (database, code generation)

Files:

  • Makefile
🧠 Learnings (22)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses

Applied to files:

  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/handlers/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Applied to files:

  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*service*,*handler*}.go : Retrieve database sessions from context via db.NewContext(ctx) rather than creating new GORM connections

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*db*,*model*,migrations}.go : Use GORM for database access with automatic migrations via gorm.AutoMigrate at startup

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/*.go : Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman

Applied to files:

  • Dockerfile.openapi
  • pkg/handlers/openapi.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile} : Use Podman for containerized development and build consistency (database, code generation)

Applied to files:

  • Dockerfile.openapi
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility

Applied to files:

  • Dockerfile.openapi
  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/api/cluster_types.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to pkg/api/openapi/api/openapi.yaml : Only edit openapi/openapi.yaml as the source; the generated pkg/api/openapi/api/openapi.yaml will be overwritten by 'make generate'

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/environments/*.go : Use environment-based configuration via OCM_ENV variable (development, unit_testing, integration_testing, production) for environment-specific behavior

Applied to files:

  • cmd/hyperfleet-api/environments/framework_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/api/cluster_types.go
  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns to avoid separate status tables for each resource type

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/services/*.go : Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Store resource specifications in JSONB columns (spec field) to enable flexible cloud provider configuration

Applied to files:

  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use TypeSpec to define API specifications, which compile to OpenAPI 3.0.3, then generate Go models using openapi-generator-cli v7.16.0

Applied to files:

  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/transaction_middleware.go : All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/server/*.go : Use JWT authentication from Red Hat SSO (via --jwk-cert-url flag) with account ID and username extraction from claims

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk} : Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Applied to files:

  • Makefile
🧬 Code graph analysis (4)
cmd/hyperfleet-api/server/api_server.go (2)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
cmd/hyperfleet-api/environments/framework_test.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Services (44-47)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
🔇 Additional comments (27)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)

14-14: Boolean simplification preserves behavior

The simplified doLog condition correctly keeps logging disabled only for /api/hyperfleet (with or without trailing slash after normalization) and enabled for all other paths; behavior and intent are preserved while improving readability.

Dockerfile.openapi (1)

3-5: Podman rootless sandbox workaround is well-documented and correctly applied.

The changes appropriately address the Prow/rootless podman compatibility issue described in the PR objectives. The explanatory comment is clear and helpful for future maintainers.

Makefile (2)

114-118: Approved: lint target simplification with direct golangci-lint invocation.

Removing the GOLANGCI_LINT_BIN variable and invoking golangci-lint directly is cleaner and aligns with the new .golangci.yml configuration. The change assumes golangci-lint is in PATH, which is reasonable for a tool that developers typically install globally or via go install.


55-56: Verify Go version constraint enforces 1.24.9 minimum.

The GO_VERSION pattern go1.24. will match any Go 1.24.x version (including 1.24.0), but the learnings state "Use Go 1.24.9 or later for all builds." The current pattern does not enforce the minimum patch version (1.24.9). Consider whether this is intentional leniency or if stricter matching (e.g., checking for patch ≥ 9) is needed.

pkg/services/generic.go (2)

43-43: LGTM: Typo fixed.

The comment now correctly spells "function".


173-173: LGTM: Clean refactoring to remove unused return value.

The signature change simplifies the API by eliminating the unused tsl.Node return value. The call site at line 173 and all three return statements (lines 336, 342, 345) have been updated consistently to match the new signature.

Also applies to: 332-345

openapitools.json (1)

5-5: Confirm that generated code was properly regenerated with v7.16.0 and validate compatibility.

This is a major version jump (5.4.0 → 7.16.0) with documented breaking changes between v6.0.0 and v7.16.0, including:

  • Templating refactors affecting variable names and template structure
  • Inline schema/$ref resolution behavior changes that can alter generated model structures
  • Enum and name sanitization rule changes that can modify identifier names in generated code
  • CodegenOperation/CodegenProperty API changes affecting operation and parameter handling

Ensure that:

  1. All generated files in pkg/api/openapi/api/ were properly regenerated via make generate
  2. Generated model names, field names, and operation signatures remain compatible with handler implementations
  3. Any enum sanitization changes did not break code that references these enums

Review the v6.0.0 and v7.x release notes (particularly v7.10–v7.16) to identify generator-specific changes for your use case.

cmd/hyperfleet-api/environments/framework_test.go (1)

39-39: LGTM: Safe reflection pattern.

The change to use reflect.ValueOf(&env.Services).Elem() creates an addressable reflection value, which is a common Go idiom. While addressability isn't strictly required for the read-only operations performed here (field iteration and nil checks), the pattern is safe, correct, and may have addressed a linter concern.

cmd/hyperfleet-api/server/api_server.go (1)

149-151: LGTM: Proper error handling for database cleanup.

The explicit error check and logging for SessionFactory.Close() follows Go best practices and aligns with the broader error-handling improvements across this PR.

pkg/db/db_session/default.go (1)

111-113: LGTM: Proper error handling for async ping.

Handling and logging the error from l.Ping() in the goroutine improves observability without affecting control flow.

pkg/db/transaction_middleware.go (1)

44-52: LGTM: Explicit error handling for response marshaling and writing.

The early returns on marshal/write errors follow best practices and align with similar improvements across the codebase (pkg/auth/helpers.go, pkg/handlers/helpers.go).

pkg/auth/helpers.go (1)

30-38: LGTM: Consistent error handling pattern.

The explicit error checks for JSON marshaling and response writing match the pattern applied in pkg/db/transaction_middleware.go and pkg/handlers/helpers.go, ensuring consistent error handling across authentication flows.

pkg/services/cluster.go (1)

139-139: LGTM: Clearer variable naming.

Renaming the error variable to unmarshalErr improves readability by distinguishing it from other error variables in outer scopes.

pkg/handlers/helpers.go (1)

18-26: LGTM: Essential error handling for widely-used helper.

Since writeJSONResponse is called by multiple handlers throughout the codebase, the explicit error handling for marshaling and writing improves reliability across all API endpoints.

pkg/handlers/nodepool_status.go (1)

92-95: LGTM: Improved error visibility with structured logging.

The inline error handling and structured logging with context (nodepool_id, error) improves observability while correctly treating aggregation failures as non-fatal. This follows the coding guideline to use logger.NewOCMLogger(ctx) with context-aware fields.

Based on coding guidelines, this follows the structured logging pattern.

pkg/client/ocm/client.go (1)

78-82: Verify structured logging requirement and error handling by all Close() callers.

The signature change to return error aligns with Go conventions, and the nil connection handling is correct. However, per coding guidelines, all Go files must use structured logging via logger.NewOCMLogger(ctx) with context-aware fields (opid, accountID, tx_id). Confirm whether Close() should include structured logging for error/success reporting, or if this method is exempt. Additionally, verify that all callers of Client.Close() have been updated to handle the returned error appropriately.

pkg/api/adapter_status_types.go (5)

55-58: LGTM!

The error handling ensures that unmarshal failures result in a safe empty slice rather than a nil value or panic.


64-67: LGTM!

Consistent error handling pattern that ensures Data is always a valid map, preventing downstream nil dereferences.


122-127: LGTM!

The fallback to empty array JSON is appropriate. While marshal failures are rare for well-formed Go structs, this ensures the database receives valid JSON.


130-140: LGTM!

The error scope issue from the previous review has been correctly resolved by using a separate dataErr variable. The logic now properly handles both nil data and marshal failures.


145-149: LGTM!

The nil fallback for optional metadata is appropriate and maintains consistency with the field's optional nature.

pkg/auth/context.go (3)

45-48: LGTM!

The safe type assertion with ok-check prevents potential panics when the context value is not a string.


82-96: LGTM!

The safe type assertions for all primary claims prevent panics when JWT tokens contain unexpected claim types or are malformed.


100-127: LGTM!

All fallback claim extractions use safe type assertions, and the name-splitting logic is properly guarded with an ok-check. This robustly handles different JWT token formats from various issuers (RHSSO vs RHD).

pkg/handlers/openapi.go (1)

61-64: LGTM!

Checking the Write error is good defensive coding practice, even though the response has already been committed and no error can be sent to the client. This makes error handling explicit and aligns with the broader project pattern of explicit error checking observed across handlers.

Also applies to: 70-73

pkg/api/cluster_types.go (2)

72-90: LGTM!

The error handling for all unmarshal operations ensures safe defaults (empty maps/slices) when JSONB data is corrupt or invalid. The comment on Line 85 helpfully clarifies the storage type for StatusConditions.


138-157: LGTM!

The error handling for all marshal operations ensures the database always receives valid JSON, with appropriate fallbacks. This pattern is consistent with the defensive approach in adapter_status_types.go.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Makefile (2)

56-56: Enforce Go 1.24.9 minimum rather than permissive 1.24.x pattern.

The GO_VERSION:=go1.24. pattern matches any Go 1.24.x release (1.24.0, 1.24.1, 1.24.9, etc.). Per learnings, Go 1.24.9 or later is required for all builds. The current pattern allows older patch versions that may have bugs or security issues. Specify the minimum version explicitly.

Apply this diff to enforce the minimum required version:

-GO_VERSION:=go1.24.
+GO_VERSION:=go1.24.9

Note: Update the verify and install targets' grep checks to use a regex or exact match as appropriate for your version pinning strategy.


122-125: Binary target missing FIPS-compliant crypto flags.

The binary target does not apply CGO_ENABLED=$(CGO_ENABLED) GOEXPERIMENT=boringcrypto, while the install target (line 129) and cmds target (line 256) both correctly use these flags. Per coding guidelines, Go binaries must be built with FIPS-compliant crypto for enterprise/government deployments.

Apply this diff to enforce FIPS compliance in the binary target:

 binary: check-gopath
 	echo "Building version: ${build_version}"
-	${GO} build -ldflags="$(ldflags)" -o hyperfleet-api ./cmd/hyperfleet-api
+	CGO_ENABLED=$(CGO_ENABLED) GOEXPERIMENT=boringcrypto ${GO} build -ldflags="$(ldflags)" -o hyperfleet-api ./cmd/hyperfleet-api
🧹 Nitpick comments (5)
pkg/client/ocm/client.go (1)

78-83: Consider making Close() tolerant of a nil receiver

The new Close() error implementation and nil-check on connection look good and integrate cleanly with callers that log the returned error.

You might optionally make Close() safe to call on a nil *Client to avoid potential panics if future code ever does var c *Client; c.Close():

func (c *Client) Close() error {
    if c == nil || c.connection == nil {
        return nil
    }
    return c.connection.Close()
}

Not required for this PR, but it can make the API more defensive.

pkg/db/db_session/testcontainer.go (2)

76-79: Configure SetMaxIdleConns as well to fully follow pooling guidance

You only set SetMaxOpenConns; consider also configuring SetMaxIdleConns (e.g., from config.MaxIdleConnections if available) so this session factory fully matches the db pooling guidance and behaves like the non-test session setup.

Based on learnings, configuring both max open and max idle connections is recommended for pkg/db code.


44-105: Optional: migrate glog usage to structured logger.NewOCMLogger(ctx)

This helper still uses glog with context.Background(); over time it would be better to switch to the structured logger with context fields (opid, accountID, tx_id) so logs from testcontainers are consistent with the rest of the codebase.

As per coding guidelines, new or touched Go code should prefer logger.NewOCMLogger(ctx) over bare glog.

pkg/handlers/helpers.go (1)

17-26: Handler JSON responses now guard marshal/write failures.

Adding explicit checks around JSON marshaling and w.Write aligns handlers with the new defensive pattern and avoids hidden failures while keeping existing HTTP semantics.

pkg/services/node_pool.go (1)

132-141: Local unmarshal error variable avoids shadowing outer errors.

Renaming the JSON unmarshal error to unmarshalErr keeps it clearly scoped and prevents accidental reuse of the function-level err, improving readability and safety.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ce95ab and 08bcd18.

📒 Files selected for processing (37)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (20)
  • pkg/handlers/openapi.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/api/cluster_types.go
  • pkg/db/sql_helpers.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/config/config_test.go
  • pkg/api/node_pool_types.go
  • pkg/services/cluster.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/handlers/framework.go
  • pkg/services/status_aggregation.go
  • Dockerfile.openapi
  • pkg/api/presenters/slice_filter.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/db/transaction_middleware.go
  • pkg/services/types.go
  • .golangci.yml
  • openapitools.json
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Files:

  • cmd/hyperfleet-api/main.go
  • pkg/handlers/nodepool_status.go
  • pkg/auth/context.go
  • pkg/handlers/node_pool.go
  • pkg/auth/helpers.go
  • pkg/services/node_pool.go
  • pkg/handlers/cluster.go
  • pkg/services/generic.go
  • pkg/db/db_session/test.go
  • pkg/api/adapter_status_types.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/services/generic_test.go
  • pkg/handlers/helpers.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/client/ocm/client.go
**/pkg/handlers/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Files:

  • pkg/handlers/nodepool_status.go
  • pkg/handlers/node_pool.go
  • pkg/handlers/cluster.go
  • pkg/handlers/helpers.go
**/pkg/services/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Files:

  • pkg/services/node_pool.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
**/pkg/{services,dao}/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Files:

  • pkg/services/node_pool.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
{Makefile,*.mk,**/Makefile,**/*.mk}

📄 CodeRabbit inference engine (CLAUDE.md)

Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Files:

  • Makefile
{Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Podman for containerized development and build consistency (database, code generation)

Files:

  • Makefile
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

All unit tests must run with OCM_ENV=unit_testing environment variable for proper test isolation

Files:

  • pkg/services/generic_test.go
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination

Applied to files:

  • pkg/handlers/nodepool_status.go
  • pkg/services/node_pool.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/services/*.go : Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Applied to files:

  • pkg/handlers/nodepool_status.go
  • pkg/services/node_pool.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses

Applied to files:

  • pkg/auth/helpers.go
  • pkg/handlers/helpers.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/handlers/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Applied to files:

  • pkg/auth/helpers.go
  • pkg/handlers/helpers.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk} : Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Applied to files:

  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility

Applied to files:

  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*service*,*handler*}.go : Retrieve database sessions from context via db.NewContext(ctx) rather than creating new GORM connections

Applied to files:

  • pkg/db/db_session/test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/test/integration/**/*.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure state isolation

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/services/generic_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/*.go : Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns to avoid separate status tables for each resource type

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/generic_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/cmd/hyperfleet/environments/*.go : Use environment-based configuration via OCM_ENV variable (development, unit_testing, integration_testing, production) for environment-specific behavior

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
🧬 Code graph analysis (6)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/handlers/cluster.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
🔇 Additional comments (15)
Makefile (1)

115-117: Lint target refactor aligns with centralized .golangci.yml.

Invoking golangci-lint run directly with the new .golangci.yml configuration is a cleaner approach than the previous $(GOLANGCI_LINT_BIN) variable. This allows lint rules and exclusions to be centrally managed.

pkg/handlers/nodepool_status.go (1)

11-11: Structured logging on aggregation failure looks good

Using logger.NewOCMLogger(ctx) and logging nodepool_id plus the aggregation error at warning level improves observability while keeping the aggregation failure non-fatal for the request. This aligns with the handler flow and logging guidelines; no further changes needed.

Also applies to: 92-95

pkg/db/db_session/test.go (1)

216-221: Fail-fast ResetDB improves test reliability and state tracking

ResetDB now panics on resetDB failure and only sets wasDisconnected after a successful reset. This removes the prior silent failure mode and ensures tests never continue with a dirty or partially reset database, while keeping the reconnection logic via wasDisconnected intact. Looks good.

cmd/hyperfleet-api/main.go (1)

28-30: Flag parsing error handling is correct and consistent

Wrapping flag.CommandLine.Parse([]string{}) with an error check and glog.Fatalf provides clearer startup failure behavior and is consistent with the later rootCmd.Execute() error handling. No further changes needed here.

cmd/hyperfleet-api/environments/framework.go (1)

158-162: OCM teardown is now nil-safe and error-aware

The additional guard around e.Clients.OCM and logging of Close() errors looks correct and prevents panics when the client isn’t initialized, while preserving teardown error visibility. No further changes needed here.

pkg/db/db_session/default.go (1)

111-113: LGTM! Proper error handling added.

The error handling for l.Ping() correctly addresses linting issues by checking and logging the error. The verbose logging level is appropriate for this background health-check operation.

pkg/db/db_session/testcontainer.go (1)

47-55: Port-based readiness check looks correct and should reduce flakiness

Switching from log-matching to a port-based readiness check with the same 60s timeout is a cleaner, less brittle way to wait for Postgres to be usable. This aligns well with the integration testcontainer usage in this package.

pkg/handlers/cluster.go (1)

71-77: Spec patch now safely fails on marshal errors.

Marshalling patch.Spec and surfacing a GeneralError before assigning found.Spec is correct and keeps cluster specs from being corrupted by invalid JSON; behavior is consistent with the node pool handler.

pkg/auth/helpers.go (1)

29-38: Defensive response writing is correctly added.

The added marshal and write error checks make writeJSONResponse safer without changing its external behavior, which is appropriate given headers are already committed.

pkg/handlers/node_pool.go (1)

72-78: Node pool Spec patch correctly validates JSON before persistence.

Marshalling patch.Spec with error handling and only assigning found.Spec on success provides a safe, consistent patch flow and surfaces failures as a GeneralError.

pkg/api/adapter_status_types.go (1)

55-68: AdapterStatus marshalling/unmarshalling is now robust to bad JSON.

Defaulting Conditions/Data/Metadata to safe empty values on marshal/unmarshal failure and scoping Data’s error to dataErr removes the earlier error-reuse pitfall and ensures AdapterStatus objects are always serializable without panicking.

Also applies to: 122-127, 130-140, 145-149

pkg/auth/context.go (1)

40-49: JWT claim extraction is now type-safe.

Switching to ok-checked string assertions for context username and all JWT claims (including fallbacks from alternate claim keys and name) hardens these helpers against malformed tokens without changing their intended semantics.

Also applies to: 80-127

pkg/services/generic_test.go (1)

23-24: Test updates correctly track API changes and lint expectations.

Deferring dbFactory.Close() with //nolint:errcheck is appropriate for test cleanup, and adapting to treeWalkForSqlizer’s new (Sqlizer, error) signature preserves the existing SQL translation assertions.

Also applies to: 66-71

pkg/services/generic.go (2)

173-176: LGTM! Call site correctly updated to match narrowed signature.

The change from capturing three return values (ignoring the first) to capturing two return values aligns with the updated treeWalkForSqlizer signature. Error handling remains correct.


332-345: LGTM! Signature narrowing improves clarity.

The refactoring correctly removes the unused tsl.Node return value. The function now clearly expresses its purpose: converting a search tree to a SQL-izer, not transforming the tree. Error returns properly use nil for the primary value, and the success path returns the expected (sqlizer, nil).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
pkg/client/ocm/client.go (1)

78-83: Close implementation is correct; optionally make it idempotent by nil‑ing the connection

The new signature and error propagation look good and fix the earlier issue of ignoring Close errors. If Close might be called multiple times on the same client, consider setting c.connection = nil after a successful close so subsequent calls are guaranteed to be no‑ops instead of re‑closing the same connection.

 func (c *Client) Close() error {
-	if c.connection != nil {
-		return c.connection.Close()
-	}
-	return nil
+	if c.connection == nil {
+		return nil
+	}
+
+	if err := c.connection.Close(); err != nil {
+		return err
+	}
+
+	c.connection = nil
+	return nil
 }
pkg/handlers/cluster_status.go (1)

92-95: Good observable handling of aggregation failures; consider minor logging refinements

Logging UpdateClusterStatusFromAdapters failures instead of silently ignoring them is a solid improvement, and keeping the request successful makes sense since the adapter status upsert already succeeded. As an optional tweak, you might consider:

  • Logging at an error level if aggregation failures are expected to be rare and actionable.
  • Including any available adapter identifier(s) in the log context to aid debugging.

Overall, the pattern logger.NewOCMLogger(ctx).Extra(...).Warning(...) aligns with the structured logging guideline for handlers. As per coding guidelines, this is the right logger to use with the request context.

pkg/handlers/helpers.go (1)

17-26: Marshal/write error handling is correct; consider pre‑marshalling or logging as a future refinement

The added checks for json.Marshal and w.Write fix the previous silent failure/ignored‑error issue and are fine to ship. If you touch this again, you might consider:

  • Pre‑marshalling before WriteHeader so a marshal failure can still translate into a 5xx, and/or
  • Adding a way to log marshal/write failures (e.g., by passing a context.Context or logger into this helper) so these bugs don’t get swallowed.

These are optional and not required for this PR. As per coding guidelines, we prefer structured logging for unexpected failures.

pkg/db/transaction_middleware.go (1)

44-52: Behavior is fine; align comment with implementation or add logging

The new marshal/write error checks look good and resolve ignored‑error issues. The inline comment “Log error but don't expose to client…” is now misleading because no logging happens here and there’s no context/logger in scope.

Either:

  • Wire a logger/context into this helper and actually log marshal/write failures using structured logging, or
  • Simplify the comment to just state that we return since headers are already sent and there’s nothing else we can do.

This is a minor readability issue, not a blocker. As per coding guidelines, structured logging is preferred for unexpected errors.

pkg/auth/helpers.go (1)

29-38: Consistent error handling for JSON responses; optional room for richer failure reporting

The marshal/write checks here are correct and consistent with the other helpers, and they cleanly address the previous ignored‑error behavior. Since handleError already logs the service error, this is acceptable as‑is.

If you revisit this, you might optionally:

  • Pre‑marshal before WriteHeader to allow turning marshal failures into 5xx, and/or
  • Add a way to log marshal/write failures separately from the business error if that becomes important.

No changes are required for this PR. Based on learnings, the use of the structured error type and JSON encoding of AsOpenapiError remains aligned with the API error conventions.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08bcd18 and bff8479.

📒 Files selected for processing (36)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
  • cmd/hyperfleet-api/server/logging/writer.go
  • Dockerfile.openapi
  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/handlers/openapi.go
  • pkg/db/sql_helpers.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/handlers/framework.go
  • pkg/services/node_pool.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • openapitools.json
  • pkg/handlers/node_pool.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/auth/context.go
  • pkg/services/types.go
  • pkg/handlers/cluster.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/services/generic.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Files:

  • pkg/api/node_pool_types.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/helpers.go
  • pkg/api/cluster_types.go
  • pkg/handlers/cluster_status.go
  • pkg/db/db_session/default.go
  • pkg/api/presenters/slice_filter.go
  • cmd/hyperfleet-api/main.go
  • pkg/api/adapter_status_types.go
  • pkg/services/generic_test.go
  • pkg/auth/helpers.go
  • pkg/services/status_aggregation.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/client/ocm/client.go
  • pkg/services/cluster.go
  • pkg/config/config_test.go
**/pkg/db/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/pkg/db/*.go: All database connections must respect db-sslmode parameter (disable | require | verify-ca | verify-full) with production defaulting to verify-full
Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Files:

  • pkg/db/transaction_middleware.go
**/pkg/db/transaction_middleware.go

📄 CodeRabbit inference engine (CLAUDE.md)

All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Files:

  • pkg/db/transaction_middleware.go
**/pkg/handlers/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Files:

  • pkg/handlers/helpers.go
  • pkg/handlers/cluster_status.go
**/pkg/services/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Files:

  • pkg/services/generic_test.go
  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
**/pkg/{services,dao}/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Files:

  • pkg/services/generic_test.go
  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

All unit tests must run with OCM_ENV=unit_testing environment variable for proper test isolation

Files:

  • pkg/services/generic_test.go
  • pkg/config/config_test.go
{Makefile,*.mk,**/Makefile,**/*.mk}

📄 CodeRabbit inference engine (CLAUDE.md)

Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Files:

  • Makefile
{Makefile,*.mk,**/Makefile,**/*.mk,docker-compose*,**/Dockerfile}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Podman for containerized development and build consistency (database, code generation)

Files:

  • Makefile
🧠 Learnings (21)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : NodePool resources must enforce owner_references.id (parent cluster ID) via foreign key constraint in database schema

Applied to files:

  • pkg/api/node_pool_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Store resource specifications in JSONB columns (spec field) to enable flexible cloud provider configuration

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*model*,*dao*,*service*}.go : Use Kubernetes-style conditions in adapter status to support multiple independent adapters without coordination

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
  • pkg/handlers/cluster_status.go
  • pkg/api/adapter_status_types.go
  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to openapi/openapi.yaml : Maintain openapi/openapi.yaml as the source of truth (32KB with $ref) and regenerate pkg/api/openapi/api/openapi.yaml (44KB fully resolved) via 'make generate' using Podman

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to pkg/api/openapi/api/openapi.yaml : Only edit openapi/openapi.yaml as the source; the generated pkg/api/openapi/api/openapi.yaml will be overwritten by 'make generate'

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/transaction_middleware.go : All HTTP requests automatically receive database session via TransactionMiddleware with auto-commit on success and rollback on error

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*handler*,*error*}.go : Use structured error type from pkg/errors/ with HttpCode, Code, and Reason fields for consistent API error responses

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/helpers.go
  • pkg/auth/helpers.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*db*}.go : All database operations must accept context.Context parameter to enable proper transaction propagation from middleware

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/handlers/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors with errors.SendError() → send response with appropriate status code

Applied to files:

  • pkg/handlers/helpers.go
  • pkg/auth/helpers.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use TypeSpec to define API specifications, which compile to OpenAPI 3.0.3, then generate Go models using openapi-generator-cli v7.16.0

Applied to files:

  • pkg/api/cluster_types.go
  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/api/*embed*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for runtime availability and self-contained binary

Applied to files:

  • pkg/api/cluster_types.go
  • pkg/handlers/cluster_status.go
  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/services/*.go : Calculate aggregate status.phase from adapter conditions (Ready if all adapters=True, Failed if any=False, NotReady otherwise) without manual phase setting

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/api/adapter_status_types.go
  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/migrations/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns to avoid separate status tables for each resource type

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/api/adapter_status_types.go
  • pkg/services/status_aggregation.go
  • pkg/services/cluster.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware fields including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/cluster_status.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/{services,dao}/*.go : Increment generation field on each resource spec update to enable optimistic concurrency and adapter reconciliation tracking

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/generic_test.go
  • pkg/services/cluster.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/test/integration/**/*.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure state isolation

Applied to files:

  • pkg/services/generic_test.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to {Makefile,*.mk,**/Makefile,**/*.mk} : Build Go binary using FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto for enterprise/government deployments

Applied to files:

  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Use Go 1.24.9 or later for all builds and ensure Go version compatibility

Applied to files:

  • Makefile
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/{*dao*,*service*,*handler*}.go : Retrieve database sessions from context via db.NewContext(ctx) rather than creating new GORM connections

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-11-28T06:28:37.091Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T06:28:37.091Z
Learning: Applies to **/pkg/db/*.go : Configure database connection pooling via SetMaxOpenConns() and SetMaxIdleConns() for performance optimization

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
🧬 Code graph analysis (3)
pkg/handlers/cluster_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
cmd/hyperfleet-api/server/api_server.go (2)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
🔇 Additional comments (25)
pkg/db/db_session/default.go (1)

111-113: LGTM! Good error handling improvement.

Adding error handling for the Ping() call addresses the silent failure and aligns with best practices. The verbose logging level V(10) is appropriate for periodic health checks.

Makefile (4)

128-128: FIPS-compliant build configuration in install target looks good.

The install target correctly uses CGO_ENABLED=$(CGO_ENABLED) and GOEXPERIMENT=boringcrypto to enable FIPS-compliant cryptography, aligning with the enterprise/government deployment requirements.


13-13: Container tooling properly configured for Podman-first development.

The Makefile correctly defaults to Podman (container_tool ?= podman) and uses this consistently across containerized workflows (generate, generate-vendor, and database targets), supporting reproducible builds and development environments as per coding guidelines.

Also applies to: 210-227


210-217: Verify APT sandbox workaround implementation for Prow/podman compatibility.

The PR objectives state: "Updates Makefile to add -o APT::Sandbox::User=root to accommodate Prow using podman when running 'make generate'". The provided Makefile snippet (lines 210-217) does not show this modification. The workaround may be in Dockerfile.openapi (referenced in the generate target) or requires manual verification to confirm it has been applied elsewhere in the codebase or if it still needs to be added.


115-116: Verify the narrowed lint scope doesn't create coverage gaps.

The lint target has been narrowed to only ./pkg/api/openapi.... If this is intentional, clarify the rationale for excluding ./cmd/... and most of ./pkg/... from lint coverage, especially given the PR objectives state "make lint: ran golangci-lint over ./cmd/... and ./pkg/... with 0 issues reported". If unintentional, restore the broader scope:

-	golangci-lint run -D unused \
-		./pkg/api/openapi...
+	golangci-lint run -D unused \
+		./cmd/... \
+		./pkg/...
pkg/services/status_aggregation.go (1)

155-163: LGTM! Improved observability for unexpected adapter statuses.

The switch statement is clearer than the previous if-else chain and explicitly handles unexpected statuses in the default case. Logging unexpected values like "Unknown" improves debugging without changing the behavior for "True" and "False" cases.

cmd/hyperfleet-api/server/api_server.go (1)

149-151: LGTM: Proper error handling added for database cleanup.

The error from SessionFactory.Close() is now appropriately checked and logged during shutdown. Logging without fatal termination is suitable for cleanup code, ensuring visibility of connection closure issues without disrupting graceful shutdown.

cmd/hyperfleet-api/main.go (1)

28-30: LGTM: Defensive error handling added for flag parsing.

The error from flag.CommandLine.Parse() is now explicitly checked. While unlikely to fail with an empty slice, this defensive check ensures initialization errors are surfaced rather than silently discarded. Using glog.Fatalf is appropriate for startup failures.

pkg/handlers/cluster_status.go (1)

11-11: Logger import usage is correct and aligns with guidelines

The added logger import is used below for structured, context-aware logging and is consistent with the handler logging conventions.

pkg/api/presenters/slice_filter.go (3)

114-116: LGTM: Essential error propagation for nested struct validation.

This correctly propagates validation errors from nested structs. Without this, validation failures in nested fields would be silently ignored, leading to incorrect validation results.


149-153: LGTM: Defensive error handling for regex compilation.

Good defensive programming. While pattern compilation is unlikely to fail (since name comes from JSON struct field tags), handling the error prevents potential panics. The fallback of returning the unchanged map is reasonable given the context.


194-196: LGTM: Critical nil pointer safety check.

This properly guards against nil pointer dereference when formatting time.Time values. The type assertion with the nil check prevents panics that would occur if Format() were called on a nil pointer.

pkg/config/config_test.go (1)

15-15: LGTM: nolint directives appropriate for test cleanup.

The //nolint:errcheck directives on deferred os.Remove() calls are appropriate. In test code, ignoring cleanup errors for temporary files is standard practice since failures don't affect test correctness.

Also applies to: 30-30, 45-45, 60-60

pkg/services/generic_test.go (3)

23-23: LGTM: nolint directive appropriate for test cleanup.

The //nolint:errcheck directive on the deferred dbFactory.Close() call is appropriate for test cleanup code where cleanup failures don't affect test correctness.


70-70: LGTM: Signature update correctly reflects refactored method.

The test correctly adapts to the simplified treeWalkForSqlizer signature. The removed first return value (tsl.Node) was unused, and the test still properly validates SQL generation via sqlizer.ToSql().


20-77: Ensure OCM_ENV=unit_testing is set when running this test.

All unit tests must run with OCM_ENV=unit_testing for proper test isolation per coding guidelines. Check that this environment variable is configured in your test execution setup, Makefile test targets, or CI/CD pipeline.

pkg/api/adapter_status_types.go (3)

55-67: LGTM: Robust error handling for JSON unmarshalling.

The explicit error checks with sensible fallbacks (empty slice for conditions, empty map for data) ensure the system remains resilient to corrupted JSONB data in the database.


130-140: LGTM: Scope bug fixed correctly.

The refactoring properly addresses the previous scope issue by introducing dataErr within the conditional block. The error handling now correctly applies only when json.Marshal(req.Data) is actually invoked.


123-127: LGTM: Consistent error handling for JSON marshalling.

The explicit error checks with fallbacks ("[]" for conditions, nil for metadata) prevent potential panics from programming errors and ensure deterministic behavior.

Also applies to: 145-149

pkg/api/cluster_types.go (2)

72-90: LGTM: Robust unmarshalling with appropriate fallbacks.

The error handling for Spec, Labels, and StatusConditions follows a consistent defensive pattern. The comment at line 85 helpfully clarifies that StatusConditions are stored as ResourceCondition in the database.


138-157: LGTM: Consistent marshalling error handling.

The explicit error checks with fallbacks ("{}" for Spec and Labels, "[]" for StatusConditions) ensure deterministic behavior and prevent potential panics.

pkg/api/node_pool_types.go (2)

82-100: LGTM: Consistent error handling across resource types.

The unmarshalling error handling mirrors the pattern in cluster_types.go, ensuring uniform resilience across the codebase. The clarifying comment at line 95 is helpful.


159-178: LGTM: Defensive marshalling with sensible fallbacks.

The error handling follows the established pattern, using appropriate fallbacks for each data type.

pkg/services/cluster.go (2)

132-132: LGTM: Comment accurately reflects the code.

The updated comment correctly describes that ResourceCondition objects are built from adapter Available conditions, addressing the previous review feedback.


139-141: LGTM: Better variable naming prevents shadowing.

Using unmarshalErr instead of err makes the error handling more explicit and avoids potential confusion with the err variable used elsewhere in the function (e.g., lines 122, 208, 228).

@ciaranRoche
Copy link
Contributor

@yingzhanredhat can you create a task for this in jira and add it to the sprint so we get a better estimate of team velocity please 🙏

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
test/integration/node_pools_test.go (1)

75-81: Check the HTTP request error to prevent test panics.

Discarding the error from resty.R().Post() is unsafe. If the HTTP request fails (network error, connection refused, etc.), restyResp could be nil, causing a panic when calling restyResp.StatusCode() on line 81.

Apply this diff to properly handle the error:

-	restyResp, _ := resty.R().
+	restyResp, err := resty.R().
 		SetHeader("Content-Type", "application/json").
 		SetHeader("Authorization", fmt.Sprintf("Bearer %s", jwtToken)).
 		SetBody(`{ this is invalid }`).
 		Post(h.RestURL(fmt.Sprintf("/clusters/%s/nodepools", cluster.ID)))
-
+	Expect(err).NotTo(HaveOccurred(), "HTTP request should complete successfully")
 	Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest))
test/integration/clusters_test.go (2)

68-74: Check the HTTP request error to prevent test panics.

Discarding the error from resty.R().Post() is unsafe. If the HTTP request fails (network error, connection refused, etc.), restyResp could be nil, causing a panic when calling restyResp.StatusCode() on line 74.

Apply this diff to properly handle the error:

-	restyResp, _ := resty.R().
+	restyResp, err := resty.R().
 		SetHeader("Content-Type", "application/json").
 		SetHeader("Authorization", fmt.Sprintf("Bearer %s", jwtToken)).
 		SetBody(`{ this is invalid }`).
 		Post(h.RestURL("/clusters"))
-
+	Expect(err).NotTo(HaveOccurred(), "HTTP request should complete successfully")
 	Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest))

238-242: Check the error to ensure the test validates the correct behavior.

While the nil check for resp prevents a panic, discarding the error means the test cannot distinguish between a request failure and a successful response. This could hide real issues and make the test less informative.

Apply this diff to properly handle the error:

-	_, resp, _ = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(emptyNameInput).Execute()
-	// Should either accept empty name or return 400
-	if resp != nil {
-		t.Logf("Empty name test returned status: %d", resp.StatusCode)
-	}
+	_, resp, err = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(emptyNameInput).Execute()
+	// Should either accept empty name or return 400
+	if err != nil {
+		Expect(resp).NotTo(BeNil(), "Response should be available even on error")
+		Expect(resp.StatusCode).To(BeElementOf([]int{http.StatusBadRequest, http.StatusInternalServerError}), 
+			"Empty name should return 400 or 500")
+	} else {
+		t.Logf("Empty name was accepted, returned status: %d", resp.StatusCode)
+		Expect(resp.StatusCode).To(Equal(http.StatusCreated))
+	}
♻️ Duplicate comments (1)
cmd/hyperfleet-api/server/routes.go (1)

53-59: Authorization middleware remains commented out, contradicting stated preference.

The authorization middleware initialization is still commented out and using only the mock. This contradicts yasun1's explicit comment: "I'd like to leave the entry here but not comment it out."

Security impact: Real authorization checks are bypassed, degrading the security posture.

Note: The compilation bug in the commented code has been correctly fixed by adding var err error on line 56.

Recommendation: Uncomment the authorization middleware initialization block as requested, or clarify the current plan if authorization needs to remain disabled for this PR.

🧹 Nitpick comments (5)
pkg/handlers/validation.go (1)

41-55: Prefer deleting unused code (or add clearer rationale) instead of commenting it out

The validateInclusionIn helper is now fully commented out to satisfy unused-lint, but leaving large commented blocks tends to accumulate noise over time. If this validator is not expected to come back soon, consider removing it entirely; if it is expected to return, a brief comment referencing the reason or a tracking issue (e.g. // TODO(HF-123): reintroduce when X is implemented) would be more informative than “Comment it as it is not used currently.” This also keeps the file cleaner for future readers.

cmd/hyperfleet-api/server/logging/writer.go (1)

47-47: Consider consistency with lines 23 and 28.

The change to writer.Header() is functionally correct and more idiomatic. However, lines 23 and 28 explicitly use writer.ResponseWriter.Write() and writer.ResponseWriter.WriteHeader(). For consistency, consider either using the shorter form everywhere or the explicit form everywhere.

Apply this diff to make all three consistent (shorter form):

 func (writer *loggingWriter) Write(body []byte) (int, error) {
 	writer.responseBody = body
-	return writer.ResponseWriter.Write(body)
+	return writer.Write(body)
 }
 
 func (writer *loggingWriter) WriteHeader(status int) {
 	writer.responseStatus = status
-	writer.ResponseWriter.WriteHeader(status)
+	writer.WriteHeader(status)
 }

Note: The above diff would cause infinite recursion. To use the shorter form, these methods would need to be removed entirely (letting the embedded type handle them) or the logic would need restructuring. The explicit form is safer here.

Better approach - keep the explicit form everywhere:

 func (writer *loggingWriter) prepareResponseLog(elapsed string) (string, error) {
 	info := &ResponseInfo{
-		Header:  writer.Header(),
+		Header:  writer.ResponseWriter.Header(),
 		Body:    writer.responseBody,
 		Status:  writer.responseStatus,
 		Elapsed: elapsed,
 	}
pkg/db/sql_helpers.go (1)

83-88: Tighten prefix check and remove magic index in propertiesNodeConverter

The new string/assertion and length guard are good defensive additions. You can make this a bit more robust and self-documenting by:

  • Reusing startsWithProperties to ensure the expected prefix, not just length.
  • Deriving the slice offset from the prefix length instead of hardcoding 11.
  • Fixing the propetyName typo.

For example:

-	leftStr, ok := l.Left.(string)
-	if !ok || len(leftStr) < 11 {
-		return n
-	}
-	propetyName := leftStr[11:]
+	leftStr, ok := l.Left.(string)
+	if !ok || !startsWithProperties(leftStr) {
+		return n
+	}
+	propertyName := leftStr[len("properties."):]
@@
-			Func: tsl.IdentOp,
-			Left: fmt.Sprintf("properties ->> '%s'", propetyName),
+			Func: tsl.IdentOp,
+			Left: fmt.Sprintf("properties ->> '%s'", propertyName),

This keeps the function safe if it’s ever called outside the current hasProperty gate and avoids the magic number.

pkg/auth/context.go (1)

118-127: Consider preserving already-populated fields and handling multi-word names.

Two edge cases in the name parsing logic:

  1. Overwrites existing firstName: If firstName was already set from given_name but lastName is empty, this code will overwrite firstName with data from the name claim.

  2. Multi-word names: For names like "Mary Jane Watson", only the first two words are used, resulting in lastName = "Jane" instead of "Watson" or "Jane Watson".

If you want to address these:

 if payload.FirstName == "" || payload.LastName == "" {
     if name, ok := claims["name"].(string); ok {
         names := strings.Split(name, " ")
         if len(names) > 1 {
-            payload.FirstName = names[0]
-            payload.LastName = names[1]
+            if payload.FirstName == "" {
+                payload.FirstName = names[0]
+            }
+            if payload.LastName == "" {
+                payload.LastName = strings.Join(names[1:], " ")
+            }
         } else {
-            payload.FirstName = names[0]
+            if payload.FirstName == "" {
+                payload.FirstName = names[0]
+            }
         }
     }
 }
pkg/api/presenters/slice_filter.go (1)

149-153: Good defensive programming: regexp compilation error is now handled gracefully.

The change from MustCompile (which panics) to Compile with error handling prevents potential runtime panics if the pattern contains unexpected characters. Returning the unchanged map is a safe fallback that avoids removing the wrong fields.

Optionally, consider logging the error when pattern compilation fails to aid debugging, since this scenario indicates either a programming error in pattern construction or an unusual field name:

 pat, err := regexp.Compile(pattern)
 if err != nil {
-	// Invalid pattern, return unchanged
+	// Invalid pattern, return unchanged
+	// Consider: log.Warnf("Failed to compile pattern %q: %v", pattern, err)
 	return in
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bff8479 and 84d8b35.

📒 Files selected for processing (44)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/framework_test.go (0 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/handlers/validation.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
  • test/helper.go (4 hunks)
  • test/integration/clusters_test.go (2 hunks)
  • test/integration/node_pools_test.go (1 hunks)
  • test/mocks/jwk_cert_server.go (1 hunks)
  • test/mocks/ocm.go (1 hunks)
💤 Files with no reviewable changes (1)
  • pkg/handlers/framework_test.go
✅ Files skipped from review due to trivial changes (2)
  • pkg/services/node_pool.go
  • .golangci.yml
🚧 Files skipped from review as they are similar to previous changes (16)
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/api/cluster_types.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • openapitools.json
  • pkg/db/db_session/testcontainer.go
  • pkg/api/node_pool_types.go
  • pkg/db/db_session/default.go
  • pkg/handlers/openapi.go
  • Dockerfile.openapi
  • pkg/services/types.go
  • pkg/services/cluster.go
  • pkg/config/config_test.go
  • Makefile
  • pkg/handlers/cluster.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • test/integration/clusters_test.go
  • test/mocks/jwk_cert_server.go
  • pkg/handlers/node_pool.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/handlers/framework.go
  • pkg/handlers/validation.go
  • cmd/hyperfleet-api/main.go
  • pkg/db/db_session/test.go
  • test/integration/node_pools_test.go
  • test/mocks/ocm.go
  • pkg/db/sql_helpers.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/api/adapter_status_types.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
  • pkg/auth/context.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • cmd/hyperfleet-api/server/routes.go
  • test/helper.go
  • pkg/api/presenters/slice_filter.go
  • pkg/db/transaction_middleware.go
  • pkg/services/status_aggregation.go
  • pkg/client/ocm/client.go
  • pkg/auth/helpers.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/clusters_test.go
  • test/integration/node_pools_test.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/framework.go
  • pkg/handlers/validation.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/services/status_aggregation.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/framework.go
  • pkg/handlers/validation.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/framework.go
  • pkg/handlers/validation.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/services/status_aggregation.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/framework.go
  • pkg/handlers/validation.go
  • pkg/db/db_session/test.go
  • pkg/db/sql_helpers.go
  • pkg/api/adapter_status_types.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
  • pkg/auth/context.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/api/presenters/slice_filter.go
  • pkg/db/transaction_middleware.go
  • pkg/services/status_aggregation.go
  • pkg/client/ocm/client.go
  • pkg/auth/helpers.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/db_session/test.go
  • pkg/db/sql_helpers.go
  • pkg/db/transaction_middleware.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/adapter_status_types.go
  • pkg/api/presenters/slice_filter.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/services/status_aggregation.go
🧠 Learnings (13)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • test/integration/clusters_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/handlers/node_pool.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/handlers/**/*.go : Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Applied to files:

  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Applied to files:

  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/helpers.go
  • pkg/db/transaction_middleware.go
  • pkg/auth/helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • pkg/db/db_session/test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • cmd/hyperfleet-api/environments/framework_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/status_aggregation.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/services/**/*.go : Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/**/*.go : Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Applied to files:

  • pkg/auth/context.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Use context.Context as the first parameter in all DAO methods for transaction propagation

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
🧬 Code graph analysis (5)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
cmd/hyperfleet-api/environments/framework_test.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Services (44-47)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
🔇 Additional comments (31)
test/mocks/ocm.go (1)

62-65: LGTM! Critical correctness fix.

Changing from a value receiver to a pointer receiver is correct and necessary. With a value receiver, the method would operate on a copy of the struct, discarding the field resets when the method returned. The pointer receiver ensures the changes persist, making the mock reusable as intended in the usage example at line 30.

pkg/client/ocm/client.go (1)

78-83: Close now correctly surfaces connection close errors

The new Close() error signature and implementation look good: it cleanly handles the nil connection case and propagates c.connection.Close() errors to callers. Just ensure all existing call sites have been updated to check/log the returned error appropriately (the compile-time signature change should help catch any missed sites).

pkg/db/db_session/test.go (1)

216-221: ResetDB now fails fast and keeps state consistent — looks good.

ResetDB no longer swallows resetDB errors and only sets wasDisconnected when the reset succeeds, which fixes the earlier reliability concern and is appropriate behavior for a test helper (panic on unrecoverable setup failure).

pkg/db/sql_helpers.go (2)

20-34: Safer left-hand identifier handling in hasProperty looks good

The added string assertion and guard on l.Left before calling startsWithProperties avoids potential panics from invalid node structures while preserving existing behavior.


143-151: Good defensive type check before recursing on n.Left

Adding the tsl.Node type assertion on n.Left and returning a BadRequest for invalid structures makes FieldNameWalk fail fast on malformed trees instead of panicking, which aligns well with the rest of the validation logic.

pkg/services/generic_test.go (2)

23-23: LGTM: Suppressing errcheck on deferred Close() is acceptable in test code.

The //nolint:errcheck directive is appropriate here since errors from Close() in test teardown are typically non-actionable and the mock factory likely returns nil anyway.


70-75: Test correctly updated for new treeWalkForSqlizer signature.

The test now captures only (sqlizer, serviceErr) which aligns with the simplified return type in generic.go. The subsequent assertions on serviceErr and sqlizer.ToSql() remain valid.

pkg/services/generic.go (3)

43-43: LGTM: Typo fix.


173-176: LGTM: Call site correctly updated for the simplified return type.

The unused tsl.Node return value was previously discarded with _. This cleanup properly captures only the two values that are actually used.


332-345: Good API simplification: Removed unused tsl.Node return value.

Removing the unused first return value follows Go best practices—returning only values that callers need. The error handling is consistent: nil, err on failure and sqlizer, nil on success.

pkg/services/status_aggregation.go (1)

155-163: LGTM! Clean refactor to switch statement.

The switch statement improves readability and explicitly handles unexpected adapter statuses via the default case with appropriate logging. The logic correctly implements the adapter status aggregation guideline: Ready when all required adapters report "True", Failed when any report "False", and NotReady otherwise.

Based on coding guidelines, adapter status aggregation is correctly implemented.

cmd/hyperfleet-api/main.go (1)

28-30: LGTM: Explicit error handling for flag parsing.

The change from silent error suppression to explicit error checking and fatal logging aligns well with the PR's broader error-handling improvements. While parsing an empty slice should virtually never fail in practice, this defensive approach ensures any unexpected failure is caught early.

test/helper.go (4)

83-84: LGTM: Acceptable error suppression for test log level configuration.

Intentionally ignoring the error from setting the log level flag is appropriate for test code, where custom log levels are optional and failure should not break test setup.


181-183: LGTM: Reasonable error handling for server restart.

Logging a warning instead of propagating the error on stopAPIServer failure is appropriate for the restart flow, ensuring the test helper remains resilient and continues to start the new server instance.


204-206: LGTM: Proper error handling for flag setup in Reset.

Treating AddFlags errors as fatal during Reset is appropriate, as flag setup is critical for proper test environment configuration.


441-444: LGTM: Well-documented suppression of deprecation warning.

The nolint directive for ParseRSAPrivateKeyFromPEMWithPassword is properly justified—the comment explains that no suitable alternative exists for the test fixture keys, making continued use of the deprecated function acceptable in this context.

pkg/auth/context.go (3)

45-48: LGTM! Safe type assertion prevents potential panic.

Good defensive improvement. The guarded type assertion properly handles cases where the context value is not a string.


82-96: LGTM! Safe claim extraction with proper nil handling.

All type assertions are now guarded, preventing panics when claims are missing or have unexpected types (e.g., numbers, nested objects). This aligns with JWT middleware best practices. Based on coding guidelines, this properly extracts username from claims.


99-115: LGTM! Fallback claims consistently guarded.

Good consistency with the primary claim extraction pattern. The RHD fallback claims (preferred_username, given_name, family_name) are now safely extracted.

test/mocks/jwk_cert_server.go (1)

33-35: LGTM: Defensive error handling for test mock.

The error check for fmt.Fprintf ensures that write failures in the mock JWKS server are detected and reported during tests.

pkg/auth/helpers.go (1)

30-38: LGTM: Explicit error handling for JSON response writing.

The function now validates both json.Marshal and w.Write results, returning early on errors. This prevents silent failures when serializing or writing responses. The approach correctly acknowledges that after headers are sent, changing the status code is not possible.

pkg/db/transaction_middleware.go (1)

44-52: LGTM: Consistent error handling pattern.

The error checks for json.Marshal and w.Write match the pattern used in pkg/auth/helpers.go, ensuring consistent response-writing behavior across the codebase.

pkg/handlers/framework.go (1)

109-110: LGTM: Idiomatic switch refactor.

Switching directly on serviceErr instead of boolean conditions is more idiomatic Go. The behavior remains unchanged.

pkg/handlers/nodepool_status.go (1)

92-96: LGTM: Improved observability for aggregation failures.

The inline error check with structured logging provides better visibility when status aggregation fails. Using Warning level is appropriate since the request succeeds but the aggregation is deferred. The logging pattern with context propagation aligns with the coding guidelines.

As per coding guidelines, pkg/{handlers,services,dao}/**/*.go should use structured logging via pkg/logger/logger.go with context propagation.

pkg/api/adapter_status_types.go (2)

55-58: LGTM: Defensive unmarshalling with safe fallbacks.

The error checks for json.Unmarshal with empty slice/map fallbacks prevent panics when database JSONB data is malformed. This pattern ensures ToOpenAPI always returns valid structures.

Also applies to: 64-67


122-144: LGTM: Resolved previous scope issue with guarded marshalling.

The fallback pattern (default value + conditional override on successful marshal) correctly addresses the previous review concern about error variable scope. Each field now has:

  • A safe default fallback ([] for conditions, {} for data/metadata)
  • Marshal error is checked within the conditional scope
  • Only successful marshals override the default

This approach prevents fallback logic from using stale error values and ensures deterministic JSON output.

pkg/handlers/node_pool.go (1)

73-77: LGTM: Explicit marshalling error handling in Patch.

The error check for json.Marshal ensures that spec marshalling failures are caught and returned as a GeneralError rather than silently ignored. This aligns with the coding guideline to return errors using the structured ServiceError type.

As per coding guidelines, pkg/{handlers,services,dao}/**/*.go should return errors using the structured ServiceError type from pkg/errors/.

pkg/handlers/helpers.go (1)

17-25: LGTM: Consistent error handling pattern across response writers.

The explicit error checks for json.Marshal and w.Write match the pattern implemented in pkg/auth/helpers.go, pkg/db/transaction_middleware.go, and other response-writing paths, ensuring uniform behavior throughout the codebase.

pkg/api/presenters/slice_filter.go (2)

114-116: Good fix: nested validation errors are now properly propagated.

Previously, validation errors from nested struct fields were silently discarded. This change ensures that validation failures in nested structures are correctly returned to the caller, preventing invalid nested fields from passing validation.


194-196: Good nil-safety check prevents potential panic.

The added nil check ensures that Format() is only called on non-nil *time.Time pointers, preventing a runtime panic. The compound check correctly verifies both the type assertion and nil condition.

cmd/hyperfleet-api/environments/framework_test.go (1)

39-55: Reflection now uses an addressable value; behavior remains correct

Switching to reflect.ValueOf(&env.Services).Elem() means you’re reflecting on the actual Services field rather than a copy, yielding an addressable struct Value. For the current read-only iteration and IsNil checks on function-typed exported fields, this is functionally equivalent to the old code and safe, while also being a better base if you ever need to set fields via reflection in the future.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Dockerfile.openapi (1)

23-23: Enable FIPS-compliant crypto configuration.

Line 23 sets CGO_ENABLED=0, but project learnings require CGO_ENABLED=1 with GOEXPERIMENT=boringcrypto for FIPS compliance. Update both the CGO_ENABLED flag and add the GOEXPERIMENT environment variable.

Apply this diff:

-ENV CGO_ENABLED=0
+ENV CGO_ENABLED=1
+ENV GOEXPERIMENT=boringcrypto
test/integration/node_pools_test.go (1)

73-82: Reorder assertions to avoid potential nil dereference on restyResp

Right now restyResp.StatusCode() is called (Line 81) before asserting err is nil (Line 82). If the POST ever fails at the transport layer, err will be non‑nil and restyResp will be nil, causing a panic when calling StatusCode().

Safer to assert on err first, then inspect the response:

-	Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest))
-	Expect(err).NotTo(HaveOccurred(), "Error posting object:  %v", err)
+	Expect(err).NotTo(HaveOccurred(), "Error posting object:  %v", err)
+	Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest))
test/integration/clusters_test.go (1)

231-244: Align empty-name boundary test assertion with the documented expected behavior

The comment (Lines 239–242) says: “Should either accept empty name or return 400”, implying both behaviors are acceptable. The new assertion Expect(err).ToNot(HaveOccurred()) (Line 243) only allows the “accept empty name” path—if the API ever legitimately returns 400 here (as the comment allows), err will be non‑nil and this test will fail.

Consider either:

  • Making the behavior strict by deciding on one expected outcome (e.g., must succeed) and updating the comment and assertions accordingly (e.g., assert 201/200), or
  • Allowing both behaviors explicitly, e.g. by asserting on resp.StatusCode to permit either 2xx success or a controlled 400, and adjusting the err expectation based on the status.
♻️ Duplicate comments (2)
Dockerfile.openapi (1)

8-10: Go version must be updated to 1.24.9.

Line 8 specifies go1.24.0.linux-amd64.tar.gz, but project requirements mandate Go 1.24.9 or later (per learnings and the prior review flagged in the PR history). This issue was marked as addressed in commit 08bcd18, but the code still reflects the older version. Update all references on lines 8–10 to use go1.24.9.linux-amd64.tar.gz.

Apply this diff:

-RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz && \
-    tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz && \
-    rm go1.24.0.linux-amd64.tar.gz
+RUN wget https://go.dev/dl/go1.24.9.linux-amd64.tar.gz && \
+    tar -C /usr/local -xzf go1.24.9.linux-amd64.tar.gz && \
+    rm go1.24.9.linux-amd64.tar.gz
cmd/hyperfleet-api/server/routes.go (1)

53-59: Authorization middleware remains permanently mocked—wire real authz behind config

authzMiddleware is always a mock, and the real auth.NewAuthzMiddleware initialization is fully commented out. That means route-level authorization is effectively disabled even if env().Config.Server.EnableAuthz is set, which weakens the security posture. This mirrors a concern raised in a previous review on this area.

At minimum, please either:

  • Gate the real middleware behind EnableAuthz (keeping the mock as the default when false), or
  • Clearly document and track in an issue that authorization is intentionally disabled and must be enabled before any non-test deployment.

For example:

-	authzMiddleware := auth.NewAuthzMiddlewareMock()
-	// TODO: Create issue to track enabling authorization middleware
-	// if env().Config.Server.EnableAuthz {
-	// 	var err error
-	// 	authzMiddleware, err = auth.NewAuthzMiddleware()
-	// 	check(err, "Unable to create authz middleware")
-	// }
+	authzMiddleware := auth.NewAuthzMiddlewareMock()
+	// TODO: link tracking issue for enabling authorization middleware in non-test environments.
+	if env().Config.Server.EnableAuthz {
+		var err error
+		authzMiddleware, err = auth.NewAuthzMiddleware()
+		check(err, "Unable to create authz middleware")
+	}
🧹 Nitpick comments (4)
pkg/api/node_pool_types.go (2)

80-101: Defensive JSON unmarshal defaults in ToOpenAPI look good

The added error checks for Spec, Labels, and StatusConditions give you safe empty defaults instead of propagating bad JSON to the API level, and they align with the patterns noted for clusters/adapter status types. Only optional consideration: if you ever want Spec/Labels/Conditions to always serialize as {}/[] (rather than null) even when the DB field is empty, you could initialize them to empty values up front instead of only on unmarshal errors. Based on learnings, this keeps behavior consistent across pkg/api types.


159-178: Marshal error handling in NodePoolFromOpenAPICreate is robust and consistent

Catching json.Marshal errors for Spec, Labels, and StatusConditions and falling back to "{}" / "[]" ensures the JSONB columns remain valid even in unexpected cases, and matches the defensive style used elsewhere in the API models. If you later introduce structured logging, it might be worth logging these rare marshal errors before defaulting so they’re observable, but that’s optional and not required for this PR’s goal of stabilizing lint/tests.

pkg/handlers/helpers.go (1)

17-25: Error handling added, but errors are silently discarded.

The error checks prevent panics and satisfy linter requirements. However, these errors are silently swallowed without any logging, making debugging difficult in production.

Consider adding a context parameter to enable structured logging for these failure cases:

-func writeJSONResponse(w http.ResponseWriter, code int, payload interface{}) {
+func writeJSONResponse(ctx context.Context, w http.ResponseWriter, code int, payload interface{}) {
+	log := logger.NewOCMLogger(ctx)
 	w.Header().Set("Content-Type", "application/json")
 	// ...
 	if payload != nil {
 		response, err := json.Marshal(payload)
 		if err != nil {
-			// Headers already sent, can't change status code
+			log.Errorf("Failed to marshal JSON response: %v", err)
 			return
 		}
 		if _, err := w.Write(response); err != nil {
-			// Writing failed, nothing we can do at this point
+			log.Errorf("Failed to write response: %v", err)
 			return
 		}
 	}
 }

Based on learnings, handlers should use structured logging via pkg/logger/logger.go with context propagation.

pkg/auth/helpers.go (1)

30-38: Same silent error handling pattern as pkg/handlers/helpers.go.

This file already imports logger and the caller handleError has context. Consider passing context to writeJSONResponse to enable logging of marshal/write failures.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84d8b35 and 3129fc3.

📒 Files selected for processing (44)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/framework_test.go (0 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/handlers/validation.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
  • test/helper.go (4 hunks)
  • test/integration/clusters_test.go (2 hunks)
  • test/integration/node_pools_test.go (1 hunks)
  • test/mocks/jwk_cert_server.go (1 hunks)
  • test/mocks/ocm.go (1 hunks)
💤 Files with no reviewable changes (1)
  • pkg/handlers/framework_test.go
🚧 Files skipped from review as they are similar to previous changes (22)
  • pkg/config/config_test.go
  • pkg/handlers/cluster_status.go
  • pkg/services/cluster.go
  • pkg/handlers/validation.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/framework.go
  • pkg/services/node_pool.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/services/types.go
  • pkg/db/db_session/default.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/openapi.go
  • pkg/auth/context.go
  • pkg/api/cluster_types.go
  • .golangci.yml
  • pkg/db/transaction_middleware.go
  • Makefile
  • openapitools.json
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/api/adapter_status_types.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • test/mocks/ocm.go
  • pkg/handlers/node_pool.go
  • cmd/hyperfleet-api/server/routes.go
  • test/mocks/jwk_cert_server.go
  • test/integration/node_pools_test.go
  • pkg/handlers/helpers.go
  • pkg/api/node_pool_types.go
  • cmd/hyperfleet-api/main.go
  • test/helper.go
  • pkg/auth/helpers.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/client/ocm/client.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/handlers/cluster.go
  • test/integration/clusters_test.go
  • pkg/api/presenters/slice_filter.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/api/node_pool_types.go
  • pkg/auth/helpers.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/client/ocm/client.go
  • pkg/handlers/cluster.go
  • pkg/api/presenters/slice_filter.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/nodepool_status.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/node_pools_test.go
  • test/integration/clusters_test.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/node_pool_types.go
  • pkg/api/presenters/slice_filter.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/db_session/testcontainer.go
🧠 Learnings (14)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/handlers/node_pool.go
  • test/integration/node_pools_test.go
  • pkg/api/node_pool_types.go
  • test/integration/clusters_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/**/*.go : Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
  • test/mocks/jwk_cert_server.go
  • pkg/auth/helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/handlers/**/*.go : Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Applied to files:

  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Applied to files:

  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • cmd/hyperfleet-api/environments/framework_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • test/integration/clusters_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/services/**/*.go : Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/openapi/**/*.go : Use make generate to regenerate Go models from openapi/openapi.yaml via openapi-generator-cli v7.16.0 in Podman

Applied to files:

  • Dockerfile.openapi
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • Dockerfile.openapi
🧬 Code graph analysis (5)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
cmd/hyperfleet-api/environments/framework_test.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Services (44-47)
pkg/handlers/cluster.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
🔇 Additional comments (24)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)

14-28: Simplified doLog initialization looks correct

The boolean now directly encodes the intended behavior: log everything except /api/hyperfleet (with trailing slash already stripped). This is clearer than a multi-step assignment and preserves existing semantics.

Dockerfile.openapi (1)

3-5: Approve the APT sandbox workaround for rootless podman.

The -o APT::Sandbox::User=root flag correctly addresses the Prow/podman setgroups error without affecting subsequent build logic.

test/integration/clusters_test.go (1)

66-75: Good addition: explicitly asserting no transport error for invalid JSON test

Adding Expect(err).ToNot(HaveOccurred(), ...) before checking restyResp.StatusCode() ensures the test fails cleanly on network/transport issues while still asserting the 400 application‑level response. The ordering (error check first, then status) is correct.

test/helper.go (4)

83-84: LGTM: Appropriate error handling for optional test configuration.

Explicitly ignoring the error from setting the log level flag is reasonable in test code, and the inline comment clearly documents the intent.


181-183: LGTM: Improved error visibility in test server restart.

The change adds error checking and warning logging while pragmatically allowing the restart to proceed. This improves observability without breaking test robustness.


204-206: LGTM: Proper defensive error handling added.

The fatal error on AddFlags failure is appropriate for the Reset function, which requires a clean environment state to proceed correctly.


441-445: LGTM: Well-documented use of deprecated function in test fixtures.

The comment clearly explains why the deprecated function is still necessary (no suitable alternative for password-protected test fixture keys), and the nolint:staticcheck directive is appropriately placed to suppress the linter warning.

pkg/client/ocm/client.go (1)

78-83: Verify all callers of Client.Close() handle error returns and confirm coding guideline compliance.

The Close() method now correctly returns an error, following Go best practices for resource cleanup. This is a breaking API change.

Ensure:

  1. All callers of Client.Close() have been updated to handle the error return value
  2. JWT authentication middleware is implemented to validate tokens and extract account ID and username from claims (coding guideline for pkg/**/*.go)
  3. Code complies with Go 1.24.9 using FIPS-compliant crypto (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
pkg/db/db_session/testcontainer.go (1)

52-54: Good fix—port-based wait is simpler and resolves the test failure.

Switching from log-based to port-based readiness checking eliminates the log-parsing flakiness that caused the presubmit failure. Since the immediate connection attempt at lines 63–74 provides implicit verification of database readiness, this approach is acceptable for integration tests.

For even more robust readiness checking, consider using a SQL-based wait strategy if available in your testcontainers-go version (e.g., wait.ForSQL()), which explicitly verifies that PostgreSQL accepts queries before proceeding.

Please monitor integration test runs over the next few days to confirm this change doesn't introduce intermittent flakiness.

pkg/handlers/cluster.go (1)

72-75: Spec marshal error handling looks correct and consistent

Capturing the json.Marshal error and converting it to a ServiceError via errors.GeneralError is appropriate and keeps handler error handling consistent across the API surface.

test/mocks/jwk_cert_server.go (1)

33-35: Good addition of write-error reporting in mock JWK server

Checking the fmt.Fprintf error and failing the test via t.Errorf makes the mock more robust without changing its semantics.

pkg/handlers/node_pool.go (1)

73-76: NodePool spec marshal error is handled appropriately

Marshaling patch.Spec and returning a errors.GeneralError on failure cleanly propagates bad-spec issues through the existing ServiceError flow, matching the cluster handler behavior.

cmd/hyperfleet-api/environments/framework_test.go (1)

39-39: Reflection change is safe and slightly more robust

Using reflect.ValueOf(&env.Services).Elem() still inspects the same struct type while producing an addressable value, which keeps the test behavior intact and future-proofs against potential reflective writes.

cmd/hyperfleet-api/main.go (1)

28-30: Flag parse error handling is appropriate for the entrypoint

Checking the error from flag.CommandLine.Parse([]string{}) and exiting via glog.Fatalf is correct and avoids silent startup failures.

test/mocks/ocm.go (1)

62-65: Pointer receiver on Reset fixes state reset semantics

Switching Reset to a pointer receiver aligns with the other methods and ensures the recorded Action/ResourceType are actually cleared on the shared mock instance.

pkg/handlers/nodepool_status.go (1)

11-11: Structured logging for aggregation failures is a good pattern

Logging UpdateNodePoolStatusFromAdapters failures with logger.NewOCMLogger(ctx) while keeping the write-path successful is a sensible choice for best-effort aggregation, and it follows the structured logging guidance for handlers.

Also applies to: 92-95

pkg/api/presenters/slice_filter.go (3)

114-116: LGTM! Correct error propagation from nested validation.

Previously, errors from nested validate calls were silently discarded. Now they are properly returned, ensuring validation failures in nested structs surface to the caller.


149-153: Defensive error handling is good, though pattern is always valid.

The regex pattern (name\..*) constructed from name will always compile successfully since name comes from JSON struct tags (alphanumeric/underscores). However, the defensive check is a reasonable safeguard against future changes.


194-196: LGTM! Proper nil guard for time pointer.

The nil check prevents a panic when formatting a nil *time.Time value.

pkg/services/generic_test.go (2)

23-23: LGTM! Acceptable use of nolint directive for test cleanup.

Ignoring the error from a deferred Close() in tests is a common and acceptable pattern.


70-70: LGTM! Correctly updated to match new treeWalkForSqlizer signature.

The call site is properly updated to expect (Sqlizer, error) instead of the previous three-value return.

pkg/services/generic.go (3)

43-43: LGTM! Typo fix.


173-173: LGTM! Call site correctly updated for simplified return signature.


332-345: LGTM! Good simplification of the return signature.

Removing the unused tsl.Node return value simplifies the API. The function now returns only (squirrel.Sqlizer, *errors.ServiceError), which is all that callers need. Error handling paths correctly return (nil, error).

@yingzhanredhat yingzhanredhat changed the title Fix the error from presubmit jobs: lint/unit/integration HYPERFLEET-295: Fix the error from presubmit jobs: lint/unit/integration Dec 4, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Dockerfile.openapi (2)

8-10: ❌ Go version must be updated to 1.24.9 (regression from prior review).

The code still specifies Go 1.24.0, but the prior review flagged this must be Go 1.24.9 (marked as "Addressed in commit 08bcd18"). Additionally, per project learnings, Go 1.24.9 is required for FIPS-compliant crypto builds. This appears to be a regression or incomplete fix.

Update lines 8–10 to use go1.24.9.linux-amd64.tar.gz:

-RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz && \
-    tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz && \
-    rm go1.24.0.linux-amd64.tar.gz
+RUN wget https://go.dev/dl/go1.24.9.linux-amd64.tar.gz && \
+    tar -C /usr/local -xzf go1.24.9.linux-amd64.tar.gz && \
+    rm go1.24.9.linux-amd64.tar.gz

23-23: ❌ CGO_ENABLED must be 1 and GOEXPERIMENT must be set for FIPS-compliant crypto.

Per project learnings, builds must use CGO_ENABLED=1 with GOEXPERIMENT=boringcrypto for FIPS-compliant cryptography. Currently, CGO is disabled (line 23), which prevents boring crypto integration.

Update lines 23 to enable CGO and add the GOEXPERIMENT setting:

-ENV CGO_ENABLED=0
+ENV CGO_ENABLED=1
+ENV GOEXPERIMENT=boringcrypto
test/integration/node_pools_test.go (1)

73-83: Avoid potential nil deref by checking err before using restyResp

The new Expect(err).NotTo(HaveOccurred()) is good, but restyResp.StatusCode() is still called first. If the POST fails before a response is created, restyResp can be nil and this will panic instead of cleanly failing the test.

Consider reordering:

-	Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest))
-	Expect(err).NotTo(HaveOccurred(), "Error posting object:  %v", err)
+	Expect(err).NotTo(HaveOccurred(), "Error posting object:  %v", err)
+	Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest))
♻️ Duplicate comments (2)
cmd/hyperfleet-api/server/routes.go (1)

53-59: Authorization middleware still permanently mocked; consider enabling behind config + link tracking issue

Even with the new TODO, authzMiddleware is always auth.NewAuthzMiddlewareMock() at runtime, so no real authz is ever performed. That may be acceptable only for tests/dev; it’s risky if this binary is used in any production-like environment.

Consider uncommenting the block and gating the real middleware on env().Config.Server.EnableAuthz, keeping the mock only when the flag is false, and update the TODO to reference the concrete tracking issue ID so it’s clear when/where authz must be enabled:

-	authzMiddleware := auth.NewAuthzMiddlewareMock()
-	// TODO: Create issue to track enabling authorization middleware
-	// if env().Config.Server.EnableAuthz {
-	// 	var err error
-	// 	authzMiddleware, err = auth.NewAuthzMiddleware()
-	// 	check(err, "Unable to create authz middleware")
-	// }
+	authzMiddleware := auth.NewAuthzMiddlewareMock()
+	if env().Config.Server.EnableAuthz {
+		var err error
+		authzMiddleware, err = auth.NewAuthzMiddleware()
+		check(err, "Unable to create authz middleware")
+	}
+	// TODO(HF-XXXX): ensure production runs with EnableAuthz=true; mock is for dev/test only
pkg/api/cluster_types.go (1)

70-73: LGTM - addresses past review feedback.

The change to use make(map[string]interface{}) addresses the suggestion from the previous review comment by yasun1.

🧹 Nitpick comments (11)
pkg/db/db_session/test.go (1)

216-221: Previous concern addressed: error is no longer silently ignored.

The panic-on-failure approach is valid for test helpers where tests cannot reliably continue with an unclean database. The error is now properly checked, and wasDisconnected is only set after a successful reset.

Optionally, returning the error (func (f *Test) ResetDB() error) instead of panicking would give individual test callers more control—for example, to log additional context or perform cleanup before failing. However, since panicking was one of the three approaches suggested in the previous review and is reasonable for test infrastructure, the current implementation is acceptable.

pkg/client/ocm/client.go (1)

78-83: Close implementation is correct; consider clearing the connection after close

Returning the underlying Close() error and treating a nil connection as a no-op is appropriate here. You might optionally set c.connection = nil after a successful close to avoid accidental reuse or double-close, but that's not strictly required.

pkg/services/generic_test.go (1)

23-24: Consider checking dbFactory.Close error instead of suppressing

Using //nolint:errcheck is acceptable in tests, but you might get slightly better safety by asserting the error is nil instead of ignoring it altogether (e.g., require.NoError(t, dbFactory.Close()) in a t.Cleanup). Not required for this PR, just a minor improvement to catch regressions in the mock.

pkg/handlers/openapi.go (2)

61-64: Consider logging write errors for observability.

While the early return is correct since headers are already committed, silently discarding the error makes debugging difficult. Logging the error would help identify network issues or client disconnects.

+	"github.com/golang/glog"
 	if _, err := w.Write(h.openAPIDefinitions); err != nil {
-		// Response already committed, can't report error
+		glog.Warningf("Failed to write OpenAPI response: %v", err)
 		return
 	}

70-73: Same logging improvement applies here.

For consistency, log the write error before returning.

pkg/auth/helpers.go (1)

30-38: Consider logging marshal/write failures.

The error handling logic is correct, but silent failures make debugging difficult. When json.Marshal fails, the client receives the HTTP status code with an empty body, which could be confusing. Logging these errors would help diagnose issues.

 	if payload != nil {
 		response, err := json.Marshal(payload)
 		if err != nil {
-			// Headers already sent, can't change status code
+			// Headers already sent, can't change status code - log for debugging
+			logger.NewOCMLogger(context.Background()).Errorf("Failed to marshal response: %v", err)
 			return
 		}
 		if _, err := w.Write(response); err != nil {
-			// Writing failed, nothing we can do at this point
+			logger.NewOCMLogger(context.Background()).Warningf("Failed to write response: %v", err)
 			return
 		}
 	}

Note: You'd need to add "context" to imports or accept a context parameter.

pkg/api/presenters/slice_filter.go (1)

149-153: Silent return on regex compile failure may mask issues.

While the pattern is constructed from JSON struct tags (developer-controlled), silently returning the unchanged map on compile failure could lead to unexpected behavior where fields meant to be filtered remain in the output. Consider logging a warning to aid debugging.

 	pat, err := regexp.Compile(pattern)
 	if err != nil {
-		// Invalid pattern, return unchanged
+		// Invalid pattern - this shouldn't happen with valid JSON tags
+		// but log for debugging if it does
 		return in
 	}

Alternatively, since the pattern is constructed programmatically and should always be valid, you could use regexp.MustCompile with a comment explaining the invariant.

pkg/api/cluster_types.go (2)

78-88: Consider logging unmarshal errors as they may indicate data corruption.

While defaulting to empty values is reasonable for presentation, json.Unmarshal failures on database-sourced JSONB columns could indicate data corruption or schema issues. Logging these errors would help identify data integrity problems.

 	if len(c.Labels) > 0 {
 		if err := json.Unmarshal(c.Labels, &labels); err != nil {
+			// Log: could indicate data corruption
 			labels = make(map[string]string)
 		}
 	}

 	// Unmarshal StatusConditions (stored as ResourceCondition in DB)
 	var statusConditions []openapi.ResourceCondition
 	if len(c.StatusConditions) > 0 {
 		if err := json.Unmarshal(c.StatusConditions, &statusConditions); err != nil {
+			// Log: could indicate data corruption
 			statusConditions = []openapi.ResourceCondition{}
 		}
 	}

136-155: Defensive defaults are appropriate, but consider logging.

Marshaling simple Go types rarely fails, so these defaults are mostly defensive. However, if marshaling does fail, it could indicate a deeper issue (e.g., unexpected data types). Consider logging when these fallback paths are taken.

pkg/api/node_pool_types.go (1)

82-84: Consider logging unmarshal errors instead of silently using fallbacks.

While the fallbacks prevent panics and provide safe defaults, silently ignoring unmarshal errors in ToOpenAPI could mask data corruption or schema issues in the database. If JSONB data becomes malformed, you won't know until you investigate why a nodepool has no spec or labels.

Consider adding structured logging for these unmarshal failures:

 	var spec map[string]interface{}
 	if len(np.Spec) > 0 {
 		if err := json.Unmarshal(np.Spec, &spec); err != nil {
+			// Log the error with context
+			// logger.Errorf("Failed to unmarshal NodePool spec for ID %s: %v", np.ID, err)
 			spec = make(map[string]interface{})
 		}
 	}

Apply similar logging for Labels and StatusConditions.

As per coding guidelines, use structured logging via pkg/logger/logger.go with context propagation.

Also applies to: 90-92, 98-100

pkg/api/adapter_status_types.go (1)

55-58: Consider logging unmarshal errors instead of silently using fallbacks.

Similar to the pattern in node_pool_types.go, silently falling back to empty defaults when unmarshaling from the database could mask data corruption or schema migration issues. If adapter status data becomes malformed in the DB, these silent fallbacks will hide the problem.

Add structured logging for unmarshal failures:

 	if len(as.Conditions) > 0 {
 		if err := json.Unmarshal(as.Conditions, &conditions); err != nil {
-			// If unmarshal fails, use empty slice
+			// Log unmarshal failure with context
+			// logger.Errorf("Failed to unmarshal AdapterStatus conditions for ID %s: %v", as.ID, err)
 			conditions = []openapi.AdapterCondition{}
 		}
 	}

As per coding guidelines, use structured logging via pkg/logger/logger.go with context propagation.

Also applies to: 64-67

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3129fc3 and 0128ec8.

📒 Files selected for processing (44)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/framework_test.go (0 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/handlers/validation.go (1 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
  • test/helper.go (4 hunks)
  • test/integration/clusters_test.go (2 hunks)
  • test/integration/node_pools_test.go (1 hunks)
  • test/mocks/jwk_cert_server.go (1 hunks)
  • test/mocks/ocm.go (1 hunks)
💤 Files with no reviewable changes (1)
  • pkg/handlers/framework_test.go
🚧 Files skipped from review as they are similar to previous changes (19)
  • pkg/config/config_test.go
  • .golangci.yml
  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
  • pkg/db/sql_helpers.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster.go
  • test/mocks/jwk_cert_server.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/handlers/cluster_status.go
  • pkg/services/types.go
  • cmd/hyperfleet-api/main.go
  • pkg/services/cluster.go
  • pkg/db/db_session/default.go
  • test/integration/clusters_test.go
  • test/mocks/ocm.go
  • pkg/db/db_session/testcontainer.go
  • openapitools.json
  • pkg/handlers/helpers.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/api/adapter_status_types.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/handlers/openapi.go
  • pkg/db/transaction_middleware.go
  • test/integration/node_pools_test.go
  • pkg/auth/context.go
  • pkg/db/db_session/test.go
  • pkg/api/node_pool_types.go
  • pkg/services/node_pool.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/handlers/validation.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/api/presenters/slice_filter.go
  • pkg/api/cluster_types.go
  • pkg/auth/helpers.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/client/ocm/client.go
  • test/helper.go
  • pkg/handlers/node_pool.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/adapter_status_types.go
  • pkg/api/node_pool_types.go
  • pkg/api/presenters/slice_filter.go
  • pkg/api/cluster_types.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/api/adapter_status_types.go
  • pkg/handlers/openapi.go
  • pkg/db/transaction_middleware.go
  • pkg/auth/context.go
  • pkg/db/db_session/test.go
  • pkg/api/node_pool_types.go
  • pkg/services/node_pool.go
  • pkg/handlers/validation.go
  • pkg/api/presenters/slice_filter.go
  • pkg/api/cluster_types.go
  • pkg/auth/helpers.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/client/ocm/client.go
  • pkg/handlers/node_pool.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/handlers/openapi.go
  • pkg/services/node_pool.go
  • pkg/handlers/validation.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/handlers/node_pool.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/openapi.go
  • pkg/handlers/validation.go
  • pkg/handlers/node_pool.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/handlers/openapi.go
  • pkg/services/node_pool.go
  • pkg/handlers/validation.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
  • pkg/handlers/node_pool.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/test.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/node_pools_test.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/node_pool.go
  • pkg/services/generic.go
  • pkg/services/generic_test.go
🧠 Learnings (15)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/api/adapter_status_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/services/**/*.go : Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/services/node_pool.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • pkg/api/adapter_status_types.go
  • pkg/handlers/openapi.go
  • pkg/api/node_pool_types.go
  • Makefile
  • pkg/api/cluster_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/**/*.go : Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
  • pkg/auth/context.go
  • pkg/auth/helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/node_pool.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • test/integration/node_pools_test.go
  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
  • pkg/handlers/node_pool.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Use context.Context as the first parameter in all DAO methods for transaction propagation

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/openapi/**/*.go : Use make generate to regenerate Go models from openapi/openapi.yaml via openapi-generator-cli v7.16.0 in Podman

Applied to files:

  • Makefile
  • pkg/api/cluster_types.go
  • Dockerfile.openapi
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • Makefile
  • Dockerfile.openapi
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • Makefile
  • cmd/hyperfleet-api/environments/framework_test.go
  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to openapi/openapi.yaml : TypeSpec definitions are maintained separately in hyperfleet-api-spec repository; only edit openapi/openapi.yaml in this repository for spec changes

Applied to files:

  • pkg/api/cluster_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
🧬 Code graph analysis (5)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
cmd/hyperfleet-api/environments/framework_test.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Services (44-47)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
cmd/hyperfleet-api/server/api_server.go (2)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
🔇 Additional comments (20)
Dockerfile.openapi (1)

3-5: ✓ APT sandbox workaround correctly applied.

The -o APT::Sandbox::User=root flag is properly applied to both apt-get invocations with a clear explanatory comment. This addresses the Prow rootless podman integration issue mentioned in the PR objectives.

cmd/hyperfleet-api/server/logging/writer.go (1)

47-47: LGTM! More idiomatic use of embedded method.

Using the promoted Header() method directly is cleaner and follows Go conventions for embedded types.

Makefile (1)

116-117: Verify .golangci.yml configuration exists and properly excludes generated code.

The lint target now relies on a .golangci.yml configuration file instead of explicit flags and paths. Ensure this config file exists, excludes pkg/api/openapi (generated code), and doesn't unintentionally enable the unused check by default.

cmd/hyperfleet-api/server/api_server.go (1)

149-151: LGTM! Proper error handling for database cleanup.

The addition of error checking and logging for the database connection close operation is a good defensive programming practice. Logging the error (rather than treating it as fatal) is appropriate for cleanup code that runs after the server has already exited.

cmd/hyperfleet-api/environments/framework.go (1)

158-162: OCM teardown now safely guarded and error-aware

The nil check around e.Clients.OCM and logging of any Close() error bring this in line with the database teardown pattern and avoid potential panics when the client isn’t initialized.

cmd/hyperfleet-api/environments/framework_test.go (1)

39-39: Reflecting on &env.Services is safe and future‑proof

Switching to reflect.ValueOf(&env.Services).Elem() correctly targets the live Services struct rather than a copy and keeps the field-inspection logic intact; this is a fine and slightly more robust approach.

pkg/services/generic.go (3)

43-56: Comment typo fix is correct and keeps intent clear

The updated comment (“LIST function”) is accurate and keeps the purpose of listContext clear. No further changes needed here.


151-186: buildSearchValues correctly adapts to new treeWalkForSqlizer signature

The call sqlizer, serviceErr := s.treeWalkForSqlizer(listCtx, tslTree) matches the simplified return type, and the error handling (if serviceErr != nil { return "", nil, serviceErr }) preserves the previous control flow and behavior. This refactor is safe and keeps the search pipeline behavior unchanged.


332-346: treeWalkForSqlizer refactor improves clarity and error semantics

The new (squirrel.Sqlizer, *errors.ServiceError) signature and returning nil on error (both for FieldNameWalk and sqlFilter.Walk) align with how callers actually use the function and avoid leaking partially processed state. Using errors.BadRequest for walker failures is consistent with the other TSL walkers in this file. No issues from a correctness or API-usage standpoint.

pkg/services/generic_test.go (1)

70-76: Test correctly updated for new treeWalkForSqlizer return type

The test now binds only sqlizer, serviceErr and asserts on serviceErr and sqlizer.ToSql() output, which matches the updated production signature and behavior. This keeps the SQL translation coverage intact.

pkg/handlers/node_pool.go (1)

73-77: LGTM!

Good addition of error handling for JSON marshaling. The use of errors.GeneralError aligns with the structured ServiceError pattern, and the error message provides sufficient context for debugging.

pkg/api/presenters/slice_filter.go (2)

114-116: LGTM!

Good fix - propagating the validation error from nested structs prevents silent failures and ensures callers are aware of invalid field requests.


194-196: LGTM!

Good defensive nil-pointer check for *time.Time fields before formatting, preventing potential panics.

pkg/services/node_pool.go (1)

139-140: Good fix for variable shadowing.

Renaming the unmarshal error variable to unmarshalErr prevents shadowing the outer err variable and makes the error handling clearer.

pkg/api/adapter_status_types.go (1)

129-135: LGTM: Scope bug fixed with safe fallback pattern.

The current implementation correctly addresses the variable scope issue flagged in previous reviews. The pattern of initializing dataJSON and metadataJSON with fallback defaults, then conditionally overriding on successful marshal, is clean and avoids the scope bug.

Also applies to: 138-144

pkg/auth/context.go (2)

45-48: Good defensive fix for type assertion.

Replacing the direct type assertion with a safe checked assertion prevents potential panics if the context value is not a string.


82-127: Excellent defensive programming for JWT claims extraction.

Converting all JWT claim extractions from direct type assertions to checked assertions (value, ok := claim.(string)) prevents panics when:

  • Claims are missing
  • Claims have unexpected types
  • Malformed tokens are processed

This aligns with the JWT authentication middleware coding guideline and ensures robust token validation.

test/helper.go (3)

180-185: Graceful restart logging improvement looks good

Logging a warning when stopAPIServer fails before restarting improves debuggability without changing the external behavior of tests. The restart path remains straightforward.


200-215: Stricter error handling on env.AddFlags during reset is appropriate

Treating failures from env.AddFlags(flagset) as fatal during Reset() makes test environment initialization fail fast and avoids running tests under a partially configured env. This aligns with how initialization is handled in NewHelper.


441-445: Clear justification for //nolint:staticcheck around deprecated key parsing

The added comment explains why ParseRSAPrivateKeyFromPEMWithPassword is still used for test fixtures and why the staticcheck warning is suppressed locally. This keeps the linter noise scoped and documented.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
pkg/db/transaction_middleware.go (1)

44-52: Comment states "Log error" but logging is not implemented.

The comment at line 46 says "Log error but don't expose to client since headers already sent" yet no logging is performed. Since the logger package is already imported, consider adding actual logging to match the stated intent.

Based on coding guidelines, use structured logging via pkg/logger/logger.go with context propagation. The function would need a context parameter to obtain the logger.

cmd/hyperfleet-api/server/routes.go (1)

53-59: Authorization still uses mock only; TODO should be tied to a real issue and/or config‑gated implementation

You’ve fixed the compile bug in the commented block (the err declaration), which is good. However, as written:

  • authzMiddleware is always auth.NewAuthzMiddlewareMock().
  • The EnableAuthz config flag is effectively unused because the real initialization is entirely commented out.
  • The TODO is generic and doesn’t reference an actual Jira/issue, so it’s easy to lose track of when real authz must be enabled.

Given this was already flagged earlier, I’d suggest at minimum:

  • Update the TODO to reference the concrete tracking item (e.g., a HYPERFLEET‑xxx Jira), and clearly state that real authorization must be enabled before any production deployment.
  • Optionally, consider restoring the conditional initialization as real code (gated by env().Config.Server.EnableAuthz) and keeping the mock as the default when the flag is false, instead of relying on commented code.

For example, when you’re ready to re‑enable it, something along these lines would remove the need for commented blocks:

-	authzMiddleware := auth.NewAuthzMiddlewareMock()
-	// TODO(<JIRA-ID>): Create issue to track enabling authorization middleware
-	// if env().Config.Server.EnableAuthz {
-	// 	var err error
-	// 	authzMiddleware, err = auth.NewAuthzMiddleware()
-	// 	check(err, "Unable to create authz middleware")
-	// }
+	authzMiddleware := auth.NewAuthzMiddlewareMock()
+	if env().Config.Server.EnableAuthz {
+		var err error
+		authzMiddleware, err = auth.NewAuthzMiddleware()
+		check(err, "Unable to create authz middleware")
+	}
+	// TODO(<JIRA-ID>): Verify EnableAuthz is correctly set in prod configs before rollout.
test/helper.go (1)

81-85: Fix flag name passed to pflag.CommandLine.Set so LOGLEVEL works

FlagSet.Set expects the flag name without a leading dash; using "-v" will always return an error, which you now explicitly ignore, so LOGLEVEL never actually changes verbosity.

You can keep ignoring the error in tests while fixing the flag name:

-		if logLevel := os.Getenv("LOGLEVEL"); logLevel != "" {
-			glog.Infof("Using custom loglevel: %s", logLevel)
-			// Intentionally ignore error from Set — acceptable for tests
-			_ = pflag.CommandLine.Set("-v", logLevel)
-		}
+		if logLevel := os.Getenv("LOGLEVEL"); logLevel != "" {
+			glog.Infof("Using custom loglevel: %s", logLevel)
+			// Intentionally ignore error from Set — acceptable for tests
+			_ = pflag.CommandLine.Set("v", logLevel)
+		}
🧹 Nitpick comments (7)
pkg/handlers/nodepool_status.go (1)

11-12: Non-fatal aggregation logging is consistent and well-structured

Using logger.NewOCMLogger(ctx) with Extra("nodepool_id", ...) and Extra("error", aggregateErr) gives good observability while keeping the Create path successful when status aggregation fails, which fits the handler pattern (primary write succeeds, derived status is best-effort). This is aligned with the structured logging guidance for handlers.

Also applies to: 92-96

pkg/handlers/cluster_status.go (1)

11-12: Improved visibility into cluster status aggregation failures

The inline aggregation call with a structured warning log (including cluster_id and the error) cleanly surfaces failures without changing the Create response semantics. This matches the nodepool handler and aligns with the structured logging expectations.

Also applies to: 92-96

test/integration/clusters_test.go (1)

231-244: Clarify expected behavior for empty cluster name; assertion now enforces only the “accept” path

On Line 243 you assert Expect(err).ToNot(HaveOccurred()) after the empty‑name POST, but the comment on Lines 239–241 says “Should either accept empty name or return 400”. With this assertion, the test will now fail if the API evolves to start returning a 4xx (which the comment currently treats as acceptable).

Consider either:

  • Making the test explicit about the required behavior (e.g., require 201 and non‑empty ID, and update the comment accordingly), or
  • Keeping the test tolerant by branching on resp.StatusCode and asserting appropriately for both success and 400 cases, while still checking that err matches the status‑code branch.

For example:

_, resp, err = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(emptyNameInput).Execute()
if resp != nil && resp.StatusCode == http.StatusBadRequest {
    Expect(err).To(HaveOccurred(), "Should return an error for empty name with 400 status")
} else {
    Expect(err).NotTo(HaveOccurred(), "Should accept empty name when API allows it")
    Expect(resp.StatusCode).To(Equal(http.StatusCreated))
}

This keeps the linter happy and aligns the assertions with the intent described in the comment.

pkg/db/db_session/default.go (1)

111-113: Nice addition of defensive error handling around l.Ping()

Checking the Ping error and logging it at low verbosity improves observability without changing control flow or introducing new failure modes. This is a clean, low‑risk improvement to the listener health‑check path.

If you ever revisit this area, you might consider including the channel name or other context in the ping‑failure log to ease correlation with specific listeners, but this is strictly a nice‑to‑have and not required for this PR.

pkg/api/adapter_status_types.go (2)

54-68: Consider logging unmarshal failures to detect data integrity issues.

The explicit error handling with fallbacks is good defensive programming. However, if json.Unmarshal fails on data stored in the database, it indicates a data integrity problem (corrupted JSON in the conditions or data JSONB columns). Silent fallbacks mask these issues and could hide database corruption or encoding problems.

Consider adding structured logging when unmarshaling fails:

if err := json.Unmarshal(as.Conditions, &conditions); err != nil {
	// Log data integrity issue
	// logger.Warnf("Failed to unmarshal adapter conditions for ID %s: %v", as.ID, err)
	conditions = []openapi.AdapterCondition{}
}

Apply similar logging for the Data unmarshal at lines 64-67.


122-129: Implement or remove the commented-out logging statement.

The commented-out logger.Errorf at lines 126-127 suggests logging was considered but not implemented. Since json.Marshal failures on a well-formed struct indicate programming errors (e.g., non-serializable types), these failures should be surfaced rather than silently ignored.

Either implement the logging as suggested:

 	conditionsJSON, err := json.Marshal(adapterConditions)
 	if err != nil {
-		// Fallback to empty array JSON
-		// Log marshal failure - this indicates a programming error
-		// logger.Errorf("Failed to marshal adapter conditions: %v", err)
+		// Log marshal failure - this indicates a programming error
+		logger.Errorf("Failed to marshal adapter conditions: %v", err)
 		conditionsJSON = []byte("[]")
 	}

Or remove the commented-out code if logging is not desired at this time.

Dockerfile.openapi (1)

3-5: APT sandbox workaround is fine; consider minor Dockerfile tightening (optional).

The -o APT::Sandbox::User=root flag with the added comment is a reasonable workaround for rootless Podman setgroups issues in Prow. As an optional cleanup, you might later combine update and install into a single RUN and clear apt lists to keep the image a bit leaner, but that isn't required for correctness here.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0128ec8 and 95f28bb.

⛔ Files ignored due to path filters (1)
  • .bingo/golangci-lint.sum is excluded by !**/*.sum
📒 Files selected for processing (46)
  • .bingo/Variables.mk (1 hunks)
  • .bingo/golangci-lint.mod (1 hunks)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/framework_test.go (0 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/handlers/validation.go (0 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (1 hunks)
  • pkg/services/types.go (1 hunks)
  • test/helper.go (4 hunks)
  • test/integration/clusters_test.go (2 hunks)
  • test/integration/node_pools_test.go (1 hunks)
  • test/mocks/jwk_cert_server.go (1 hunks)
  • test/mocks/ocm.go (1 hunks)
💤 Files with no reviewable changes (2)
  • pkg/handlers/framework_test.go
  • pkg/handlers/validation.go
🚧 Files skipped from review as they are similar to previous changes (17)
  • pkg/config/config_test.go
  • pkg/services/generic_test.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/openapi.go
  • test/integration/node_pools_test.go
  • pkg/services/types.go
  • pkg/api/cluster_types.go
  • openapitools.json
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/client/ocm/client.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • pkg/services/node_pool.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/api/presenters/slice_filter.go
  • Makefile
  • pkg/api/node_pool_types.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • cmd/hyperfleet-api/server/routes.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/auth/context.go
  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/auth/helpers.go
  • pkg/db/db_session/test.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/main.go
  • test/mocks/jwk_cert_server.go
  • pkg/handlers/node_pool.go
  • test/mocks/ocm.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
  • test/integration/clusters_test.go
  • test/helper.go
  • pkg/handlers/cluster.go
  • pkg/services/status_aggregation.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/api/adapter_status_types.go
  • pkg/services/generic.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/test.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/auth/context.go
  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/auth/helpers.go
  • pkg/db/db_session/test.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
  • pkg/handlers/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/api/adapter_status_types.go
  • pkg/services/generic.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
  • pkg/handlers/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/services/generic.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
  • pkg/handlers/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/services/generic.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/services/generic.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/node_pool.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
  • pkg/handlers/cluster.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/clusters_test.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/adapter_status_types.go
🧠 Learnings (14)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/**/*.go : Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
  • pkg/db/transaction_middleware.go
  • pkg/auth/context.go
  • test/mocks/jwk_cert_server.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
  • pkg/handlers/cluster.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Use context.Context as the first parameter in all DAO methods for transaction propagation

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/services/**/*.go : Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Applied to files:

  • pkg/services/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/services/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • pkg/handlers/nodepool_status.go
  • .golangci.yml
  • pkg/handlers/cluster_status.go
  • pkg/api/adapter_status_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/handlers/**/*.go : Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Applied to files:

  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/helpers.go
  • pkg/handlers/framework.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • .golangci.yml
  • .bingo/Variables.mk
  • .bingo/golangci-lint.mod
  • Dockerfile.openapi
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/openapi/**/*.go : Use make generate to regenerate Go models from openapi/openapi.yaml via openapi-generator-cli v7.16.0 in Podman

Applied to files:

  • .golangci.yml
  • Dockerfile.openapi
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/handlers/node_pool.go
  • test/integration/clusters_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • test/integration/clusters_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
🧬 Code graph analysis (7)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/handlers/cluster_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
pkg/handlers/cluster.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
pkg/api/adapter_status_types.go (1)
pkg/api/metadata_types.go (1)
  • Metadata (28-34)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
🔇 Additional comments (29)
test/mocks/jwk_cert_server.go (1)

33-35: Good addition of write error handling in the mock

Checking the result of fmt.Fprintf and surfacing failures via t.Errorf makes this mock more robust and easier to debug if the ResponseWriter misbehaves. No functional behavior change for successful writes, and no further changes are needed here.

test/mocks/ocm.go (1)

62-65: Pointer receiver on Reset correctly updates shared mock state

Switching Reset to use a pointer receiver matches the other methods and ensures calls like authzMock.Reset() and any use through ocm.Authorization mutate the original mock instance instead of a copy. The body is simple and correct; no further changes needed here.

cmd/hyperfleet-api/environments/framework.go (1)

158-162: OCM client teardown nil-guard correctly prevents panics

The added nil check around e.Clients.OCM.Close() cleanly avoids potential panics in cases where the OCM client was never initialized, while still logging any shutdown errors. This matches the updated Close() signature and is appropriate for teardown paths.

pkg/services/cluster.go (1)

132-141: Comment clarification and local error var rename look good

The updated comment now accurately describes the adapters slice, and renaming the inner json.Unmarshal error to unmarshalErr avoids confusion with the outer err while preserving behavior. No issues from a correctness or aggregation standpoint.

pkg/db/db_session/test.go (1)

216-221: ResetDB now fails fast on DB reset errors (good change).

Panicking on resetDB failure and only setting wasDisconnected after a successful reset prevents tests from running against a dirty database while preserving the existing reconnection behavior in New(). This resolves the earlier issue of silently ignored reset failures.

pkg/auth/context.go (4)

45-48: LGTM! Defensive type assertion prevents panics.

The guarded type assertion correctly handles cases where the context value might not be a string, returning an empty string instead of panicking.


82-96: LGTM! Guarded claim extractions prevent type assertion panics.

All primary claim extractions now use the ok pattern to safely handle unexpected types in JWT claims, leaving fields at their zero values when claims are missing or not strings.


100-114: LGTM! Consistent defensive pattern for fallback claims.

The fallback claim extractions (preferred_username, given_name, family_name) consistently apply the same guarded type assertion pattern as the primary claims.


119-127: LGTM! Safe name parsing with guarded extraction.

The name claim extraction and parsing logic correctly uses a guarded type assertion and safely handles both single and multi-part names. The split logic is safe even for edge cases like empty strings.

pkg/services/generic.go (3)

43-56: LGTM!

The added documentation comment clarifies the purpose of the listContext struct, improving code readability.


172-176: LGTM!

The call site correctly adapts to the simplified treeWalkForSqlizer signature. The previously returned tslTree was unused after this point, so removing it from the return is appropriate.


332-345: Clean signature simplification.

The refactor correctly removes the unused tsl.Node return value. The tslTree is still properly modified by FieldNameWalk (line 334) and consumed by sqlFilter.Walk (line 340) internally. Since only the sqlizer is needed by callers, this simplifies the API without changing behavior.

cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)

14-14: LGTM—cleaner boolean assignment.

The simplified assignment preserves the original logic while improving readability.

pkg/handlers/helpers.go (1)

17-25: LGTM—proper error handling for response marshaling and writing.

The early returns with explanatory comments appropriately handle marshal and write errors. Since headers are already sent (line 14), returning silently is the correct approach.

cmd/hyperfleet-api/main.go (1)

28-30: LGTM—proper error handling for flag parsing.

Checking the parse error and failing fast with a clear message prevents silent initialization failures.

pkg/auth/helpers.go (1)

30-38: LGTM—consistent error handling pattern.

The error checks for marshal and write operations follow the same pattern as other response utilities in this PR, with appropriate early returns after headers are sent.

pkg/services/status_aggregation.go (1)

155-163: LGTM—more idiomatic with explicit handling of unexpected statuses.

The switch statement is clearer than the if-else chain, and the default case with logging improves robustness by explicitly handling unexpected adapter status values.

pkg/handlers/cluster.go (1)

72-76: LGTM—proper error handling for spec marshaling.

The marshal error check prevents silent failures and uses the structured ServiceError type to propagate a descriptive error to the client.

pkg/handlers/framework.go (1)

109-114: LGTM—more idiomatic switch usage.

Switching directly on serviceErr is clearer and more idiomatic than using boolean conditions in the cases.

pkg/handlers/node_pool.go (1)

73-77: Good defensive error handling added.

The error handling for json.Marshal is a solid defensive improvement. While marshaling well-formed structs rarely fails, this change properly captures and surfaces any potential issues (e.g., cyclic references or unsupported types) using the structured GeneralError pattern.

test/integration/clusters_test.go (1)

66-75: Good: now asserting client-level success before checking for 400 Bad Request

Adding Expect(err).ToNot(HaveOccurred()) on Line 73 ensures transport/configuration issues from the resty call don’t get mistaken for an expected 400 response and will fail the test loudly instead. This is a solid tightening of the assertion around the bad‑JSON path.

pkg/api/adapter_status_types.go (2)

131-137: Excellent fix for the scope bug from past review.

The pattern used here—default fallback with conditional override—cleanly resolves the previous issue where err was checked outside its assignment scope. The implementation matches the suggestion from past reviews and correctly handles the optional Data field.


140-146: Good implementation of the suggested fallback pattern.

The safe fallback approach for Metadata marshaling is consistent with the Data handling and implements the pattern suggested in past reviews. This ensures valid JSON is always stored, even when marshaling fails.

.bingo/golangci-lint.mod (1)

3-5: golangci-lint v2 module path and Go directive look correct.

The go 1.24.0 directive and the github.com/golangci/golangci-lint/v2 v2.7.0 requirement are consistent with the v2 module layout and the updated Variables.mk wiring; this auto-generated Bingo module is coherent with the new tooling setup.

Please just confirm locally that make lint is invoking this v2.7.0 binary as expected in your environment.

.golangci.yml (1)

1-4: Minimal GolangCI-Lint v2 config looks appropriate.

Setting version: 2 and excluding pkg/api/openapi as generated code is a sensible baseline for the upgraded linter and matches the intent to avoid linting generated OpenAPI models.

Please run make lint once more to confirm golangci-lint v2 picks up this config and correctly skips pkg/api/openapi.

.bingo/Variables.mk (1)

26-30: GolangCI-Lint v2.7.0 Bingo wiring is consistent and correct.

The GOLANGCI_LINT binary name, echo message, and go build target all consistently reference golangci-lint-v2.7.0 and the github.com/golangci/golangci-lint/v2/cmd/golangci-lint module path, so the upgraded toolchain should install and run cleanly.

After running make vendor-bingo/make lint in CI or locally, verify that the reported golangci-lint version is v2.7.0.

test/helper.go (3)

180-185: RestartServer’s softer stop error handling looks reasonable

Logging a warning instead of fatalling on stopAPIServer errors is fine in test helpers, and subsequent startAPIServer() will still fail fast if the listener cannot be bound, preserving clear failures on real issues.


441-445: Nolint/staticcheck and comments around JWT key parsing are appropriate for tests

Given this is test-only code and you’ve documented why the deprecated helper is acceptable here, the //nolint:staticcheck and explanatory comments are a reasonable way to keep static analysis clean without reworking the fixtures.


200-208: New FlagSet in Reset is never parsed; CLI flags on reset are effectively ignored

The added fatal on env.AddFlags(flagset) is good, but note that you add flags to a fresh FlagSet and then call pflag.Parse() on the global pflag.CommandLine. The new flagset is never parsed, so any flags added there won't influence configuration during Reset.

If the intent is to re-read CLI flags on reset without redefining glog flags, consider either:

  • Adding flags to the global set again, or
  • Parsing the new flagset explicitly and using it consistently.

For example, if you're okay reusing the global set:

-	// Also on reset, we don't care to be re-defining 'v' and other glog flags
-	flagset := pflag.NewFlagSet(helper.NewID(), pflag.ContinueOnError)
-	if err := env.AddFlags(flagset); err != nil {
+	// Also on reset, we don't care to be re-defining 'v' and other glog flags
+	flagset := pflag.CommandLine
+	if err := env.AddFlags(flagset); err != nil {
 		glog.Fatalf("Unable to add environment flags on Reset: %s", err.Error())
 	}
 	pflag.Parse()

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
pkg/api/node_pool_types.go (1)

159-163: Treat marshal failures as hard errors rather than silently defaulting

Right now, if json.Marshal fails for Spec, Labels, or StatusConditions, you silently store "{}" or "[]". That can hide bad user input and persist data that doesn’t reflect what was requested.

Consider changing NodePoolFromOpenAPICreate to return an error and fail fast on marshal issues:

-func NodePoolFromOpenAPICreate(req *openapi.NodePoolCreateRequest, ownerID, createdBy string) *NodePool {
+func NodePoolFromOpenAPICreate(req *openapi.NodePoolCreateRequest, ownerID, createdBy string) (*NodePool, error) {
 	// Marshal Spec
 	specJSON, err := json.Marshal(req.Spec)
-	if err != nil {
-		//logger.Errorf("Failed to marshal NodePool spec: %v", err)
-		specJSON = []byte("{}")
-	}
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal NodePool spec: %w", err)
+	}
@@
-	labelsJSON, err := json.Marshal(labels)
-	if err != nil {
-		labelsJSON = []byte("{}")
-	}
+	labelsJSON, err := json.Marshal(labels)
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal NodePool labels: %w", err)
+	}
@@
-	statusConditionsJSON, err := json.Marshal([]openapi.ResourceCondition{})
-	if err != nil {
-		statusConditionsJSON = []byte("[]")
-	}
+	statusConditionsJSON, err := json.Marshal([]openapi.ResourceCondition{})
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal NodePool status conditions: %w", err)
+	}
@@
-	return &NodePool{
+	return &NodePool{
@@
-	}
+	}, nil

Callers can then translate these errors into appropriate HTTP 4xx responses. If changing the signature is too invasive right now, at least log the marshal errors instead of silently masking them.

Also applies to: 170-173, 176-179

🧹 Nitpick comments (4)
pkg/db/db_session/default.go (1)

100-116: Error handling on Ping looks good; consider context and log level as optional improvements

The new if err := l.Ping(); err != nil { ... } guard is correct and avoids silent failures from Ping, 👍.

Two optional refinements you might consider:

  1. Context‑aware shutdown: waitForNotification currently loops only on l.Notify and time.After, while newListener takes a ctx but never checks ctx.Done(). If you ever need to stop the listener cleanly (e.g., on server shutdown), it may be worth threading ctx into waitForNotification and adding a case <-ctx.Done(): return branch.

  2. Log severity for ping failures: A failed Ping usually indicates a connectivity or backend problem. If this signal is important operationally, you may want to log it at a higher level than V(10) (e.g., a warning/error logger call) so it’s not lost in very‑verbose logs.

test/integration/node_pools_test.go (1)

73-82: Good addition to assert no client/network error; consider reordering expectations

The new Expect(err).NotTo(HaveOccurred(), ...) is useful to ensure only the server’s 400 response is under test, not a client/network failure. For extra safety and clarity, you might want to assert on err before calling restyResp.StatusCode() so the test can’t accidentally dereference a nil response if Resty’s behavior changes or a transport error occurs in the future.

pkg/services/node_pool.go (1)

141-142: Good variable naming to avoid shadowing.

Renaming to unmarshalErr prevents shadowing the outer err variable and makes the error handling clearer.

However, consider logging unmarshal failures at Warning level to surface potential data corruption or schema mismatches in adapter status conditions, rather than silently skipping adapters.

Apply this diff to add logging:

 		if unmarshalErr := json.Unmarshal(adapterStatus.Conditions, &conditions); unmarshalErr != nil {
+			logger.NewOCMLogger(ctx).Extra("adapter_status_id", adapterStatus.ID).Extra("error", unmarshalErr).Warning("Failed to unmarshal adapter conditions, skipping")
 			continue // Skip if can't unmarshal
 		}

As per coding guidelines, use structured logging via pkg/logger/logger.go with context propagation.

pkg/auth/context.go (1)

80-127: JWT claim extraction is now safely typed; minor name parsing nit

The guarded string assertions for all claims plus the fallback logic from alternative keys and name make GetAuthPayloadFromContext much more robust against malformed tokens.

If you ever touch this again, consider strings.Fields(name) instead of strings.Split(name, " ") so multiple spaces or leading/trailing whitespace don’t produce empty elements, but current behavior is acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95f28bb and ef90317.

⛔ Files ignored due to path filters (1)
  • .bingo/golangci-lint.sum is excluded by !**/*.sum
📒 Files selected for processing (46)
  • .bingo/Variables.mk (1 hunks)
  • .bingo/golangci-lint.mod (1 hunks)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/framework_test.go (0 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/handlers/validation.go (0 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (2 hunks)
  • pkg/services/types.go (1 hunks)
  • test/helper.go (4 hunks)
  • test/integration/clusters_test.go (2 hunks)
  • test/integration/node_pools_test.go (1 hunks)
  • test/mocks/jwk_cert_server.go (1 hunks)
  • test/mocks/ocm.go (1 hunks)
💤 Files with no reviewable changes (2)
  • pkg/handlers/validation.go
  • pkg/handlers/framework_test.go
🚧 Files skipped from review as they are similar to previous changes (28)
  • test/integration/clusters_test.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/services/status_aggregation.go
  • pkg/api/presenters/slice_filter.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/handlers/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/framework.go
  • pkg/api/cluster_types.go
  • pkg/services/cluster.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/main.go
  • Makefile
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/cluster.go
  • pkg/services/types.go
  • pkg/handlers/node_pool.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/api/adapter_status_types.go
  • test/mocks/jwk_cert_server.go
  • pkg/auth/helpers.go
  • test/helper.go
  • pkg/db/db_session/test.go
  • .bingo/Variables.mk
  • cmd/hyperfleet-api/server/logging/writer.go
  • .bingo/golangci-lint.mod
  • Dockerfile.openapi
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/config/config_test.go
  • pkg/db/sql_helpers.go
  • pkg/services/node_pool.go
  • pkg/db/db_session/default.go
  • test/integration/node_pools_test.go
  • pkg/client/ocm/client.go
  • pkg/auth/context.go
  • pkg/api/node_pool_types.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/environments/framework.go
  • test/mocks/ocm.go
  • pkg/db/db_session/testcontainer.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/config/config_test.go
  • pkg/db/sql_helpers.go
  • pkg/services/node_pool.go
  • pkg/db/db_session/default.go
  • pkg/client/ocm/client.go
  • pkg/auth/context.go
  • pkg/api/node_pool_types.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/db/db_session/testcontainer.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/sql_helpers.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/services/node_pool.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/services/node_pool.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/node_pool.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/node_pools_test.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/node_pool_types.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/nodepool_status.go
🧠 Learnings (15)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/services/**/*.go : Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Applied to files:

  • pkg/services/node_pool.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • test/integration/node_pools_test.go
  • pkg/api/node_pool_types.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • test/integration/node_pools_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/**/*.go : Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Use context.Context as the first parameter in all DAO methods for transaction propagation

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • pkg/api/node_pool_types.go
  • pkg/handlers/nodepool_status.go
  • .golangci.yml
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/openapi/**/*.go : Use make generate to regenerate Go models from openapi/openapi.yaml via openapi-generator-cli v7.16.0 in Podman

Applied to files:

  • pkg/api/node_pool_types.go
  • .golangci.yml
  • openapitools.json
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/handlers/**/*.go : Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • .golangci.yml
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • pkg/db/db_session/testcontainer.go
🧬 Code graph analysis (4)
pkg/db/sql_helpers.go (1)
pkg/errors/errors.go (1)
  • BadRequest (196-198)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • NewOCMLogger (36-44)
pkg/services/generic.go (2)
pkg/errors/errors.go (2)
  • ServiceError (94-101)
  • BadRequest (196-198)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (108-199)
cmd/hyperfleet-api/environments/framework.go (1)
cmd/hyperfleet-api/environments/types.go (1)
  • Clients (67-69)
🔇 Additional comments (12)
test/mocks/ocm.go (1)

62-65: Pointer receiver on Reset correctly clears mock state

Switching Reset to a pointer receiver aligns it with the other methods and ensures Action/ResourceType are actually cleared on the shared mock instance instead of a copy. This is the correct behavior for a reusable test mock.

pkg/handlers/nodepool_status.go (1)

92-95: Excellent error handling improvement.

The inline error handling with structured logging properly captures aggregation failures while maintaining request success for eventual consistency. The logging includes appropriate context (nodepool_id, error) and uses the correct Warning level.

As per coding guidelines, this follows the structured logging pattern via pkg/logger/logger.go with context propagation.

pkg/client/ocm/client.go (1)

78-82: Close now safely propagates connection errors

The nil guard plus returning c.connection.Close() is correct and lets callers handle teardown failures explicitly. No further changes needed here.

cmd/hyperfleet-api/environments/framework.go (1)

158-162: Teardown OCM close is now nil‑safe and logged

Guarding e.Clients.OCM before calling Close() and logging any error mirrors the DB teardown pattern and prevents possible panics during shutdown.

pkg/auth/context.go (1)

40-49: Defensive username lookup avoids bad type panics

Adding the nil check and guarded string assertion ensures GetUsernameFromContext returns "" instead of panicking when the value is missing or of an unexpected type.

pkg/api/node_pool_types.go (1)

82-84: Resilient JSON unmarshal in ToOpenAPI

Falling back to {} for Spec/Labels and [] for StatusConditions when json.Unmarshal fails prevents panics and still produces a sane OpenAPI representation even if the stored JSON is malformed.

Also applies to: 90-92, 98-100

.golangci.yml (1)

1-4: Verify minimal golangci-lint v2 configuration is complete and functional.

The configuration includes version: 2 and exclude-dirs for the generated code directory. According to golangci-lint v2 documentation, this minimal configuration will use the default "standard" linters set. However, verification of the following requires direct repository access and cannot be completed:

  1. Confirm the file is complete (no additional configuration beyond line 4)
  2. Verify golangci-lint run executes successfully with this config
  3. Validate that the default "standard" linters are appropriate for the codebase
  4. Confirm pkg/api/openapi is properly excluded from linting

Note: The prior concerns about staticcheck being disabled and redundant exclude-files entries are no longer present, aligning with the PR objective.

openapitools.json (1)

5-5: Confirm OpenAPI generator output compatibility after v7.16.0 upgrade.

The version upgrade from 5.4.0 to 7.16.0 introduces significant breaking changes in code generation including model naming, enum handling, allOf/inline model resolution, and OpenAPI 3.1 / JSON Schema semantics. Before merging, verify that:

  1. Generated code in pkg/api/openapi was regenerated with v7.16.0
  2. Generated Go code integrates correctly with downstream usage
  3. No unexpected type or API definition changes broke existing code paths
pkg/db/sql_helpers.go (2)

20-34: LGTM! Defensive type assertion prevents panic.

The added type assertion on l.Left (line 28) safely guards against non-string values and prevents potential runtime panics.


144-154: LGTM! Proper type validation prevents panic.

The type assertion check on n.Left (lines 145-149) prevents potential panics from invalid node structures and returns an appropriate user-facing error. This is consistent with the defensive patterns used elsewhere in this function.

pkg/services/generic_test.go (1)

22-24: Updated Close() nolint and treeWalkForSqlizer usage look correct

Using //nolint:errcheck on the deferred dbFactory.Close() is reasonable for test cleanup, and the updated treeWalkForSqlizer call correctly targets the new (Sqlizer, *ServiceError) signature while preserving the existing assertions on the error and the generated SQL.

Also applies to: 68-72

pkg/services/generic.go (1)

45-45: treeWalkForSqlizer signature change and call-site update are consistent

The refactor to have treeWalkForSqlizer return only (squirrel.Sqlizer, *errors.ServiceError) aligns with how it’s used in buildSearchValues (the transformed tree isn’t needed), and the updated call-site correctly handles the new two-value return. Field-name validation via db.FieldNameWalk and the SQL filter construction now both surface *errors.ServiceError, keeping error propagation consistent with the service layer’s conventions.

Also applies to: 153-188, 334-348

Comment on lines 14 to 18
stringFile, err := createConfigFile("string", "example\n")
defer os.Remove(stringFile.Name())
defer os.Remove(stringFile.Name()) //nolint:errcheck
if err != nil {
log.Fatal(err)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Defer cleanup after checking for file creation errors

createConfigFile can return (nil, err) (e.g., if os.CreateTemp fails), but Name() is called in the defer before checking err, which would panic in that case. The new //nolint:errcheck is fine for ignoring best-effort cleanup, but it’d be safer to move the defer after the error check in all four tests.

For example:

- stringFile, err := createConfigFile("string", "example\n")
- defer os.Remove(stringFile.Name()) //nolint:errcheck
- if err != nil {
-     log.Fatal(err)
- }
+ stringFile, err := createConfigFile("string", "example\n")
+ if err != nil {
+     log.Fatal(err)
+ }
+ defer os.Remove(stringFile.Name()) //nolint:errcheck

(and similarly for the int/bool/quoted tests).

Also applies to: 29-33, 44-48, 59-63

🤖 Prompt for AI Agents
In pkg/config/config_test.go around lines 14-18 (and similarly at 29-33, 44-48,
59-63), the test calls createConfigFile and immediately defers
os.Remove(stringFile.Name()) before verifying err, which can panic if
createConfigFile returned nil and an error; move each defer
os.Remove(file.Name()) to after the subsequent if err != nil check so you only
defer cleanup when file creation succeeded (keep the //nolint:errcheck if
desired).

Comment on lines 52 to 54
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2).
wait.ForListeningPort("5432/tcp").
WithStartupTimeout(60*time.Second)),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

testcontainers-go PostgreSQL ForListeningPort wait strategy best practices

💡 Result:

Short best-practices for using wait.ForListeningPort with PostgreSQL in testcontainers-go

  • Prefer a combined wait strategy (logs + listening port) instead of only ForListeningPort. Example recommended by the postgres module: wait for the Postgres "ready" log (often with WithOccurrence(2) for Postgres self‑restart) then wait.ForListeningPort("5432/tcp"). This reduces flakiness across platforms. [1]

  • If you need to ensure DB is actually usable (not just a bound port), add wait.ForSQL (run a simple SELECT 1) after the listening-port check. That verifies the server accepts queries. [2][3]

  • Increase startup timeout and poll interval for slow CI/containers. Defaults are 60s timeout / 100ms poll; set WithStartupTimeout(...) when needed. Combined strategies can set per-strategy timeouts or an overall deadline (wait.ForAll / ForAll.WithDeadline). [4][2]

  • On macOS/Windows (or Docker Desktop/Rancher Desktop) explicitly wait for the mapped host port — ForListeningPort checks the container side but you should also ensure the host-side mapping is ready (the postgres module example calls out this proxy issue). [1][5]

  • Avoid time.Sleep-based waits. Prefer deterministic checks (log, listening port, SQL). [4]

  • For compose-based runs, ensure you reference the correct service/container name when waiting for a service; otherwise ForListeningPort may target the wrong container. (Give unique container_name if necessary.) [6]

Concise example (pattern):

  • wait.ForLog("database system is ready to accept connections").WithOccurrence(2)
  • wait.ForListeningPort("5432/tcp").WithStartupTimeout(120*time.Second)
  • wait.ForSQL(nat.Port("5432/tcp"), "postgres", dbURL).WithQuery("SELECT 1").WithStartupTimeout(30*time.Second)

Sources:
[1] testcontainers-go Postgres module docs (wait strategy examples).
[2] testcontainers-go wait/host_port docs (ForListeningPort behavior/options).
[3] testcontainers-go wait/sql docs (ForSQL usage).
[4] testcontainers-go wait/introduction & multi (timeouts, combining strategies).
[5] testcontainers-go networking / postgres module note about host proxy on non‑Linux OSes.
[6] testcontainers-go GitHub / compose guidance (unique container_name for waits).


🏁 Script executed:

cat -n pkg/db/db_session/testcontainer.go | head -120

Repository: openshift-hyperfleet/hyperfleet-api

Length of output: 214


Incomplete wait strategy—combine log, port, and SQL verification for reliability.

Using only ForListeningPort does not follow testcontainers-go best practices for PostgreSQL. The recommended pattern combines three checks:

  1. Log-based detection (wait.ForLog("database system is ready to accept connections").WithOccurrence(2)) — catches PostgreSQL initialization
  2. Port listening (wait.ForListeningPort("5432/tcp")) — confirms the port is bound
  3. SQL verification (wait.ForSQL(...) with a SELECT 1 query) — ensures the database actually accepts connections

Port listening alone is insufficient because a bound port does not guarantee the database is accepting queries. Add the log-based check and SQL verification to prevent flaky tests across different platforms and CI environments. Also consider increasing the timeout beyond 60s for slower CI environments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (4)
cmd/hyperfleet-api/server/routes.go (1)

54-59: Commented authz init is now syntactically correct; tie TODO to a concrete tracking issue.

The previously noted err scoping bug in the commented block is fixed, and the snippet will compile when uncommented. Since this block governs real authorization, consider tightening the TODO to reference the actual Jira (e.g., // TODO(HYPERFLEET-XXX): enable authorization middleware) so it’s easy to track and enforce before any production rollout.

pkg/db/transaction_middleware.go (2)

44-48: Past review comment not addressed: implement the logging mentioned in the comment.

The comment on line 46 states "Log error but don't expose to client" but no logging is actually performed. A previous review already flagged this issue and suggested using the logger package (which is imported) to log the marshaling error. This prevents debugging issues when JSON marshaling fails silently.

As suggested in the previous review, add context propagation and logging:

-func writeJSONResponse(w http.ResponseWriter, code int, payload interface{}) {
+func writeJSONResponse(w http.ResponseWriter, code int, payload interface{}, ctx context.Context) {
 	w.Header().Set("Content-Type", "application/json")
 	w.WriteHeader(code)
 
 	if payload != nil {
 		response, err := json.Marshal(payload)
 		if err != nil {
-			// Log error but don't expose to client since headers already sent
+			logger.NewOCMLogger(ctx).Errorf("Failed to marshal JSON response: %v", err)
 			return
 		}

Then update the call site at line 23 to pass ctx.


49-52: Add logging for response write failures.

While nothing can be sent to the client after headers are written, the write error should still be logged for debugging and observability purposes. As per coding guidelines, use structured logging with context propagation.

Apply this diff (assuming ctx is added to the function signature as suggested above):

 		if _, err := w.Write(response); err != nil {
-			// Response writing failed, nothing we can do at this point
+			logger.NewOCMLogger(ctx).Warningf("Failed to write response: %v", err)
 			return
 		}

Based on coding guidelines, structured logging should be used throughout pkg/**/*.go.

test/helper.go (1)

83-84: Fix the flag name: use "v" instead of "-v".

The pflag.FlagSet.Set method expects the flag name without a leading dash. Using "-v" causes the call to fail, so the LOGLEVEL environment variable never adjusts verbosity.

Apply this diff:

-		// Intentionally ignore error from Set — acceptable for tests
-		_ = pflag.CommandLine.Set("-v", logLevel)
+		if err := pflag.CommandLine.Set("v", logLevel); err != nil {
+			glog.Warningf("Unable to set loglevel from LOGLEVEL=%s: %v", logLevel, err)
+		}
🧹 Nitpick comments (2)
pkg/handlers/nodepool_status.go (1)

11-11: Aggregation error logging and handler flow look correct

Using logger.NewOCMLogger(ctx) and logging nodepool_id plus the aggregation error while keeping the request successful preserves the intended “best‑effort aggregation” behavior and aligns with the structured logging guidance for handlers. The overall flow (parse request → service calls → handle errors → send response) remains intact, and the non‑fatal aggregation failure is now observable without impacting clients.

If you want to enrich observability further, you could consider adding the adapter name (from req.Adapter) into the log context so issues can be correlated to specific adapters when debugging.

Based on learnings, this aligns with the structured logging requirements for handlers.

Also applies to: 92-95

pkg/auth/context.go (1)

98-115: Consider making name fallbacks more robust and less overwriting

The guarded fallbacks (preferred_username, given_name, family_name) are good hardening. For the name-based fallback, there are a couple of small refinements you might consider (not required for this PR):

  • The comment says “If given and family names are not present,” but the condition is if payload.FirstName == "" || payload.LastName == "", which can overwrite an already-populated first or last name.
  • strings.Split(name, " ") is brittle for multiple or irregular spaces and multi-word names.

If you want to tighten this up later, something along these lines would avoid overwriting existing values and handle spacing better:

-	if payload.FirstName == "" || payload.LastName == "" {
-		if name, ok := claims["name"].(string); ok {
-			names := strings.Split(name, " ")
-			if len(names) > 1 {
-				payload.FirstName = names[0]
-				payload.LastName = names[1]
-			} else {
-				payload.FirstName = names[0]
-			}
-		}
-	}
+	if (payload.FirstName == "" || payload.LastName == "") {
+		if name, ok := claims["name"].(string); ok {
+			parts := strings.Fields(name)
+			if len(parts) > 0 && payload.FirstName == "" {
+				payload.FirstName = parts[0]
+			}
+			if len(parts) > 1 && payload.LastName == "" {
+				// Use the last part as surname to better support multi-word names
+				payload.LastName = parts[len(parts)-1]
+			}
+		}
+	}

Also applies to: 117-126

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef90317 and b550730.

⛔ Files ignored due to path filters (2)
  • .bingo/golangci-lint.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (47)
  • .bingo/Variables.mk (1 hunks)
  • .bingo/golangci-lint.mod (1 hunks)
  • .golangci.yml (1 hunks)
  • Dockerfile.openapi (1 hunks)
  • Makefile (1 hunks)
  • cmd/hyperfleet-api/environments/framework.go (1 hunks)
  • cmd/hyperfleet-api/environments/framework_test.go (1 hunks)
  • cmd/hyperfleet-api/main.go (1 hunks)
  • cmd/hyperfleet-api/server/api_server.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1 hunks)
  • cmd/hyperfleet-api/server/logging/writer.go (1 hunks)
  • cmd/hyperfleet-api/server/routes.go (1 hunks)
  • go.mod (2 hunks)
  • openapitools.json (1 hunks)
  • pkg/api/adapter_status_types.go (2 hunks)
  • pkg/api/cluster_types.go (2 hunks)
  • pkg/api/node_pool_types.go (2 hunks)
  • pkg/api/presenters/slice_filter.go (3 hunks)
  • pkg/auth/context.go (2 hunks)
  • pkg/auth/helpers.go (1 hunks)
  • pkg/client/ocm/client.go (1 hunks)
  • pkg/config/config_test.go (4 hunks)
  • pkg/db/db_session/default.go (1 hunks)
  • pkg/db/db_session/test.go (1 hunks)
  • pkg/db/db_session/testcontainer.go (1 hunks)
  • pkg/db/sql_helpers.go (4 hunks)
  • pkg/db/transaction_middleware.go (1 hunks)
  • pkg/handlers/cluster.go (1 hunks)
  • pkg/handlers/cluster_status.go (2 hunks)
  • pkg/handlers/framework.go (1 hunks)
  • pkg/handlers/framework_test.go (0 hunks)
  • pkg/handlers/helpers.go (1 hunks)
  • pkg/handlers/node_pool.go (1 hunks)
  • pkg/handlers/nodepool_status.go (2 hunks)
  • pkg/handlers/openapi.go (1 hunks)
  • pkg/handlers/validation.go (0 hunks)
  • pkg/services/cluster.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (2 hunks)
  • pkg/services/node_pool.go (1 hunks)
  • pkg/services/status_aggregation.go (2 hunks)
  • pkg/services/types.go (1 hunks)
  • test/helper.go (4 hunks)
  • test/integration/clusters_test.go (2 hunks)
  • test/integration/node_pools_test.go (1 hunks)
  • test/mocks/jwk_cert_server.go (1 hunks)
  • test/mocks/ocm.go (1 hunks)
💤 Files with no reviewable changes (2)
  • pkg/handlers/framework_test.go
  • pkg/handlers/validation.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/services/status_aggregation.go
🚧 Files skipped from review as they are similar to previous changes (26)
  • pkg/config/config_test.go
  • cmd/hyperfleet-api/environments/framework.go
  • test/integration/node_pools_test.go
  • pkg/services/cluster.go
  • pkg/handlers/helpers.go
  • .golangci.yml
  • pkg/handlers/openapi.go
  • test/integration/clusters_test.go
  • pkg/api/node_pool_types.go
  • pkg/api/cluster_types.go
  • pkg/auth/helpers.go
  • Dockerfile.openapi
  • pkg/db/db_session/testcontainer.go
  • .bingo/golangci-lint.mod
  • openapitools.json
  • pkg/services/node_pool.go
  • pkg/api/adapter_status_types.go
  • Makefile
  • .bingo/Variables.mk
  • pkg/services/types.go
  • pkg/api/presenters/slice_filter.go
  • pkg/client/ocm/client.go
  • test/mocks/jwk_cert_server.go
  • cmd/hyperfleet-api/environments/framework_test.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/server/logging/writer.go
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • test/helper.go
  • pkg/handlers/node_pool.go
  • pkg/services/generic_test.go
  • pkg/handlers/cluster.go
  • cmd/hyperfleet-api/main.go
  • pkg/auth/context.go
  • pkg/db/db_session/test.go
  • pkg/handlers/nodepool_status.go
  • test/mocks/ocm.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/db/sql_helpers.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/test.go
  • pkg/db/sql_helpers.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • pkg/handlers/node_pool.go
  • pkg/services/generic_test.go
  • pkg/handlers/cluster.go
  • pkg/auth/context.go
  • pkg/db/db_session/test.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
  • pkg/db/sql_helpers.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/handlers/framework.go
  • pkg/handlers/node_pool.go
  • pkg/services/generic_test.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/framework.go
  • pkg/handlers/node_pool.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/handlers/framework.go
  • pkg/handlers/node_pool.go
  • pkg/services/generic_test.go
  • pkg/handlers/cluster.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/**/*.go : Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/auth/context.go
  • cmd/hyperfleet-api/server/routes.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/handlers/**/*.go : Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Applied to files:

  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/handlers/node_pool.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
  • go.mod
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Increment generation field on each spec update to enable optimistic concurrency control

Applied to files:

  • pkg/handlers/cluster.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Use context.Context as the first parameter in all DAO methods for transaction propagation

Applied to files:

  • pkg/auth/context.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/services/**/*.go : Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • pkg/handlers/nodepool_status.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • go.mod
🧬 Code graph analysis (2)
pkg/handlers/node_pool.go (1)
pkg/errors/errors.go (1)
  • GeneralError (164-166)
cmd/hyperfleet-api/server/api_server.go (2)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
🔇 Additional comments (25)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)

14-14: Simplified logging condition keeps behavior and improves clarity

Using doLog := path != "/api/hyperfleet" preserves the existing behavior (only that endpoint is skipped) while making the intent clearer and removing redundant branching. No issues spotted.

go.mod (3)

3-5: Verify Go 1.24.0 compatibility with all dependencies.

The Go version is being updated to 1.24.0 (with toolchain 1.24.9), which is quite recent. The PR notes mention "unexpected Go version (go1.24)" warnings during the build. Before merging, ensure that all direct and indirect dependencies are compatible with these Go versions and that the version bump is intentional.


12-12: LGTM!

Adding github.com/docker/go-connections v0.6.0 as a direct dependency is appropriate given the integration test updates that use Testcontainers for PostgreSQL (as indicated in the PR objectives and learnings).


33-33: LGTM!

Adding go.uber.org/mock v0.6.0 as a direct dependency aligns with the test mock updates mentioned in the AI summary and supports the shift from active authz initialization to mocking in tests.

test/mocks/ocm.go (1)

62-65: LGTM! Correct receiver type for mutating method.

Changing Reset to use a pointer receiver ensures the method actually modifies the mock's state. With a value receiver, Reset would have operated on a copy, leaving the original Action and ResourceType fields unchanged. This change aligns with the pointer receivers used by SelfAccessReview and AccessReview, maintaining consistency across the mock.

pkg/handlers/cluster.go (1)

72-76: LGTM! Defensive error handling for spec marshaling.

The addition of explicit error handling for json.Marshal is good defensive programming. Using errors.GeneralError() follows the project's structured error pattern.

pkg/handlers/node_pool.go (1)

73-77: LGTM! Consistent defensive error handling.

The addition of explicit error handling for json.Marshal matches the pattern in cluster.go and provides appropriate defensive programming. The structured error response aligns with project conventions.

pkg/auth/context.go (2)

40-49: Safe username extraction from context looks good

Guarding the type assertion with if str, ok := username.(string); ok removes the panic risk from unexpected context values while preserving the empty-string fallback. No further changes needed here.


80-96: Defensive JWT claim extraction is correct

Using guarded string assertions for all primary claims (username, first_name, last_name, email, clientId) is a solid hardening step: it avoids panics on malformed tokens and cleanly integrates with the existing “empty means try fallbacks” logic.

pkg/services/generic.go (3)

45-58: listContext remains a cohesive LIST pipeline container

The expanded listContext still cleanly centralizes all state needed across LIST builders (context, paging, logging, joins, etc.); the updated comment accurately reflects that role and doesn’t introduce any behavioral change.


153-188: buildSearchValues correctly adapts to the new treeWalkForSqlizer signature

The updated call to treeWalkForSqlizer and subsequent error handling preserve the previous control flow: on error you return early with the ServiceError, and on success sqlizer.ToSql() is invoked exactly as before. There’s no new path where a nil sqlizer is used without an accompanying error.


334-348: treeWalkForSqlizer’s simplified return type is correct and keeps error semantics intact

The refactored treeWalkForSqlizer:

  • Validates field names via db.FieldNameWalk using *listCtx.disallowedFields and returns the ServiceError unchanged.
  • Converts the resulting tree with sqlFilter.Walk and wraps failures in errors.BadRequest.

This matches the previous behavior while simplifying the API to return only the squirrel.Sqlizer plus *errors.ServiceError, and continues to use the structured ServiceError type as required for services. As per coding guidelines, this keeps error propagation consistent in pkg/services.

pkg/services/generic_test.go (2)

20-26: Deferred dbFactory.Close improves test resource cleanup

Adding defer dbFactory.Close() //nolint:errcheck ensures the mock session factory is closed after the test run without complicating assertions, which is appropriate in this test context.


62-72: Tests correctly updated for the new treeWalkForSqlizer return values

The test now consumes (sqlizer, serviceErr) directly from treeWalkForSqlizer, aligning with the new signature while preserving the existing expectations on generated SQL and parameters. This keeps coverage for the happy-path translation intact.

cmd/hyperfleet-api/server/api_server.go (1)

149-151: LGTM!

Explicit error handling during shutdown is good practice. Logging the Close error ensures visibility into potential connection pool cleanup issues.

pkg/db/db_session/test.go (1)

217-219: LGTM!

The panic-on-failure approach ensures tests cannot continue with an inconsistent database state. This properly addresses the previous review concern about silently ignoring reset errors.

pkg/db/db_session/default.go (1)

111-113: LGTM!

Adding error handling for the Ping call prevents silently ignoring connection issues in the background goroutine. The V(10) log level is appropriate for verbose diagnostics.

pkg/handlers/framework.go (1)

109-110: LGTM!

Switching on the serviceErr value instead of using boolean cases within a valueless switch is cleaner and more idiomatic.

cmd/hyperfleet-api/main.go (1)

28-30: LGTM!

Explicit error handling for flag parsing prevents silent failures during initialization. The fatal log ensures visibility if parsing fails.

pkg/db/sql_helpers.go (3)

28-29: LGTM!

The type assertion with error checking prevents a potential panic if l.Left is not a string. The short-circuit evaluation ensures leftStr is only used when the type assertion succeeds.


83-87: LGTM!

The corrected variable name (propertyName) and tightened length validation (<= 11) prevent generating SQL with an empty property accessor like properties ->> ''.


145-150: LGTM!

Type assertion validation before the recursive call prevents a runtime panic if n.Left is not a tsl.Node, and returns a clear error to the caller instead.

test/helper.go (3)

181-183: LGTM!

Logging a warning on stop failure allows the restart to proceed while still surfacing the issue. This is appropriate for test harness robustness.


204-206: LGTM!

Fatal logging on AddFlags failure ensures test initialization errors are visible and block execution, preventing tests from running with incomplete configuration.


441-444: LGTM!

Documenting why the deprecated function is still used and suppressing the staticcheck warning with //nolint:staticcheck is appropriate for test fixtures with legacy key formats.

@yasun1
Copy link
Contributor

yasun1 commented Dec 5, 2025

/lgtm

@openshift-ci
Copy link

openshift-ci bot commented Dec 5, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: yasun1, yingzhanredhat

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [yasun1,yingzhanredhat]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants