Skip to content

Conversation

@yasun1
Copy link
Contributor

@yasun1 yasun1 commented Jan 7, 2026

Observability upgrade: migrates from glog to slog structured logging, adds OpenTelemetry distributed tracing, and implements data masking for sensitive data protection.

What's New

Structured Logging (slog)

  • Migrated from custom glog to Go stdlib slog
  • Automatic context propagation: operation_id, trace_id, span_id
  • JSON/text output with auto-injected metadata fields
  • Lock-free atomic operations for better performance

OpenTelemetry Tracing

  • W3C trace context extraction and propagation
  • Configurable sampling rate (stdout exporter, upgrade path to OTLP)
  • Automatic trace correlation across logs

Data Masking

  • HTTP header masking (Authorization, API keys)
  • JSON body field masking (password, token, secret)
  • Regex fallback for emails, credit cards, tokens
  • Configurable sensitive field lists

Documentation & Testing

  • 509 lines comprehensive logging guide (docs/logging.md)
  • 690+ lines new tests (masking middleware, logger refactor)
  • OCM SDK backward compatibility bridge

Summary

@coderabbitai summary

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Walkthrough

Replaces legacy glog logging with a centralized slog-based logger and adds a LoggingConfig to ApplicationConfig (flags + env binding). Introduces pkg/logger (global init, HyperFleetHandler, OCM bridge, HTTP attrs, context keys), pkg/telemetry (InitTraceProvider, Shutdown), and middleware: masking and OTel instrumentation. CLI/server startup now initializes logging, optionally initializes OpenTelemetry with deferred shutdown, and starts API, Metrics, and Health servers. Request logging was reworked to accept a masking middleware, capture response status/duration, and emit structured logs. Multiple files updated to use context-aware logging; legacy HTTP log formatter/writer types removed.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CLI as CLI (serve/migrate)
  participant Config as Config Loader
  participant Logger as pkg/logger
  participant Telemetry as pkg/telemetry
  participant Router as API Router
  participant Middleware as pkg/middleware
  participant Server as Servers (API/Metrics/Health)

  CLI->>Config: parse flags & bind env (incl. Logging)
  CLI->>Logger: initLogger(cfg.Logging)
  Logger-->>CLI: global logger ready
  alt OTel enabled
    CLI->>Telemetry: InitTraceProvider(service, version, samplingRate)
    Telemetry-->>CLI: TracerProvider (deferred Shutdown)
  end
  CLI->>Router: register middleware (OTel?, Masking, RequestLogging)
  Server->>Middleware: incoming HTTP request
  alt OTel enabled
    Middleware->>Telemetry: otelhttp -> start span
    Telemetry-->>Middleware: span context (trace_id/span_id)
    Middleware->>Logger: inject trace/span IDs into context
  end
  Middleware->>Middleware: mask headers/body (if enabled)
  Middleware->>Logger: emit structured request log
  Middleware->>Server: forward to handler
  Server->>Logger: handler logs enriched with context fields
  Middleware->>Logger: emit structured response log (status, duration)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

lgtm, approved

Suggested reviewers

  • aredenba-rh
  • rh-amarin
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 describes the main change: introducing a slog-based logger aligned with the logging specification. It accurately reflects the primary objective evident across the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b4c857 and 3c0994d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (54)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/auth/helpers.go
  • pkg/config/config.go
  • pkg/config/logging.go
  • pkg/config/logging_test.go
  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • pkg/errors/errors.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/http.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/logger/operationid_middleware.go
  • pkg/middleware/masking.go
  • pkg/middleware/masking_test.go
  • pkg/middleware/otel.go
  • pkg/middleware/schema_validation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go

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

@openshift-ci
Copy link

openshift-ci bot commented Jan 7, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign yasun1 for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

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

🤖 Fix all issues with AI agents
In @pkg/config/logging_test.go:
- Around line 309-344: Rename the test function to reflect observed behavior:
change TestLoggingConfig_FlagsPrecedenceOverEnv to
TestLoggingConfig_EnvPrecedenceOverFlags (or a similar name indicating env
overrides flags), and update any test comments to match; leave the test body and
assertions as-is (they correctly verify that cfg.BindEnv() causes the LOG_LEVEL
env var to override the parsed flag), ensuring the new function name is unique
and the test still runs.
🧹 Nitpick comments (5)
cmd/hyperfleet-api/servecmd/cmd.go (1)

37-49: Consider using ApplicationConfig.Logging instead of hardcoded values.

The logger is initialized with hardcoded configuration while ApplicationConfig now includes a Logging field that supports CLI flags and environment variables. Even for demonstration purposes, wiring this properly would validate the configuration integration.

// Example integration approach:
cfg := environments.Environment().Config
cfg.Logging.BindEnv()
level, _ := logger.ParseLogLevel(cfg.Logging.Level)
format, _ := logger.ParseLogFormat(cfg.Logging.Format)
output, _ := logger.ParseLogOutput(cfg.Logging.Output)
logConfig := &logger.LogConfig{
    Level:     level,
    Format:    format,
    Output:    output,
    Component: "api",
    Version:   "dev",
    Hostname:  hostname,
}
pkg/config/logging.go (1)

64-101: Consider validating SamplingRate bounds.

OTEL_SAMPLING_RATE accepts any float value, but sampling rates are typically constrained to [0.0, 1.0]. Invalid values like -1.0 or 2.0 would silently become the configured rate.

🔎 Proposed fix
 	if val := os.Getenv("OTEL_SAMPLING_RATE"); val != "" {
 		rate, err := strconv.ParseFloat(val, 64)
-		if err == nil {
+		if err == nil && rate >= 0.0 && rate <= 1.0 {
 			l.OTel.SamplingRate = rate
 		}
 	}
pkg/logger/context.go (1)

64-100: Inconsistent getter return signatures.

GetOperationID returns only string, while all other getters (GetTraceID, GetSpanID, etc.) return (string, bool). Consider aligning the signatures for API consistency.

🔎 Proposed fix
 // GetOperationID retrieves operation ID from context
-func GetOperationID(ctx context.Context) string {
-	if opID, ok := ctx.Value(OpIDKey).(string); ok {
-		return opID
-	}
-	return ""
+func GetOperationID(ctx context.Context) (string, bool) {
+	opID, ok := ctx.Value(OpIDKey).(string)
+	return opID, ok
 }
pkg/logger/logger.go (2)

45-81: Type assertion may panic if slog passes unexpected Level type.

Line 56 uses a direct type assertion a.Value.Any().(slog.Level) which will panic if the value isn't a slog.Level. While slog should always pass a valid level, defensive coding would use the comma-ok idiom.

🔎 Proposed fix
 		if a.Key == slog.LevelKey {
-			level := a.Value.Any().(slog.Level)
-			return slog.Attr{Key: "level", Value: slog.StringValue(strings.ToLower(level.String()))}
+			if level, ok := a.Value.Any().(slog.Level); ok {
+				return slog.Attr{Key: "level", Value: slog.StringValue(strings.ToLower(level.String()))}
+			}
+			return a
 		}

165-180: Consider thread-safe initialization for globalLogger.

InitGlobalLogger writes to globalLogger and GetLogger reads from it without synchronization. If called from multiple goroutines during startup, this could cause a data race.

🔎 Proposed fix using sync.Once
+import "sync"
+
+var (
+	globalLogger *slog.Logger
+	initOnce     sync.Once
+)
+
 // InitGlobalLogger initializes the global logger with the given configuration
 func InitGlobalLogger(cfg *LogConfig) {
-	handler := NewHyperFleetHandler(cfg)
-	globalLogger = slog.New(handler)
-	slog.SetDefault(globalLogger)
+	initOnce.Do(func() {
+		handler := NewHyperFleetHandler(cfg)
+		globalLogger = slog.New(handler)
+		slog.SetDefault(globalLogger)
+	})
 }

Alternatively, if re-initialization is needed (e.g., for testing), use an atomic pointer or mutex.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 460f0ae and 7d2c017.

📒 Files selected for processing (8)
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/config/config.go
  • pkg/config/logging.go
  • pkg/config/logging_test.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/operationid_middleware.go
💤 Files with no reviewable changes (1)
  • pkg/logger/operationid_middleware.go
🧰 Additional context used
📓 Path-based instructions (1)
pkg/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Files:

  • pkg/config/config.go
  • pkg/config/logging_test.go
  • pkg/config/logging.go
  • pkg/logger/context.go
  • pkg/logger/logger_test.go
  • pkg/logger/logger.go
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Applied to files:

  • pkg/config/config.go
  • pkg/config/logging_test.go
  • pkg/config/logging.go
  • pkg/logger/context.go
  • pkg/logger/logger_test.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/logger/logger.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use environment-based configuration via OCM_ENV to select development, unit_testing, integration_testing, or production settings

Applied to files:

  • pkg/config/logging_test.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs

Applied to files:

  • pkg/logger/context.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{dao,services}/**/*.go : Use context.Context as the first parameter in all DAO and service method signatures

Applied to files:

  • pkg/logger/context.go
🧬 Code graph analysis (3)
pkg/config/config.go (1)
pkg/config/logging.go (2)
  • LoggingConfig (12-19)
  • NewLoggingConfig (35-50)
pkg/config/logging_test.go (1)
pkg/config/logging.go (2)
  • NewLoggingConfig (35-50)
  • LoggingConfig (12-19)
cmd/hyperfleet-api/servecmd/cmd.go (1)
pkg/logger/logger.go (4)
  • LogConfig (27-34)
  • FormatJSON (23-23)
  • InitGlobalLogger (168-172)
  • Info (226-228)
🔇 Additional comments (25)
pkg/config/config.go (1)

21-21: LGTM! Logging configuration integration follows established patterns.

The Logging field is consistently wired through NewApplicationConfig(), AddFlags(), and ReadFiles(), matching the existing convention for other configuration subsystems.

Also applies to: 31-31, 42-42, 55-55

pkg/config/logging_test.go (5)

1-42: LGTM! Well-structured default value tests.

The table-driven approach is clean, and the validation of both exact values and non-empty string constraints for sensitive headers/fields is appropriate.


44-99: LGTM! Flag registration and parsing tests are thorough.

Good coverage for flag existence checks and value parsing verification.


101-107: LGTM! ReadFiles stub test is appropriate.

Confirms the no-op behavior as expected.


109-221: LGTM! Comprehensive BindEnv tests with proper isolation.

Good coverage of basic logging vars, OTel settings, masking settings, and invalid value handling. The save/restore pattern ensures test isolation.


223-307: LGTM! Parsing utility tests cover key edge cases.

Whitespace trimming and empty string handling are properly validated.

pkg/logger/logger_test.go (7)

13-51: LGTM! ParseLogLevel tests cover all valid levels and edge cases.

Good coverage including case insensitivity, whitespace handling, and the "warning" alias.


53-123: LGTM! ParseLogFormat and ParseLogOutput tests are comprehensive.

Proper validation of valid inputs, invalid inputs, and the empty-defaults-to-stdout behavior.


125-193: LGTM! JSON and Text format output tests verify expected fields.

Good validation of message, level, component, version, hostname, and custom key-value pairs.


195-304: LGTM! Context field extraction tests are thorough.

Both individual and combined context field tests provide good coverage for trace_id, span_id, request_id, cluster_id, resource_type, and resource_id.


306-369: LGTM! Stack trace tests validate error-level behavior correctly.

Verifies stack traces appear only for error-level logs and validates the array structure.


371-427: LGTM! Log level filtering and GetLogger uninitialized tests are appropriate.

Good coverage of filtering behavior and fallback to slog.Default().


429-572: LGTM! Convenience functions and formatted variants tests are comprehensive.

Good coverage of Debug, Warn, and formatted functions (Debugf, Infof, Warnf, Errorf) with proper validation of level, message, error field, and stack trace expectations.

pkg/config/logging.go (3)

11-50: LGTM! Well-structured configuration with sensible defaults.

The LoggingConfig, OTelConfig, and MaskingConfig structs are clean. Default sensitive headers and fields cover common security-sensitive values.


52-62: LGTM! AddFlags and ReadFiles follow established patterns.

CLI flags are properly registered with descriptive help text.


103-125: LGTM! Parsing utilities correctly handle whitespace and empty input.

The implementations are clean and match the test expectations.

pkg/logger/context.go (2)

9-26: LGTM! Type-safe context keys provide good API safety.

Using distinct types for each key prevents accidental key collisions and improves code clarity.


28-62: LGTM! Context setters are clean and consistent.

All With* functions follow the same pattern. WithOpID correctly avoids regenerating an existing operation ID.

pkg/logger/logger.go (7)

16-34: LGTM! LogFormat and LogConfig provide clean configuration structure.

The enum-based format and structured config enable type-safe logger initialization.


83-117: LGTM! Handler correctly extracts context fields and adds error metadata.

Context field injection and error-level stack trace capture are well-implemented. The handler properly delegates to the underlying JSON/Text handler.


119-142: LGTM! Handler interface methods correctly preserve HyperFleet metadata.

WithAttrs and WithGroup properly propagate component, version, and hostname to child handlers.


144-163: LGTM! Stack trace capture filters runtime and testing frames appropriately.

The implementation provides useful stack traces for debugging without internal framework noise.


182-220: LGTM! Parsing utilities are robust with clear error messages.

Good handling of case insensitivity, whitespace trimming, and informative error messages listing valid options.


222-252: LGTM! Context-aware logging convenience functions follow slog patterns.

Both structured (Debug, Info, Warn, Error) and formatted (Debugf, Infof, Warnf, Errorf) variants are properly implemented.


254-370: LGTM! Legacy OCMLogger preserved with clear deprecation notices.

The backward-compatible glog-based logger is properly maintained with deprecation warnings, enabling incremental migration. Based on learnings, the structured logging with Extra() method for adding context fields is preserved.

Add slog logger with config, context & testing

update
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

🤖 Fix all issues with AI agents
In @pkg/logger/context.go:
- Around line 19-20: OpIDHeader is incorrectly typed as OperationIDKey even
though it represents an HTTP header name; change its type to string (e.g.,
declare OpIDHeader as a plain string constant with value "X-Operation-ID") while
leaving OpIDKey as OperationIDKey, and update any usages expecting a string
header name (references to OpIDHeader) to use the new string type.
- Line 6: Update the ksuid dependency to v1.0.4: change the import reference in
pkg/logger/context.go remains the same but run `go get
github.com/segmentio/ksuid@v1.0.4` (or edit go.mod to set
github.com/segmentio/ksuid v1.0.4), then run `go mod tidy` and recompile/tests
to ensure no regressions; verify any uses of ksuid (e.g., calls in context.go
that generate IDs) still work against the updated API and adjust call sites if
the v1.0.4 API differs.

In @pkg/logger/logger.go:
- Around line 110-114: The code is adding a redundant "error" attribute by
calling r.AddAttrs(slog.String("error", r.Message)) even though slog already
records r.Message as "message"; remove that r.AddAttrs line so only the stack
trace is added for error-level records (leave captureStackTrace(4) and the
r.AddAttrs(slog.Any("stack_trace", stackTrace)) intact), ensuring no duplicate
"error" field is emitted.
🧹 Nitpick comments (2)
pkg/config/logging.go (1)

103-125: Consider filtering empty strings in list parsing.

The GetSensitiveHeadersList and GetSensitiveFieldsList functions trim whitespace but don't filter out empty strings. This means input like "Authorization,,Cookie" would produce ["Authorization", "", "Cookie"], which could cause issues when matching headers/fields.

🔎 Proposed enhancement
 func (l *LoggingConfig) GetSensitiveHeadersList() []string {
 	if l.Masking.SensitiveHeaders == "" {
 		return []string{}
 	}
 	headers := strings.Split(l.Masking.SensitiveHeaders, ",")
+	result := make([]string, 0, len(headers))
 	for i := range headers {
-		headers[i] = strings.TrimSpace(headers[i])
+		trimmed := strings.TrimSpace(headers[i])
+		if trimmed != "" {
+			result = append(result, trimmed)
+		}
 	}
-	return headers
+	return result
 }
 
 func (l *LoggingConfig) GetSensitiveFieldsList() []string {
 	if l.Masking.SensitiveFields == "" {
 		return []string{}
 	}
 	fields := strings.Split(l.Masking.SensitiveFields, ",")
+	result := make([]string, 0, len(fields))
 	for i := range fields {
-		fields[i] = strings.TrimSpace(fields[i])
+		trimmed := strings.TrimSpace(fields[i])
+		if trimmed != "" {
+			result = append(result, trimmed)
+		}
 	}
-	return fields
+	return result
 }
pkg/logger/logger.go (1)

91-108: Use context helper functions for consistency.

The code uses raw type assertions ctx.Value(TraceIDCtxKey).(string) to extract context values, but dedicated helper functions (GetTraceID, GetSpanID, GetClusterID, etc.) are defined in pkg/logger/context.go. Using the helpers improves consistency, maintainability, and ensures the same extraction logic is used throughout the codebase.

🔎 Proposed refactor
-	if traceID, ok := ctx.Value(TraceIDCtxKey).(string); ok && traceID != "" {
+	if traceID, ok := GetTraceID(ctx); ok && traceID != "" {
 		r.AddAttrs(slog.String("trace_id", traceID))
 	}
-	if spanID, ok := ctx.Value(SpanIDCtxKey).(string); ok && spanID != "" {
+	if spanID, ok := GetSpanID(ctx); ok && spanID != "" {
 		r.AddAttrs(slog.String("span_id", spanID))
 	}
-	if requestID, ok := ctx.Value(OpIDKey).(string); ok && requestID != "" {
+	if requestID, ok := GetOperationID(ctx); ok && requestID != "" {
 		r.AddAttrs(slog.String("request_id", requestID))
 	}
-	if clusterID, ok := ctx.Value(ClusterIDCtxKey).(string); ok && clusterID != "" {
+	if clusterID, ok := GetClusterID(ctx); ok && clusterID != "" {
 		r.AddAttrs(slog.String("cluster_id", clusterID))
 	}
-	if resourceType, ok := ctx.Value(ResourceTypeCtxKey).(string); ok && resourceType != "" {
+	if resourceType, ok := GetResourceType(ctx); ok && resourceType != "" {
 		r.AddAttrs(slog.String("resource_type", resourceType))
 	}
-	if resourceID, ok := ctx.Value(ResourceIDCtxKey).(string); ok && resourceID != "" {
+	if resourceID, ok := GetResourceID(ctx); ok && resourceID != "" {
 		r.AddAttrs(slog.String("resource_id", resourceID))
 	}

Note: This refactor assumes GetOperationID is updated to return (string, bool) as suggested in the review of pkg/logger/context.go.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d2c017 and 9815764.

📒 Files selected for processing (8)
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/config/config.go
  • pkg/config/logging.go
  • pkg/config/logging_test.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/operationid_middleware.go
💤 Files with no reviewable changes (1)
  • pkg/logger/operationid_middleware.go
🧰 Additional context used
📓 Path-based instructions (1)
pkg/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Files:

  • pkg/config/logging_test.go
  • pkg/logger/logger_test.go
  • pkg/config/config.go
  • pkg/logger/context.go
  • pkg/config/logging.go
  • pkg/logger/logger.go
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use environment-based configuration via OCM_ENV to select development, unit_testing, integration_testing, or production settings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Applied to files:

  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/config/logging_test.go
  • pkg/logger/logger_test.go
  • pkg/config/config.go
  • pkg/logger/context.go
  • pkg/config/logging.go
  • pkg/logger/logger.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use environment-based configuration via OCM_ENV to select development, unit_testing, integration_testing, or production settings

Applied to files:

  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/config/logging_test.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs

Applied to files:

  • pkg/logger/context.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{dao,services}/**/*.go : Use context.Context as the first parameter in all DAO and service method signatures

Applied to files:

  • pkg/logger/context.go
🧬 Code graph analysis (5)
cmd/hyperfleet-api/servecmd/cmd.go (1)
pkg/logger/logger.go (4)
  • LogConfig (27-34)
  • FormatJSON (23-23)
  • InitGlobalLogger (168-172)
  • Info (226-228)
pkg/config/logging_test.go (1)
pkg/config/logging.go (2)
  • NewLoggingConfig (35-50)
  • LoggingConfig (12-19)
pkg/logger/logger_test.go (2)
pkg/logger/logger.go (14)
  • ParseLogLevel (183-196)
  • Error (234-236)
  • Errorf (250-252)
  • LogFormat (17-17)
  • FormatText (21-21)
  • FormatJSON (23-23)
  • ParseLogFormat (199-208)
  • ParseLogOutput (211-220)
  • LogConfig (27-34)
  • InitGlobalLogger (168-172)
  • Info (226-228)
  • Debug (222-224)
  • GetLogger (175-180)
  • Warn (230-232)
pkg/logger/context.go (6)
  • WithTraceID (29-31)
  • WithSpanID (34-36)
  • OpIDKey (19-19)
  • WithClusterID (39-41)
  • WithResourceType (44-46)
  • WithResourceID (49-51)
pkg/config/config.go (1)
pkg/config/logging.go (2)
  • LoggingConfig (12-19)
  • NewLoggingConfig (35-50)
pkg/logger/logger.go (1)
pkg/logger/context.go (6)
  • TraceIDCtxKey (21-21)
  • SpanIDCtxKey (22-22)
  • OpIDKey (19-19)
  • ClusterIDCtxKey (23-23)
  • ResourceTypeCtxKey (24-24)
  • ResourceIDCtxKey (25-25)
🔇 Additional comments (14)
pkg/config/config.go (1)

21-21: LGTM! Logging configuration properly integrated.

The Logging field is correctly added to ApplicationConfig and properly wired through the constructor, flag registration, and file reading. The integration follows the same pattern as other configuration sections.

Also applies to: 31-31, 42-42, 55-55

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

37-49: Verify integration with LoggingConfig in the next PR.

The logger is initialized with hardcoded values instead of using environments.Environment().ApplicationConfig().Logging, which was added in this PR. While the comment indicates this is "demonstration only" with "full migration in PR 3", please confirm that the next PR will properly integrate the logging configuration, including:

  • Reading Level, Format, and Output from config
  • Using application version instead of hardcoded "dev"
  • Potentially making Component configurable
🔎 Example integration with config
-	// Initialize slog logger (demonstration only, full migration in PR 3)
 	ctx := context.Background()
+	env := environments.Environment()
+	loggingCfg := env.ApplicationConfig().Logging
+	
+	logLevel, err := logger.ParseLogLevel(loggingCfg.Level)
+	if err != nil {
+		glog.Fatalf("Invalid log level: %s", err.Error())
+	}
+	
+	logFormat, err := logger.ParseLogFormat(loggingCfg.Format)
+	if err != nil {
+		glog.Fatalf("Invalid log format: %s", err.Error())
+	}
+	
+	logOutput, err := logger.ParseLogOutput(loggingCfg.Output)
+	if err != nil {
+		glog.Fatalf("Invalid log output: %s", err.Error())
+	}
+	
 	hostname, _ := os.Hostname()
 	logConfig := &logger.LogConfig{
-		Level:     slog.LevelInfo,
-		Format:    logger.FormatJSON,
-		Output:    os.Stdout,
+		Level:     logLevel,
+		Format:    logFormat,
+		Output:    logOutput,
 		Component: "api",
-		Version:   "dev",
+		Version:   env.Name(),
 		Hostname:  hostname,
 	}
 	logger.InitGlobalLogger(logConfig)
-	logger.Info(ctx, "New slog logger initialized (example)", "log_level", "info", "log_format", "json")
+	logger.Info(ctx, "Logger initialized", "log_level", loggingCfg.Level, "log_format", loggingCfg.Format)
pkg/config/logging_test.go (1)

10-348: LGTM! Comprehensive test coverage for logging configuration.

The test suite thoroughly covers:

  • Default values
  • CLI flag registration and parsing
  • Environment variable binding and precedence
  • Sensitive headers/fields list parsing
  • Error handling for invalid values

The test name TestLoggingConfig_EnvOverridesFlags (line 311) now accurately reflects the behavior being tested, addressing the previous review feedback.

pkg/logger/logger_test.go (1)

1-572: LGTM! Excellent test coverage for the logger package.

The test suite is comprehensive and well-structured, covering:

  • Parsing functions (level, format, output) with error cases
  • JSON and text format output validation
  • Context field extraction (trace_id, span_id, request_id, cluster_id, resource_type, resource_id)
  • Stack trace behavior at different log levels
  • Log level filtering
  • Uninitialized logger fallback behavior
  • All convenience functions and their formatted variants

The tests properly validate structured logging behavior and ensure the new slog-based logger works correctly.

pkg/config/logging.go (2)

11-50: LGTM! Well-structured logging configuration with sensible defaults.

The configuration structs are well-organized:

  • Core logging settings (Level, Format, Output)
  • OpenTelemetry configuration with sampling
  • Masking configuration with predefined sensitive fields

Default values are production-ready:

  • info level prevents debug noise
  • json format for structured logging
  • Masking enabled by default with comprehensive sensitive field lists

52-101: LGTM! Clean separation between CLI flags and environment configuration.

The implementation makes a good design choice:

  • CLI flags expose only core logging settings (level, format, output) for common use cases
  • Advanced settings (OTel, Masking) are environment-only, keeping the CLI interface simple
  • BindEnv silently ignores parse errors for booleans/floats, maintaining default values on invalid input (tested behavior in logging_test.go lines 170-190)

This design balances usability with configurability.

pkg/logger/logger.go (7)

16-34: Well-structured logging configuration.

The LogFormat enumeration and LogConfig struct provide a clean, extensible configuration surface for the logger with clear documentation.


45-81: Solid handler implementation with proper customization.

The NewHyperFleetHandler function correctly implements the slog handler pattern with appropriate field renaming (timestamp, level, message) and format selection (JSON vs. Text).


167-180: Clean global logger initialization pattern.

The global logger initialization and retrieval functions follow standard Go patterns with a sensible fallback to slog.Default() when not initialized.


182-220: Robust parsing helpers with clear error messages.

The ParseLogLevel, ParseLogFormat, and ParseLogOutput functions provide clear error messages listing valid options, which improves developer experience.


222-252: Convenient context-aware logging API.

The top-level convenience functions (Debug, Info, Warn, Error and their formatted variants) provide an ergonomic API for context-aware logging while maintaining the slog foundation.

Based on learnings, this aligns with the guidance to use structured logging via pkg/logger/logger.go.


254-288: Deprecation notice is clear.

The legacy OCMLogger interface and NewOCMLogger constructor are clearly marked as deprecated with a specific removal timeline (PR 3), which helps with migration planning.


144-163: Add validation that skip=4 correctly identifies the actual caller.

The captureStackTrace function is called with skip=4 from Handle (line 111). The existing TestStackTrace test validates that stack traces are captured for error-level logs, but it only checks for the presence and structure of the stack trace array—it does not verify that skip=4 actually captures the caller's frame. To ensure the skip value is correct, add an assertion in the test that verifies the captured frames contain the expected caller (e.g., from the test function itself), not internal logger frames.

pkg/logger/context.go (1)

64-70: Make GetOperationID consistent with other getters.

GetOperationID returns only a string, while all other Get* functions return (string, bool). This inconsistency makes it impossible for callers to distinguish between "not present in context" and "present but empty string".

🔎 Proposed refactor
 // GetOperationID retrieves operation ID from context
-func GetOperationID(ctx context.Context) string {
+func GetOperationID(ctx context.Context) (string, bool) {
 	if opID, ok := ctx.Value(OpIDKey).(string); ok {
-		return opID
+		return opID, ok
 	}
-	return ""
+	return "", false
 }

Note: This is a breaking change. Verify and update all call sites across the codebase.

@yasun1 yasun1 changed the title feat(logger): implement slog-based logger foundation HYPERFLEET-363 | feat(logger): implement slog-based logger foundation Jan 7, 2026
@yasun1 yasun1 changed the title HYPERFLEET-363 | feat(logger): implement slog-based logger foundation [WIP] HYPERFLEET-363 | feat(logger): implement slog-based logger foundation Jan 7, 2026
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

🤖 Fix all issues with AI agents
In @pkg/middleware/otel.go:
- Around line 27-44: The middleware currently extracts span via
trace.SpanFromContext(r.Context()) before calling otelHandler.ServeHTTP, so your
WithTraceID/WithSpanID enrichments are overwritten by otelHandler's span; move
the extraction to after otelHandler has created the request span: call
otelHandler.ServeHTTP first (or wrap it so you get the request context it
establishes), then use trace.SpanFromContext on the resulting request context to
get TraceID/SpanID and call logger.WithTraceID/logger.WithSpanID on that context
before passing to downstream handlers (i.e., ensure enrichment reads span from
the context provided by otelHandler rather than the original r.Context()).

In @pkg/telemetry/otel.go:
- Line 10: Update the OpenTelemetry semantic conventions import from
"go.opentelemetry.io/otel/semconv/v1.4.0" to
"go.opentelemetry.io/otel/semconv/v1.11.0" (keep the semconv alias), and replace
any direct logging calls in this file with the structured logger from
pkg/logger/logger.go by calling the logger instance's Extra() method to attach
context fields; find usages in this file (e.g., any log.*, fmt.Println, or local
loggers) and convert them to logger.Extra(...).Error/Info/etc. with the same
message and context fields.
🧹 Nitpick comments (4)
pkg/middleware/masking_test.go (1)

253-306: Consider using reflect.DeepEqual for simpler comparison.

The custom deepEqual and deepEqualSlice functions work correctly, but reflect.DeepEqual from the standard library handles nested maps and slices out of the box, reducing code to maintain.

Simplified approach
import "reflect"

// In tests, replace:
if !deepEqual(resultMap, expectedMap) {
// with:
if !reflect.DeepEqual(resultMap, expectedMap) {
pkg/middleware/masking.go (2)

35-44: Consider cloning header values to avoid shared slice references.

Line 40 assigns the original values slice reference to masked[key]. If the original headers slice is modified elsewhere, masked would reflect those changes. While unlikely in practice, a defensive copy ensures true isolation.

Defensive copy
 		} else {
-			masked[key] = values
+			masked[key] = append([]string(nil), values...)
 		}

103-112: Substring matching is intentional but worth documenting in API.

The isSensitiveField uses substring matching (e.g., "password" matches "user_password"). This is a good security default but could cause over-masking. The tests confirm this is intentional. Consider adding a brief note in the exported MaskBody doc comment about substring-based field matching.

go.mod (1)

119-126: Align OpenTelemetry exporter version for consistency.

Line 122 pins otlptracehttp to v1.38.0 while other OpenTelemetry packages are at v1.39.0. Since v1.39.0 is available for otlptracehttp, consider upgrading to align dependency versions.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9815764 and 473fa4f.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • cmd/hyperfleet-api/server/routes.go
  • go.mod
  • pkg/middleware/masking.go
  • pkg/middleware/masking_test.go
  • pkg/middleware/otel.go
  • pkg/telemetry/otel.go
🧰 Additional context used
📓 Path-based instructions (1)
pkg/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Files:

  • pkg/telemetry/otel.go
  • pkg/middleware/masking_test.go
  • pkg/middleware/otel.go
  • pkg/middleware/masking.go
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use environment-based configuration via OCM_ENV to select development, unit_testing, integration_testing, or production settings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
  • pkg/middleware/otel.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/transaction_middleware.go : Store database sessions in HTTP request context via middleware at request entry point

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
  • pkg/middleware/otel.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Applied to files:

  • cmd/hyperfleet-api/server/routes.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/server/**/*.go : Validate JWT tokens from Red Hat SSO in middleware before allowing requests to handlers

Applied to files:

  • pkg/middleware/masking_test.go
  • pkg/middleware/masking.go
🧬 Code graph analysis (2)
cmd/hyperfleet-api/server/routes.go (1)
pkg/middleware/otel.go (1)
  • OTelMiddleware (18-45)
pkg/middleware/masking.go (1)
pkg/config/logging.go (1)
  • LoggingConfig (12-19)
🔇 Additional comments (7)
cmd/hyperfleet-api/server/routes.go (1)

71-76: LGTM!

Good middleware ordering: OperationID → OTel → RequestLogging. This ensures trace context is available for request logging when OTel is enabled. The conditional enablement via configuration is clean.

pkg/telemetry/otel.go (2)

16-57: LGTM on tracer provider initialization logic.

The sampling rate handling is correct:

  • >= 1.0 → always sample
  • <= 0.0 → never sample
  • Otherwise → ratio-based sampling

The stdout exporter with pretty-print is suitable for development/debugging. The comment about future OTLP upgrade is helpful for maintainability.


59-65: LGTM!

Graceful nil-check before shutdown prevents panics if the provider was never initialized.

pkg/middleware/masking_test.go (2)

11-90: LGTM!

Good coverage of header masking scenarios including case-insensitive matching and disabled state.


92-182: LGTM!

Thorough body masking tests covering nested objects, arrays, case-insensitive matching, non-JSON bodies, and empty bodies.

pkg/middleware/masking.go (2)

11-26: LGTM!

Clean struct design with configuration-driven initialization. Storing the parsed lists at construction time avoids repeated parsing on each request.


46-91: LGTM!

Robust JSON masking with:

  • Graceful handling of non-JSON bodies
  • Fallback on marshal errors
  • Recursive handling of nested objects and arrays

This aligns well with the coding guideline to use structured logging. Based on learnings, sensitive data protection in logs is important for this repository.

@yasun1 yasun1 changed the title [WIP] HYPERFLEET-363 | feat(logger): implement slog-based logger foundation [WIP] HYPERFLEET-363 | feat(logger): Align HyperFleet API component with logging specification Jan 7, 2026
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 (1)
pkg/db/db_session/testcontainer.go (1)

41-111: Critical: Replace os.Exit(1) with error returns.

The Init function calls os.Exit(1) at five different error paths (lines 58, 67, 76, 100, 107). Using os.Exit in library/package code is a critical anti-pattern because:

  • It terminates the entire process without allowing callers to clean up resources
  • It makes the code untestable (you cannot test error paths that call os.Exit)
  • It violates Go conventions where library functions should return errors, not kill the process
  • It prevents proper error handling and recovery by callers
♻️ Refactor Init to return error

Change the signature of Init to return an error:

-func (f *Testcontainer) Init(config *config.DatabaseConfig) {
+func (f *Testcontainer) Init(config *config.DatabaseConfig) error {
 	ctx := context.Background()
 
 	pkglogger.Info(ctx, "Starting PostgreSQL testcontainer...")
 
 	// Create PostgreSQL container
 	container, err := postgres.Run(ctx,
 		"postgres:14.2",
 		postgres.WithDatabase(config.Name),
 		postgres.WithUsername(config.Username),
 		postgres.WithPassword(config.Password),
 		testcontainers.WithWaitStrategy(
 			wait.ForListeningPort("5432/tcp").
 				WithStartupTimeout(60*time.Second)),
 	)
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to start PostgreSQL testcontainer: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to start PostgreSQL testcontainer: %w", err)
 	}

Apply similar changes to all five error paths, and update NewTestcontainerFactory to handle or propagate the error:

 func NewTestcontainerFactory(config *config.DatabaseConfig) *Testcontainer {
 	conn := &Testcontainer{
 		config: config,
 	}
-	conn.Init(config)
+	if err := conn.Init(config); err != nil {
+		// Log and return nil, or change NewTestcontainerFactory signature to return error
+		panic(err) // Or handle appropriately
+	}
 	return conn
 }

Better yet, change NewTestcontainerFactory to return (*Testcontainer, error) so callers can handle initialization failures.

🤖 Fix all issues with AI agents
In @docs/logging.md:
- Around line 31-41: Add a language specifier to the two fenced code blocks:
change the middleware chain block surrounding the diagram ("HTTP Request ↓
OperationIDMiddleware ... Handler") to start with ```text and the text output
example block (the time=2026-01-07... log line) to start with ```text so both
fenced blocks include the "text" language for proper syntax highlighting and
readability; ensure only the opening fence is modified for each block and the
closing ``` remains unchanged.

In @pkg/api/error.go:
- Around line 64-66: The code creates a new background context (ctx :=
context.Background()) before logging the error in SendResponse path, which drops
request tracing; replace the Background() usage with the incoming request
context (use r.Context()) so the logger.Error call uses the request's trace/span
context (keep the existing err construction and logger.Error invocation but pass
r.Context() instead of ctx).
- Line 73: The code is creating a root context with ctx := context.Background(),
which loses request-level tracing; replace those instances with ctx :=
r.Context() (use the incoming *http.Request's Context) wherever ctx is
initialized (e.g., the occurrences at lines shown) so panic logs retain trace
and operation IDs; ensure r is the request variable in scope and update both
occurrences accordingly.
- Around line 42-44: The code creates a new background context (ctx :=
context.Background()) before logging the error, which drops tracing information;
change it to use the incoming request context (use r.Context() as ctx or pass
r.Context() directly) so logger.Error(...) receives the request's
trace/operation IDs; update the ctx variable usage around the err assignment and
the logger.Error call (the locations referencing ctx, fmt.Errorf and
logger.Error) to preserve tracing from the request.

In @pkg/db/migrations.go:
- Around line 30-36: Replace the printf-style logger.Errorf call in the
migration error path to use structured logging via the Extra() method: capture
the context and relevant fields (e.g., migrationID and the error returned by
m.MigrateTo) and call logger.Extra(ctx, map[string]interface{}{"migration_id":
migrationID, "error": err}).Error("Could not migrate") instead of
logger.Errorf(ctx, "Could not migrate: %v", err); leave the surrounding flow
(ctx, sessionFactory.New, newGormigrate, os.Exit(1)) unchanged.

In @pkg/logger/logger.go:
- Around line 109-113: The code adds a redundant "error" field using
r.AddAttrs(slog.String("error", r.Message)) even though slog already records
r.Message as "message"; remove that line from the error-level block (the one
invoking r.AddAttrs(slog.String("error", r.Message))) so only the stack trace
(captureStackTrace(4)) is added for error-level records; keep
r.AddAttrs(slog.Any("stack_trace", stackTrace)) and leave r.Message to the
default slog "message" field.
🧹 Nitpick comments (9)
pkg/db/db_session/testcontainer.go (1)

171-171: Consider consistent structured logging throughout the file.

Line 171 uses structured logging with key-value pairs ("table", table, "error", err), which aligns with the coding guidelines for structured logging. However, the rest of the file uses formatted string logging (e.g., Infof, Errorf with format specifiers).

For consistency and to fully leverage structured logging benefits (better queryability, automatic field extraction), consider using the structured key-value pattern throughout.

Example: Structured logging for other log calls

For instance, line 70 could be changed from:

pkglogger.Infof(ctx, "PostgreSQL testcontainer started at: %s", connStr)

to:

pkglogger.Info(ctx, "PostgreSQL testcontainer started", "connection_string", connStr)

Similarly, error logs like line 57:

pkglogger.Errorf(ctx, "Failed to start PostgreSQL testcontainer: %s", err)

could become:

pkglogger.Error(ctx, "Failed to start PostgreSQL testcontainer", "error", err)

Based on coding guidelines.

pkg/errors/errors.go (1)

117-118: Consider using structured logging with Extra() for better observability.

While the migration from glog.Errorf to logger.Errorf is correct, the error code could be added as a structured field rather than interpolated into the message. This aligns with the structured logging best practices and makes logs more queryable.

Based on learnings and coding guidelines.

♻️ Proposed refactor using Extra()
-	ctx := context.Background()
-	logger.Errorf(ctx, "Undefined error code used: %d", code)
+	ctx := context.Background()
+	logger.Extra(ctx, "error_code", code).Errorf("Undefined error code used")

This approach adds the error code as a structured field, making it easier to query and aggregate in log analysis tools.

pkg/logger/ocm_bridge.go (2)

14-23: Clarify the purpose of the stored context field.

The ctx field is stored in the struct but isn't used by the logging methods (Debug, Info, Warn, Error, Fatal), which instead use the ctx parameter passed by the OCM SDK. The stored context is only referenced by the *Enabled() methods. This inconsistency may confuse future maintainers.

Consider either:

  1. Removing the stored ctx field and using the parameter ctx in all methods, or
  2. Adding a comment explaining why *Enabled() methods use the stored context while logging methods use the parameter context.

25-43: Consider preserving structured logging instead of pre-formatting messages.

The current implementation uses fmt.Sprintf(format, args...) to format messages before passing them to the logger. This loses the benefits of structured logging, as the formatted string cannot be parsed or queried effectively.

While the OCM SDK uses format strings, you could parse the format string and args to extract structured fields, or at minimum, log the raw format and args as separate fields for better observability.

Example: Preserve format and args as structured fields
 func (b *OCMLoggerBridge) Debug(ctx context.Context, format string, args ...interface{}) {
-	GetLogger().DebugContext(ctx, fmt.Sprintf(format, args...))
+	GetLogger().DebugContext(ctx, fmt.Sprintf(format, args...), "ocm_format", format)
 }

This approach logs the formatted message while preserving the original format string for structured queries.

test/helper.go (1)

544-555: Consider using a higher log level for tests by default.

The test logger is configured with slog.LevelInfo, which may produce verbose output during test runs. Consider using slog.LevelWarn as the default, with the option to override via the LOGLEVEL environment variable (which is already supported at Line 87).

💡 Optional refinement
 func initTestLogger() {
 	cfg := &logger.LogConfig{
-		Level:     slog.LevelInfo,
+		Level:     slog.LevelWarn,
 		Format:    logger.FormatText, // Use text format for test readability
 		Output:    os.Stdout,
 		Component: "hyperfleet-api-test",
 		Version:   "test",
 		Hostname:  "test-host",
 	}
 	logger.InitGlobalLogger(cfg)
 }
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)

15-27: Consider caching the masking middleware instance.

The masking middleware is recreated on every request (Line 26). Since the configuration doesn't change per-request, consider creating it once during middleware initialization to avoid repeated allocations.

♻️ Optional optimization
+var masker *middleware.MaskingMiddleware
+var maskerOnce sync.Once
+
 func RequestLoggingMiddleware(handler http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		ctx := r.Context()
 
 		// Skip health check endpoint to reduce log spam
 		if strings.TrimSuffix(r.URL.Path, "/") == "/api/hyperfleet" {
 			handler.ServeHTTP(w, r)
 			return
 		}
 
-		// Initialize masking middleware from environment config
-		masker := middleware.NewMaskingMiddleware(environments.Environment().Config.Logging)
+		// Initialize masking middleware once from environment config
+		maskerOnce.Do(func() {
+			masker = middleware.NewMaskingMiddleware(environments.Environment().Config.Logging)
+		})
 		maskedHeaders := masker.MaskHeaders(r.Header)

Don't forget to add the sync import.

cmd/hyperfleet-api/servecmd/cmd.go (2)

57-61: Use parent context for graceful shutdown.

The shutdown defer uses context.Background(), which ignores any cancellation from the parent context. If the application needs to perform graceful shutdown when the command context is canceled, consider using ctx instead.

♻️ Proposed fix
 		defer func() {
-			if err := telemetry.Shutdown(context.Background(), tp); err != nil {
+			if err := telemetry.Shutdown(ctx, tp); err != nil {
 				logger.Error(ctx, "Failed to shutdown OpenTelemetry", "error", err)
 			}
 		}()

108-112: Consider defaulting to text format for invalid values.

When an invalid log format is provided, the code defaults to FormatJSON. Users might expect a simpler text format as the default, especially in development environments.

♻️ Proposed change
 	format, err := logger.ParseLogFormat(cfg.Format)
 	if err != nil {
 		logger.Warn(ctx, "Invalid log format, using default", "format", cfg.Format, "error", err)
-		format = logger.FormatJSON // Default to JSON
+		format = logger.FormatText // Default to text
 	}
pkg/logger/logger.go (1)

54-56: Add type assertion safety check.

The type assertion a.Value.Any().(slog.Level) could panic if the value is not a slog.Level. While slog internals are stable, defensive programming suggests checking the assertion.

🛡️ Proposed fix
 		if a.Key == slog.LevelKey {
-			level := a.Value.Any().(slog.Level)
-			return slog.Attr{Key: "level", Value: slog.StringValue(strings.ToLower(level.String()))}
+			if level, ok := a.Value.Any().(slog.Level); ok {
+				return slog.Attr{Key: "level", Value: slog.StringValue(strings.ToLower(level.String()))}
+			}
+			return a
 		}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 473fa4f and 7b2cb63.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (30)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/middleware/otel.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (5)
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/logging.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/logger/logger_test.go
🧰 Additional context used
📓 Path-based instructions (7)
pkg/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Files:

  • pkg/db/db_session/test.go
  • pkg/middleware/otel.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
  • pkg/errors/errors.go
  • pkg/api/error.go
  • pkg/logger/ocm_bridge.go
  • pkg/handlers/openapi.go
  • pkg/telemetry/otel.go
  • pkg/logger/logger.go
test/integration/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use Testcontainers to spin up isolated PostgreSQL instances for integration tests

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Retrieve database sessions from context via db.NewContext() rather than creating new GORM connections

Files:

  • pkg/handlers/openapi.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: Automatically convert errors to OpenAPI error responses using errors.SendError() in HTTP handlers
Parse HTTP request bodies using json.NewDecoder(r.Body) in handler functions
Write HTTP responses using json.NewEncoder(w) for consistency with request parsing
Ensure all HTTP handlers follow the pattern: parse request, call service/DAO, handle errors, send response
Implement handlers as methods on handler structs that accept (w http.ResponseWriter, r *http.Request) parameters
Set HTTP status codes correctly: 201 Created for POST, 200 OK for GET, 204 No Content for DELETE operations

Files:

  • pkg/handlers/openapi.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Support pagination in list operations via ListArgs parameter with configurable limit and offset

Files:

  • pkg/handlers/openapi.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Return ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields in error handling

Files:

  • pkg/handlers/openapi.go
pkg/{db/transaction_middleware.go,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Store request operation IDs in context for tracing and debugging purposes in error logs

Files:

  • pkg/handlers/openapi.go
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Retrieve database sessions from context via db.NewContext() rather than creating new GORM connections

Applied to files:

  • pkg/db/db_session/test.go
  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/servecmd/cmd.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Applied to files:

  • pkg/db/db_session/test.go
  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/auth/authz_middleware_mock.go
  • docs/logging.md
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/main.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/api/error.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • test/helper.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/logger/ocm_bridge.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/telemetry/otel.go
  • pkg/logger/logger.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{dao,services}/**/*.go : Use context.Context as the first parameter in all DAO and service method signatures

Applied to files:

  • pkg/db/db_session/test.go
  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/main.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/api/error.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/servecmd/cmd.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/middleware/otel.go
  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/api/error.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/transaction_middleware.go : Store database sessions in HTTP request context via middleware at request entry point

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/migrations/**/*.go : Use GORM AutoMigrate for schema management instead of manual SQL scripts

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to test/integration/**/*.go : Use Testcontainers to spin up isolated PostgreSQL instances for integration tests

Applied to files:

  • pkg/db/db_session/test.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to test/factories/**/*.go : Provide test data factories in test/factories/ for consistent test fixture creation

Applied to files:

  • pkg/db/db_session/test.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • test/helper.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use environment-based configuration via OCM_ENV to select development, unit_testing, integration_testing, or production settings

Applied to files:

  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/main.go
  • test/helper.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/api/openapi_embed.go : Embed OpenAPI specification at compile time using //go:embed directive

Applied to files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/api_server.go
  • test/helper.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and node pool specifications as JSONB columns to allow flexible cloud provider configurations

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : Use polymorphic status tables with owner_type and owner_id columns to support multiple resource types

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/server/**/*.go : Extract user account ID and username from JWT claims for audit logging and authorization checks

Applied to files:

  • cmd/hyperfleet-api/server/server.go
  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/main.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • test/helper.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/server/**/*.go : Validate JWT tokens from Red Hat SSO in middleware before allowing requests to handlers

Applied to files:

  • pkg/auth/authz_middleware_mock.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Automatically convert errors to OpenAPI error responses using errors.SendError() in HTTP handlers

Applied to files:

  • pkg/errors/errors.go
  • pkg/api/error.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields in error handling

Applied to files:

  • pkg/errors/errors.go
  • pkg/api/error.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/api/openapi/model_*.go : Support JSON request/response serialization with proper struct tags on generated OpenAPI models

Applied to files:

  • pkg/errors/errors.go
  • test/helper.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Implement handlers as methods on handler structs that accept (w http.ResponseWriter, r *http.Request) parameters

Applied to files:

  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/api/error.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Write HTTP responses using json.NewEncoder(w) for consistency with request parsing

Applied to files:

  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/api/error.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Set HTTP status codes correctly: 201 Created for POST, 200 OK for GET, 204 No Content for DELETE operations

Applied to files:

  • pkg/api/error.go
  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Use FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto build flags

Applied to files:

  • test/helper.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Ensure all HTTP handlers follow the pattern: parse request, call service/DAO, handle errors, send response

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/services/**/*.go : Implement service layer between handlers and DAOs for business logic like status aggregation and generation tracking

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/servecmd/cmd.go
🧬 Code graph analysis (14)
pkg/middleware/otel.go (1)
pkg/logger/context.go (2)
  • WithTraceID (29-31)
  • WithSpanID (34-36)
test/integration/integration_test.go (1)
pkg/logger/logger.go (2)
  • Infof (241-243)
  • Warn (229-231)
cmd/hyperfleet-api/migrate/cmd.go (4)
pkg/logger/logger.go (1)
  • Error (233-235)
pkg/db/db_session/default.go (1)
  • NewProdFactory (33-37)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
  • New (112-132)
cmd/hyperfleet-api/server/server.go (1)
pkg/logger/logger.go (1)
  • Error (233-235)
pkg/auth/authz_middleware_mock.go (1)
pkg/logger/logger.go (1)
  • Info (225-227)
pkg/db/db_session/testcontainer.go (2)
pkg/logger/logger.go (4)
  • Info (225-227)
  • Errorf (249-251)
  • Infof (241-243)
  • Error (233-235)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (249-251)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (3)
pkg/middleware/masking.go (1)
  • NewMaskingMiddleware (20-26)
cmd/hyperfleet-api/environments/framework.go (1)
  • Environment (54-56)
pkg/logger/logger.go (1)
  • Info (225-227)
cmd/hyperfleet-api/server/api_server.go (4)
pkg/logger/ocm_bridge.go (1)
  • NewOCMLoggerBridge (19-23)
pkg/logger/logger.go (2)
  • Info (225-227)
  • Error (233-235)
cmd/hyperfleet-api/server/server.go (1)
  • Server (13-18)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/api/error.go (1)
pkg/logger/logger.go (2)
  • Errorf (249-251)
  • Error (233-235)
cmd/hyperfleet-api/server/healthcheck_server.go (2)
pkg/logger/logger.go (2)
  • Infof (241-243)
  • Info (225-227)
cmd/hyperfleet-api/server/server.go (1)
  • Server (13-18)
pkg/logger/ocm_bridge.go (1)
pkg/logger/logger.go (5)
  • Debug (221-223)
  • GetLogger (174-179)
  • Info (225-227)
  • Warn (229-231)
  • Error (233-235)
cmd/hyperfleet-api/servecmd/cmd.go (3)
pkg/logger/logger.go (9)
  • Error (233-235)
  • Warn (229-231)
  • Info (225-227)
  • ParseLogLevel (182-195)
  • ParseLogFormat (198-207)
  • FormatJSON (22-22)
  • ParseLogOutput (210-219)
  • LogConfig (26-33)
  • InitGlobalLogger (167-171)
cmd/hyperfleet-api/environments/framework.go (1)
  • Environment (54-56)
pkg/telemetry/otel.go (2)
  • InitTraceProvider (16-57)
  • Shutdown (60-65)
pkg/logger/logger.go (1)
pkg/logger/context.go (6)
  • TraceIDCtxKey (21-21)
  • SpanIDCtxKey (22-22)
  • OpIDKey (19-19)
  • ClusterIDCtxKey (23-23)
  • ResourceTypeCtxKey (24-24)
  • ResourceIDCtxKey (25-25)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

🪛 LanguageTool
docs/logging.md

[grammar] ~297-~297: Ensure spelling is correct
Context: ...)` | ### Context-Aware Migration Old (glog): ```go func MyHandler(w http.ResponseW...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~315-~315: Ensure spelling is correct
Context: ...span_id } ### Error Handling Old (glog):go if err != nil { glog.Errorf...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
docs/logging.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


195-195: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (38)
pkg/db/db_session/testcontainer.go (3)

7-7: Imports support problematic os.Exit pattern.

The os import is only used for os.Exit(1) calls within the Init function. Using os.Exit in library/package code is a critical anti-pattern that prevents proper error handling and makes the code untestable. Consider refactoring Init to return an error instead.

Related to lines 58, 67, 76, 100, 107 where os.Exit(1) is called.

Also applies to: 19-19


133-153: LGTM!

The Close function correctly migrates to context-aware logging with pkglogger. Error handling is appropriate for cleanup code, where logging and continuing is acceptable.


177-186: LGTM!

The NewListener function correctly migrates to context-aware logging with pkglogger. Error handling is appropriate.

pkg/errors/errors.go (1)

9-9: No action required; the openapi import is valid.

The import at line 9 is correct and necessary—the AsOpenapiError() method at line 154 and related functions use openapi.Error, openapi.ErrorDetailsInner, and openapi.PtrString. The package is code-generated by the make generate build target, so it does not exist until generation is run. The static analysis warning is expected and will resolve during the normal build process.

pkg/auth/authz_middleware_mock.go (1)

6-6: LGTM! Clean migration to context-aware structured logging.

The migration from glog to the new logger package is correct and follows best practices. The logging call properly uses the request context and structured key-value pairs for better queryability.

Based on learnings, this follows the coding guideline to use structured logging via pkg/logger/logger.go.

Also applies to: 19-19

docs/logging.md (1)

1-498: Excellent comprehensive logging documentation.

The documentation thoroughly covers the new slog-based logging system, including:

  • Clear architecture and component descriptions
  • Configuration examples with environment variables
  • Usage patterns and best practices
  • OpenTelemetry integration details
  • Data masking capabilities
  • Migration guide from glog

This will be valuable for developers adopting the new logging system.

README.md (1)

158-158: LGTM! Documentation entry added correctly.

The new Logging documentation entry is properly formatted and provides a clear, concise description of the logging features.

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

68-119: LGTM! Proper migration to context-aware logging in initialization flow.

The migration correctly uses context.Background() for initialization logging, which is appropriate since this code runs during application startup before request contexts are available. The error handling pattern (log then exit) is consistent throughout.

Based on learnings, this follows the coding guideline to use structured logging via pkg/logger/logger.go.


135-160: LGTM! Clean logging migration in LoadClients.

The client loading logic properly uses context-aware logging with structured messages. The use of logger.Info and logger.Errorf with context is correct.


163-174: LGTM! Proper cleanup logging in Teardown.

The teardown function correctly uses context-aware error logging for resource cleanup failures.


176-185: Excellent use of structured logging with key-value pairs.

Line 180 correctly uses the structured logging pattern with explicit key-value pairs ("flag", name, "error", err) instead of string formatting, which improves log queryability.

go.mod (2)

32-36: LGTM! OpenTelemetry dependencies added correctly.

The OpenTelemetry dependencies are properly versioned (v1.39.0) and include the necessary components for HTTP instrumentation, tracing, and exporters.


72-72: glog is still a direct dependency, not an indirect one.

The go mod graph output shows github.com/openshift-hyperfleet/hyperfleet-api github.com/golang/glog@v1.2.5, indicating glog v1.2.5 is a direct dependency of the project, not merely an indirect transitive dependency. While the go.mod file marks it as // indirect, the module graph confirms it's pulled in directly by the project itself. Since no direct code imports of glog were found, it should be removed entirely if it's no longer needed.

Likely an incorrect or invalid review comment.

pkg/logger/ocm_bridge.go (1)

45-49: LGTM: Fatal implementation follows expected behavior.

The Fatal method correctly logs at error level with a "FATAL: " prefix and exits immediately. This is standard behavior for fatal errors in logging bridges.

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

52-61: LGTM: Clean migration to structured logging.

The migration from glog to the internal logger package is correctly implemented. The context is appropriately created and passed to all logging calls, following the new structured logging pattern.

pkg/handlers/openapi.go (2)

25-53: LGTM: Proper context usage and logging migration.

The initialization correctly creates a context and uses it for structured logging during OpenAPI spec loading. The migration from glog to the internal logger follows the established patterns.


61-83: LGTM: Excellent use of structured logging with context.

The handler methods correctly use logger.NewOCMLogger with the Extra() method to add structured fields (endpoint, method, status_code, error), following the coding guidelines for structured logging. The error handling after response commitment is also appropriate.

Based on coding guidelines: Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields.

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

27-34: LGTM: Proper structured logging migration.

The check() function correctly migrates to structured logging using logger.Error with key-value pairs. The context is appropriately created and the error is logged as a structured field.

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

43-63: LGTM: Clean logging migration with proper context usage.

The Start() method correctly migrates from glog to the internal logger with appropriate context creation and usage across all logging statements. The migration maintains the informational logging while adding context awareness.

pkg/api/error.go (1)

92-92: LGTM: Appropriate use of Background() in init.

Using context.Background() is correct here since init() runs at package initialization time before any HTTP requests exist.

Also applies to: 113-113

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

33-44: LGTM: Proper context and logging for CLI command.

The use of context.Background() is appropriate for this CLI migration command, and the structured logging follows the project's logging guidelines.

test/helper.go (1)

74-74: LGTM: Appropriate use of Background() in test infrastructure.

Using context.Background() is correct throughout this test helper since these are test lifecycle functions without HTTP request contexts.

Also applies to: 142-142, 166-166, 176-176, 184-184, 194-194, 203-203, 210-210, 221-221, 228-228

test/integration/integration_test.go (1)

15-52: LGTM: Proper structured logging in test setup.

The TestMain function correctly uses context.Background() and structured logging. The schema path detection logic is well-structured with appropriate error handling.

cmd/hyperfleet-api/server/logging/request_logging_middleware.go (2)

30-54: LGTM: Well-structured request/response logging.

The middleware correctly:

  • Uses request context for trace propagation (Line 17)
  • Masks sensitive headers (Line 27)
  • Captures status codes via response wrapper (Lines 39-43)
  • Logs both request start and completion with appropriate fields

This implementation aligns with the structured logging guidelines.


59-68: LGTM: Correct response writer wrapper implementation.

The responseWriter correctly captures the HTTP status code while delegating to the wrapped writer. The default status of http.StatusOK (Line 39) properly handles cases where handlers don't explicitly call WriteHeader.

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

26-34: LGTM! Early logger initialization and consistent error handling.

The implementation correctly:

  • Initializes the logger before any operations that might log
  • Uses consistent error handling pattern (logger.Error + os.Exit(1))
  • Creates a context for structured logging

54-66: LGTM! Bootstrap logger with sensible defaults.

The default logger initialization is appropriate for early startup before configuration is loaded. The "unknown" version (line 62) will be replaced when the full configuration is initialized in the serve command.


68-74: LGTM! Graceful fallback for hostname retrieval.

The silent fallback to "unknown" is appropriate during bootstrap initialization, as hostname is not critical for logger functionality.

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

39-40: LGTM! Correct OCM logger bridge initialization.

The authentication handler correctly uses logger.NewOCMLoggerBridge with a background context, which is appropriate for a long-lived component.


106-127: LGTM! Proper structured logging with context.

The logging implementation correctly:

  • Creates and uses context for all logging calls
  • Uses structured fields for contextual information ("bind_address")
  • Provides clear lifecycle messages for TLS/non-TLS serving and termination

138-151: LGTM! Consistent error handling and structured logging.

The error handling correctly:

  • Uses context for all logging calls
  • Applies structured fields for error details
  • Follows the consistent pattern of logger.Error + os.Exit(1) for fatal startup errors
  • Logs database connection close errors appropriately
cmd/hyperfleet-api/servecmd/cmd.go (2)

17-32: LGTM!

The migration from glog to structured logging is correctly implemented. The error handling pattern using logger.Error with explicit os.Exit(1) provides better control over the shutdown process.


68-74: Good diagnostic logging.

Logging the configuration details at startup provides valuable context for troubleshooting and auditing.

pkg/logger/logger.go (5)

15-33: LGTM!

The type definitions are clean and well-documented. The LogConfig struct provides all necessary configuration options for the logger.


164-179: LGTM!

The global logger initialization and retrieval pattern is clean. The fallback to slog.Default() ensures the logger is always available, even if initialization hasn't occurred yet.


181-219: LGTM!

The parser functions provide clear validation with helpful error messages. The support for aliases (e.g., "warn" and "warning") improves usability.


221-251: LGTM!

The helper functions provide a clean, consistent API for structured logging. All functions are context-aware, which aligns with the coding guidelines.

Based on learnings, this correctly implements structured logging via the Extra() method pattern (available through the OCMLogger interface).


253-355: LGTM!

The OCM Logger compatibility layer is well-implemented:

  • Immutable logger pattern in Extra() prevents unintended side effects
  • Context fields (account_id, tx_id) are extracted and preserved
  • The V() method correctly maps verbosity levels to debug/info
  • Fatal() appropriately exits the process

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

🤖 Fix all issues with AI agents
In @cmd/hyperfleet-api/migrate/cmd.go:
- Line 37: The migrate command currently calls os.Exit() which bypasses deferred
cleanup and leaves the DB pool from db_session.NewProdFactory() open; after
creating the production DB factory (the call to db_session.NewProdFactory() that
returns connection), add a defer connection.Close() immediately so the
connection pool is released on any exit path, and ensure any early os.Exit()
paths call connection.Close() (or return instead) so resources are not leaked.

In @cmd/hyperfleet-api/server/logging/request_logging_middleware.go:
- Around line 26-27: The request logger currently creates a new masker on every
request by calling middleware.NewMaskingMiddleware inside
RequestLoggingMiddleware; instead, initialize the masker once and reuse it:
change RequestLoggingMiddleware to accept a prepared masker (or reference a
package-level masker) and remove middleware.NewMaskingMiddleware from the
per-request path, then call masker.MaskHeaders(r.Header) inside the handler as
before; update router setup to construct the masker once via
middleware.NewMaskingMiddleware(env.Config.Logging) and pass it into
RequestLoggingMiddleware when registering the middleware.
🧹 Nitpick comments (8)
cmd/hyperfleet-api/migrate/cmd.go (2)

36-37: Make error messages more specific.

The generic message "Fatal error" doesn't convey what operation failed. Consider using a more descriptive message like "Failed to read database configuration files".

📝 Suggested improvement
-		logger.Error(ctx, "Fatal error", "error", err)
+		logger.Error(ctx, "Failed to read database configuration files", "error", err)

41-44: Make error messages more specific.

Similar to the previous error handler, use a more descriptive message like "Failed to run database migrations" instead of the generic "Fatal error".

📝 Suggested improvement
-		logger.Error(ctx, "Fatal error", "error", err)
+		logger.Error(ctx, "Failed to run database migrations", "error", err)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)

30-36: Consider reducing header logging verbosity.

Logging all headers on every request may produce very verbose logs and could impact performance, even with masking. Consider:

  • Only logging headers in debug mode
  • Logging a subset of important headers (e.g., Content-Type, Authorization status)
  • Moving detailed header logging to trace level if available
pkg/logger/ocm_bridge.go (1)

14-23: Consider removing the stored context field.

If the context field is only used by the Enabled methods and those are updated to accept context parameters, the ctx field in the struct becomes unnecessary. Consider simplifying the bridge to be stateless.

♻️ Simplified implementation
 // OCMLoggerBridge bridges slog to OCM SDK's Logger interface
 // Note: Uses "Bridge" instead of "Adapter" to avoid confusion with HyperFleet adapters (GCP/Azure adapters)
-type OCMLoggerBridge struct {
-	ctx context.Context
-}
+type OCMLoggerBridge struct{}
 
 // NewOCMLoggerBridge creates an OCM SDK logger bridge
-func NewOCMLoggerBridge(ctx context.Context) sdk.Logger {
-	return &OCMLoggerBridge{
-		ctx: ctx,
-	}
+func NewOCMLoggerBridge(ctx context.Context) sdk.Logger {
+	return &OCMLoggerBridge{}
 }
pkg/errors/errors.go (1)

117-118: Consider accepting context as a parameter for better tracing.

The use of context.Background() is acceptable, but for improved observability, consider updating the New() function signature to accept a context parameter. This would allow operation IDs and trace IDs to be included in error logs.

♻️ Optional refactor
-func New(code ServiceErrorCode, reason string, values ...interface{}) *ServiceError {
+func New(ctx context.Context, code ServiceErrorCode, reason string, values ...interface{}) *ServiceError {
 	// If the code isn't defined, use the general error code
 	var err *ServiceError
 	exists, err := Find(code)
 	if !exists {
-		ctx := context.Background()
 		logger.Errorf(ctx, "Undefined error code used: %d", code)

Note: This would require updating all call sites of New() and the wrapper functions (NotFound, GeneralError, etc.) throughout the codebase.

Based on learnings, request operation IDs should be stored in context for tracing and debugging purposes in error logs.

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

68-119: Consider consolidating context creation.

The function creates ctx := context.Background() at the start and uses it throughout, which is correct. However, multiple functions in this file create their own background contexts (lines 68, 135, 163, 177). Consider propagating a context from the caller to enable better trace propagation and testing.

💡 Refactoring suggestion

Update the method signatures to accept context:

-func (e *Env) Initialize() error {
-	ctx := context.Background()
+func (e *Env) Initialize(ctx context.Context) error {
 	logger.Infof(ctx, "Initializing %s environment", e.Name)

Apply similar changes to LoadClients, Teardown, and setConfigDefaults.

pkg/logger/context.go (1)

11-11: Reconsider the type for OpIDHeader.

OpIDHeader represents an HTTP header name ("X-Operation-ID"), which should be a plain string constant rather than a custom type. While OperationIDHeaderKey provides type safety, HTTP header names are conventionally strings in Go's standard library (e.g., http.Header uses string keys). This custom type may cause friction when used with standard HTTP APIs.

🔄 Proposed refactor
 // Context key types for type safety
 type OperationIDKey string
-type OperationIDHeaderKey string
 type TraceIDKey string
 type SpanIDKey string
 type ClusterIDKey string
 type ResourceTypeKey string
 type ResourceIDKey string
 
 // Context keys for storing correlation fields
 const (
 	OpIDKey            OperationIDKey       = "opID"
-	OpIDHeader         OperationIDHeaderKey = "X-Operation-ID"
+	OpIDHeader                              = "X-Operation-ID"
 	TraceIDCtxKey      TraceIDKey           = "trace_id"

Also applies to: 21-21

pkg/logger/logger.go (1)

282-284: Consider using a typed context key for txid.

Line 282 uses the string "txid" directly as a context key, which is inconsistent with the typed key pattern established in pkg/logger/context.go. For consistency and type safety, consider defining a typed key for transaction IDs similar to OperationIDKey, TraceIDKey, etc.

🔄 Suggested enhancement

In pkg/logger/context.go, add:

type TransactionIDKey string

const (
    // ... existing constants
    TxIDCtxKey TransactionIDKey = "txid"
)

Then update the usage in logger.go:

-	if txid, ok := ctx.Value("txid").(int64); ok {
+	if txid, ok := ctx.Value(TxIDCtxKey).(int64); ok {
 		attrs = append(attrs, slog.Int64("tx_id", txid))
 	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b2cb63 and 5e80608.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (31)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/middleware/otel.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (5)
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
🚧 Files skipped from review as they are similar to previous changes (11)
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/openapi.go
  • pkg/logger/logger_test.go
  • cmd/hyperfleet-api/main.go
  • pkg/telemetry/otel.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/middleware/otel.go
🧰 Additional context used
📓 Path-based instructions (2)
test/integration/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use Testcontainers to spin up isolated PostgreSQL instances for integration tests

Files:

  • test/integration/integration_test.go
pkg/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Files:

  • pkg/api/error.go
  • pkg/db/migrations.go
  • pkg/logger/ocm_bridge.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
  • pkg/db/db_session/test.go
  • pkg/errors/errors.go
🧠 Learnings (23)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/**/*.go : Use structured logging via pkg/logger/logger.go with Extra() method for adding context fields

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
  • test/helper.go
  • test/integration/integration_test.go
  • pkg/api/error.go
  • docs/logging.md
  • pkg/db/migrations.go
  • pkg/logger/ocm_bridge.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
  • pkg/db/db_session/test.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use environment-based configuration via OCM_ENV to select development, unit_testing, integration_testing, or production settings

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
  • test/helper.go
  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{dao,services}/**/*.go : Use context.Context as the first parameter in all DAO and service method signatures

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
  • test/integration/integration_test.go
  • pkg/api/error.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/logger/context.go
  • pkg/db/db_session/test.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Retrieve database sessions from context via db.NewContext() rather than creating new GORM connections

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
  • test/integration/integration_test.go
  • pkg/api/error.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/test.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/server/**/*.go : Extract user account ID and username from JWT claims for audit logging and authorization checks

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to test/factories/**/*.go : Provide test data factories in test/factories/ for consistent test fixture creation

Applied to files:

  • test/helper.go
  • test/integration/integration_test.go
  • pkg/db/db_session/test.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Use FIPS-compliant crypto with CGO_ENABLED=1 and GOEXPERIMENT=boringcrypto build flags

Applied to files:

  • test/helper.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/api/openapi_embed.go : Embed OpenAPI specification at compile time using //go:embed directive

Applied to files:

  • test/helper.go
  • test/integration/integration_test.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/api/openapi/model_*.go : Support JSON request/response serialization with proper struct tags on generated OpenAPI models

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to test/integration/**/*.go : Use Testcontainers to spin up isolated PostgreSQL instances for integration tests

Applied to files:

  • test/integration/integration_test.go
  • pkg/db/db_session/test.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Automatically convert errors to OpenAPI error responses using errors.SendError() in HTTP handlers

Applied to files:

  • pkg/api/error.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Return ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields in error handling

Applied to files:

  • pkg/api/error.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/transaction_middleware.go,handlers}/**/*.go : Store request operation IDs in context for tracing and debugging purposes in error logs

Applied to files:

  • pkg/api/error.go
  • docs/logging.md
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/logger/context.go
  • pkg/db/db_session/test.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Set HTTP status codes correctly: 201 Created for POST, 200 OK for GET, 204 No Content for DELETE operations

Applied to files:

  • pkg/api/error.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Write HTTP responses using json.NewEncoder(w) for consistency with request parsing

Applied to files:

  • pkg/api/error.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/handlers/**/*.go : Implement handlers as methods on handler structs that accept (w http.ResponseWriter, r *http.Request) parameters

Applied to files:

  • pkg/api/error.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to cmd/hyperfleet/server/**/*.go : Validate JWT tokens from Red Hat SSO in middleware before allowing requests to handlers

Applied to files:

  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/errors/errors.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/transaction_middleware.go : Store database sessions in HTTP request context via middleware at request entry point

Applied to files:

  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/test.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/migrations/**/*.go : Use GORM AutoMigrate for schema management instead of manual SQL scripts

Applied to files:

  • pkg/db/migrations.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • go.mod
  • pkg/db/db_session/test.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : Use polymorphic status tables with owner_type and owner_id columns to support multiple resource types

Applied to files:

  • pkg/db/migrations.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and node pool specifications as JSONB columns to allow flexible cloud provider configurations

Applied to files:

  • pkg/db/migrations.go
  • cmd/hyperfleet-api/migrate/cmd.go
📚 Learning: 2025-12-12T11:04:29.971Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T11:04:29.971Z
Learning: Applies to pkg/{db/migrations,dao}/adapter_status.go : Store adapter statuses with observed_generation field to track which resource generation each adapter has processed

Applied to files:

  • pkg/db/migrations.go
🧬 Code graph analysis (7)
test/helper.go (1)
pkg/logger/logger.go (5)
  • Error (232-234)
  • Info (224-226)
  • Debug (220-222)
  • LogConfig (26-33)
  • InitGlobalLogger (166-170)
test/integration/integration_test.go (1)
pkg/logger/logger.go (2)
  • Infof (240-242)
  • Warn (228-230)
pkg/api/error.go (1)
pkg/logger/logger.go (1)
  • Error (232-234)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (3)
pkg/middleware/masking.go (1)
  • NewMaskingMiddleware (20-26)
cmd/hyperfleet-api/environments/framework.go (1)
  • Environment (54-56)
pkg/logger/logger.go (1)
  • Info (224-226)
pkg/logger/logger.go (2)
pkg/logger/context.go (6)
  • TraceIDCtxKey (22-22)
  • SpanIDCtxKey (23-23)
  • OpIDKey (20-20)
  • ClusterIDCtxKey (24-24)
  • ResourceTypeCtxKey (25-25)
  • ResourceIDCtxKey (26-26)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/db/db_session/test.go (1)
pkg/logger/logger.go (1)
  • Errorf (248-250)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (248-250)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

🪛 markdownlint-cli2 (0.18.1)
docs/logging.md

29-29: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


181-181: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (21)
cmd/hyperfleet-api/migrate/cmd.go (2)

6-6: LGTM: Import changes support the glog-to-slog migration.

The new imports (os, db_session, logger) correctly support the refactored error handling and database initialization pattern.

Also applies to: 12-13


33-38: The logger is properly initialized. The main() function calls initDefaultLogger() before creating or executing any commands, which invokes logger.InitGlobalLogger(). By the time runMigrate() executes, the logger is guaranteed to be initialized with a global logger instance.

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

58-68: LGTM! Clean responseWriter wrapper.

The responseWriter wrapper correctly captures the status code with proper delegation to the underlying ResponseWriter.

pkg/logger/ocm_bridge.go (2)

26-49: LGTM! Clean delegation to slog logger.

The logging methods correctly delegate to GetLogger() with proper context propagation and formatted messages. The Fatal method appropriately prefixes with "FATAL: " and exits.


52-69: The proposed fix violates the OCM SDK's Logger interface contract.

The Enabled methods in the OCM SDK's Logger interface are defined without context parameters: DebugEnabled() bool, InfoEnabled() bool, etc. The current implementation correctly adheres to this interface. Adding context parameters as suggested would break the interface implementation.

The underlying concern about using stale stored context is valid—b.ctx captured at construction time may lack current request-specific values. However, this is an unavoidable constraint of the OCM SDK's Logger interface design. The stored context approach is the only way to implement these methods given the interface signature.

Consider documenting this limitation in a comment to clarify the design tradeoff: that while other logging methods receive fresh context, the Enabled predicates must use the construction-time context due to interface constraints.

Likely an incorrect or invalid review comment.

README.md (1)

158-158: LGTM! Documentation link added correctly.

The logging documentation link is properly formatted and placed in the Core Documentation section with an accurate description.

go.mod (2)

32-36: Verify OpenTelemetry versions are pre-release or unreleased.

The PR specifies OpenTelemetry dependencies at v1.39.0, but the latest stable version reported is v1.38.0. Confirm whether v1.39.0 is a pre-release, unreleased, or if there is a newer stable release available. Using versions ahead of the latest stable may introduce instability.

Likely an incorrect or invalid review comment.


26-26: Verify ksuid version bump and check for breaking changes.

Review the changelog for the ksuid upgrade. No formal upstream release notes were found for the v1.0.2 → v1.0.4 transition; check the commit history directly: segmentio/ksuid@v1.0.2...v1.0.4

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

16-16: LGTM! Context usage is appropriate for initialization.

The structured logger import and context creation align with the logging migration. Using context.Background() in the sync.Once initialization block is correct since no parent context exists at this stage.

Based on learnings, using structured logging via pkg/logger/logger.go with context.

Also applies to: 52-54


54-54: LGTM! Error logging correctly migrated to structured logger.

The migration from glog to pkglogger.Errorf maintains error visibility while adding structured logging benefits. Context is properly passed to logging calls.

Based on learnings, using structured logging via pkg/logger/logger.go.

Also applies to: 59-59

pkg/api/error.go (1)

89-110: LGTM! Appropriate use of Background context in init.

Using context.Background() in the init() function is correct since no request context exists during package initialization.

test/helper.go (2)

544-555: LGTM! Test logger initialization is well-structured.

The initTestLogger function properly configures the global logger for tests with appropriate defaults (text format, info level). This provides consistent logging behavior across the test suite.


72-74: LGTM! Context usage is appropriate for test infrastructure.

Using context.Background() throughout test setup, teardown, and server lifecycle functions is correct. Test infrastructure code doesn't have parent request contexts, so Background() is the appropriate choice.

Based on learnings, using structured logging via pkg/logger/logger.go.

Also applies to: 84-86, 96-97, 142-149, 166-172, 176-179, 184-189, 194-199, 203-206, 210-211, 221-222

docs/logging.md (1)

1-509: Excellent documentation! Comprehensive logging guide.

This documentation provides thorough coverage of the new structured logging system including:

  • Clear architecture overview with middleware chain
  • Practical examples with actual output
  • Configuration options and environment variables
  • Best practices and troubleshooting guidance
  • OpenTelemetry integration details
  • Data masking configuration
test/integration/integration_test.go (1)

4-43: LGTM! Clean migration to structured logging.

The integration test has been successfully migrated from glog to the new context-aware logger. The use of structured logging with key-value pairs (line 40) aligns with the coding guidelines for pkg/**/*.go files.

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

73-80: Fatal error handling pattern is consistent.

The use of logger.Errorf followed by os.Exit(1) for fatal errors is consistent across the file and appropriate for initialization failures that should terminate the process.

pkg/logger/context.go (1)

57-63: LGTM! Idempotent operation ID generation.

The WithOpID function correctly checks if an operation ID already exists before generating a new KSUID, preventing unnecessary allocations and maintaining ID stability across context propagation.

pkg/logger/logger.go (4)

109-112: Skip outdated review comment.

The past review comment suggesting removal of a duplicate error field on line 112 is no longer applicable. The current code correctly adds only the stack trace for error-level logs without duplicating the message field.


45-80: LGTM! Well-structured slog handler implementation.

The HyperFleetHandler correctly implements the slog.Handler interface with appropriate field renaming (timestamp, level, message) and support for both JSON and text formats. The attribute replacement logic properly handles slog's built-in keys.


142-161: LGTM! Stack trace filtering is appropriate.

The stack trace capture correctly filters out runtime.* and testing.* frames to focus on application code, making error logs more actionable. The 15-frame limit prevents excessive log size while capturing sufficient context.


166-170: Verify the impact of calling slog.SetDefault.

Line 169 sets the global default logger, which affects all slog usage across the application and any third-party libraries using slog.Default(). This is intentional for this migration but ensure all existing code paths have been tested with this change.

Based on learnings, structured logging via pkg/logger/logger.go is the established pattern for this codebase.

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

Caution

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

⚠️ Outside diff range comments (1)
cmd/hyperfleet-api/servecmd/cmd.go (1)

76-92: Implement signal handling for graceful shutdown of the three servers.

The servers are started in separate goroutines and the main goroutine blocks indefinitely with select{}, but there is no signal handling (SIGTERM/SIGINT). While each server has a Stop() method that gracefully shuts down the HTTP server, these methods are never invoked. This causes the application to be forcefully terminated without proper cleanup:

  • In-flight requests are abruptly terminated
  • Database connections and telemetry resources may not be properly released
  • Kubernetes termination grace period will timeout, forcing a hard kill

Implement signal handling (e.g., using signal.Notify for syscall.SIGTERM and syscall.SIGINT) to call Stop() on all three servers and await their completion before exiting.

🤖 Fix all issues with AI agents
In @pkg/logger/logger.go:
- Around line 163-170: InitGlobalLogger currently writes the package-level
globalLogger without synchronization causing a race if called concurrently;
protect initialization by adding a package-level sync.Once (e.g., var initOnce
sync.Once) and wrap the existing setup (NewHyperFleetHandler(cfg), globalLogger
= slog.New(handler), slog.SetDefault(globalLogger)) inside initOnce.Do so the
logger is created exactly once; update InitGlobalLogger to call initOnce.Do with
a closure performing the existing logic (or alternatively document that
InitGlobalLogger must only be invoked during single-threaded startup).
🧹 Nitpick comments (8)
pkg/db/db_session/testcontainer.go (1)

57-58: Prefer structured logging over printf-style formatting.

Multiple error logging statements use printf-style formatting (Errorf, Infof) rather than structured logging. Line 171 demonstrates the preferred structured approach with key-value pairs. For consistency and better log analysis, consider using structured logging throughout.

♻️ Example refactors for structured logging
 if err != nil {
-	pkglogger.Errorf(ctx, "Failed to start PostgreSQL testcontainer: %s", err)
+	pkglogger.Error(ctx, "Failed to start PostgreSQL testcontainer", "error", err)
 	os.Exit(1)
 }
 if err != nil {
-	pkglogger.Errorf(ctx, "Failed to get connection string from testcontainer: %s", err)
+	pkglogger.Error(ctx, "Failed to get connection string from testcontainer", "error", err)
 	os.Exit(1)
 }
-pkglogger.Infof(ctx, "PostgreSQL testcontainer started at: %s", connStr)
+pkglogger.Info(ctx, "PostgreSQL testcontainer started", "connection_string", connStr)

Similar patterns can be applied to lines 75-76, 99-100, 106-107, and 181.

Based on coding guidelines emphasizing structured logging.

Also applies to: 66-67, 75-76, 99-100, 106-107, 181-181

pkg/errors/errors.go (1)

117-118: Prefer structured logging with error code as a field.

The current implementation uses printf-style formatting. For better log analysis and consistency with the structured logging approach, consider capturing the error code as a structured field.

♻️ Proposed refactor
 if !exists {
 	ctx := context.Background()
-	logger.Errorf(ctx, "Undefined error code used: %d", code)
+	logger.Error(ctx, "Undefined error code used", "code", code)
 	err = &ServiceError{
 		Code:     ErrorGeneral,
 		Reason:   "Unspecified error",
 		HttpCode: 500,
 	}
 }

Based on coding guidelines emphasizing structured logging.

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

58-68: Consider implementing additional http.ResponseWriter optional interfaces.

The responseWriter wrapper only implements http.ResponseWriter. If downstream handlers use http.Hijacker (WebSockets), http.Flusher (SSE), or http.Pusher (HTTP/2 push), they will fail type assertions.

♻️ Optional: Interface delegation pattern
// Unwrap returns the underlying ResponseWriter for interface upgrades
func (rw *responseWriter) Unwrap() http.ResponseWriter {
	return rw.ResponseWriter
}

Go 1.20+ http.ResponseController uses Unwrap() to access underlying interfaces.

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

54-66: Consider injecting version at build time.

The hardcoded Version: "unknown" works, but consider using linker flags (-ldflags) to inject the actual version at build time for better observability.

// Inject at build time with: go build -ldflags "-X main.version=1.2.3"
var version = "unknown"

func initDefaultLogger() {
    cfg := &logger.LogConfig{
        // ...
        Version: version,
        // ...
    }
}
cmd/hyperfleet-api/environments/framework.go (1)

176-184: Minor: Inconsistent logging style with other methods in this file.

This method uses structured logging (logger.Error with key-value pairs) while other methods use formatted logging (logger.Errorf). Consider aligning for consistency.

♻️ Option 1: Use Errorf for consistency
 func setConfigDefaults(flags *pflag.FlagSet, defaults map[string]string) error {
 	ctx := context.Background()
 	for name, value := range defaults {
 		if err := flags.Set(name, value); err != nil {
-			logger.Error(ctx, "Error setting flag", "flag", name, "error", err)
+			logger.Errorf(ctx, "Error setting flag %s: %s", name, err)
 			return err
 		}
 	}
 	return nil
 }

Alternatively, migrate all methods to structured logging style for better log aggregation.

cmd/hyperfleet-api/servecmd/cmd.go (2)

57-61: Consider adding a timeout to the OpenTelemetry shutdown context.

The deferred shutdown uses context.Background(), which could block indefinitely. Consider using a timeout context to ensure graceful shutdown completes within a reasonable timeframe.

♻️ Proposed enhancement
 		} else {
 			defer func() {
-				if err := telemetry.Shutdown(context.Background(), tp); err != nil {
+				shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+				defer cancel()
+				if err := telemetry.Shutdown(shutdownCtx, tp); err != nil {
 					logger.Error(ctx, "Failed to shutdown OpenTelemetry", "error", err)
 				}
 			}()

121-122: Log hostname retrieval errors for better observability.

The error from os.Hostname() is silently ignored. While falling back to an empty hostname is acceptable, logging the error would aid debugging in environments where hostname resolution fails.

♻️ Proposed enhancement
 	// Get hostname
-	hostname, _ := os.Hostname()
+	hostname, err := os.Hostname()
+	if err != nil {
+		logger.Warn(ctx, "Failed to get hostname", "error", err)
+		hostname = ""
+	}
pkg/logger/logger.go (1)

282-284: Use a typed constant for the "txid" context key.

The hardcoded string "txid" should be defined as a typed constant (similar to TraceIDCtxKey, SpanIDCtxKey) for type safety and consistency with the rest of the codebase.

♻️ Proposed refactor

In pkg/logger/context.go, add:

 type ResourceTypeKey string
 type ResourceIDKey string
+type TransactionIDKey string

 const (
 	OpIDKey            OperationIDKey       = "opID"
 	OpIDHeader         OperationIDHeaderKey = "X-Operation-ID"
 	TraceIDCtxKey      TraceIDKey           = "trace_id"
 	SpanIDCtxKey       SpanIDKey            = "span_id"
 	ClusterIDCtxKey    ClusterIDKey         = "cluster_id"
 	ResourceTypeCtxKey ResourceTypeKey      = "resource_type"
 	ResourceIDCtxKey   ResourceIDKey        = "resource_id"
+	TxIDCtxKey         TransactionIDKey     = "txid"
 )

Then update the usage in logger.go:

-	if txid, ok := ctx.Value("txid").(int64); ok {
+	if txid, ok := ctx.Value(TxIDCtxKey).(int64); ok {
 		attrs = append(attrs, slog.Int64("tx_id", txid))
 	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e80608 and 96c7f9e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (32)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/middleware/otel.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (5)
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/auth/authz_middleware_mock.go
  • README.md
  • pkg/api/error.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • docs/logging.md
🧰 Additional context used
📓 Path-based instructions (8)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Files:

  • test/helper.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/handlers/openapi.go
  • pkg/telemetry/otel.go
  • pkg/logger/logger_test.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/middleware/otel.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/migrations.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes
Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error
Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL
Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table
Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support
Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes
Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Files:

  • pkg/handlers/openapi.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Files:

  • pkg/handlers/openapi.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Files:

  • pkg/handlers/openapi.go
pkg/{handlers,services,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Files:

  • pkg/handlers/openapi.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use PostgreSQL 14.2 with GORM ORM for database operations

Files:

  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
pkg/{dao,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Files:

  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
test/integration/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

test/integration/**/*_test.go: Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage
All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Files:

  • test/integration/integration_test.go
🧠 Learnings (25)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/handlers/openapi.go
  • pkg/telemetry/otel.go
  • pkg/logger/logger_test.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/main.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/migrations.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/context.go
  • pkg/logger/logger.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/**/*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • test/integration/integration_test.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • test/helper.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/migrate/**/*.go : Use GORM AutoMigrate at startup via ./bin/hyperfleet-api migrate command; migrations are idempotent and track applied migrations

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/main.go
  • pkg/db/migrations.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/main.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for database operations

Applied to files:

  • test/helper.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/openapi/model_*.go : Generated Go models from openapi-generator-cli include JSON tags and validation metadata; do not manually edit generated model_*.go files

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Environment-based configuration: use OCM_ENV variable to select development, unit_testing, integration_testing, or production configuration

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/environments/framework.go
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Applied to files:

  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/middleware/otel.go
  • pkg/db/migrations.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/context.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO methods must accept context.Context as the first parameter for transaction propagation

Applied to files:

  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/logger/context.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All database sessions must be retrieved from context via db.NewContext(ctx) rather than creating new database connections

Applied to files:

  • pkg/errors/errors.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Applied to files:

  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,services}/**/*.go : Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Applied to files:

  • pkg/telemetry/otel.go
  • pkg/db/migrations.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/**/*{auth,middleware}*.go : JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Applied to files:

  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/migrations/**/*.go : Store spec and conditions as JSONB in PostgreSQL to enable complex field queries

Applied to files:

  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : Always retrieve database sessions from context in DAO methods; never create direct gorm.Open() connections

Applied to files:

  • pkg/db/migrations.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Applied to files:

  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage

Applied to files:

  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
🧬 Code graph analysis (17)
test/helper.go (1)
pkg/logger/logger.go (6)
  • Error (232-234)
  • Info (224-226)
  • Debug (220-222)
  • Warn (228-230)
  • LogConfig (26-33)
  • InitGlobalLogger (166-170)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (248-250)
cmd/hyperfleet-api/server/healthcheck_server.go (1)
pkg/logger/logger.go (2)
  • Infof (240-242)
  • Info (224-226)
pkg/handlers/openapi.go (1)
pkg/logger/logger.go (1)
  • Info (224-226)
cmd/hyperfleet-api/server/api_server.go (5)
pkg/logger/ocm_bridge.go (1)
  • NewOCMLoggerBridge (19-23)
pkg/logger/logger.go (2)
  • Info (224-226)
  • Error (232-234)
cmd/hyperfleet-api/server/server.go (1)
  • Server (13-18)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
cmd/hyperfleet-api/server/server.go (1)
pkg/logger/logger.go (1)
  • Error (232-234)
cmd/hyperfleet-api/servecmd/cmd.go (3)
pkg/logger/logger.go (6)
  • ParseLogLevel (181-194)
  • ParseLogFormat (197-206)
  • FormatJSON (22-22)
  • ParseLogOutput (209-218)
  • LogConfig (26-33)
  • InitGlobalLogger (166-170)
cmd/hyperfleet-api/environments/framework.go (1)
  • Environment (54-56)
pkg/telemetry/otel.go (2)
  • InitTraceProvider (16-57)
  • Shutdown (60-65)
cmd/hyperfleet-api/server/routes.go (3)
pkg/middleware/otel.go (1)
  • OTelMiddleware (18-50)
pkg/middleware/masking.go (1)
  • NewMaskingMiddleware (20-26)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)
  • RequestLoggingMiddleware (14-56)
cmd/hyperfleet-api/main.go (1)
pkg/logger/logger.go (4)
  • Error (232-234)
  • LogConfig (26-33)
  • FormatJSON (22-22)
  • InitGlobalLogger (166-170)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (2)
pkg/middleware/masking.go (1)
  • MaskingMiddleware (13-17)
pkg/logger/logger.go (1)
  • Info (224-226)
pkg/middleware/otel.go (1)
pkg/logger/context.go (2)
  • WithTraceID (30-32)
  • WithSpanID (35-37)
pkg/logger/ocm_bridge.go (1)
pkg/logger/logger.go (5)
  • Debug (220-222)
  • GetLogger (173-178)
  • Info (224-226)
  • Warn (228-230)
  • Error (232-234)
pkg/db/migrations.go (2)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/logger/logger.go (2)
  • NewOCMLogger (274-297)
  • Error (232-234)
test/integration/integration_test.go (1)
pkg/logger/logger.go (2)
  • Infof (240-242)
  • Warn (228-230)
pkg/db/db_session/testcontainer.go (2)
pkg/logger/logger.go (4)
  • Info (224-226)
  • Errorf (248-250)
  • Infof (240-242)
  • Error (232-234)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/logger/context.go (1)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/logger/logger.go (1)
pkg/logger/context.go (6)
  • TraceIDCtxKey (22-22)
  • SpanIDCtxKey (23-23)
  • OpIDKey (20-20)
  • ClusterIDCtxKey (24-24)
  • ResourceTypeCtxKey (25-25)
  • ResourceIDCtxKey (26-26)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

🔇 Additional comments (30)
pkg/db/migrations.go (1)

30-38: LGTM! Structured logging implementation is correct.

The migration error handling now correctly uses logger.NewOCMLogger(ctx) with the Extra() method for structured logging, properly capturing the migration ID and error context. This aligns with the coding guidelines and addresses the previous review feedback.

pkg/handlers/openapi.go (1)

25-25: LGTM! Context-aware logging for initialization.

The logging statements appropriately use context-aware logging for initialization messages. The simple logger.Info() calls are suitable for these straightforward informational messages without additional structured fields.

Also applies to: 43-43, 53-53

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

28-33: LGTM! Proper structured logging implementation.

The error handling correctly uses context-aware structured logging with key-value pairs ("error", err), which aligns with the coding guidelines and enables better log analysis.

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

44-62: LGTM! Logger migration completed correctly.

The migration from glog to the structured logger is correct. Using context.Background() with the top-level logger.Infof/Info functions is appropriate for server startup logging, where request-scoped context is not available.

test/helper.go (2)

544-555: LGTM! Test logger initialization is well-structured.

The test logger configuration is appropriate for testing:

  • Text format for readability
  • Reasonable defaults (component, version, hostname)
  • Uses the global logger initialization mechanism

72-74: LGTM! Consistent error handling pattern.

The replacement of log.Fatal and glog fatal calls with logger.Error(ctx, ...) followed by os.Exit(1) is correct and provides better control over error logging and exit behavior.

Also applies to: 84-86, 96-98

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

71-81: LGTM! Middleware initialization follows best practices.

The changes correctly implement:

  • Conditional OpenTelemetry middleware based on configuration
  • Single masking middleware instance reused across requests (avoiding unnecessary allocations)
  • Proper middleware ordering: OperationID → OTel → RequestLogging ensures context is enriched before logging
test/integration/integration_test.go (1)

17-43: LGTM! Excellent use of structured logging.

The migration to context-aware logging is correct, and line 40 demonstrates the proper use of structured logging with key-value pairs:

logger.Warn(ctx, "Schema file not found, skipping OPENAPI_SCHEMA_PATH setup", "schema_path", schemaPath, "error", err)

This pattern provides better log aggregation and filtering capabilities.

pkg/middleware/otel.go (1)

18-50: LGTM! The middleware correctly addresses the span ordering issue.

The implementation properly wraps enrichedHandler with otelhttp.NewHandler, ensuring the span is created before trace/span IDs are extracted. The validity check at line 25 correctly handles cases where no parent trace context exists.

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

39-56: LGTM! Authentication logger bridge correctly wired.

The NewOCMLoggerBridge is properly instantiated with a background context for the authentication handler initialization. This aligns with the project's structured logging approach per the coding guidelines.


137-152: LGTM! Server startup error handling correctly migrated.

The replacement of glog.Fatal with logger.Error followed by os.Exit(1) provides equivalent behavior while integrating with the new structured logging system. The database close error is also properly logged.

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

14-56: Good refactor! Masker is now properly reused across requests.

The signature change to accept a pre-constructed *middleware.MaskingMiddleware addresses the previous concern about per-request instantiation overhead.

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

26-34: LGTM! Logger initialization before first log is correct.

Initializing the logger before any logging ensures consistent structured output from the start. The empty flag parsing for cobra compatibility is a known pattern when mixing flag with cobra.

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

67-120: LGTM! Environment initialization correctly migrated to structured logging.

The migration from glog.Fatal to logger.Errorf followed by os.Exit(1) preserves the fail-fast behavior while integrating with the new logging system.

pkg/logger/logger_test.go (3)

125-195: Consider test isolation for global logger state.

Each test calls InitGlobalLogger(cfg) which modifies a package-level global. If tests run in parallel (t.Parallel()), they could interfere with each other. Currently tests don't use t.Parallel(), so this is safe, but worth noting for future maintenance.


400-417: Good defensive test for uninitialized logger fallback.

The test correctly saves and restores the global logger state, and verifies that GetLogger() falls back to slog.Default() when uninitialized.


308-360: Good coverage of stack trace behavior.

The test verifies that stack traces are captured for error level but not for info level, which aligns with the expected behavior for production debugging.

pkg/logger/ocm_bridge.go (3)

26-43: Context parameter is used correctly in logging methods.

Looking more closely, the logging methods correctly pass the ctx parameter received from the SDK to GetLogger().*Context() methods (e.g., GetLogger().DebugContext(ctx, ...)). This ensures request-scoped context values are properly propagated.


51-68: The *Enabled() methods use stored context, not call-site context.

The DebugEnabled(), InfoEnabled(), etc. methods use b.ctx (the context from construction time) rather than a call-site context. This is acceptable since these methods don't take a context parameter per the sdk.Logger interface, but be aware that log-level decisions are based on the construction-time context.


45-49: os.Exit(1) is correct — no changes needed.

The OCM SDK Logger interface expects Fatal to log the error and immediately exit the process (not panic). The current implementation correctly calls os.Exit(1) after logging. The use of ErrorContext with a "FATAL: " prefix is a pragmatic workaround, since the standard library slog does not provide a dedicated FATAL log level.

pkg/logger/context.go (2)

9-27: Excellent use of type-safe context keys.

The use of distinct types for context keys (OperationIDKey, TraceIDKey, SpanIDKey, etc.) is a Go best practice that prevents context key collisions. This ensures type safety and makes the codebase more maintainable.


54-63: KSUID-based operation ID generation looks correct.

The WithOpID function properly checks for existing operation IDs before generating a new one, preventing unnecessary allocations. The KSUID provides a sortable, globally unique identifier suitable for distributed tracing.

pkg/logger/logger.go (6)

15-33: Well-structured logging configuration.

The LogFormat enum and LogConfig struct provide a clean abstraction for logger configuration with appropriate fields for component, version, and hostname. This aligns well with structured logging best practices.


44-80: Handler implementation follows slog patterns correctly.

The NewHyperFleetHandler properly wraps slog's built-in handlers (JSON/Text) with custom attribute replacement. The ReplaceAttr function correctly standardizes field names (timestamp, level, message) across output formats.


90-107: Context field extraction is comprehensive and correct.

The handler properly extracts correlation fields from context (trace_id, span_id, request_id, cluster_id, resource_type, resource_id) and adds them to log records. The type assertions with the ok pattern safely handle missing values.

Based on learnings, structured logging with context-aware fields including operation ID, account ID, and transaction ID is required, which this implementation provides.


142-161: Stack trace filtering is appropriate.

The captureStackTrace function correctly filters out runtime and testing frames while limiting depth to 15 frames, providing useful debugging information without noise.


273-297: OCMLogger backward compatibility maintained correctly.

The NewOCMLogger function properly extracts account_id and tx_id from context and creates a logger with those attributes. This maintains backward compatibility while leveraging the new slog infrastructure.

Based on learnings, structured logging via logger.NewOCMLogger(ctx) with context-aware log fields is required, which this implementation provides.


350-354: Fatal method behavior is appropriate.

The Fatal method logs an error and calls os.Exit(1), which is the expected behavior for fatal errors. The "FATAL: " prefix clearly distinguishes fatal errors in logs.

go.mod (2)

72-72: Intentional transition of glog from direct to indirect dependency is confirmed.

No direct imports of github.com/golang/glog remain in the codebase. The migration to log/slog has been successfully completed across all relevant files (logger.go, logger_test.go, ocm_bridge.go, main.go, servecmd/cmd.go, and test helper). The glog package remains as an indirect dependency, which is expected if it's pulled in by other transitive dependencies.


32-36: The OpenTelemetry dependencies at versions v1.39.0 and v0.49.0 are compatible with the project's Go version 1.24.9 and have no known security vulnerabilities. No action is required.

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

Caution

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

⚠️ Outside diff range comments (5)
pkg/db/db_session/testcontainer.go (3)

41-111: Replace os.Exit calls with error returns in test infrastructure.

The Init method calls os.Exit(1) on multiple error paths (lines 58, 67, 76, 100, 107), which terminates the entire test process. This prevents:

  • Other tests from running
  • Test frameworks from reporting failures properly
  • Cleanup code from executing
  • Proper debugging and error inspection

Refactor Init to return an error, and update NewTestcontainerFactory to handle it appropriately.

🔧 Proposed refactor to return errors instead of calling os.Exit
-func (f *Testcontainer) Init(config *config.DatabaseConfig) {
+func (f *Testcontainer) Init(config *config.DatabaseConfig) error {
 	ctx := context.Background()
 
 	pkglogger.Info(ctx, "Starting PostgreSQL testcontainer...")
 
 	// Create PostgreSQL container
 	container, err := postgres.Run(ctx,
 		"postgres:14.2",
 		postgres.WithDatabase(config.Name),
 		postgres.WithUsername(config.Username),
 		postgres.WithPassword(config.Password),
 		testcontainers.WithWaitStrategy(
 			wait.ForListeningPort("5432/tcp").
 				WithStartupTimeout(60*time.Second)),
 	)
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to start PostgreSQL testcontainer: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to start PostgreSQL testcontainer: %w", err)
 	}
 
 	f.container = container
 
 	// Get connection string from container
 	connStr, err := container.ConnectionString(ctx, "sslmode=disable")
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to get connection string from testcontainer: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to get connection string: %w", err)
 	}
 
 	pkglogger.Infof(ctx, "PostgreSQL testcontainer started at: %s", connStr)
 
 	// Open SQL connection
 	f.sqlDB, err = sql.Open("postgres", connStr)
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to connect to testcontainer database: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to connect to database: %w", err)
 	}
 
 	// ... rest of method
 	
 	f.g2, err = gorm.Open(gormpostgres.New(gormpostgres.Config{
 		Conn:                 f.sqlDB,
 		PreferSimpleProtocol: true,
 	}), conf)
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to connect GORM to testcontainer database: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to connect GORM: %w", err)
 	}
 
 	// Run migrations
 	pkglogger.Info(ctx, "Running database migrations on testcontainer...")
 	if err := db.Migrate(f.g2); err != nil {
 		pkglogger.Errorf(ctx, "Failed to run migrations on testcontainer: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to run migrations: %w", err)
 	}
 
 	pkglogger.Info(ctx, "Testcontainer database initialized successfully")
+	return nil
 }

Then update the constructor:

 func NewTestcontainerFactory(config *config.DatabaseConfig) *Testcontainer {
 	conn := &Testcontainer{
 		config: config,
 	}
-	conn.Init(config)
+	if err := conn.Init(config); err != nil {
+		// Let the caller handle the error appropriately
+		panic(fmt.Sprintf("failed to initialize testcontainer: %v", err))
+	}
 	return conn
 }

Note: Using panic in a constructor is still not ideal, but it's significantly better than os.Exit. Consider refactoring NewTestcontainerFactory to also return an error for proper error handling.


44-110: Use structured logging consistently with logger.NewOCMLogger(ctx).

The code mixes printf-style logging (e.g., pkglogger.Infof, pkglogger.Errorf) with structured logging (line 171 uses key-value pairs). According to coding guidelines, structured logging should be used via logger.NewOCMLogger(ctx) with context-aware log fields.

While test infrastructure may not need operation/account IDs, using structured logging consistently improves log parsing and observability.

♻️ Proposed refactor to use structured logging consistently
 func (f *Testcontainer) Init(config *config.DatabaseConfig) {
 	ctx := context.Background()
+	log := pkglogger.NewOCMLogger(ctx)
 
-	pkglogger.Info(ctx, "Starting PostgreSQL testcontainer...")
+	log.Info("Starting PostgreSQL testcontainer")
 
 	// Create PostgreSQL container
 	container, err := postgres.Run(ctx,
 		// ... config ...
 	)
 	if err != nil {
-		pkglogger.Errorf(ctx, "Failed to start PostgreSQL testcontainer: %s", err)
+		log.Error("Failed to start PostgreSQL testcontainer", "error", err)
 		os.Exit(1)
 	}
 	
 	// ... rest of method similarly updated
 	
-	pkglogger.Infof(ctx, "PostgreSQL testcontainer started at: %s", connStr)
+	log.Info("PostgreSQL testcontainer started", "connection_string", connStr)

Apply similar changes throughout the file for consistency.

Based on coding guidelines.


155-175: Consider returning errors from ResetDB.

The ResetDB method logs truncation errors (line 171) but doesn't return them, which could leave the database in an inconsistent state. Callers have no way to detect if the reset succeeded, potentially leading to flaky tests running with stale data.

♻️ Proposed refactor to return errors
-func (f *Testcontainer) ResetDB() {
+func (f *Testcontainer) ResetDB() error {
 	// For testcontainers, we can just truncate all tables
 	ctx := context.Background()
 	g2 := f.New(ctx)
 
 	// Truncate all business tables in the correct order (respecting FK constraints)
 	// Using CASCADE to handle foreign key constraints automatically
 	tables := []string{
 		"adapter_statuses", // Polymorphic table, no FK constraints
 		"node_pools",       // Has FK to clusters
 		"clusters",         // Referenced by node_pools
 		"events",           // Independent table
 	}
 	for _, table := range tables {
 		if g2.Migrator().HasTable(table) {
 			if err := g2.Exec(fmt.Sprintf("TRUNCATE TABLE %s CASCADE", table)).Error; err != nil {
 				pkglogger.Error(ctx, "Error truncating table", "table", table, "error", err)
+				return fmt.Errorf("failed to truncate table %s: %w", table, err)
 			}
 		}
 	}
+	return nil
 }
cmd/hyperfleet-api/environments/framework.go (1)

67-120: Inconsistent error handling: some paths return error, others call os.Exit(1).

The Initialize method has mixed error handling patterns:

  • Lines 94-97: Returns error from LoadClients
  • Lines 109-112: Returns error from Seed
  • Other failures: Call logger.Errorf followed by os.Exit(1)

This inconsistency makes error handling unpredictable for callers. Consider either consistently returning errors (preferred for testability) or documenting why certain failures are unrecoverable.

♻️ Suggested refactor for consistent error handling
 func (e *Env) Initialize() error {
 	ctx := context.Background()
 	logger.Infof(ctx, "Initializing %s environment", e.Name)
 
 	envImpl, found := environments[e.Name]
 	if !found {
-		logger.Errorf(ctx, "Unknown runtime environment: %s", e.Name)
-		os.Exit(1)
+		return fmt.Errorf("unknown runtime environment: %s", e.Name)
 	}
 
 	if err := envImpl.OverrideConfig(e.Config); err != nil {
-		logger.Errorf(ctx, "Failed to configure ApplicationConfig: %s", err)
-		os.Exit(1)
+		return fmt.Errorf("failed to configure ApplicationConfig: %w", err)
 	}
 	// ... apply similar pattern to other error paths
 }

Then let the caller (e.g., runServe) decide whether to log and exit.

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

77-92: No graceful shutdown or error propagation from server goroutines.

The three servers are started in goroutines with no mechanism to:

  1. Detect if a server fails to start
  2. Gracefully shut down on signals (SIGTERM/SIGINT)
  3. Coordinate shutdown of all servers

The select {} blocks forever, making it impossible to cleanly terminate the process.

♻️ Suggested pattern for graceful shutdown
// Handle shutdown signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

errChan := make(chan error, 3)

go func() {
	apiserver := server.NewAPIServer()
	if err := apiserver.Start(); err != nil {
		errChan <- fmt.Errorf("API server: %w", err)
	}
}()
// ... similar for other servers

select {
case sig := <-sigChan:
	logger.Info(ctx, "Received signal, shutting down", "signal", sig)
	// Trigger graceful shutdown of servers
case err := <-errChan:
	logger.Error(ctx, "Server failed", "error", err)
	os.Exit(1)
}
🤖 Fix all issues with AI agents
In @cmd/hyperfleet-api/servecmd/cmd.go:
- Around line 95-136: initLogger() never updates the active logger because
InitGlobalLogger uses sync.Once and initDefaultLogger() ran earlier; add a
reconfiguration path by implementing a ReconfigureGlobalLogger(cfg
*logger.LogConfig) in the logger package that creates a new handler
(NewHyperFleetHandler or equivalent), constructs a new slog.Logger, sets
slog.SetDefault(newLogger) and replaces the package global, then call
logger.ReconfigureGlobalLogger(logConfig) from initLogger() instead of
InitGlobalLogger so environment config takes effect without removing the
existing InitGlobalLogger usage.

In @pkg/logger/logger.go:
- Around line 55-57: The code handling the attribute with key slog.LevelKey uses
an unchecked type assertion a.Value.Any().(slog.Level) which can panic; update
the block that checks slog.LevelKey (around the attr handling in
pkg/logger/logger.go) to perform a safe type assertion (e.g., use the "val, ok
:= a.Value.Any().(slog.Level)" pattern or a type switch) and handle the non-ok
case by falling back to a safe string representation (e.g.,
fmt.Sprint(a.Value.Any()) or a default level string) so the function returns a
slog.Attr with a non-panicking, lowercased level string in all cases.

In @pkg/telemetry/otel.go:
- Line 10: Update the semconv import to the newer module version: replace the
current import alias semconv referencing
"go.opentelemetry.io/otel/semconv/v1.11.0" with the compatible
"go.opentelemetry.io/otel/semconv/v1.37.0" so the package used by the code
(semconv) matches OTEL SDK v1.39.0; locate the import statement that defines
semconv and update its version string accordingly and then run go mod tidy to
refresh dependencies.
🧹 Nitpick comments (5)
pkg/db/db_session/testcontainer.go (1)

177-186: Consider returning an error or cleanup function from NewListener.

The method silently fails if it cannot obtain a connection string (line 181-182). While logging and returning early is acceptable for test infrastructure, returning an error would allow callers to handle failures explicitly.

Alternatively, consider returning a cleanup function (like io.Closer) to allow callers to manage listener lifecycle.

♻️ Proposed refactor options

Option 1: Return error

-func (f *Testcontainer) NewListener(ctx context.Context, channel string, callback func(id string)) {
+func (f *Testcontainer) NewListener(ctx context.Context, channel string, callback func(id string)) error {
 	// Get the connection string for the listener
 	connStr, err := f.container.ConnectionString(ctx, "sslmode=disable")
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to get connection string for listener: %s", err)
-		return
+		return fmt.Errorf("failed to get connection string for listener: %w", err)
 	}
 
 	newListener(ctx, connStr, channel, callback)
+	return nil
 }

Option 2: Return cleanup function

-func (f *Testcontainer) NewListener(ctx context.Context, channel string, callback func(id string)) {
+func (f *Testcontainer) NewListener(ctx context.Context, channel string, callback func(id string)) (func() error, error) {
 	connStr, err := f.container.ConnectionString(ctx, "sslmode=disable")
 	if err != nil {
 		pkglogger.Errorf(ctx, "Failed to get connection string for listener: %s", err)
-		return
+		return nil, fmt.Errorf("failed to get connection string for listener: %w", err)
 	}
 
-	newListener(ctx, connStr, channel, callback)
+	cleanup := newListener(ctx, connStr, channel, callback)
+	return cleanup, nil
 }

Note: Option 2 assumes newListener can be refactored to return a cleanup function.

pkg/logger/context.go (1)

60-96: Consider consistent return signatures for getter functions.

GetOperationID returns a string (empty on miss), while GetTraceID, GetSpanID, etc., return (string, bool). This inconsistency may cause confusion for callers who need to distinguish between "empty value" and "value not found."

♻️ Refactor to consistent return signature
-// GetOperationID retrieves operation ID from context
-func GetOperationID(ctx context.Context) string {
-	if opID, ok := ctx.Value(OpIDKey).(string); ok {
-		return opID
-	}
-	return ""
-}
+// GetOperationID retrieves operation ID from context
+func GetOperationID(ctx context.Context) (string, bool) {
+	opID, ok := ctx.Value(OpIDKey).(string)
+	return opID, ok
+}

Note: This would require updating all call sites that use GetOperationID.

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

59-69: Consider implementing additional http.ResponseWriter interfaces.

The responseWriter wrapper correctly captures the status code, but it may break middleware that requires http.Flusher, http.Hijacker, or http.Pusher interfaces (e.g., for SSE, WebSockets, or HTTP/2). If your API uses any of these features, consider implementing interface assertions.

♻️ Optional enhancement for interface compatibility
 // responseWriter wraps http.ResponseWriter to capture status code
 type responseWriter struct {
 	http.ResponseWriter
 	statusCode int
 }

 // WriteHeader captures the status code before writing
 func (rw *responseWriter) WriteHeader(code int) {
 	rw.statusCode = code
 	rw.ResponseWriter.WriteHeader(code)
 }
+
+// Flush implements http.Flusher if the underlying writer supports it
+func (rw *responseWriter) Flush() {
+	if f, ok := rw.ResponseWriter.(http.Flusher); ok {
+		f.Flush()
+	}
+}
cmd/hyperfleet-api/main.go (1)

30-34: Redundant flag parsing before Cobra.

Parsing flag.CommandLine with an empty slice before Cobra's Execute() appears unnecessary. Cobra handles flag parsing internally. Unless there's a specific compatibility requirement with packages that register flags via flag package, this can likely be removed.

♻️ Consider removing if not needed
 func main() {
 	// Initialize logger first (before any logging occurs)
 	initDefaultLogger()
 	ctx := context.Background()
 
-	// Parse flags (needed for cobra compatibility)
-	if err := flag.CommandLine.Parse([]string{}); err != nil {
-		logger.Error(ctx, "Failed to parse flags", "error", err)
-		os.Exit(1)
-	}
-
 	rootCmd := &cobra.Command{

If this is required for compatibility with glog or another library that registers flags, please add a comment explaining why.

pkg/logger/logger.go (1)

179-184: resetForTesting is unsafe for concurrent tests.

Resetting initOnce = sync.Once{} while other goroutines might be accessing globalLogger introduces a data race. Consider using a sync.RWMutex to protect both the logger and the Once, or document that tests using this function must run serially.

♻️ Safer approach with mutex
 var (
 	globalLogger *slog.Logger
 	initOnce     sync.Once
+	loggerMu     sync.RWMutex
 )

 func InitGlobalLogger(cfg *LogConfig) {
 	initOnce.Do(func() {
+		loggerMu.Lock()
+		defer loggerMu.Unlock()
 		handler := NewHyperFleetHandler(cfg)
 		globalLogger = slog.New(handler)
 		slog.SetDefault(globalLogger)
 	})
 }

 func resetForTesting() {
+	loggerMu.Lock()
+	defer loggerMu.Unlock()
 	globalLogger = nil
 	initOnce = sync.Once{}
 }

 func GetLogger() *slog.Logger {
+	loggerMu.RLock()
+	defer loggerMu.RUnlock()
 	if globalLogger == nil {
 		return slog.Default()
 	}
 	return globalLogger
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96c7f9e and 9ad6eb7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/http.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/middleware/otel.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (5)
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/logging.go
🚧 Files skipped from review as they are similar to previous changes (11)
  • cmd/hyperfleet-api/server/server.go
  • pkg/auth/authz_middleware_mock.go
  • README.md
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • test/integration/integration_test.go
  • docs/logging.md
  • pkg/db/migrations.go
  • pkg/api/error.go
  • cmd/hyperfleet-api/server/api_server.go
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Files:

  • test/helper.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/logger/http.go
  • pkg/middleware/otel.go
  • pkg/db/db_session/testcontainer.go
  • pkg/telemetry/otel.go
  • pkg/errors/errors.go
  • pkg/logger/context.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/logger/logger.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use PostgreSQL 14.2 with GORM ORM for database operations

Files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
pkg/{dao,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
pkg/{handlers,services,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/openapi.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes
Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error
Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL
Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table
Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support
Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes
Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Files:

  • pkg/handlers/openapi.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Files:

  • pkg/handlers/openapi.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Files:

  • pkg/handlers/openapi.go
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Applied to files:

  • test/helper.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/logger/http.go
  • pkg/db/db_session/testcontainer.go
  • pkg/telemetry/otel.go
  • pkg/errors/errors.go
  • pkg/logger/context.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/logger/logger.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Applied to files:

  • test/helper.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/middleware/otel.go
  • pkg/db/db_session/testcontainer.go
  • pkg/errors/errors.go
  • pkg/logger/context.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/**/*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • test/helper.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/migrate/**/*.go : Use GORM AutoMigrate at startup via ./bin/hyperfleet-api migrate command; migrations are idempotent and track applied migrations

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for database operations

Applied to files:

  • test/helper.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes

Applied to files:

  • test/helper.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/openapi/model_*.go : Generated Go models from openapi-generator-cli include JSON tags and validation metadata; do not manually edit generated model_*.go files

Applied to files:

  • test/helper.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Environment-based configuration: use OCM_ENV variable to select development, unit_testing, integration_testing, or production configuration

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All database sessions must be retrieved from context via db.NewContext(ctx) rather than creating new database connections

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : Always retrieve database sessions from context in DAO methods; never create direct gorm.Open() connections

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/migrations/**/*.go : Store spec and conditions as JSONB in PostgreSQL to enable complex field queries

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : NodePool owner_references.id must enforce foreign key relationship to parent cluster to maintain referential integrity

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO methods must accept context.Context as the first parameter for transaction propagation

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • pkg/errors/errors.go
  • pkg/logger/context.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,services}/**/*.go : Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • pkg/telemetry/otel.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Applied to files:

  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support

Applied to files:

  • pkg/handlers/openapi.go
🧬 Code graph analysis (8)
test/helper.go (2)
pkg/logger/logger.go (7)
  • Error (246-248)
  • Info (238-240)
  • Debug (234-236)
  • Warn (242-244)
  • LogConfig (27-34)
  • FormatText (21-21)
  • InitGlobalLogger (171-177)
cmd/hyperfleet-api/server/api_server.go (1)
  • NewAPIServer (30-101)
cmd/hyperfleet-api/migrate/cmd.go (4)
pkg/logger/logger.go (2)
  • Error (246-248)
  • Info (238-240)
pkg/db/db_session/default.go (1)
  • NewProdFactory (33-37)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
  • New (112-132)
cmd/hyperfleet-api/environments/framework.go (3)
pkg/logger/logger.go (4)
  • Infof (254-256)
  • Errorf (262-264)
  • Info (238-240)
  • Error (246-248)
pkg/client/ocm/client.go (3)
  • Config (17-24)
  • NewClientMock (47-53)
  • NewClient (26-45)
cmd/hyperfleet-api/environments/types.go (2)
  • Database (36-38)
  • Clients (67-69)
pkg/middleware/otel.go (1)
pkg/logger/context.go (2)
  • WithTraceID (25-27)
  • WithSpanID (30-32)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (262-264)
pkg/logger/context.go (1)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/handlers/openapi.go (1)
pkg/logger/logger.go (1)
  • Info (238-240)
cmd/hyperfleet-api/server/routes.go (3)
pkg/middleware/otel.go (1)
  • OTelMiddleware (18-50)
pkg/middleware/masking.go (1)
  • NewMaskingMiddleware (20-26)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)
  • RequestLoggingMiddleware (15-57)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

🔇 Additional comments (21)
go.mod (1)

26-26: LGTM! Dependency updates are well-structured.

The OpenTelemetry dependencies are consistently versioned at v1.39.0, and the ksuid package has been updated to v1.0.4 as previously recommended. The migration of glog from direct to indirect dependency aligns with the structured logging refactor.

Also applies to: 32-36

pkg/logger/context.go (1)

14-22: Context key definitions are well-structured.

The use of an unexported contextKey type for context keys is a Go best practice that prevents collisions. The keys are appropriately typed and named for their purposes.

pkg/handlers/openapi.go (1)

24-59: LGTM! Proper context usage and structured logging migration.

The initialization function correctly uses context.Background() for startup logging, which is appropriate for non-request initialization code. The migration from glog to structured logging follows the project's coding guidelines.

Based on coding guidelines and learnings.

pkg/errors/errors.go (1)

117-118: LGTM! Appropriate use of background context for error logging.

Using context.Background() in the undefined error code path is correct, as no request context is available at this point. The migration to structured logging with logger.Errorf follows the project's coding guidelines.

The static analysis hint about the missing import is a false positive—the import is correctly present at line 9.

Based on coding guidelines and learnings.

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

16-16: LGTM! Logging migration looks good.

The switch from glog to the structured logger (pkg/logger) with context-aware logging is correct. The use of pkglogger.Errorf(ctx, ...) is appropriate for test database initialization error paths.

Also applies to: 52-61

cmd/hyperfleet-api/server/routes.go (3)

71-75: LGTM! Conditional OTel middleware registration is well-placed.

The conditional registration based on configuration and placement after the Operation ID middleware are appropriate. The middleware will extract trace context when enabled without adding overhead when disabled.


77-78: LGTM! Masking middleware initialization is efficient.

Initializing the masking middleware once at startup and reusing it across all requests is the correct approach for a stateless middleware component.


81-81: LGTM! Request logging middleware integration is correct.

Passing the masking middleware to RequestLoggingMiddleware enables sensitive data redaction in HTTP logs, as designed.

pkg/middleware/otel.go (1)

18-50: LGTM! OTel middleware implementation is correct.

The middleware correctly wraps the enrichment logic (enrichedHandler) inside the OTel handler (otelHandler). The execution flow ensures:

  1. otelHandler creates the span and establishes the trace context
  2. enrichedHandler extracts the span from that context
  3. Trace ID and span ID are added to the logger context
  4. The business handler receives the enriched context

This addresses the previous review concern about context timing.

test/helper.go (3)

72-97: LGTM! Test logger initialization and error handling are correct.

The early initialization of the test logger (line 73) and consistent use of context-based logging throughout the setup phase align with the structured logging guidelines. Using os.Exit(1) for fatal setup errors is appropriate for test infrastructure.


142-207: LGTM! Server lifecycle logging is consistent and context-aware.

The consistent pattern of creating a local background context and using structured logging (logger.Debug, logger.Error, logger.Warn) across all server lifecycle methods (API, metrics, health check) is correct and aligns with the logging guidelines.


544-555: LGTM! Test logger configuration is appropriate.

The initTestLogger function correctly initializes the global logger with sensible defaults for the test environment:

  • Text format for readability
  • Info level for standard test output
  • Appropriate component/version metadata
cmd/hyperfleet-api/migrate/cmd.go (2)

33-45: LGTM! Database connection cleanup properly handles the resource leak.

The addition of the defer block (lines 41-45) immediately after creating the connection ensures the database connection is closed on all exit paths, addressing the previous review concern about resource leaks when os.Exit() is called.


47-56: LGTM! Migration execution with proper error handling and logging.

The migration flow correctly:

  • Uses connection.New(ctx) for context-aware database operations
  • Logs errors with structured logging
  • Explicitly closes the connection before os.Exit(1) to ensure cleanup
  • Logs successful completion

The explicit close on the error path (lines 50-52) ensures cleanup even though os.Exit(1) bypasses the defer.

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

15-57: LGTM! Middleware structure is well-designed.

The refactored middleware correctly:

  • Accepts the masker as a parameter (avoiding per-request allocation)
  • Skips health check endpoints to reduce log noise
  • Captures both request and response metrics with proper timing
  • Uses structured logging with HTTP attribute helpers
cmd/hyperfleet-api/environments/framework.go (1)

134-160: LGTM! Context-aware logging in LoadClients.

The OCM client creation path correctly uses structured logging with context. The mock vs real client branching is clear.

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

54-66: LGTM! Default logger initialization.

The early logger initialization ensures logging works before configuration is loaded. Using "unknown" for version is appropriate since the real version is set later in servecmd.initLogger().

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

50-66: LGTM! OpenTelemetry initialization with proper shutdown.

The OTel integration correctly:

  • Checks the enabled flag before initializing
  • Uses deferred shutdown with error handling
  • Logs the configuration state for observability
pkg/logger/http.go (1)

1-49: LGTM! Clean HTTP attribute helpers.

These helpers provide consistent, typed attribute construction for HTTP request/response logging. The separation of individual helpers (e.g., HTTPMethod) and aggregate helpers (e.g., HTTPRequestAttrs) offers flexibility for different logging contexts.

pkg/logger/logger.go (2)

266-311: LGTM! OCMLogger backward compatibility layer.

The ocmLogger implementation provides a clean bridge from the legacy OCMLogger interface to the new slog-based system:

  • Correctly extracts account_id and tx_id from context
  • Properly handles verbosity levels (V > 1 maps to debug)
  • Implements immutable Extra() with proper slice copying

Based on learnings, this aligns with the guideline to "Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields."


83-116: LGTM! Context-aware log record enrichment.

The Handle method correctly extracts correlation IDs from context (trace_id, span_id, operation_id, cluster_id, resource_type, resource_id) and adds stack traces for error-level logs. This aligns with the coding guidelines for context-aware logging.

@yasun1
Copy link
Contributor Author

yasun1 commented Jan 8, 2026

@coderabbitai help

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
    • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
    • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

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

Caution

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

⚠️ Outside diff range comments (1)
cmd/hyperfleet-api/servecmd/cmd.go (1)

76-92: Missing graceful shutdown and server error handling.

The servers are started in fire-and-forget goroutines with no error propagation or supervision. The select {} blocks forever with no signal handling (SIGTERM, SIGINT), meaning:

  1. The deferred OTel shutdown at line 57-61 will never execute on normal termination
  2. If a server crashes, others continue without notification
  3. No graceful shutdown capability for container orchestration
🛠️ Suggested improvement
// Handle shutdown signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

// Start servers with error channels
errChan := make(chan error, 3)
go func() {
    apiserver := server.NewAPIServer()
    if err := apiserver.Start(); err != nil {
        errChan <- fmt.Errorf("API server: %w", err)
    }
}()
// ... similar for other servers

// Wait for shutdown signal or error
select {
case sig := <-sigChan:
    logger.Info(ctx, "Received shutdown signal", "signal", sig)
case err := <-errChan:
    logger.Error(ctx, "Server error", "error", err)
}
// Graceful shutdown logic here
🤖 Fix all issues with AI agents
In @cmd/hyperfleet-api/server/metrics_server.go:
- Around line 59-60: The TLS metrics startup log uses the wrong config field;
update the logger.Info call that logs "Serving Metrics with TLS" to use
env().Config.Metrics.BindAddress instead of env().Config.Server.BindAddress so
it matches the non-TLS log and the actual server bind used during
initialization.
🧹 Nitpick comments (10)
cmd/hyperfleet-api/migrate/cmd.go (1)

33-33: Consider enriching the context with a migration-specific operation ID.

While context.Background() is appropriate for a CLI command, adding a unique operation ID (e.g., logger.ContextWithOperationID(ctx, "migrate")) would align with the structured logging guidelines and improve log correlation.

Based on coding guidelines requiring context-aware log fields including operation ID.

pkg/db/context.go (1)

33-33: Minor inconsistency in error logging.

Lines 33 and 40 call err.Error() to convert the error to a string, while line 54 passes the err object directly. While slog handles both approaches, consistent usage within the same file is preferable.

♻️ Suggested consistency fix
-	logger.Info(ctx, "Marked transaction for rollback", "error", err)
+	logger.Info(ctx, "Marked transaction for rollback", "error", err.Error())

Or alternatively, update lines 33 and 40 to pass the error object directly (which is idiomatic for slog).

Also applies to: 40-40, 54-54

pkg/handlers/framework.go (1)

31-40: Consider adding structured fields for consistency.

While the logging correctly uses the centralized logger with context, it only logs err.Error() as a plain message. For consistency with other error logging in the codebase (e.g., pkg/handlers/metadata.go lines 48-51, pkg/middleware/schema_validation.go lines 26-30), consider adding structured fields:

logger.Info(r.Context(), err.Error(),
    "endpoint", r.URL.Path,
    "method", r.Method,
    "status_code", err.HttpCode)

and similarly for the Error call at line 37.

This would provide better traceability and align with the structured logging pattern established elsewhere.

pkg/middleware/schema_validation.go (1)

20-20: Add structured fields for consistency.

Line 20 logs the validation error without structured fields, while lines 26-30 in the same function include structured fields (endpoint, method, status_code, error). For consistency:

♻️ Suggested improvement
-	logger.Info(r.Context(), err.Error())
+	logger.Info(r.Context(), err.Error(),
+		"endpoint", r.URL.Path,
+		"method", r.Method,
+		"status_code", err.HttpCode)

This provides better traceability and aligns with the structured logging pattern used elsewhere in this function.

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

100-120: Accept context parameter to preserve trace information.

The waitForNotification function creates a new Background() context, but it's called from newListener (Line 136) which receives a context parameter. This breaks context propagation and loses trace IDs, operation IDs, and other correlation data that may be present in the parent context.

♻️ Refactor to accept and use parent context
-func waitForNotification(l *pq.Listener, callback func(id string)) {
-	ctx := context.Background()
+func waitForNotification(ctx context.Context, l *pq.Listener, callback func(id string)) {
 	for {
 		select {
 		case n := <-l.Notify:
 			logger.Info(ctx, "Received data from channel",
 				"channel", n.Channel,
 				"data", n.Extra)
 			callback(n.Extra)
 			return
 		case <-time.After(10 * time.Second):
 			logger.Debug(ctx, "Received no events on channel during interval. Pinging source")
 			go func() {
 				if err := l.Ping(); err != nil {
 					logger.Debug(ctx, "Ping failed", "error", err)
 				}
 			}()
 			return
 		}
 	}
 }

Then update the call site in newListener:

 func newListener(ctx context.Context, connstr, channel string, callback func(id string)) {
 	// ... existing code ...
 	logger.Info(ctx, "Starting channeling monitor", "channel", channel)
 	for {
-		waitForNotification(listener, callback)
+		waitForNotification(ctx, listener, callback)
 	}
 }

Based on learnings, context values should be used for passing request-scoped data including operation ID and transaction ID.

pkg/telemetry/otel.go (2)

16-57: Consider adding structured logging for trace provider initialization.

Per coding guidelines, all **/*.go files should use structured logging. Consider adding log statements for successful initialization and any errors encountered during setup to aid debugging and monitoring.

Example logging integration
 func InitTraceProvider(serviceName, serviceVersion string, samplingRate float64) (*trace.TracerProvider, error) {
+	ctx := context.Background()
+	logger.Info(ctx, "Initializing OpenTelemetry trace provider",
+		"service_name", serviceName,
+		"service_version", serviceVersion,
+		"sampling_rate", samplingRate)
+
 	// Create stdout exporter
 	exporter, err := stdouttrace.New(
 		stdouttrace.WithPrettyPrint(), // Formatted output
 	)
 	if err != nil {
+		logger.Error(ctx, "Failed to create stdout exporter", "error", err)
 		return nil, err
 	}
 
 	// ... rest of function ...
 	
 	// Set global trace provider
 	otel.SetTracerProvider(tp)
+	logger.Info(ctx, "OpenTelemetry trace provider initialized successfully")
 
 	return tp, nil
 }

60-65: Consider adding logging for trace provider shutdown.

Adding a log statement when shutting down the trace provider would help with debugging and observability, especially if the shutdown encounters errors.

Example shutdown logging
 func Shutdown(ctx context.Context, tp *trace.TracerProvider) error {
 	if tp == nil {
 		return nil
 	}
+	logger.Info(ctx, "Shutting down OpenTelemetry trace provider")
-	return tp.Shutdown(ctx)
+	err := tp.Shutdown(ctx)
+	if err != nil {
+		logger.Error(ctx, "Error shutting down trace provider", "error", err)
+	} else {
+		logger.Info(ctx, "OpenTelemetry trace provider shut down successfully")
+	}
+	return err
 }
cmd/hyperfleet-api/main.go (1)

54-74: Consider setting version from build information.

The logger version is currently hardcoded to "unknown". Consider using build-time variables to inject the actual application version for better observability.

Example using build-time version
+var (
+	Version = "unknown" // Set via -ldflags at build time
+)
+
 func initDefaultLogger() {
 	cfg := &logger.LogConfig{
 		Level:     slog.LevelInfo,
 		Format:    logger.FormatJSON,
 		Output:    os.Stdout,
 		Component: "hyperfleet-api",
-		Version:   "unknown",
+		Version:   Version,
 		Hostname:  getHostname(),
 	}
 	logger.InitGlobalLogger(cfg)
 }

Then build with: go build -ldflags "-X main.Version=v1.0.0"

pkg/logger/ocm_bridge.go (1)

13-22: Stored context may become stale for long-lived bridges.

The ctx field is captured at construction time but *Enabled() methods use it later. If the bridge is long-lived (e.g., stored in a connection object), this context may become stale or cancelled. Consider using context.Background() for level checks, or accepting context in *Enabled() if the SDK interface allows.

♻️ Suggested approach
 // DebugEnabled implements sdk.Logger
 func (b *OCMLoggerBridge) DebugEnabled() bool {
-	return GetLogger().Enabled(b.ctx, slog.LevelDebug)
+	return GetLogger().Enabled(context.Background(), slog.LevelDebug)
 }

Apply similarly to InfoEnabled, WarnEnabled, and ErrorEnabled.

pkg/logger/logger.go (1)

274-288: The formatted variants lose structured logging benefits.

The *f functions (Debugf, Infof, Warnf, Errorf) format the message with fmt.Sprintf, embedding arguments into the message string. This loses the structured logging benefit of searchable key-value pairs. Consider documenting that the non-formatted variants are preferred for observability, or deprecating the *f variants.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ad6eb7 and 85b2ed0.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (48)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/auth/helpers.go
  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • pkg/errors/errors.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/http.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/middleware/otel.go
  • pkg/middleware/schema_validation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (5)
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • cmd/hyperfleet-api/server/routes.go
  • docs/logging.md
  • test/helper.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/middleware/otel.go
  • pkg/logger/context.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
🧰 Additional context used
📓 Path-based instructions (12)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Files:

  • pkg/auth/authz_middleware_mock.go
  • pkg/handlers/cluster_status.go
  • pkg/db/context.go
  • pkg/services/node_pool.go
  • pkg/handlers/metadata.go
  • pkg/api/error.go
  • pkg/handlers/helpers.go
  • pkg/services/cluster.go
  • pkg/db/db_session/testcontainer.go
  • pkg/middleware/schema_validation.go
  • pkg/logger/logger_test.go
  • test/integration/integration_test.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/nodepool_status.go
  • pkg/logger/http.go
  • cmd/hyperfleet-api/main.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/framework.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/logger/logger.go
  • pkg/errors/errors.go
pkg/**/*{auth,middleware}*.go

📄 CodeRabbit inference engine (AGENTS.md)

JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes
Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error
Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL
Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table
Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support
Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes
Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/metadata.go
  • pkg/handlers/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/framework.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/metadata.go
  • pkg/handlers/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/framework.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Files:

  • pkg/handlers/cluster_status.go
  • pkg/services/node_pool.go
  • pkg/handlers/metadata.go
  • pkg/handlers/helpers.go
  • pkg/services/cluster.go
  • pkg/handlers/openapi.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
  • pkg/handlers/framework.go
pkg/{handlers,services,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Files:

  • pkg/handlers/cluster_status.go
  • pkg/db/context.go
  • pkg/services/node_pool.go
  • pkg/handlers/metadata.go
  • pkg/handlers/helpers.go
  • pkg/services/cluster.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/openapi.go
  • pkg/services/status_aggregation.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
  • pkg/handlers/framework.go
  • pkg/db/migrations.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use PostgreSQL 14.2 with GORM ORM for database operations

Files:

  • pkg/db/context.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/db/migrations.go
pkg/{dao,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Files:

  • pkg/db/context.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/db/migrations.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/services/**/*.go: Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise
Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Files:

  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/services/generic.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Files:

  • pkg/api/error.go
test/integration/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

test/integration/**/*_test.go: Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage
All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Files:

  • test/integration/integration_test.go
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/**/*{auth,middleware}*.go : JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Applied to files:

  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Applied to files:

  • pkg/auth/authz_middleware_mock.go
  • pkg/handlers/cluster_status.go
  • pkg/db/context.go
  • pkg/services/node_pool.go
  • pkg/handlers/metadata.go
  • pkg/api/error.go
  • pkg/handlers/helpers.go
  • pkg/services/cluster.go
  • pkg/db/db_session/testcontainer.go
  • pkg/middleware/schema_validation.go
  • pkg/logger/logger_test.go
  • test/integration/integration_test.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/main.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/framework.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/logger/logger.go
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Applied to files:

  • pkg/db/context.go
  • pkg/api/error.go
  • pkg/handlers/helpers.go
  • pkg/services/cluster.go
  • pkg/db/db_session/testcontainer.go
  • pkg/middleware/schema_validation.go
  • test/integration/integration_test.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/openapi.go
  • pkg/services/status_aggregation.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/framework.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All database sessions must be retrieved from context via db.NewContext(ctx) rather than creating new database connections

Applied to files:

  • pkg/db/context.go
  • pkg/api/error.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO methods must accept context.Context as the first parameter for transaction propagation

Applied to files:

  • pkg/db/context.go
  • pkg/api/error.go
  • pkg/db/transaction_middleware.go
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : Always retrieve database sessions from context in DAO methods; never create direct gorm.Open() connections

Applied to files:

  • pkg/db/context.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : NodePool owner_references.id must enforce foreign key relationship to parent cluster to maintain referential integrity

Applied to files:

  • pkg/services/node_pool.go
  • cmd/hyperfleet-api/migrate/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Applied to files:

  • pkg/handlers/metadata.go
  • pkg/api/error.go
  • pkg/handlers/helpers.go
  • pkg/middleware/schema_validation.go
  • test/integration/integration_test.go
  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/framework.go
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes

Applied to files:

  • pkg/handlers/helpers.go
  • pkg/auth/helpers.go
  • pkg/handlers/openapi.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for database operations

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/openapi.go
  • pkg/db/db_session/default.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,services}/**/*.go : Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • test/integration/integration_test.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/migrate/**/*.go : Use GORM AutoMigrate at startup via ./bin/hyperfleet-api migrate command; migrations are idempotent and track applied migrations

Applied to files:

  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/db/migrations.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/**/*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Applied to files:

  • test/integration/integration_test.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • go.mod
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/openapi/model_*.go : Generated Go models from openapi-generator-cli include JSON tags and validation metadata; do not manually edit generated model_*.go files

Applied to files:

  • test/integration/integration_test.go
  • pkg/handlers/openapi.go
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Applied to files:

  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Environment-based configuration: use OCM_ENV variable to select development, unit_testing, integration_testing, or production configuration

Applied to files:

  • test/integration/integration_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/migrations/**/*.go : Store spec and conditions as JSONB in PostgreSQL to enable complex field queries

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/errors/errors.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/db/db_session/default.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/services/generic.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,handlers}/**/*.go : List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Applied to files:

  • pkg/services/generic.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • go.mod
🧬 Code graph analysis (20)
pkg/auth/authz_middleware_mock.go (1)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/db/context.go (2)
pkg/logger/logger.go (2)
  • Error (270-272)
  • Info (262-264)
pkg/db/db_context/db_context.go (1)
  • Transaction (23-26)
pkg/services/node_pool.go (1)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/api/error.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
pkg/db/db_session/testcontainer.go (4)
pkg/logger/logger.go (4)
  • Info (262-264)
  • Errorf (286-288)
  • Infof (278-280)
  • Error (270-272)
pkg/db/db_session/default.go (1)
  • Default (20-29)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/middleware/schema_validation.go (1)
pkg/logger/logger.go (3)
  • Info (262-264)
  • Error (270-272)
  • Warn (266-268)
pkg/logger/logger_test.go (2)
pkg/logger/logger.go (14)
  • ParseLogLevel (219-232)
  • Error (270-272)
  • Errorf (286-288)
  • LogFormat (15-15)
  • FormatText (19-19)
  • FormatJSON (21-21)
  • ParseLogFormat (235-244)
  • ParseLogOutput (247-256)
  • LogConfig (25-32)
  • InitGlobalLogger (174-182)
  • Info (262-264)
  • Debug (258-260)
  • GetLogger (208-216)
  • Warn (266-268)
pkg/logger/context.go (6)
  • WithTraceID (25-27)
  • WithSpanID (30-32)
  • OpIDKey (15-15)
  • WithClusterID (35-37)
  • WithResourceType (40-42)
  • WithResourceID (45-47)
pkg/auth/helpers.go (1)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
cmd/hyperfleet-api/migrate/cmd.go (4)
pkg/logger/logger.go (2)
  • Error (270-272)
  • Info (262-264)
pkg/db/db_session/default.go (1)
  • NewProdFactory (33-37)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/handlers/openapi.go (1)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
cmd/hyperfleet-api/server/api_server.go (6)
pkg/logger/ocm_bridge.go (1)
  • NewOCMLoggerBridge (18-22)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
pkg/client/ocm/client.go (1)
  • Config (17-24)
cmd/hyperfleet-api/server/server.go (1)
  • Server (13-18)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/services/status_aggregation.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
pkg/logger/ocm_bridge.go (1)
pkg/logger/logger.go (5)
  • Debug (258-260)
  • GetLogger (208-216)
  • Info (262-264)
  • Warn (266-268)
  • Error (270-272)
pkg/db/db_session/default.go (1)
pkg/logger/logger.go (3)
  • Info (262-264)
  • Debug (258-260)
  • Error (270-272)
cmd/hyperfleet-api/main.go (1)
pkg/logger/logger.go (4)
  • Error (270-272)
  • LogConfig (25-32)
  • FormatJSON (21-21)
  • InitGlobalLogger (174-182)
pkg/services/generic.go (1)
pkg/logger/logger.go (2)
  • Warn (266-268)
  • Info (262-264)
cmd/hyperfleet-api/servecmd/cmd.go (3)
pkg/logger/logger.go (9)
  • Error (270-272)
  • Warn (266-268)
  • Info (262-264)
  • ParseLogLevel (219-232)
  • ParseLogFormat (235-244)
  • FormatJSON (21-21)
  • ParseLogOutput (247-256)
  • LogConfig (25-32)
  • ReconfigureGlobalLogger (187-195)
cmd/hyperfleet-api/environments/framework.go (1)
  • Environment (54-56)
pkg/telemetry/otel.go (2)
  • InitTraceProvider (16-57)
  • Shutdown (60-65)
pkg/db/migrations.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
cmd/hyperfleet-api/server/metrics_server.go (1)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/logger/logger.go (2)
pkg/logger/context.go (4)
  • GetOperationID (61-66)
  • GetClusterID (81-84)
  • GetResourceType (87-90)
  • GetResourceID (93-96)
pkg/errors/errors.go (1)
  • New (112-132)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

@yasun1
Copy link
Contributor Author

yasun1 commented Jan 8, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

✅ Actions performed

Full review triggered.

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: 4

🤖 Fix all issues with AI agents
In @cmd/hyperfleet-api/server/metrics_server.go:
- Around line 59-60: The TLS startup log uses the wrong config field: change the
logger.Info call that currently logs env().Config.Server.BindAddress to log
env().Config.Metrics.BindAddress instead so the TLS message matches the non-TLS
case and the actual server initialization (update the logger.Info invocation
that prints "Serving Metrics with TLS" to reference
env().Config.Metrics.BindAddress).

In @pkg/db/transaction_middleware.go:
- Line 18: The error logging after NewContext can use an invalid ctx when
NewContext fails; update the error branch so logger.Error is called with the
original request context r.Context() instead of the potentially nil/invalid ctx.
Locate the NewContext call and its error-handling block in
transaction_middleware.go and change the logger.Error invocation (the one
currently passing ctx and err.Error()) to use r.Context() and include the same
message and error detail; ensure other uses in that error path do not
dereference ctx.

In @pkg/logger/ocm_bridge.go:
- Around line 13-68: The stored context field in OCMLoggerBridge causes
inconsistent context usage between the level checks and logging calls; remove
the ctx field from OCMLoggerBridge and stop storing a context in
NewOCMLoggerBridge, and update DebugEnabled/InfoEnabled/WarnEnabled/ErrorEnabled
to call GetLogger().Enabled with a global context (e.g., context.Background() or
context.TODO()) instead of b.ctx so the enabled checks are context-independent
and consistent with the logging methods that accept a request context (symbols:
type OCMLoggerBridge, NewOCMLoggerBridge, DebugEnabled, InfoEnabled,
WarnEnabled, ErrorEnabled).
🧹 Nitpick comments (18)
pkg/db/context.go (1)

47-55: Consider using err.Error() for consistency.

Line 54 passes the error directly, while Lines 33 and 40 use err.Error(). Although slog handles both, using err.Error() consistently improves readability and ensures uniform string formatting across all log statements in this file.

♻️ Proposed fix for consistency
-	logger.Info(ctx, "Marked transaction for rollback", "error", err)
+	logger.Info(ctx, "Marked transaction for rollback", "error", err.Error())
pkg/db/migrations.go (1)

35-38: LGTM! Structured logging properly implemented.

The error logging correctly uses structured fields with the context. The migration failure appropriately terminates the application with os.Exit(1).

Optional: Pass error directly instead of converting to string

The logger can handle error values directly:

 	logger.Error(ctx, "Could not migrate",
 		"migration_id", migrationID,
-		"error", err.Error())
+		"error", err)

This is a minor style preference—both approaches work correctly.

Based on learnings, structured logging with context-aware fields is properly implemented.

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

122-138: Consider using structured error field in listener error callback.

At line 125, the error is passed as the message string via err.Error(). For consistency with the structured logging approach used elsewhere, consider using a structured error field:

-			logger.Error(ctx, err.Error())
+			logger.Error(ctx, "listener error", "error", err)

This allows log aggregation tools to parse the error as a structured field.

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

104-135: LGTM! Schema validation logging migrated to centralized logger.

The schema validation initialization now uses context-aware structured logging with appropriate fields (schema_path, error). The guidance messages on lines 127-129 provide clear instructions for enabling schema validation in different environments.

Optional: Consolidate guidance messages

The three separate logger.Info calls on lines 127-129 could be consolidated into a single multi-line message for slightly cleaner logs, but the current approach is perfectly acceptable.

pkg/middleware/masking_test.go (1)

253-306: LGTM! Deep comparison helpers correctly handle JSON structure comparison.

The recursive comparison functions properly handle maps, slices, and nested structures, ensuring JSON comparisons are order-independent. The implementation is correct and well-suited for validating masked JSON output.

Optional: Extract to test utilities

If these deep comparison helpers are useful for other test files, consider extracting them to a shared test utility package (e.g., test/testutil). For now, keeping them local is perfectly fine.

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

40-57: LGTM! Connection cleanup correctly handles both success and error paths.

The cleanup pattern properly addresses the previous review concern:

  • Success path: The defer on lines 41-45 closes the connection after successful migration
  • Error path: Explicit close on line 50 ensures cleanup before os.Exit(1) (which bypasses defers)

This is the correct approach for handling resource cleanup with os.Exit() calls.

Optional: Add clarity comment

Consider adding a comment above line 50 explaining why explicit close is needed:

// Close connection explicitly before os.Exit (defers don't run with os.Exit)
if closeErr := connection.Close(); closeErr != nil {

This would help future maintainers understand the nuanced cleanup pattern.

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

57-58: Prefer structured logging over printf-style format strings.

The error logging uses logger.Errorf with printf-style formatting, which is less structured than the recommended approach. Consider using logger.Error with key-value pairs for consistency with the coding guidelines.

♻️ Proposed refactor
-		logger.Errorf(ctx, "Failed to start PostgreSQL testcontainer: %s", err)
+		logger.Error(ctx, "Failed to start PostgreSQL testcontainer", "error", err)

Apply similar changes to lines 66, 75, 99, and 106 for consistency.

Based on coding guidelines requiring structured logging with context-aware log fields.

Also applies to: 66-67, 75-76, 99-100, 106-107


70-70: Prefer structured logging for connection string.

Line 70 uses logger.Infof with printf-style formatting. For consistency with structured logging patterns used elsewhere in this file (e.g., line 171), consider using key-value pairs.

♻️ Proposed refactor
-	logger.Infof(ctx, "PostgreSQL testcontainer started at: %s", connStr)
+	logger.Info(ctx, "PostgreSQL testcontainer started", "connection_string", connStr)

139-139: Prefer structured logging in error paths.

Lines 139 and 181 use logger.Errorf with printf-style formatting. For consistency with the structured logging approach recommended in the coding guidelines, use logger.Error with key-value pairs.

♻️ Proposed refactor
-			logger.Errorf(ctx, "Error closing SQL connection: %s", err)
+			logger.Error(ctx, "Error closing SQL connection", "error", err)

Apply the same pattern to line 181.

Also applies to: 181-181

pkg/middleware/masking.go (1)

104-112: Ensure sensitive field keywords are specific to avoid false positives.

Line 107 uses strings.Contains for substring matching, which means overly generic keywords (e.g., "id") could match unrelated fields ("video", "identifier"). Ensure the sensitive fields list in LoggingConfig uses specific keywords to avoid over-redaction.

cmd/hyperfleet-api/servecmd/cmd.go (2)

51-66: LGTM: OpenTelemetry initialization is robust.

The OTel initialization properly handles failures without blocking server startup (lines 54-56), implements graceful shutdown with error logging (lines 57-61), and provides clear visibility into the configuration state (lines 62, 65).

📝 Optional: Add timeout for shutdown

Consider creating a timeout context for the shutdown operation to prevent hanging:

 			defer func() {
-				if err := telemetry.Shutdown(context.Background(), tp); err != nil {
+				shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+				defer cancel()
+				if err := telemetry.Shutdown(shutdownCtx, tp); err != nil {
 					logger.Error(ctx, "Failed to shutdown OpenTelemetry", "error", err)
 				}
 			}()

95-138: LGTM: Logger initialization with proper reconfiguration.

The initLogger function correctly uses ReconfigureGlobalLogger (line 137) to apply environment configuration, addressing the previously identified sync.Once issue. The fallback defaults (lines 104, 111, 118) ensure safe operation even with invalid configuration.

📝 Optional: Log hostname retrieval failure

Line 122 silently ignores hostname errors. Consider logging a warning for visibility:

 	// Get hostname
-	hostname, _ := os.Hostname()
+	hostname, err := os.Hostname()
+	if err != nil {
+		logger.Warn(ctx, "Failed to get hostname, using empty string", "error", err)
+		hostname = ""
+	}
pkg/telemetry/otel.go (2)

16-34: LGTM: Exporter and resource creation follow OTel best practices.

The stdout exporter with pretty-printing (lines 18-20) is appropriate for initial implementation, and the resource attributes (lines 27-29) correctly use semantic conventions. The comment about future OTLP HTTP upgrade (lines 14-15) shows good forward planning.

📝 Optional: Accept context parameter

Consider accepting a context parameter instead of using context.Background() on line 26 for better context propagation:

-func InitTraceProvider(serviceName, serviceVersion string, samplingRate float64) (*trace.TracerProvider, error) {
+func InitTraceProvider(ctx context.Context, serviceName, serviceVersion string, samplingRate float64) (*trace.TracerProvider, error) {
 	// Create stdout exporter
 	exporter, err := stdouttrace.New(
 		stdouttrace.WithPrettyPrint(), // Formatted output
 	)
 	if err != nil {
 		return nil, err
 	}
 
 	// Create resource (service information)
-	res, err := resource.New(context.Background(),
+	res, err := resource.New(ctx,

This would require updating the caller in servecmd/cmd.go line 53.


36-44: LGTM: Sampler logic correctly handles standard cases.

The sampling rate logic (lines 38-43) properly handles the three standard scenarios: always sample (≥1.0), never sample (≤0.0), and ratio-based sampling (0.0-1.0).

📝 Optional: Add input validation

Consider validating the sampling rate for edge cases (NaN, Inf):

 func InitTraceProvider(serviceName, serviceVersion string, samplingRate float64) (*trace.TracerProvider, error) {
+	// Validate sampling rate
+	if math.IsNaN(samplingRate) || math.IsInf(samplingRate, 0) {
+		return nil, fmt.Errorf("invalid sampling rate: %v", samplingRate)
+	}
+
 	// Create stdout exporter
pkg/config/logging.go (1)

65-101: Consider logging environment variable parsing errors.

The BindEnv method silently ignores parsing errors for boolean and float values (lines 77-87, 90-94). While this fail-safe behavior is acceptable, consider logging a warning when parsing fails so operators can detect configuration issues.

🔍 Optional enhancement
 if val := os.Getenv("OTEL_ENABLED"); val != "" {
 	enabled, err := strconv.ParseBool(val)
-	if err == nil {
+	if err != nil {
+		// Could log warning here if logger is initialized
+		// For now, silently keep default
+	} else {
 		l.OTel.Enabled = enabled
 	}
 }

Note: This is a nitpick and the current silent behavior is reasonable for a configuration loader.

pkg/logger/context.go (1)

61-96: Consider consistent getter signatures across all context helpers.

GetOperationID returns a string (empty if not found), while other getters (GetTraceID, GetSpanID, etc.) return (string, bool) tuples. This inconsistency makes error handling less uniform for callers.

♻️ Proposed fix for consistency
-// GetOperationID retrieves operation ID from context
-func GetOperationID(ctx context.Context) string {
-	if opID, ok := ctx.Value(OpIDKey).(string); ok {
-		return opID
-	}
-	return ""
-}
+// GetOperationID retrieves operation ID from context
+func GetOperationID(ctx context.Context) (string, bool) {
+	opID, ok := ctx.Value(OpIDKey).(string)
+	return opID, ok
+}

This would require updating call sites that expect a single string return value.

pkg/logger/logger.go (2)

174-182: Consider removing redundant lock inside sync.Once.

The sync.Once.Do already provides synchronization guarantees - the function passed to Do will execute exactly once and all other callers will block until completion. The loggerMutex.Lock() inside is redundant for initialization safety.

However, keeping it maintains consistency with ReconfigureGlobalLogger and GetLogger which use the same mutex, so this is a minor point.

♻️ Optional simplification
 func InitGlobalLogger(cfg *LogConfig) {
 	initOnce.Do(func() {
 		handler := NewHyperFleetHandler(cfg)
-		loggerMutex.Lock()
 		globalLogger = slog.New(handler)
 		slog.SetDefault(globalLogger)
-		loggerMutex.Unlock()
 	})
 }

274-288: Consider deprecating or documenting the *f variants.

The Debugf, Infof, Warnf, Errorf functions use fmt.Sprintf which embeds arguments directly into the message string, bypassing slog's structured logging. This loses the queryability benefits of structured logging (e.g., filtering by field values in log aggregation systems).

These functions may exist for compatibility with legacy code, but new code should prefer the structured variants:

// Instead of:
logger.Infof(ctx, "Processing cluster %s", clusterID)

// Prefer:
logger.Info(ctx, "Processing cluster", "cluster_id", clusterID)

If these are intended for migration compatibility, consider adding a deprecation comment.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b4c857 and 85b2ed0.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (54)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/auth/helpers.go
  • pkg/config/config.go
  • pkg/config/logging.go
  • pkg/config/logging_test.go
  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • pkg/errors/errors.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/http.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/logger/operationid_middleware.go
  • pkg/middleware/masking.go
  • pkg/middleware/masking_test.go
  • pkg/middleware/otel.go
  • pkg/middleware/schema_validation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (6)
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • pkg/logger/operationid_middleware.go
🧰 Additional context used
📓 Path-based instructions (12)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/handlers/cluster_status.go
  • pkg/db/transaction_middleware.go
  • pkg/services/node_pool.go
  • pkg/middleware/masking_test.go
  • test/helper.go
  • pkg/handlers/openapi.go
  • pkg/logger/logger_test.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/helpers.go
  • pkg/logger/http.go
  • pkg/middleware/otel.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/middleware/schema_validation.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/db/context.go
  • pkg/config/logging_test.go
  • pkg/telemetry/otel.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/middleware/masking.go
  • pkg/config/logging.go
  • pkg/services/cluster.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/context.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/api/error.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • pkg/handlers/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/config/config.go
  • pkg/logger/logger.go
  • pkg/services/status_aggregation.go
  • cmd/hyperfleet-api/server/routes.go
test/integration/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

test/integration/**/*_test.go: Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage
All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Files:

  • test/integration/integration_test.go
pkg/**/*{auth,middleware}*.go

📄 CodeRabbit inference engine (AGENTS.md)

JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes
Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error
Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL
Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table
Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support
Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes
Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/framework.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/framework.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Files:

  • pkg/handlers/cluster_status.go
  • pkg/services/node_pool.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/services/cluster.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
  • pkg/handlers/framework.go
  • pkg/services/status_aggregation.go
pkg/{handlers,services,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Files:

  • pkg/handlers/cluster_status.go
  • pkg/db/transaction_middleware.go
  • pkg/services/node_pool.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/db/db_session/test.go
  • pkg/db/context.go
  • pkg/services/cluster.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/generic.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • pkg/handlers/framework.go
  • pkg/services/status_aggregation.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use PostgreSQL 14.2 with GORM ORM for database operations

Files:

  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/test.go
  • pkg/db/context.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
pkg/{dao,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Files:

  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/test.go
  • pkg/db/context.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/services/**/*.go: Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise
Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Files:

  • pkg/services/node_pool.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/services/status_aggregation.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Files:

  • pkg/api/error.go
🧠 Learnings (31)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Applied to files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/handlers/cluster_status.go
  • pkg/db/transaction_middleware.go
  • pkg/services/node_pool.go
  • test/helper.go
  • pkg/handlers/openapi.go
  • pkg/logger/logger_test.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/helpers.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • docs/logging.md
  • pkg/middleware/schema_validation.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/context.go
  • pkg/telemetry/otel.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/config/logging.go
  • pkg/services/cluster.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/context.go
  • pkg/handlers/metadata.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/api/error.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • pkg/handlers/framework.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/logger/logger.go
  • pkg/services/status_aggregation.go
  • cmd/hyperfleet-api/server/routes.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Applied to files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • pkg/db/transaction_middleware.go
  • test/helper.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/handlers/helpers.go
  • pkg/middleware/otel.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/middleware/schema_validation.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/context.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/services/cluster.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/context.go
  • pkg/handlers/metadata.go
  • pkg/services/generic.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/api/error.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • pkg/handlers/framework.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/migrate/**/*.go : Use GORM AutoMigrate at startup via ./bin/hyperfleet-api migrate command; migrations are idempotent and track applied migrations

Applied to files:

  • test/integration/integration_test.go
  • test/helper.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • go.mod
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/servecmd/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/**/*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Applied to files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • test/helper.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/api_server.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/openapi/model_*.go : Generated Go models from openapi-generator-cli include JSON tags and validation metadata; do not manually edit generated model_*.go files

Applied to files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • test/helper.go
  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Applied to files:

  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage

Applied to files:

  • test/integration/integration_test.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Applied to files:

  • test/integration/integration_test.go
  • pkg/errors/errors.go
  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/middleware/schema_validation.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/auth/helpers.go
  • pkg/handlers/metadata.go
  • pkg/api/error.go
  • pkg/handlers/framework.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,services}/**/*.go : Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Applied to files:

  • test/integration/integration_test.go
  • pkg/db/db_session/test.go
  • pkg/telemetry/otel.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Environment-based configuration: use OCM_ENV variable to select development, unit_testing, integration_testing, or production configuration

Applied to files:

  • test/integration/integration_test.go
  • test/helper.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/config/logging_test.go
  • cmd/hyperfleet-api/migrate/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO methods must accept context.Context as the first parameter for transaction propagation

Applied to files:

  • pkg/errors/errors.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/context.go
  • pkg/logger/context.go
  • pkg/api/error.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags

Applied to files:

  • pkg/errors/errors.go
  • test/helper.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/servecmd/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/**/*{auth,middleware}*.go : JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Applied to files:

  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/nodepool_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error

Applied to files:

  • pkg/db/transaction_middleware.go
  • pkg/handlers/openapi.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : NodePool owner_references.id must enforce foreign key relationship to parent cluster to maintain referential integrity

Applied to files:

  • pkg/services/node_pool.go
  • cmd/hyperfleet-api/migrate/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • test/helper.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for database operations

Applied to files:

  • test/helper.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
  • go.mod
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes

Applied to files:

  • test/helper.go
  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to openapi/**/*.yaml : Use TypeSpec for API specification, which compiles to OpenAPI 3.0.3, then generates Go models via openapi-generator-cli v7.16.0

Applied to files:

  • pkg/handlers/openapi.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/db/db_session/default.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/auth/helpers.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/metrics_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/services/generic.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : Always retrieve database sessions from context in DAO methods; never create direct gorm.Open() connections

Applied to files:

  • pkg/db/db_session/test.go
  • pkg/db/context.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All database sessions must be retrieved from context via db.NewContext(ctx) rather than creating new database connections

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/context.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/db_session/testcontainer.go
  • pkg/api/error.go
  • pkg/db/db_session/default.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/migrations/**/*.go : Store spec and conditions as JSONB in PostgreSQL to enable complex field queries

Applied to files:

  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,handlers}/**/*.go : List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Applied to files:

  • pkg/services/generic.go
🧬 Code graph analysis (32)
test/integration/integration_test.go (1)
pkg/logger/logger.go (2)
  • Infof (278-280)
  • Warn (266-268)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (286-288)
pkg/auth/authz_middleware_mock.go (1)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/handlers/cluster_status.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
pkg/db/transaction_middleware.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
pkg/services/node_pool.go (1)
pkg/logger/logger.go (1)
  • Info (262-264)
test/helper.go (1)
pkg/logger/logger.go (7)
  • Error (270-272)
  • Info (262-264)
  • Debug (258-260)
  • Warn (266-268)
  • LogConfig (25-32)
  • FormatText (19-19)
  • InitGlobalLogger (174-182)
pkg/handlers/openapi.go (1)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
pkg/logger/logger_test.go (2)
pkg/logger/logger.go (17)
  • ParseLogLevel (219-232)
  • Error (270-272)
  • Errorf (286-288)
  • LogFormat (15-15)
  • FormatText (19-19)
  • FormatJSON (21-21)
  • ParseLogFormat (235-244)
  • ParseLogOutput (247-256)
  • LogConfig (25-32)
  • InitGlobalLogger (174-182)
  • Info (262-264)
  • Debug (258-260)
  • GetLogger (208-216)
  • Warn (266-268)
  • Debugf (274-276)
  • Infof (278-280)
  • Warnf (282-284)
pkg/logger/context.go (6)
  • WithTraceID (25-27)
  • WithSpanID (30-32)
  • OpIDKey (15-15)
  • WithClusterID (35-37)
  • WithResourceType (40-42)
  • WithResourceID (45-47)
cmd/hyperfleet-api/environments/framework.go (1)
pkg/logger/logger.go (4)
  • Infof (278-280)
  • Errorf (286-288)
  • Info (262-264)
  • Error (270-272)
pkg/handlers/helpers.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
cmd/hyperfleet-api/main.go (1)
pkg/logger/logger.go (4)
  • Error (270-272)
  • LogConfig (25-32)
  • FormatJSON (21-21)
  • InitGlobalLogger (174-182)
pkg/middleware/schema_validation.go (1)
pkg/logger/logger.go (3)
  • Info (262-264)
  • Error (270-272)
  • Warn (266-268)
pkg/logger/ocm_bridge.go (1)
pkg/logger/logger.go (5)
  • Debug (258-260)
  • GetLogger (208-216)
  • Info (262-264)
  • Warn (266-268)
  • Error (270-272)
pkg/db/db_session/test.go (3)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/logger/logger.go (1)
  • Errorf (286-288)
pkg/db/db_session/default.go (1)
  • Default (20-29)
cmd/hyperfleet-api/server/server.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (3)
pkg/middleware/masking.go (1)
  • MaskingMiddleware (13-17)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/logger/http.go (5)
  • HTTPMethod (10-12)
  • HTTPPath (15-17)
  • HTTPUserAgent (30-32)
  • HTTPStatusCode (20-22)
  • HTTPDuration (25-27)
pkg/config/logging_test.go (1)
pkg/config/logging.go (2)
  • NewLoggingConfig (35-50)
  • LoggingConfig (12-19)
cmd/hyperfleet-api/migrate/cmd.go (4)
pkg/logger/logger.go (2)
  • Error (270-272)
  • Info (262-264)
pkg/db/db_session/default.go (1)
  • NewProdFactory (33-37)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/middleware/masking.go (1)
pkg/config/logging.go (1)
  • LoggingConfig (12-19)
pkg/services/cluster.go (1)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/handlers/metadata.go (2)
pkg/logger/logger.go (1)
  • Error (270-272)
pkg/api/error.go (1)
  • SendPanic (70-81)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
pkg/services/generic.go (1)
pkg/logger/logger.go (2)
  • Warn (266-268)
  • Info (262-264)
cmd/hyperfleet-api/server/healthcheck_server.go (1)
pkg/logger/logger.go (2)
  • Infof (278-280)
  • Info (262-264)
pkg/db/db_session/default.go (1)
pkg/logger/logger.go (3)
  • Info (262-264)
  • Debug (258-260)
  • Error (270-272)
pkg/db/migrations.go (2)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/logger/logger.go (1)
  • Error (270-272)
pkg/handlers/framework.go (1)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
pkg/config/config.go (1)
pkg/config/logging.go (2)
  • LoggingConfig (12-19)
  • NewLoggingConfig (35-50)
pkg/logger/logger.go (2)
pkg/logger/context.go (6)
  • GetTraceID (69-72)
  • GetSpanID (75-78)
  • GetOperationID (61-66)
  • GetClusterID (81-84)
  • GetResourceType (87-90)
  • GetResourceID (93-96)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/services/status_aggregation.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
cmd/hyperfleet-api/server/routes.go (4)
pkg/middleware/otel.go (1)
  • OTelMiddleware (18-50)
pkg/middleware/masking.go (1)
  • NewMaskingMiddleware (20-26)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (1)
  • RequestLoggingMiddleware (15-57)
pkg/logger/logger.go (3)
  • Warn (266-268)
  • Error (270-272)
  • Info (262-264)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

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

Caution

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

⚠️ Outside diff range comments (1)
pkg/db/db_session/default.go (1)

100-120: Context propagation issue in waitForNotification.

waitForNotification creates its own context.Background() instead of accepting the context from newListener. This breaks context propagation, meaning any context-specific log fields (operation ID, trace ID) from the caller won't be included in these logs.

🔧 Proposed fix
-func waitForNotification(l *pq.Listener, callback func(id string)) {
-	ctx := context.Background()
+func waitForNotification(ctx context.Context, l *pq.Listener, callback func(id string)) {
 	for {
 		select {
 		case n := <-l.Notify:

And update the call site in newListener:

 	for {
-		waitForNotification(listener, callback)
+		waitForNotification(ctx, listener, callback)
 	}
🤖 Fix all issues with AI agents
In @cmd/hyperfleet-api/server/logging/request_logging_middleware.go:
- Around line 59-69: The responseWriter wrapper needs a Write method so implicit
calls that trigger a 200 status still update rw.statusCode; add a method on
responseWriter: func (rw *responseWriter) Write(b []byte) (int, error) { if
rw.statusCode == 0 { rw.statusCode = http.StatusOK } return
rw.ResponseWriter.Write(b) } to ensure statusCode is set when WriteHeader wasn't
called explicitly (referencing the responseWriter type and its WriteHeader
method).

In @pkg/services/node_pool.go:
- Line 107: Replace the vague, grammatically incorrect logger.Info call with a
context-aware logger from logger.NewOCMLogger(ctx) and a clearer message: create
a logger via l := logger.NewOCMLogger(ctx) and call l.Info("Perform idempotent
operations on node pool", "nodepool_id", nodePool.ID) so the log includes the
corrected message and automatic context fields (operation ID, account ID,
transaction ID).
🧹 Nitpick comments (14)
pkg/services/node_pool.go (1)

113-113: Use logger.NewOCMLogger per coding guidelines.

Per coding guidelines, use logger.NewOCMLogger(ctx) instead of logger.Info(ctx, ...) to ensure context-aware fields like operation ID, account ID, and transaction ID are automatically included from the context.

Based on coding guidelines and learnings.

📝 Suggested fix
-	logger.Info(ctx, "This nodePool has been deleted", "nodepool_id", id)
+	log := logger.NewOCMLogger(ctx)
+	log.Info("NodePool deleted", "nodepool_id", id)
pkg/errors/errors.go (1)

117-118: LGTM - Context-aware logging for undefined error codes.

The migration to the centralized logger is correct. Using context.Background() is acceptable here since New() doesn't have a caller-provided context.

As a future enhancement, consider accepting context.Context as an optional parameter to propagate request-scoped data (operation ID, account ID) for better traceability. Based on learnings, the codebase emphasizes context-aware logging with operation ID, account ID, and transaction ID.

pkg/handlers/framework.go (1)

31-40: LGTM - Proper context-aware error logging.

The migration correctly uses r.Context() for logging, enabling request-scoped data to be included. The distinction between 4xx (Info level) and other errors (Error level) is appropriate.

Based on coding guidelines, consider including the operationID in the log message for enhanced traceability:

♻️ Optional enhancement
 func handleError(r *http.Request, w http.ResponseWriter, err *errors.ServiceError) {
 	operationID := logger.GetOperationID(r.Context())
 	// If this is a 400 error, its the user's issue, log as info rather than error
 	if err.HttpCode >= 400 && err.HttpCode <= 499 {
-		logger.Info(r.Context(), err.Error())
+		logger.Info(r.Context(), err.Error(), "operation_id", operationID)
 	} else {
-		logger.Error(r.Context(), err.Error())
+		logger.Error(r.Context(), err.Error(), "operation_id", operationID)
 	}
 	writeJSONResponse(w, r, err.HttpCode, err.AsOpenapiError(operationID))
 }
cmd/hyperfleet-api/environments/framework.go (1)

176-184: Minor inconsistency in logging style.

Line 180 uses logger.Error with structured key-value pairs ("flag", name, "error", err), while other error paths in this file use logger.Errorf with format strings. Consider aligning the style for consistency:

♻️ Suggested fix for consistency
 func setConfigDefaults(flags *pflag.FlagSet, defaults map[string]string) error {
 	ctx := context.Background()
 	for name, value := range defaults {
 		if err := flags.Set(name, value); err != nil {
-			logger.Error(ctx, "Error setting flag", "flag", name, "error", err)
+			logger.Errorf(ctx, "Error setting flag %s: %s", name, err)
 			return err
 		}
 	}
 	return nil
 }

Alternatively, migrate all Errorf calls to the structured Error style with key-value pairs for better consistency with modern slog patterns.

pkg/logger/http.go (1)

34-41: Consider nil safety for HTTPRequestAttrs.

HTTPRequestAttrs will panic if called with a nil *http.Request. While callers should validate inputs, a defensive nil check could prevent unexpected panics in edge cases.

♻️ Optional defensive check
 // HTTPRequestAttrs returns a slice of slog attributes for HTTP request
 func HTTPRequestAttrs(r *http.Request) []slog.Attr {
+	if r == nil {
+		return nil
+	}
 	return []slog.Attr{
 		HTTPMethod(r.Method),
 		HTTPPath(r.URL.Path),
 		HTTPUserAgent(r.UserAgent()),
 	}
 }
pkg/logger/logger.go (1)

246-256: Consider supporting file output in ParseLogOutput.

Currently, only stdout and stderr are supported. For production environments, file-based logging is often required for log aggregation systems.

♻️ Optional enhancement for file output support
 // ParseLogOutput converts string to io.Writer
 func ParseLogOutput(output string) (io.Writer, error) {
 	switch strings.ToLower(strings.TrimSpace(output)) {
 	case "stdout", "":
 		return os.Stdout, nil
 	case "stderr":
 		return os.Stderr, nil
 	default:
-		return nil, fmt.Errorf("unknown log output: %s (valid: stdout, stderr)", output)
+		// Attempt to open as file path
+		file, err := os.OpenFile(output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+		if err != nil {
+			return nil, fmt.Errorf("failed to open log file %s: %w", output, err)
+		}
+		return file, nil
 	}
 }

Note: If file output is added, ensure proper file handle cleanup is managed elsewhere.

pkg/db/context.go (1)

46-55: Inconsistent error value formatting.

Line 54 passes err directly as the value, while other locations (lines 33, 40) use err.Error(). For consistent structured logging output, consider using .Error() uniformly or relying on slog's automatic error formatting.

♻️ Proposed fix for consistency
 func MarkForRollback(ctx context.Context, err error) {
 	transaction, ok := dbContext.Transaction(ctx)
 	if !ok {
 		logger.Error(ctx, "failed to mark transaction for rollback: could not retrieve transaction from context")
 		return
 	}
 	transaction.SetRollbackFlag(true)
-	logger.Info(ctx, "Marked transaction for rollback", "error", err)
+	logger.Info(ctx, "Marked transaction for rollback", "error", err.Error())
 }
pkg/db/transaction_middleware.go (1)

36-45: Consider logging marshaling and write errors.

Silent failures in writeJSONResponse may make debugging difficult. While headers are already sent, logging these errors could help with troubleshooting.

♻️ Optional: Add error logging for visibility
+import "context"
+
 func writeJSONResponse(w http.ResponseWriter, code int, payload interface{}) {
 	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.Error(context.Background(), "Failed to marshal JSON response", "error", err.Error())
 			return
 		}
 		if _, err := w.Write(response); err != nil {
-			// Response writing failed, nothing we can do at this point
+			logger.Error(context.Background(), "Failed to write response", "error", err.Error())
 			return
 		}
 	}
 }

Note: This function lacks request context, so context.Background() would be used. Consider passing context as a parameter if request tracing is needed.

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

28-34: Consider accepting context as a parameter for better composability.

The check function currently creates a new context via context.Background(). While this is acceptable for a top-level error handler that exits the process, accepting a context parameter would improve composability and allow propagation of existing context values (e.g., trace IDs, operation IDs) into error logs.

♻️ Proposed refactor
-func check(err error, msg string) {
-	ctx := context.Background()
+func check(ctx context.Context, err error, msg string) {
 	if err != nil && err != http.ErrServerClosed {
 		logger.Error(ctx, msg, "error", err)
 		os.Exit(1)
 	}
 }

Then update call sites to pass context (e.g., from Start/Stop methods).

test/helper.go (1)

84-86: Consider using t.Fatal instead of os.Exit(1) for test setup failures.

os.Exit(1) bypasses Go's test framework reporting and cleanup. While this preserves the previous glog.Fatal behavior, switching to t.Fatal (or propagating errors) would provide better test diagnostics.

♻️ Example refactor
 		if err != nil {
-			logger.Error(ctx, "Unable to add environment flags", "error", err)
-			os.Exit(1)
+			t.Fatalf("Unable to add environment flags: %v", err)
 		}

Note: This would require passing t into the once.Do block or restructuring the initialization.

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

41-54: Double-close pattern may cause issues if Close() is not idempotent.

The explicit Close() call before os.Exit(1) (lines 50-52) is necessary since defer doesn't run on os.Exit. However, if the code is later refactored to use return instead of os.Exit, Close() would be called twice.

Consider restructuring to avoid the explicit close:

♻️ Alternative approach using a helper
 func runMigrate(_ *cobra.Command, _ []string) {
 	ctx := context.Background()
+	if err := doMigrate(ctx); err != nil {
+		logger.Error(ctx, "Migration failed", "error", err)
+		os.Exit(1)
+	}
+	logger.Info(ctx, "Migration completed successfully")
+}
+
+func doMigrate(ctx context.Context) error {
 	err := dbConfig.ReadFiles()
 	if err != nil {
-		logger.Error(ctx, "Fatal error", "error", err)
-		os.Exit(1)
+		return fmt.Errorf("reading config files: %w", err)
 	}
 
 	connection := db_session.NewProdFactory(dbConfig)
 	defer func() {
 		if closeErr := connection.Close(); closeErr != nil {
 			logger.Error(ctx, "Failed to close database connection", "error", closeErr)
 		}
 	}()
 
-	if err := db.Migrate(connection.New(ctx)); err != nil {
-		logger.Error(ctx, "Migration failed", "error", err)
-		// Close connection before exit to avoid resource leak
-		if closeErr := connection.Close(); closeErr != nil {
-			logger.Error(ctx, "Failed to close database connection", "error", closeErr)
-		}
-		os.Exit(1)
-	}
-
-	logger.Info(ctx, "Migration completed successfully")
+	return db.Migrate(connection.New(ctx))
 }
cmd/hyperfleet-api/servecmd/cmd.go (1)

76-93: Consider adding explicit signal handling for graceful shutdown.

The select {} on line 92 blocks indefinitely, causing the deferred telemetry.Shutdown (lines 57-61) to rely on process termination signals for execution. While Go does run deferred functions before process exit, explicit signal handling (using os/signal package) would improve clarity and allow for coordinated graceful shutdown of the API, metrics, and health check servers before telemetry is flushed.

pkg/middleware/masking.go (2)

19-26: Consider adding defensive nil check for cfg parameter.

If cfg is nil, this constructor will panic when accessing cfg.Masking.Enabled. While the caller likely always provides a valid config, adding a nil check or panic with a clear message would make the code more defensive and easier to debug.

🛡️ Proposed defensive nil check
 func NewMaskingMiddleware(cfg *config.LoggingConfig) *MaskingMiddleware {
+	if cfg == nil {
+		panic("NewMaskingMiddleware: cfg cannot be nil")
+	}
 	return &MaskingMiddleware{
 		enabled:          cfg.Masking.Enabled,
 		sensitiveHeaders: cfg.GetSensitiveHeadersList(),
 		sensitiveFields:  cfg.GetSensitiveFieldsList(),
 	}
 }

114-123: Be aware: substring matching may produce false positives.

The substring matching approach (line 118) will catch field name variants like password, user_password, passwordHash, but may also mask unintended fields. For example, if "pass" is configured as a sensitive field, it would match compass or passenger.

This is a reasonable trade-off for broader coverage, but consider documenting this behavior or evaluating if exact matches should be required for certain field names.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85b2ed0 and 73f62c8.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (50)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/auth/helpers.go
  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • pkg/errors/errors.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/http.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/middleware/masking.go
  • pkg/middleware/masking_test.go
  • pkg/middleware/otel.go
  • pkg/middleware/schema_validation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (5)
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
🚧 Files skipped from review as they are similar to previous changes (16)
  • pkg/services/cluster.go
  • pkg/handlers/openapi.go
  • pkg/telemetry/otel.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/handlers/helpers.go
  • pkg/logger/logger_test.go
  • pkg/handlers/metadata.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/middleware/otel.go
  • pkg/logger/context.go
  • test/integration/integration_test.go
  • pkg/db/db_session/test.go
  • pkg/services/generic.go
  • pkg/middleware/masking_test.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/auth/helpers.go
🧰 Additional context used
📓 Path-based instructions (11)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Files:

  • pkg/errors/errors.go
  • pkg/db/context.go
  • pkg/services/status_aggregation.go
  • pkg/middleware/schema_validation.go
  • pkg/logger/http.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/handlers/cluster_status.go
  • pkg/services/node_pool.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/framework.go
  • cmd/hyperfleet-api/main.go
  • test/helper.go
  • pkg/middleware/masking.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/nodepool_status.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/logger/ocm_bridge.go
  • pkg/logger/logger.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/api/error.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use PostgreSQL 14.2 with GORM ORM for database operations

Files:

  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
pkg/{dao,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Files:

  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
pkg/{handlers,services,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Files:

  • pkg/db/context.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster_status.go
  • pkg/services/node_pool.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/services/**/*.go: Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise
Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Files:

  • pkg/services/status_aggregation.go
  • pkg/services/node_pool.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Files:

  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster_status.go
  • pkg/services/node_pool.go
  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
pkg/**/*{auth,middleware}*.go

📄 CodeRabbit inference engine (AGENTS.md)

JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes
Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error
Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL
Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table
Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support
Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes
Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Files:

  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/nodepool_status.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Files:

  • pkg/api/error.go
🧠 Learnings (28)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Applied to files:

  • pkg/errors/errors.go
  • pkg/db/context.go
  • pkg/services/status_aggregation.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/handlers/cluster_status.go
  • pkg/services/node_pool.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/framework.go
  • cmd/hyperfleet-api/main.go
  • test/helper.go
  • docs/logging.md
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/logger/ocm_bridge.go
  • pkg/logger/logger.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/api/error.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Applied to files:

  • pkg/errors/errors.go
  • pkg/db/context.go
  • pkg/services/status_aggregation.go
  • pkg/middleware/schema_validation.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/handlers/framework.go
  • test/helper.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/logger/ocm_bridge.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/api/error.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Applied to files:

  • pkg/errors/errors.go
  • pkg/services/status_aggregation.go
  • pkg/middleware/schema_validation.go
  • pkg/handlers/framework.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/api/error.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO methods must accept context.Context as the first parameter for transaction propagation

Applied to files:

  • pkg/errors/errors.go
  • pkg/db/context.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/api/error.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All database sessions must be retrieved from context via db.NewContext(ctx) rather than creating new database connections

Applied to files:

  • pkg/errors/errors.go
  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/api/error.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags

Applied to files:

  • pkg/errors/errors.go
  • cmd/hyperfleet-api/main.go
  • test/helper.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/**/*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Applied to files:

  • pkg/errors/errors.go
  • test/helper.go
  • go.mod
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/openapi/model_*.go : Generated Go models from openapi-generator-cli include JSON tags and validation metadata; do not manually edit generated model_*.go files

Applied to files:

  • pkg/errors/errors.go
  • test/helper.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : Always retrieve database sessions from context in DAO methods; never create direct gorm.Open() connections

Applied to files:

  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,services}/**/*.go : Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Applied to files:

  • pkg/db/context.go
  • pkg/services/status_aggregation.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Applied to files:

  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise

Applied to files:

  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table

Applied to files:

  • pkg/services/status_aggregation.go
  • pkg/handlers/cluster_status.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/**/*{auth,middleware}*.go : JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Applied to files:

  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : NodePool owner_references.id must enforce foreign key relationship to parent cluster to maintain referential integrity

Applied to files:

  • pkg/services/node_pool.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/handlers/nodepool_status.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for database operations

Applied to files:

  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • test/helper.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • go.mod
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error

Applied to files:

  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/migrate/**/*.go : Use GORM AutoMigrate at startup via ./bin/hyperfleet-api migrate command; migrations are idempotent and track applied migrations

Applied to files:

  • pkg/db/db_session/default.go
  • cmd/hyperfleet-api/main.go
  • test/helper.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • go.mod
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL

Applied to files:

  • pkg/db/db_session/default.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage

Applied to files:

  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/migrations/**/*.go : Store spec and conditions as JSONB in PostgreSQL to enable complex field queries

Applied to files:

  • pkg/db/db_session/testcontainer.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes

Applied to files:

  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • test/helper.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/server/server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Environment-based configuration: use OCM_ENV variable to select development, unit_testing, integration_testing, or production configuration

Applied to files:

  • test/helper.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,db}/**/*.go : Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Applied to files:

  • pkg/db/migrations.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO interface methods must include Create, Get, List, Update, Delete operations with consistent signatures taking context.Context

Applied to files:

  • pkg/db/transaction_middleware.go
🧬 Code graph analysis (18)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (286-288)
pkg/services/status_aggregation.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
pkg/handlers/cluster_status.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
pkg/db/db_session/default.go (1)
pkg/logger/logger.go (3)
  • Info (262-264)
  • Debug (258-260)
  • Error (270-272)
pkg/db/db_session/testcontainer.go (3)
pkg/logger/logger.go (5)
  • Info (262-264)
  • Errorf (286-288)
  • Infof (278-280)
  • Debug (258-260)
  • Error (270-272)
pkg/db/db_session/default.go (1)
  • Default (20-29)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/handlers/framework.go (1)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
cmd/hyperfleet-api/main.go (1)
pkg/logger/logger.go (4)
  • Error (270-272)
  • LogConfig (25-32)
  • FormatJSON (21-21)
  • InitGlobalLogger (174-182)
test/helper.go (1)
pkg/logger/logger.go (7)
  • Error (270-272)
  • Info (262-264)
  • Debug (258-260)
  • Warn (266-268)
  • LogConfig (25-32)
  • FormatText (19-19)
  • InitGlobalLogger (174-182)
pkg/middleware/masking.go (1)
pkg/config/logging.go (1)
  • LoggingConfig (12-19)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (3)
pkg/middleware/masking.go (1)
  • MaskingMiddleware (13-17)
pkg/logger/logger.go (1)
  • Info (262-264)
pkg/logger/http.go (5)
  • HTTPMethod (10-12)
  • HTTPPath (15-17)
  • HTTPUserAgent (30-32)
  • HTTPStatusCode (20-22)
  • HTTPDuration (25-27)
cmd/hyperfleet-api/migrate/cmd.go (4)
pkg/logger/logger.go (2)
  • Error (270-272)
  • Info (262-264)
pkg/db/db_session/default.go (1)
  • NewProdFactory (33-37)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • Warn (266-268)
cmd/hyperfleet-api/server/api_server.go (5)
pkg/logger/ocm_bridge.go (1)
  • NewOCMLoggerBridge (16-18)
pkg/logger/logger.go (2)
  • Info (262-264)
  • Error (270-272)
cmd/hyperfleet-api/server/server.go (1)
  • Server (13-18)
cmd/hyperfleet-api/environments/types.go (1)
  • Database (36-38)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/db/migrations.go (2)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/logger/logger.go (1)
  • Error (270-272)
pkg/logger/ocm_bridge.go (1)
pkg/logger/logger.go (5)
  • Debug (258-260)
  • GetLogger (208-216)
  • Info (262-264)
  • Warn (266-268)
  • Error (270-272)
pkg/logger/logger.go (2)
pkg/logger/context.go (6)
  • GetTraceID (69-72)
  • GetSpanID (75-78)
  • GetOperationID (61-66)
  • GetClusterID (81-84)
  • GetResourceType (87-90)
  • GetResourceID (93-96)
pkg/errors/errors.go (1)
  • New (112-132)
cmd/hyperfleet-api/server/server.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
pkg/api/error.go (1)
pkg/logger/logger.go (1)
  • Error (270-272)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

🔇 Additional comments (34)
pkg/handlers/nodepool_status.go (1)

101-104: LGTM - Properly structured warning log.

The migration correctly uses the context-aware logger.Warn with structured fields (nodepool_id, error). The warning level is appropriate since the aggregation failure is non-fatal and the request should still succeed.

pkg/handlers/cluster_status.go (1)

101-104: LGTM - Consistent with nodepool_status handler pattern.

The logging migration mirrors the pattern in nodepool_status.go, maintaining consistency across status handlers. Context-aware logging with structured fields is correctly implemented.

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

68-117: Logging migration in Initialize() looks correct.

The use of context.Background() is appropriate for startup/initialization code that runs outside request context. The logger.Errorf followed by os.Exit(1) pattern correctly replaces the previous fatal logging behavior.

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

11-17: Good import aliasing to avoid collision.

Using gormlogger as an alias for gorm.io/gorm/logger avoids collision with the hyperfleet logger package. This is a clean approach.


144-153: LGTM - GORM logger configuration updated correctly.

The gormlogger.Silent reference correctly uses the aliased import for the GORM logger level.

pkg/logger/http.go (1)

1-49: Well-structured HTTP logging helpers.

The attribute helpers follow slog conventions and provide a clean API for structured HTTP request/response logging. The separation of request and response attributes is a good design choice.

Consider adding HTTPRemoteAddr or HTTPClientIP attributes for request tracing in future iterations, as client identification can be valuable for debugging and security auditing.

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

41-111: LGTM - Proper migration to structured logging.

The initialization flow correctly uses the new context-aware logger with appropriate error handling. The os.Exit(1) pattern after logger.Errorf is acceptable for startup failures that should prevent the application from running.


155-175: Good use of structured logging with key-value pairs.

The ResetDB function correctly uses the structured logging API with inline fields ("table", "error"), which aligns with the coding guidelines for context-aware log fields.

pkg/logger/logger.go (2)

43-83: Well-designed custom slog handler with context enrichment.

The HyperFleetHandler correctly implements the slog.Handler interface with proper delegation to the underlying handler. The safe type assertion for slog.Level (lines 55-59) addresses the previous review feedback. The context field extraction (trace_id, span_id, operation_id, etc.) aligns well with the coding guidelines for structured logging.


166-182: Thread-safe global logger initialization.

The use of sync.Once ensures the logger is initialized exactly once, addressing the previous review feedback about race conditions. The loggerMutex inside initOnce.Do is technically redundant since sync.Once already provides synchronization, but it doesn't cause issues and maintains consistency with ReconfigureGlobalLogger.

pkg/auth/authz_middleware_mock.go (1)

17-22: LGTM - Proper structured logging migration.

The mock middleware correctly uses r.Context() for context-aware logging with structured fields. This aligns with the coding guidelines for using context values for request-scoped data.

pkg/api/error.go (2)

40-46: LGTM - Request context properly used for tracing.

The previous review feedback has been addressed. Using r.Context() preserves trace IDs, span IDs, and operation IDs from the request context, maintaining observability. Based on learnings, this aligns with using context values for request-scoped data.


88-112: Appropriate use of Background context in init().

Using context.Background() in the init() function is correct since there's no request context available during package initialization. The error logging before os.Exit(1) provides visibility into initialization failures.

pkg/db/transaction_middleware.go (1)

14-29: LGTM - Correct transaction management with proper error handling.

The middleware correctly:

  1. Uses r.Context() for logging when NewContext fails (addressing past review feedback)
  2. Assigns the transaction context to the request via r.WithContext(ctx)
  3. Defers Resolve to handle commit/rollback on request completion

This aligns with the coding guidelines that database transactions are automatically managed by TransactionMiddleware.

pkg/services/status_aggregation.go (1)

142-142: LGTM! Structured logging correctly implemented.

The migration to structured logging with logger.Warn properly uses context and key-value pairs for both the missing adapter warning and stale generation scenarios. The structured fields (adapter, observed_generation, expected_generation) enable better queryability and align with the project's logging specification.

Based on learnings, this follows the requirement to use structured logging via logger with context-aware log fields.

Also applies to: 149-152

README.md (1)

158-158: LGTM! Documentation link appropriately added.

The new logging documentation reference complements the broader logging infrastructure introduced in this PR.

pkg/middleware/schema_validation.go (1)

20-20: LGTM! Context-aware structured logging correctly implemented.

The migration replaces per-request logger initialization with centralized logger calls, properly using r.Context() for request-scoped data. Log levels are appropriately chosen (Info for validation errors, Error for encoding failures, Warn for close errors), and structured fields enable better queryability.

Based on learnings, this correctly uses context values for passing request-scoped data.

Also applies to: 26-30, 53-54

docs/logging.md (1)

1-509: Excellent comprehensive logging documentation.

This documentation provides thorough coverage of the logging system, including architecture, configuration, usage patterns, and best practices. The examples are practical and well-explained, particularly:

  • Clear middleware chain visualization
  • Concrete log output examples for different scenarios
  • Security guidance on data masking
  • Troubleshooting section for common issues

The documentation will serve as a valuable reference for developers working with the HyperFleet API logging system.

pkg/db/migrations.go (1)

30-39: LGTM! Structured logging with context properly implemented.

The migration error handling now uses the new slog-based logger with structured fields (migration_id, error), and the context is correctly passed to sessionFactory.New(ctx).

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

54-66: Good early logger initialization pattern.

Initializing the logger before any logging ensures consistent log output. The configuration with JSON format and INFO level provides sensible defaults before environment-specific configuration is loaded by subcommands.


68-74: LGTM!

Graceful fallback to "unknown" when hostname retrieval fails is appropriate for non-critical logging metadata.

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

15-57: LGTM! Clean middleware implementation with proper masking integration.

The middleware correctly accepts the masker as a parameter (addressing the previous per-request allocation concern), skips noisy health check logging, and captures request/response metrics with structured logging.

test/helper.go (2)

72-74: Good test logger initialization with text format for readability.

Initializing the logger early and using text format for tests improves debugging experience. The static hostname ensures deterministic test output.


544-555: LGTM!

Test logger configuration with text format, static hostname, and "test" version provides consistent and readable output for debugging.

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

39-56: LGTM! Authentication handler properly wired with OCM logger bridge.

The NewOCMLoggerBridge() provides compatibility with the OCM SDK's logging interface while routing logs through the centralized slog-based logger.


105-128: LGTM!

Serve method properly logs TLS configuration status with bind address for operational visibility.


137-152: LGTM!

Start method properly handles startup errors and database connection cleanup with structured logging.

cmd/hyperfleet-api/servecmd/cmd.go (2)

95-138: LGTM! Logger reconfiguration properly handles the sync.Once limitation.

Using ReconfigureGlobalLogger instead of InitGlobalLogger correctly addresses the previous issue where the default logger from main.go couldn't be updated. The fallback pattern for parsing errors ensures the server starts even with invalid configuration.


50-66: OpenTelemetry initialization with proper shutdown handling.

Implementation correctly uses conditional initialization, properly handles deferred shutdown with nil checks (telemetry.Shutdown returns nil if TracerProvider is nil), and logs both success and disabled states. Structured logging is automatically applied via the HyperFleetHandler which enriches all logs with context-aware fields including operation_id, trace_id, span_id, and component metadata. The code is well-implemented.

pkg/middleware/masking.go (2)

28-44: LGTM! Header masking implementation is correct.

The method correctly:

  • Guards with the enabled flag
  • Creates a new header map to avoid modifying the original
  • Uses case-insensitive matching for sensitive headers
  • Replaces sensitive values while preserving others

69-102: Excellent recursive masking implementation.

The recursive masking correctly handles:

  • Top-level maps and arrays via type switch
  • Nested objects and arrays
  • Array elements that are maps
  • Sensitive field detection at any nesting level

The implementation comprehensively masks sensitive data in complex JSON structures.

pkg/logger/ocm_bridge.go (3)

20-38: Correct context-aware logging bridge implementation.

The logging methods correctly:

  • Accept and pass through context for request-scoped logging
  • Bridge printf-style formatting to slog using fmt.Sprintf
  • Use the global logger via thread-safe GetLogger()
  • Implement the OCM SDK logger interface

Based on learnings, this properly uses context-aware logging with operation ID, account ID, and transaction ID when present in the context.


40-44: Note: Fatal terminates the process immediately.

The Fatal method correctly logs the fatal message and then calls os.Exit(1), which immediately terminates the process. This bypasses deferred function calls and cleanup handlers.

This is the expected behavior for fatal errors, but ensure Fatal is only called for truly unrecoverable errors where immediate termination is appropriate.


46-64: Appropriate use of context.Background() for level checks.

The level-enabled methods correctly use context.Background() for checking if a log level is enabled. This is appropriate because:

  • Log level configuration is typically a global setting, not context-specific
  • The checks don't need request-scoped context values
  • This resolves the past context inconsistency issue

@yasun1
Copy link
Contributor Author

yasun1 commented Jan 8, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

✅ Actions performed

Full review triggered.

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: 7

Caution

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

⚠️ Outside diff range comments (2)
pkg/db/db_session/default.go (1)

100-120: Fix context propagation to preserve request-scoped data.

Creating a new context.Background() on line 101 breaks the context chain. Since waitForNotification is called from newListener (line 136), which receives a context parameter, this loses critical request-scoped fields (operation ID, account ID, transaction ID) that should propagate through the logging calls.

Based on learnings, as per coding guidelines for pkg/db/**/*.go.

🔄 Proposed fix to pass context through the call chain

Update the function signature to accept context:

-func waitForNotification(l *pq.Listener, callback func(id string)) {
-	ctx := context.Background()
+func waitForNotification(ctx context.Context, l *pq.Listener, callback func(id string)) {
 	for {
 		select {
 		case n := <-l.Notify:

And update the call site on line 136:

 	logger.Info(ctx, "Starting channeling monitor", "channel", channel)
 	for {
-		waitForNotification(listener, callback)
+		waitForNotification(ctx, listener, callback)
 	}
pkg/auth/helpers.go (1)

12-22: Enhance error logging with structured fields.

The current implementation logs err.Error() as a plain string, which loses the structured information available in the ServiceError type. This makes log filtering, alerting, and correlation more difficult.

♻️ Proposed refactor to use structured logging
 func handleError(ctx context.Context, w http.ResponseWriter, code errors.ServiceErrorCode, reason string) {
 	operationID := logger.GetOperationID(ctx)
 	err := errors.New(code, "%s", reason)
 	if err.HttpCode >= 400 && err.HttpCode <= 499 {
-		logger.Info(ctx, err.Error())
+		logger.Info(ctx, "Client error during authentication", 
+			"error_code", err.Code, 
+			"reason", err.Reason, 
+			"http_code", err.HttpCode,
+			"operation_id", operationID)
 	} else {
-		logger.Error(ctx, err.Error())
+		logger.Error(ctx, "Server error during authentication", 
+			"error_code", err.Code, 
+			"reason", err.Reason, 
+			"http_code", err.HttpCode,
+			"operation_id", operationID)
 	}
 
 	writeJSONResponse(w, err.HttpCode, err.AsOpenapiError(operationID))
 }

Based on coding guidelines requiring structured logging with context-aware log fields.

🤖 Fix all issues with AI agents
In @cmd/hyperfleet-api/server/logging/request_logging_middleware.go:
- Around line 59-77: The responseWriter's statusCode is being initialized to
http.StatusOK elsewhere, making the check in responseWriter.Write (if
rw.statusCode == 0 { rw.statusCode = http.StatusOK }) dead; fix by removing the
explicit initialization so statusCode defaults to 0 and the Write method
correctly sets it to 200 when WriteHeader wasn't called—locate the
responseWriter construction/initialization and stop pre-setting statusCode to
http.StatusOK (keep the WriteHeader and Write methods as-is), ensuring the
wrapper follows the standard pattern of starting with statusCode == 0.

In @pkg/errors/errors.go:
- Line 9: The build is failing because the generated OpenAPI package referenced
by the import "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi"
is missing; run the repository code generator (make generate or make
generate-all) which uses the generate target (Dockerfile.openapi) to recreate
pkg/api/openapi, then re-run your build so the symbols used in errors.go (the
openapi models referenced on lines ~154–184) resolve.

In @pkg/handlers/framework.go:
- Around line 31-40: In handleError, replace the current
logger.Info/Logger.Error calls that only pass err.Error() with structured
logging that includes operationID and ServiceError fields (err.Code,
err.HttpCode, err.Reason) so logs are queryable; update the logger.Info and
logger.Error invocations (inside function handleError) to pass a message plus
these structured fields (operationID, code, http_code, reason) and keep the
existing writeJSONResponse call unchanged.

In @pkg/logger/context.go:
- Around line 14-22: OpIDHeader is currently typed as contextKey but represents
an HTTP header name; change it to a plain string constant (e.g., OpIDHeader
string = "X-Operation-ID") or move it out of the contextKey block so it is not
part of the contextKey type, and keep the other keys (OpIDKey, TraceIDCtxKey,
SpanIDCtxKey, etc.) as contextKey; update any uses of OpIDHeader to expect a
string header name rather than a context key.

In @pkg/middleware/masking.go:
- Around line 50-67: MaskBody currently returns the original body when
json.Unmarshal or json.Marshal fails, which can leak sensitive data; change
MaskBody so that when m.enabled is true and JSON parsing/marshaling fails it
does NOT return the raw body but instead (1) logs a warning and returns a
deterministic redacted placeholder (e.g., "[REDACTED]" or "{}") and/or (2) calls
a new fallback function (e.g., maskTextFallback) that applies simple text-based
regex heuristics to redact emails, credit-card-like sequences, API keys/tokens,
and form-encoded key-values before returning the redacted bytes; implement
maskTextFallback and invoke it from MaskBody on both json.Unmarshal and
json.Marshal error paths while preserving existing behavior when m.enabled is
false and still using maskRecursive for successful JSON parsing.

In @pkg/middleware/otel.go:
- Around line 18-50: OTelMiddleware currently enriches context with trace and
span IDs via logger.WithTraceID and logger.WithSpanID; add analogous calls
logger.WithAccountID and logger.WithTransactionID inside the enrichedHandler
before r = r.WithContext(ctx) so downstream logs include those fields. Extract
account_id and transaction_id from the incoming request (e.g.,
r.Header.Get("X-Account-ID") / "X-Transaction-ID" or existing context values set
earlier) and only add them when non-empty; ensure names match your logger
helpers and keep operation_id enrichment as-is.
🧹 Nitpick comments (11)
pkg/errors/errors.go (1)

117-118: Consider propagating context to error constructors.

Using context.Background() here means the error log won't include operation_id, trace_id, or other context-aware fields, making it harder to correlate with the originating request.

Since New() and its wrapper functions (e.g., NotFound, GeneralError) don't currently accept a context parameter, this may require a broader refactoring. However, for better observability, consider:

  1. Adding a context parameter to error constructors
  2. Or, deferring the logging to the error handler (where context is available)
🔧 Potential refactor to accept context

Example approach (requires updating all error constructor call sites):

-func New(code ServiceErrorCode, reason string, values ...interface{}) *ServiceError {
+func New(ctx context.Context, code ServiceErrorCode, reason string, values ...interface{}) *ServiceError {
 	var err *ServiceError
 	exists, err := Find(code)
 	if !exists {
-		ctx := context.Background()
 		logger.Errorf(ctx, "Undefined error code used: %d", code)
 		err = &ServiceError{
 			Code:     ErrorGeneral,
 			Reason:   "Unspecified error",
 			HttpCode: 500,
 		}
 	}
 	// ...
}

Then update wrapper functions:

-func NotFound(reason string, values ...interface{}) *ServiceError {
-	return New(ErrorNotFound, reason, values...)
+func NotFound(ctx context.Context, reason string, values ...interface{}) *ServiceError {
+	return New(ctx, ErrorNotFound, reason, values...)
}
cmd/hyperfleet-api/server/healthcheck_server.go (1)

43-63: Consider using structured logging for consistency.

The changes correctly replace glog with the centralized logger. However, the formatted string logging (logger.Infof) could be enhanced with structured key-value pairs for better log parsing and filtering.

♻️ Optional refactor to structured logging
 func (s healthCheckServer) Start() {
 	ctx := context.Background()
 	var err error
 	if env().Config.HealthCheck.EnableHTTPS {
 		if env().Config.Server.HTTPSCertFile == "" || env().Config.Server.HTTPSKeyFile == "" {
 			check(
 				fmt.Errorf("unspecified required --https-cert-file, --https-key-file"),
 				"Can't start https server",
 			)
 		}
 
 		// Serve with TLS
-		logger.Infof(ctx, "Serving HealthCheck with TLS at %s", env().Config.HealthCheck.BindAddress)
+		logger.Info(ctx, "Serving HealthCheck with TLS", 
+			"bind_address", env().Config.HealthCheck.BindAddress,
+			"tls_enabled", true)
 		err = s.httpServer.ListenAndServeTLS(env().Config.Server.HTTPSCertFile, env().Config.Server.HTTPSKeyFile)
 	} else {
-		logger.Infof(ctx, "Serving HealthCheck without TLS at %s", env().Config.HealthCheck.BindAddress)
+		logger.Info(ctx, "Serving HealthCheck without TLS", 
+			"bind_address", env().Config.HealthCheck.BindAddress,
+			"tls_enabled", false)
 		err = s.httpServer.ListenAndServe()
 	}
 	check(err, "HealthCheck server terminated with errors")
-	logger.Info(ctx, "HealthCheck server terminated")
+	logger.Info(ctx, "HealthCheck server terminated", "server", "healthcheck")
 }

Based on coding guidelines encouraging structured logging with key-value pairs for better observability.

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

33-56: LGTM! Proper resource management and logging.

The migration to structured logging is well-implemented:

  • Background context created at start (line 33)
  • Structured logging throughout with appropriate log levels
  • Success confirmation added (line 56)

The dual connection-close pattern (defer + explicit close before os.Exit) is correct:

  • Defer (lines 41-45) handles cleanup on success
  • Explicit close (lines 50-52) ensures cleanup before os.Exit(1) since defers don't run when the process exits

This is the proper pattern for cobra command handlers that use os.Exit.

📝 Optional: Add clarifying comment

Consider adding a comment explaining why connection cleanup appears twice:

 	defer func() {
+		// Cleanup on successful completion (defers don't run on os.Exit)
 		if closeErr := connection.Close(); closeErr != nil {
 			logger.Error(ctx, "Failed to close database connection", "error", closeErr)
 		}
 	}()
 
 	if err := db.Migrate(connection.New(ctx)); err != nil {
 		logger.Error(ctx, "Migration failed", "error", err)
-		// Close connection before exit to avoid resource leak
+		// Explicit close required before os.Exit (bypasses defers)
 		if closeErr := connection.Close(); closeErr != nil {

This clarifies the pattern for future maintainers.

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

25-52: LGTM with optional enhancement opportunity.

The initialization flow correctly prioritizes logger setup before any logging occurs. Flag parsing and error handling are properly routed through the centralized logger.

Optional: Consider passing context to commands

The context created on line 28 is currently only used in error handlers. If subcommands need context for structured logging (as per the coding guidelines), consider setting it on the Cobra command:

 func main() {
 	// Initialize logger first (before any logging occurs)
 	initDefaultLogger()
 	ctx := context.Background()

 	// Parse flags (needed for cobra compatibility)
 	if err := flag.CommandLine.Parse([]string{}); err != nil {
 		logger.Error(ctx, "Failed to parse flags", "error", err)
 		os.Exit(1)
 	}

 	rootCmd := &cobra.Command{
 		Use:  "hyperfleet",
 		Long: "hyperfleet serves as a template for new microservices",
 	}
+	rootCmd.SetContext(ctx)

 	// All subcommands under root
 	migrateCmd := migrate.NewMigrateCommand()
 	serveCmd := servecmd.NewServeCommand()
cmd/hyperfleet-api/servecmd/cmd.go (1)

122-122: Consider logging hostname resolution failure.

The error from os.Hostname() is silently ignored. While an empty hostname is acceptable fallback behavior, logging a debug message when hostname resolution fails would aid troubleshooting in containerized environments where hostname configuration may be significant.

🔧 Optional enhancement
 	// Get hostname
-	hostname, _ := os.Hostname()
+	hostname, err := os.Hostname()
+	if err != nil {
+		logger.Debug(ctx, "Failed to resolve hostname", "error", err)
+	}
pkg/middleware/masking.go (1)

13-17: Optional: Extract redaction string as a constant.

The redaction placeholder "***REDACTED***" is duplicated in MaskHeaders (line 38) and maskMapRecursive (line 85). Consider extracting it as a package-level constant to improve maintainability.

♻️ Suggested refactor
+const redactionPlaceholder = "***REDACTED***"
+
 // MaskingMiddleware handles sensitive data masking for logging
 type MaskingMiddleware struct {
 	enabled          bool
 	sensitiveHeaders []string
 	sensitiveFields  []string
 }

Then update usage:

 func (m *MaskingMiddleware) MaskHeaders(headers http.Header) http.Header {
 	// ...
 	for key, values := range headers {
 		if m.isSensitiveHeader(key) {
-			masked[key] = []string{"***REDACTED***"}
+			masked[key] = []string{redactionPlaceholder}
 		} else {
 			masked[key] = values
 		}
 	}
 	// ...
 }
 func (m *MaskingMiddleware) maskMapRecursive(data map[string]interface{}) {
 	for key, value := range data {
 		if m.isSensitiveField(key) {
-			data[key] = "***REDACTED***"
+			data[key] = redactionPlaceholder
 			continue
 		}
 		// ...
 	}
 }

Also applies to: 28-44, 82-87

pkg/config/logging.go (1)

103-125: Optional: Filter empty strings from parsed lists.

The GetSensitiveHeadersList and GetSensitiveFieldsList methods trim whitespace but don't filter empty strings. If the configuration contains consecutive commas (e.g., "Authorization,,Cookie"), the resulting slice will contain empty strings, which could cause false positives in sensitivity checks.

♻️ Suggested enhancement
 func (l *LoggingConfig) GetSensitiveHeadersList() []string {
 	if l.Masking.SensitiveHeaders == "" {
 		return []string{}
 	}
 	headers := strings.Split(l.Masking.SensitiveHeaders, ",")
+	result := make([]string, 0, len(headers))
 	for i := range headers {
 		headers[i] = strings.TrimSpace(headers[i])
+		if headers[i] != "" {
+			result = append(result, headers[i])
+		}
 	}
-	return headers
+	return result
 }
 
 func (l *LoggingConfig) GetSensitiveFieldsList() []string {
 	if l.Masking.SensitiveFields == "" {
 		return []string{}
 	}
 	fields := strings.Split(l.Masking.SensitiveFields, ",")
+	result := make([]string, 0, len(fields))
 	for i := range fields {
 		fields[i] = strings.TrimSpace(fields[i])
+		if fields[i] != "" {
+			result = append(result, fields[i])
+		}
 	}
-	return fields
+	return result
 }
pkg/logger/context.go (1)

60-66: Optional: Inconsistent return signature in GetOperationID.

GetOperationID returns only string, while all other Get* functions (GetTraceID, GetSpanID, etc.) follow the standard Go idiom of returning (string, bool). This inconsistency makes it impossible for callers to distinguish between "operation ID not present in context" and "operation ID is an empty string."

Consider aligning with the other getters for API consistency:

♻️ Suggested refactor
-// GetOperationID retrieves operation ID from context
-func GetOperationID(ctx context.Context) string {
-	if opID, ok := ctx.Value(OpIDKey).(string); ok {
-		return opID
-	}
-	return ""
-}
+// GetOperationID retrieves operation ID from context
+func GetOperationID(ctx context.Context) (string, bool) {
+	opID, ok := ctx.Value(OpIDKey).(string)
+	return opID, ok
+}

Note: This is a breaking API change, so evaluate whether existing callers can be updated.

pkg/logger/logger.go (3)

44-48: Consider adding nil check for cfg parameter.

If NewHyperFleetHandler is called with a nil config, the function will panic when accessing cfg.Level. Adding a guard would make the API more defensive.

🛠️ Suggested fix
 func NewHyperFleetHandler(cfg *LogConfig) *HyperFleetHandler {
+	if cfg == nil {
+		cfg = &LogConfig{
+			Level:  slog.LevelInfo,
+			Format: FormatJSON,
+			Output: os.Stdout,
+		}
+	}
 	var baseHandler slog.Handler
 	opts := &slog.HandlerOptions{

145-164: Consider filtering log/slog internal frames from stack trace.

The filter excludes runtime. and testing. frames but may include internal log/slog frames which aren't useful for debugging application errors.

🛠️ Suggested enhancement
 		if !strings.Contains(frame.Function, "runtime.") &&
-			!strings.Contains(frame.Function, "testing.") {
+			!strings.Contains(frame.Function, "testing.") &&
+			!strings.Contains(frame.Function, "log/slog.") {
 			stackTrace = append(stackTrace,

265-279: Formatted variants (*f functions) lose structured logging benefits.

These functions format args into the message string, losing the key-value structure that slog is designed for. Per coding guidelines emphasizing structured logging, consider adding a doc comment warning users to prefer the non-formatted variants with structured args.

📝 Suggested documentation
+// Debugf logs a debug message with formatted string.
+// Prefer Debug() with structured args (e.g., slog.String("key", "value"))
+// for better log analysis and querying capabilities.
 func Debugf(ctx context.Context, format string, args ...interface{}) {
 	GetLogger().DebugContext(ctx, fmt.Sprintf(format, args...))
 }

+// Infof logs an info message with formatted string.
+// Prefer Info() with structured args for better log analysis.
 func Infof(ctx context.Context, format string, args ...interface{}) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b4c857 and 33d1b7e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (54)
  • README.md
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • cmd/hyperfleet-api/server/api_server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • cmd/hyperfleet-api/server/routes.go
  • cmd/hyperfleet-api/server/server.go
  • docs/logging.md
  • go.mod
  • pkg/api/error.go
  • pkg/auth/authz_middleware_mock.go
  • pkg/auth/helpers.go
  • pkg/config/config.go
  • pkg/config/logging.go
  • pkg/config/logging_test.go
  • pkg/db/context.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/test.go
  • pkg/db/db_session/testcontainer.go
  • pkg/db/migrations.go
  • pkg/db/transaction_middleware.go
  • pkg/errors/errors.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/framework.go
  • pkg/handlers/helpers.go
  • pkg/handlers/metadata.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/openapi.go
  • pkg/logger/context.go
  • pkg/logger/http.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/logger/operationid_middleware.go
  • pkg/middleware/masking.go
  • pkg/middleware/masking_test.go
  • pkg/middleware/otel.go
  • pkg/middleware/schema_validation.go
  • pkg/services/cluster.go
  • pkg/services/generic.go
  • pkg/services/node_pool.go
  • pkg/services/status_aggregation.go
  • pkg/telemetry/otel.go
  • test/helper.go
  • test/integration/integration_test.go
💤 Files with no reviewable changes (6)
  • cmd/hyperfleet-api/server/logging/writer.go
  • cmd/hyperfleet-api/server/logging/logging.go
  • cmd/hyperfleet-api/server/logging/formatter.go
  • cmd/hyperfleet-api/server/logging/formatter_json.go
  • cmd/hyperfleet-api/server/logging/responseinfo.go
  • pkg/logger/operationid_middleware.go
🧰 Additional context used
📓 Path-based instructions (12)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)
Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/db/context.go
  • test/helper.go
  • pkg/logger/logger_test.go
  • pkg/telemetry/otel.go
  • pkg/auth/helpers.go
  • pkg/middleware/otel.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/services/generic.go
  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
  • pkg/middleware/schema_validation.go
  • pkg/db/migrations.go
  • pkg/config/config.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/errors/errors.go
  • pkg/handlers/metadata.go
  • pkg/config/logging.go
  • cmd/hyperfleet-api/main.go
  • pkg/logger/http.go
  • pkg/services/cluster.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/middleware/masking.go
  • pkg/api/error.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/services/status_aggregation.go
  • pkg/middleware/masking_test.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/logger/context.go
  • pkg/config/logging_test.go
  • pkg/services/node_pool.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • test/integration/integration_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/logger.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/handlers/**/*.go: HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes
Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error
Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL
Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table
Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support
Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes
Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Files:

  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/metadata.go
  • pkg/handlers/framework.go
pkg/{dao,handlers}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Files:

  • pkg/handlers/openapi.go
  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/metadata.go
  • pkg/handlers/framework.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Files:

  • pkg/handlers/openapi.go
  • pkg/services/generic.go
  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/metadata.go
  • pkg/services/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/services/node_pool.go
  • pkg/handlers/framework.go
pkg/{handlers,services,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Files:

  • pkg/handlers/openapi.go
  • pkg/db/context.go
  • pkg/services/generic.go
  • pkg/handlers/helpers.go
  • pkg/handlers/nodepool_status.go
  • pkg/db/migrations.go
  • pkg/handlers/cluster_status.go
  • pkg/handlers/metadata.go
  • pkg/services/cluster.go
  • pkg/db/db_session/test.go
  • pkg/services/status_aggregation.go
  • pkg/services/node_pool.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
pkg/**/*{auth,middleware}*.go

📄 CodeRabbit inference engine (AGENTS.md)

JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Use PostgreSQL 14.2 with GORM ORM for database operations

Files:

  • pkg/db/context.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
pkg/{dao,db}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use polymorphic status tables with owner_type + owner_id to support multiple resource types without creating separate status tables

Files:

  • pkg/db/context.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

pkg/services/**/*.go: Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise
Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Files:

  • pkg/services/generic.go
  • pkg/services/cluster.go
  • pkg/services/status_aggregation.go
  • pkg/services/node_pool.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Files:

  • pkg/api/error.go
test/integration/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

test/integration/**/*_test.go: Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage
All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Files:

  • test/integration/integration_test.go
🧠 Learnings (31)
📓 Common learnings
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,db}/**/*.go : Use context values for passing request-scoped data (session, operation ID, user info) rather than function parameters or global state

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/db/context.go
  • pkg/auth/helpers.go
  • pkg/middleware/otel.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/services/generic.go
  • pkg/handlers/helpers.go
  • pkg/middleware/schema_validation.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/errors/errors.go
  • pkg/handlers/metadata.go
  • pkg/services/cluster.go
  • pkg/db/db_session/test.go
  • pkg/api/error.go
  • pkg/services/status_aggregation.go
  • pkg/logger/context.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • test/integration/integration_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/**/*.go : Embed OpenAPI specification at compile time using Go 1.16+ //go:embed directive for self-contained binary deployment

Applied to files:

  • pkg/handlers/openapi.go
  • test/helper.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/errors/errors.go
  • go.mod
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use structured logging via logger.NewOCMLogger(ctx) with context-aware log fields including operation ID, account ID, and transaction ID

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • pkg/db/context.go
  • docs/logging.md
  • test/helper.go
  • pkg/logger/logger_test.go
  • pkg/telemetry/otel.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/services/generic.go
  • pkg/handlers/helpers.go
  • pkg/middleware/schema_validation.go
  • pkg/db/migrations.go
  • pkg/handlers/cluster_status.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/errors/errors.go
  • pkg/handlers/metadata.go
  • pkg/config/logging.go
  • cmd/hyperfleet-api/main.go
  • pkg/services/cluster.go
  • cmd/hyperfleet-api/server/routes.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • pkg/api/error.go
  • pkg/services/status_aggregation.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/logger/context.go
  • pkg/config/logging_test.go
  • pkg/services/node_pool.go
  • pkg/db/transaction_middleware.go
  • pkg/handlers/framework.go
  • test/integration/integration_test.go
  • pkg/logger/ocm_bridge.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
  • pkg/logger/logger.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/api/openapi/model_*.go : Generated Go models from openapi-generator-cli include JSON tags and validation metadata; do not manually edit generated model_*.go files

Applied to files:

  • pkg/handlers/openapi.go
  • test/helper.go
  • pkg/errors/errors.go
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{handlers,services,dao}/**/*.go : Errors must use structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields; send via errors.SendError(w, r, err)

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/auth/helpers.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/handlers/helpers.go
  • pkg/middleware/schema_validation.go
  • pkg/errors/errors.go
  • pkg/handlers/metadata.go
  • pkg/api/error.go
  • pkg/services/status_aggregation.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : HTTP handlers must follow the pattern: parse request → call service/DAO → handle errors → send response with appropriate HTTP status codes

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/auth/helpers.go
  • pkg/handlers/helpers.go
  • cmd/hyperfleet-api/server/logging/request_logging_middleware.go
  • pkg/handlers/framework.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept resource creation/update/delete requests via REST endpoints, validate, and persist to PostgreSQL

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/db/db_session/default.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Health check endpoint at GET /healthcheck returns 200 OK when database is accessible; used by Kubernetes liveness/readiness probes

Applied to files:

  • pkg/handlers/openapi.go
  • test/helper.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/serve/**/*.go : API server runs on port 8000, metrics endpoint on 8080, health check on 8083; these are configurable via CLI flags

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • cmd/hyperfleet-api/server/metrics_server.go
  • test/helper.go
  • cmd/hyperfleet-api/server/server.go
  • cmd/hyperfleet-api/server/api_server.go
  • pkg/errors/errors.go
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/servecmd/cmd.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Prometheus metrics are exposed at GET /metrics endpoint for monitoring; configure metric collection via --label-metrics-inclusion-duration

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/server/metrics_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Database transactions are automatically managed by TransactionMiddleware for all HTTP requests; store session in context and auto-commit on success, rollback on error

Applied to files:

  • pkg/handlers/openapi.go
  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/context.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Serve resource data to Sentinel via GET /{resourceType} endpoints with pagination and filtering support

Applied to files:

  • pkg/handlers/openapi.go
  • pkg/services/generic.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/migrate/**/*.go : Use GORM AutoMigrate at startup via ./bin/hyperfleet-api migrate command; migrations are idempotent and track applied migrations

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • test/helper.go
  • cmd/hyperfleet-api/server/server.go
  • pkg/db/migrations.go
  • cmd/hyperfleet-api/main.go
  • pkg/db/db_session/test.go
  • cmd/hyperfleet-api/server/healthcheck_server.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • go.mod
  • test/integration/integration_test.go
  • pkg/db/db_session/default.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for database operations

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • test/helper.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
  • go.mod
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/db/migrations/**/*.go : Store spec and conditions as JSONB in PostgreSQL to enable complex field queries

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,services}/**/*.go : Increment the generation field on each spec update to enable optimistic concurrency control and track reconciliation progress

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/telemetry/otel.go
  • pkg/db/migrations.go
  • pkg/services/cluster.go
  • pkg/db/db_session/test.go
  • pkg/services/status_aggregation.go
  • go.mod
  • test/integration/integration_test.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{db/migrations,dao}/**/*.go : NodePool owner_references.id must enforce foreign key relationship to parent cluster to maintain referential integrity

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/services/node_pool.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All database sessions must be retrieved from context via db.NewContext(ctx) rather than creating new database connections

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • pkg/db/context.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
  • pkg/api/error.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : Always retrieve database sessions from context in DAO methods; never create direct gorm.Open() connections

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • pkg/db/context.go
  • pkg/db/migrations.go
  • pkg/db/db_session/test.go
  • pkg/db/transaction_middleware.go
  • pkg/db/db_session/default.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Environment-based configuration: use OCM_ENV variable to select development, unit_testing, integration_testing, or production configuration

Applied to files:

  • cmd/hyperfleet-api/migrate/cmd.go
  • cmd/hyperfleet-api/environments/framework.go
  • test/helper.go
  • pkg/config/logging_test.go
  • test/integration/integration_test.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/**/*{auth,middleware}*.go : JWT authentication validates tokens from Red Hat SSO and extracts account ID and username from claims for authorization

Applied to files:

  • pkg/auth/authz_middleware_mock.go
  • cmd/hyperfleet-api/server/api_server.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO methods must accept context.Context as the first parameter for transaction propagation

Applied to files:

  • cmd/hyperfleet-api/environments/framework.go
  • pkg/db/context.go
  • pkg/errors/errors.go
  • pkg/api/error.go
  • pkg/db/transaction_middleware.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • test/helper.go
  • go.mod
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/{dao,handlers}/**/*.go : List operations must support pagination via ListArgs struct with limit, offset, and search parameters

Applied to files:

  • pkg/services/generic.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Calculate aggregate status from adapter conditions and return in status.phase field (Ready, Failed, NotReady)

Applied to files:

  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/services/**/*.go : Status aggregation: Phase is 'Ready' if all adapters report Ready=True, 'Failed' if any adapter reports Ready=False, 'NotReady' otherwise

Applied to files:

  • pkg/handlers/nodepool_status.go
  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/handlers/**/*.go : Accept status updates from adapters via POST /{resourceType}/{id}/statuses and store in adapter_statuses table

Applied to files:

  • pkg/handlers/cluster_status.go
  • pkg/services/status_aggregation.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : Integration tests must use Testcontainers to create isolated PostgreSQL instances for each test suite to ensure no state leakage

Applied to files:

  • pkg/db/db_session/test.go
  • test/integration/integration_test.go
  • pkg/db/db_session/testcontainer.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to pkg/dao/**/*.go : All DAO interface methods must include Create, Get, List, Update, Delete operations with consistent signatures taking context.Context

Applied to files:

  • pkg/db/transaction_middleware.go
📚 Learning: 2026-01-08T01:23:20.634Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:23:20.634Z
Learning: Applies to test/integration/**/*_test.go : All API endpoints must have integration test coverage in test/integration/ including CRUD operations, pagination, search, error cases, and status aggregation

Applied to files:

  • test/integration/integration_test.go
🧬 Code graph analysis (36)
pkg/handlers/openapi.go (1)
pkg/logger/logger.go (2)
  • Info (253-255)
  • Error (261-263)
cmd/hyperfleet-api/migrate/cmd.go (4)
pkg/logger/logger.go (2)
  • Error (261-263)
  • Info (253-255)
pkg/db/db_session/default.go (1)
  • NewProdFactory (33-37)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/auth/authz_middleware_mock.go (1)
pkg/logger/logger.go (1)
  • Info (253-255)
cmd/hyperfleet-api/environments/framework.go (3)
pkg/logger/logger.go (4)
  • Infof (269-271)
  • Errorf (277-279)
  • Info (253-255)
  • Error (261-263)
cmd/hyperfleet-api/environments/types.go (2)
  • Database (36-38)
  • Clients (67-69)
pkg/db/session.go (1)
  • SessionFactory (12-20)
cmd/hyperfleet-api/server/metrics_server.go (2)
pkg/logger/logger.go (1)
  • Info (253-255)
cmd/hyperfleet-api/server/server.go (1)
  • Server (13-18)
test/helper.go (2)
pkg/logger/logger.go (6)
  • Error (261-263)
  • Info (253-255)
  • Debug (249-251)
  • Warn (257-259)
  • LogConfig (25-32)
  • InitGlobalLogger (170-181)
cmd/hyperfleet-api/server/api_server.go (1)
  • NewAPIServer (30-101)
pkg/logger/logger_test.go (2)
pkg/logger/logger.go (11)
  • ParseLogLevel (210-223)
  • Error (261-263)
  • Errorf (277-279)
  • LogFormat (15-15)
  • FormatText (19-19)
  • FormatJSON (21-21)
  • ParseLogFormat (226-235)
  • ParseLogOutput (238-247)
  • Info (253-255)
  • Debug (249-251)
  • Warn (257-259)
pkg/logger/context.go (6)
  • WithTraceID (25-27)
  • WithSpanID (30-32)
  • OpIDKey (15-15)
  • WithClusterID (35-37)
  • WithResourceType (40-42)
  • WithResourceID (45-47)
pkg/auth/helpers.go (1)
pkg/logger/logger.go (2)
  • Info (253-255)
  • Error (261-263)
pkg/middleware/otel.go (1)
pkg/logger/context.go (2)
  • WithTraceID (25-27)
  • WithSpanID (30-32)
cmd/hyperfleet-api/server/server.go (1)
pkg/logger/logger.go (1)
  • Error (261-263)
pkg/handlers/helpers.go (1)
pkg/logger/logger.go (1)
  • Error (261-263)
pkg/handlers/nodepool_status.go (1)
pkg/logger/logger.go (1)
  • Warn (257-259)
pkg/middleware/schema_validation.go (1)
pkg/logger/logger.go (3)
  • Info (253-255)
  • Error (261-263)
  • Warn (257-259)
pkg/db/migrations.go (2)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/logger/logger.go (1)
  • Error (261-263)
pkg/config/config.go (1)
pkg/config/logging.go (2)
  • LoggingConfig (12-19)
  • NewLoggingConfig (35-50)
pkg/handlers/cluster_status.go (1)
pkg/logger/logger.go (1)
  • Warn (257-259)
pkg/errors/errors.go (1)
pkg/logger/logger.go (1)
  • Errorf (277-279)
pkg/handlers/metadata.go (2)
pkg/logger/logger.go (1)
  • Error (261-263)
pkg/api/error.go (1)
  • SendPanic (70-81)
cmd/hyperfleet-api/main.go (1)
pkg/logger/logger.go (4)
  • Error (261-263)
  • LogConfig (25-32)
  • FormatJSON (21-21)
  • InitGlobalLogger (170-181)
pkg/services/cluster.go (1)
pkg/logger/logger.go (1)
  • Info (253-255)
pkg/db/db_session/test.go (2)
pkg/logger/logger.go (1)
  • Errorf (277-279)
pkg/db/db_session/default.go (1)
  • Default (20-29)
cmd/hyperfleet-api/server/healthcheck_server.go (1)
pkg/logger/logger.go (2)
  • Infof (269-271)
  • Info (253-255)
pkg/api/error.go (1)
pkg/logger/logger.go (1)
  • Error (261-263)
cmd/hyperfleet-api/server/logging/request_logging_middleware.go (3)
pkg/middleware/masking.go (1)
  • MaskingMiddleware (13-17)
pkg/logger/logger.go (1)
  • Info (253-255)
pkg/logger/http.go (5)
  • HTTPMethod (10-12)
  • HTTPPath (15-17)
  • HTTPUserAgent (30-32)
  • HTTPStatusCode (20-22)
  • HTTPDuration (25-27)
pkg/services/status_aggregation.go (1)
pkg/logger/logger.go (1)
  • Warn (257-259)
pkg/middleware/masking_test.go (2)
pkg/config/logging.go (2)
  • LoggingConfig (12-19)
  • MaskingConfig (28-32)
pkg/middleware/masking.go (1)
  • NewMaskingMiddleware (20-26)
cmd/hyperfleet-api/servecmd/cmd.go (3)
pkg/logger/logger.go (9)
  • Error (261-263)
  • Warn (257-259)
  • Info (253-255)
  • ParseLogLevel (210-223)
  • ParseLogFormat (226-235)
  • FormatJSON (21-21)
  • ParseLogOutput (238-247)
  • LogConfig (25-32)
  • ReconfigureGlobalLogger (186-191)
cmd/hyperfleet-api/environments/framework.go (1)
  • Environment (54-56)
pkg/telemetry/otel.go (2)
  • InitTraceProvider (16-57)
  • Shutdown (60-65)
pkg/config/logging_test.go (1)
pkg/config/logging.go (2)
  • NewLoggingConfig (35-50)
  • LoggingConfig (12-19)
pkg/services/node_pool.go (1)
pkg/logger/logger.go (1)
  • Info (253-255)
pkg/db/transaction_middleware.go (3)
pkg/logger/logger.go (1)
  • Error (261-263)
pkg/errors/errors.go (1)
  • GeneralError (191-193)
pkg/logger/context.go (1)
  • GetOperationID (61-66)
pkg/handlers/framework.go (1)
pkg/logger/logger.go (2)
  • Info (253-255)
  • Error (261-263)
test/integration/integration_test.go (1)
pkg/logger/logger.go (2)
  • Infof (269-271)
  • Warn (257-259)
pkg/logger/ocm_bridge.go (1)
pkg/logger/logger.go (5)
  • Debug (249-251)
  • GetLogger (200-207)
  • Info (253-255)
  • Warn (257-259)
  • Error (261-263)
pkg/db/db_session/default.go (1)
pkg/logger/logger.go (3)
  • Info (253-255)
  • Debug (249-251)
  • Error (261-263)
pkg/db/db_session/testcontainer.go (4)
pkg/logger/logger.go (5)
  • Info (253-255)
  • Errorf (277-279)
  • Infof (269-271)
  • Debug (249-251)
  • Error (261-263)
pkg/errors/errors.go (1)
  • New (112-132)
pkg/client/ocm/client.go (1)
  • Config (17-24)
pkg/db/migrations.go (1)
  • Migrate (17-24)
pkg/logger/logger.go (1)
pkg/logger/context.go (6)
  • GetTraceID (69-72)
  • GetSpanID (75-78)
  • GetOperationID (61-66)
  • GetClusterID (81-84)
  • GetResourceType (87-90)
  • GetResourceID (93-96)
🪛 golangci-lint (2.5.0)
pkg/errors/errors.go

[error] 9-9: could not import github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi (pkg/errors/errors.go:9:2: no required module provides package github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi; to add it:
go get github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi)

(typecheck)

update

update

update

Replace OCMLogger with logger

update

Use atomic.Value instead of sync.once,metux

update
@yasun1
Copy link
Contributor Author

yasun1 commented Jan 8, 2026

@coderabbitai review

@yasun1 yasun1 changed the title [WIP] HYPERFLEET-363 | feat(logger): Align HyperFleet API component with logging specification HYPERFLEET-363 | feat(logger): Align HyperFleet API component with logging specification Jan 8, 2026
@yasun1
Copy link
Contributor Author

yasun1 commented Jan 8, 2026

@coderabbitai summary

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

✅ Actions performed

Summary regeneration triggered.

@yasun1
Copy link
Contributor Author

yasun1 commented Jan 8, 2026

coderabbit does not work well. Close the PR and move to #31

@yasun1 yasun1 closed this Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant