Skip to content

HYPERFLEET-549 - feat: standard configuration#71

Open
rh-amarin wants to merge 4 commits intoopenshift-hyperfleet:mainfrom
rh-amarin:HYPERFLEET-549
Open

HYPERFLEET-549 - feat: standard configuration#71
rh-amarin wants to merge 4 commits intoopenshift-hyperfleet:mainfrom
rh-amarin:HYPERFLEET-549

Conversation

@rh-amarin
Copy link
Contributor

@rh-amarin rh-amarin commented Mar 11, 2026

Summary

  • Standardises the sentinel configuration schema by adding yaml:"..." struct tags to all config types, producing consistent snake_case field
    names both in config files and in config-dump output.
  • Adds a config-dump CLI subcommand that prints the fully-merged configuration (file + env vars + CLI flags) as YAML, making it easy to
    inspect the effective config at runtime or in CI.
  • Adds sentinel.name and the build version to the User-Agent header sent on every HyperFleet API request hyperfleet-sentinel/<version> (<name>).
  • Adds a comprehensive config-loading test suite (test/integration/test-config-loading.sh, 83 assertions) that verifies every parameter
    loads correctly from all three sources (file, env var, CLI flag) and that CLI > env > file precedence is respected.
  • Adds an integration test (TestIntegration_ConfigLoadingScript) that runs the shell test suite inside a bash:5 testcontainer against a
    freshly cross-compiled Linux binary, so the full config path is exercised on Linux in CI.
  • Updates Helm chart, example configs, and adds docs/configuration.md and CONFIGURATION_MIGRATION.md for operators migrating from the old
    flat schema.

Test plan

  • make build — binary compiles cleanly
  • go test ./internal/config/... ./internal/client/... ./internal/sentinel/... — all unit tests pass
  • ./test/integration/test-config-loading.sh --verbose — all 83 shell tests pass against the local binary
  • make test-integration -run TestIntegration_ConfigLoadingScript — shell tests pass inside a Linux container
  • sentinel config-dump -c configs/dev-example.yaml — prints merged config with correct snake_case field names and User-Agent visible in API
    request logs

Summary by CodeRabbit

  • New Features

    • Added a config-dump command and CLI override flags to view the merged runtime configuration; merged config can be printed with sensitive values redacted.
    • Client now includes sentinel/version metadata in requests (User-Agent).
  • Configuration

    • Restructured config to nested, snake_case keys; expanded clients.hyperfleet_api and broker settings (including broker.topic).
    • Environment variables renamed (BROKER_TOPIC → HYPERFLEET_BROKER_TOPIC; LOG_* → HYPERFLEET_LOG_*).
    • Added logging, debug-config, and API retry/backoff options; message_data key standardized.
  • Documentation

    • Added a Sentinel Configuration Reference and updated running/deployment docs and chart README.
  • Tests

    • Added extensive config-loading unit tests and an integration config-loading script.

@openshift-ci
Copy link

openshift-ci bot commented Mar 11, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci
Copy link

openshift-ci bot commented Mar 11, 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 rh-amarin 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

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR restructures configuration into a nested model under clients, adds sentinel metadata, logging and debug sections, and renames many keys from camelCase to snake_case. HyperFleet API settings move to clients.hyperfleet_api, broker topic nests under clients.broker.topic, and environment variables gain a HYPERFLEET_ prefix. LoadConfig now merges CLI flags, env vars, and files with explicit precedence and validation; a new config-dump CLI command prints the merged, redacted YAML. The HyperFleet client constructor now accepts sentinel name and version to set a User-Agent. Helm charts, examples, tests, and docs updated accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI_Flags as "CLI Flags"
    participant Env as "Environment Vars"
    participant Config_File as "Config File"
    participant Loader as "config.LoadConfig"
    participant Logger as "initLogging"
    participant App as "Sentinel (serve / config-dump)"
    participant Hyperfleet as "HyperFleet Client"

    User->>CLI_Flags: provide flags (optional)
    User->>Env: set env vars (optional)
    User->>Config_File: provide config file (optional)

    CLI_Flags->>Loader: flag values
    Env->>Loader: env values
    Config_File->>Loader: file values

    Loader->>Loader: merge (flags > env > file > defaults)
    Loader->>Loader: validate & produce redacted copy

    Loader->>Logger: pass Log config
    Logger->>App: initialize logging

    Loader->>Hyperfleet: supply base_url, timeout, sentinel name, version
    App->>Hyperfleet: initialize client (User-Agent set)

    opt config-dump
      User->>App: run config-dump
      App->>Loader: load merged config
      App->>User: print redacted YAML
    end

    opt serve
      User->>App: run serve
      App->>App: start watchers & broker using merged config
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.73% 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 identifies this as a configuration standardization feature with the issue reference HYPERFLEET-549.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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

@rh-amarin rh-amarin force-pushed the HYPERFLEET-549 branch 3 times, most recently from 96cde3b to 37a82bc Compare March 12, 2026 17:28
@rh-amarin rh-amarin marked this pull request as ready for review March 12, 2026 17:42
@openshift-ci openshift-ci bot requested review from mbrudnoy and tirthct March 12, 2026 17:42
@rh-amarin
Copy link
Contributor Author

rh-amarin commented Mar 12, 2026

@coderabbitai review

@rh-amarin
Copy link
Contributor Author

@coderabbitai help

@rh-amarin rh-amarin marked this pull request as draft March 12, 2026 19:32
@rh-amarin rh-amarin marked this pull request as ready for review March 12, 2026 19:32
@openshift-ci openshift-ci bot requested review from 86254860 and aredenba-rh March 12, 2026 19:32
@rh-amarin
Copy link
Contributor Author

@coderabbitai review

@rh-amarin rh-amarin force-pushed the HYPERFLEET-549 branch 2 times, most recently from d30dabd to a4e1d5f Compare March 12, 2026 22:37
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.

🧹 Nitpick comments (2)
test/integration/test-config-loading.sh (1)

128-131: BASE_BROKER is defined but never used.

Static analysis correctly identifies that BASE_BROKER is declared but not referenced anywhere in the test sections. Either remove it or add tests that use it.

-BASE_BROKER=(
-  "  broker:"
-  "    topic: base-topic"
-)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/integration/test-config-loading.sh` around lines 128 - 131, The variable
BASE_BROKER is declared but never used; either remove the unused declaration or
add tests that reference it: update the test script to use BASE_BROKER in the
relevant test cases (e.g., source or include it when building broker config for
tests that expect "broker: topic: base-topic") or delete the BASE_BROKER array
declaration entirely to satisfy static analysis; locate the BASE_BROKER symbol
in test/integration/test-config-loading.sh and either remove the definition or
wire it into the test blocks that construct or validate broker configs (so the
"base-topic" value is actually exercised).
internal/config/config_loading_test.go (1)

359-363: Empty test section header left behind.

Lines 359-363 contain only section comment markers for TestLoadConfig_LegacyBrokerEnvVars with no implementation. Consider removing this placeholder if legacy broker env vars are no longer applicable, or add a TODO comment if tests are planned.

♻️ Suggested cleanup
-// ============================================================================
-// TestLoadConfig_LegacyBrokerEnvVars
-// ============================================================================
-
-
 // ============================================================================
 // TestLoadConfig_FilePrecedence
 // ============================================================================
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/config/config_loading_test.go` around lines 359 - 363, Remove or
complete the empty test section header for TestLoadConfig_LegacyBrokerEnvVars:
either delete the placeholder comment block if legacy broker env var tests are
no longer needed, or replace it with a short TODO comment and a minimal test
stub referencing TestLoadConfig_LegacyBrokerEnvVars so the intent is explicit;
ensure you update any surrounding test grouping comments in the file to keep
sections consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/config/config_loading_test.go`:
- Around line 359-363: Remove or complete the empty test section header for
TestLoadConfig_LegacyBrokerEnvVars: either delete the placeholder comment block
if legacy broker env var tests are no longer needed, or replace it with a short
TODO comment and a minimal test stub referencing
TestLoadConfig_LegacyBrokerEnvVars so the intent is explicit; ensure you update
any surrounding test grouping comments in the file to keep sections consistent.

In `@test/integration/test-config-loading.sh`:
- Around line 128-131: The variable BASE_BROKER is declared but never used;
either remove the unused declaration or add tests that reference it: update the
test script to use BASE_BROKER in the relevant test cases (e.g., source or
include it when building broker config for tests that expect "broker: topic:
base-topic") or delete the BASE_BROKER array declaration entirely to satisfy
static analysis; locate the BASE_BROKER symbol in
test/integration/test-config-loading.sh and either remove the definition or wire
it into the test blocks that construct or validate broker configs (so the
"base-topic" value is actually exercised).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dae37ce1-6e5e-4171-9526-b91756343c63

📥 Commits

Reviewing files that changed from the base of the PR and between d08733f and 6d23adb.

📒 Files selected for processing (25)
  • README.md
  • charts/README.md
  • charts/templates/configmap.yaml
  • charts/templates/deployment.yaml
  • charts/values.yaml
  • cmd/sentinel/main.go
  • configs/dev-example.yaml
  • configs/gcp-pubsub-example.yaml
  • configs/rabbitmq-example.yaml
  • docs/configuration.md
  • docs/running-sentinel.md
  • internal/client/client.go
  • internal/client/client_test.go
  • internal/config/config.go
  • internal/config/config_loading_test.go
  • internal/config/config_test.go
  • internal/config/testdata/full-workflow.yaml
  • internal/config/testdata/message-data-blank-id.yaml
  • internal/config/testdata/minimal.yaml
  • internal/config/testdata/unknown-field.yaml
  • internal/config/testdata/valid-complete.yaml
  • internal/sentinel/sentinel.go
  • internal/sentinel/sentinel_test.go
  • test/integration/integration_test.go
  • test/integration/test-config-loading.sh
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • internal/config/testdata/minimal.yaml
  • cmd/sentinel/main.go
  • internal/client/client.go
  • internal/sentinel/sentinel.go
  • internal/config/testdata/message-data-blank-id.yaml
  • charts/README.md
  • internal/config/testdata/unknown-field.yaml
  • docs/configuration.md
  • configs/dev-example.yaml
  • docs/running-sentinel.md

@rh-amarin rh-amarin force-pushed the HYPERFLEET-549 branch 2 times, most recently from 9a773c8 to 2806de1 Compare March 13, 2026 17:44
@rafabene
Copy link
Contributor

Category: JIRA

The PR description says it adds CONFIGURATION_MIGRATION.md for operators migrating from the
old schema, but this file isn't in the PR. Given the number of breaking changes here (env var
renames, nested config restructure, camelCase → snake_case in Helm values, strict
unknown-field rejection via UnmarshalExact), a migration guide would be really valuable.
Either add it to this PR or update the description to clarify it's a follow-up, and track it
so it doesn't get lost.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
cmd/sentinel/main.go (1)

354-358: ⚠️ Potential issue | 🟠 Major

config-dump still emits raw secrets by default.

This marshals the unredacted merged config, so sensitive values such as clients.hyperfleet_api.default_headers will be printed verbatim to stdout and are easy to leak into CI logs or shared terminals. Safer default is cfg.RedactedCopy(), with a separate explicit opt-in if raw output is still needed.

Safer default
-	data, err := yaml.Marshal(cfg)
+	data, err := yaml.Marshal(cfg.RedactedCopy())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/sentinel/main.go` around lines 354 - 358, The config-dump currently
marshals and prints the unredacted merged config (cfg), which exposes secrets;
change the marshaling to use cfg.RedactedCopy() by default (i.e., call
yaml.Marshal(cfg.RedactedCopy())) and print that redacted output, and if raw
output is still required add/obey an explicit opt-in flag (e.g., --raw) that,
when set, marshals the original cfg via yaml.Marshal(cfg) so raw secrets are
only emitted when explicitly requested; update the code paths around
yaml.Marshal and fmt.Print to reference cfg.RedactedCopy() and the new --raw
guard.
internal/config/config.go (1)

360-365: ⚠️ Potential issue | 🟠 Major

RedactedCopy() is still shallow for nested message_data values

MessageData is only copied at the top level. Nested maps/slices remain shared, so mutating the returned copy can still mutate live config, which breaks the “deep copy” contract.

Proposed fix
 func (c *SentinelConfig) RedactedCopy() *SentinelConfig {
 	cp := *c
@@
 	if c.MessageData != nil {
-		md := make(map[string]interface{}, len(c.MessageData))
-		for k, v := range c.MessageData {
-			md[k] = v
-		}
-		cp.MessageData = md
+		cp.MessageData = deepCopyInterfaceMap(c.MessageData)
 	}
@@
 	return &cp
 }
+
+func deepCopyInterfaceMap(src map[string]interface{}) map[string]interface{} {
+	dst := make(map[string]interface{}, len(src))
+	for k, v := range src {
+		dst[k] = deepCopyAny(v)
+	}
+	return dst
+}
+
+func deepCopyAny(v interface{}) interface{} {
+	switch t := v.(type) {
+	case map[string]interface{}:
+		return deepCopyInterfaceMap(t)
+	case []interface{}:
+		out := make([]interface{}, len(t))
+		for i := range t {
+			out[i] = deepCopyAny(t[i])
+		}
+		return out
+	default:
+		return t
+	}
+}

As per coding guidelines, "Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/config/config.go` around lines 360 - 365, RedactedCopy() currently
copies c.MessageData only at the top level (md[k]=v), leaving nested maps/slices
shared; change it to perform a deep copy of MessageData so mutations on
cp.MessageData cannot affect the original. Implement and call a helper like
deepCopyValue/deepCopyMap that recursively copies map[string]interface{} and
[]interface{} (and preserves primitives), then assign the result to
cp.MessageData instead of the shallow md; ensure the helper is used for each
value when populating cp.MessageData in RedactedCopy().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@charts/templates/configmap.yaml`:
- Around line 78-83: The template currently uses truthy guards that drop keys
when values are zero; update the guards to use the hasKey pattern (as used for
create_topic_if_missing) so explicit 0 is preserved. Replace the conditional
blocks that reference .Values.broker.rabbitmq.prefetch_count and
.Values.broker.rabbitmq.prefetch_size with hasKey checks (e.g. hasKey
.Values.broker.rabbitmq "prefetch_count"), and apply the same change for
max_outstanding_messages and num_goroutines so those keys are rendered when set
to 0.

In `@docs/running-sentinel.md`:
- Around line 600-601: The example YAML still uses the old flat service key;
update the snippet so the hyperfleet_api entry is nested under a clients mapping
and uses base_url instead of endpoint — i.e., replace the flat "hyperfleet_api:
base_url: ..." block with a "clients:" parent key containing a "hyperfleet_api:"
child that defines "base_url:
http://hyperfleet-api.hyperfleet-system.svc.cluster.local:8080" so the loader
sees the new clients nesting (look for the hyperfleet_api, endpoint and base_url
symbols to locate where to change).

In `@internal/client/client.go`:
- Around line 51-63: NewHyperFleetClient currently only sets the User-Agent and
ignores the configured default headers; change the constructor to accept the
configured default headers (e.g., a map[string]string from the config layer) and
in the openapi.WithRequestEditorFn used when calling
openapi.NewClientWithResponses, iterate the default headers and set them on
req.Header (ensuring User-Agent stays set by userAgent unless explicitly
overridden), so that clients.hyperfleet_api.default_headers are applied to every
request; update NewHyperFleetClient signature and its request editor closure
(references: NewHyperFleetClient, userAgent, openapi.NewClientWithResponses,
openapi.WithRequestEditorFn) accordingly.

---

Duplicate comments:
In `@cmd/sentinel/main.go`:
- Around line 354-358: The config-dump currently marshals and prints the
unredacted merged config (cfg), which exposes secrets; change the marshaling to
use cfg.RedactedCopy() by default (i.e., call yaml.Marshal(cfg.RedactedCopy()))
and print that redacted output, and if raw output is still required add/obey an
explicit opt-in flag (e.g., --raw) that, when set, marshals the original cfg via
yaml.Marshal(cfg) so raw secrets are only emitted when explicitly requested;
update the code paths around yaml.Marshal and fmt.Print to reference
cfg.RedactedCopy() and the new --raw guard.

In `@internal/config/config.go`:
- Around line 360-365: RedactedCopy() currently copies c.MessageData only at the
top level (md[k]=v), leaving nested maps/slices shared; change it to perform a
deep copy of MessageData so mutations on cp.MessageData cannot affect the
original. Implement and call a helper like deepCopyValue/deepCopyMap that
recursively copies map[string]interface{} and []interface{} (and preserves
primitives), then assign the result to cp.MessageData instead of the shallow md;
ensure the helper is used for each value when populating cp.MessageData in
RedactedCopy().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f587e82a-96bb-4ab7-aa99-7025b415c050

📥 Commits

Reviewing files that changed from the base of the PR and between 6d23adb and 2806de1.

📒 Files selected for processing (27)
  • Makefile
  • README.md
  • charts/README.md
  • charts/templates/configmap.yaml
  • charts/templates/deployment.yaml
  • charts/values.yaml
  • cmd/sentinel/main.go
  • configs/dev-example.yaml
  • configs/gcp-pubsub-example.yaml
  • configs/rabbitmq-example.yaml
  • docs/configuration.md
  • docs/multi-instance-deployment.md
  • docs/running-sentinel.md
  • internal/client/client.go
  • internal/client/client_test.go
  • internal/config/config.go
  • internal/config/config_loading_test.go
  • internal/config/config_test.go
  • internal/config/testdata/full-workflow.yaml
  • internal/config/testdata/message-data-blank-id.yaml
  • internal/config/testdata/minimal.yaml
  • internal/config/testdata/unknown-field.yaml
  • internal/config/testdata/valid-complete.yaml
  • internal/sentinel/sentinel.go
  • internal/sentinel/sentinel_test.go
  • test/integration/integration_test.go
  • test/integration/test-config-loading.sh
🚧 Files skipped from review as they are similar to previous changes (10)
  • internal/sentinel/sentinel.go
  • internal/config/testdata/message-data-blank-id.yaml
  • README.md
  • internal/config/testdata/unknown-field.yaml
  • internal/config/testdata/minimal.yaml
  • configs/dev-example.yaml
  • internal/config/testdata/valid-complete.yaml
  • configs/rabbitmq-example.yaml
  • internal/config/config_loading_test.go
  • charts/templates/deployment.yaml

@rh-amarin
Copy link
Contributor Author

Category: JIRA

The PR description says it adds CONFIGURATION_MIGRATION.md for operators migrating from the old schema, but this file isn't in the PR. Given the number of breaking changes here (env var renames, nested config restructure, camelCase → snake_case in Helm values, strict unknown-field rejection via UnmarshalExact), a migration guide would be really valuable. Either add it to this PR or update the description to clarify it's a follow-up, and track it so it doesn't get lost.

This may have been a leftover, but now is not present anymore

@rh-amarin rh-amarin force-pushed the HYPERFLEET-549 branch 2 times, most recently from 195b9d1 to 5f6f8a2 Compare March 16, 2026 19:18
@rafabene
Copy link
Contributor

rafabene commented Mar 16, 2026

The following file is NOT in this PR but may need updating due to changes in the diff:

  • docs/sentinel-operator-guide.md (lines 474, 480, 486, 501, 520, 535) — Still references old
    BROKER_TOPIC environment variable; should be updated to HYPERFLEET_BROKER_TOPIC

cfg.Output = parsed
}

// TRACING_ENABLED=true enables tracing
Copy link
Contributor

Choose a reason for hiding this comment

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

Category: Bug

Lines 187-194 are an exact duplicate of lines 196-203 — both parse
TRACING_ENABLED and set cfg.OTel.Enabled. Looks like the first block was
introduced during the refactor and the original wasn't removed. Delete one of
them.

This could be caused during the rebase.

# Should match the actual message broker being used for accurate observability.
# Valid values: rabbitmq, gcp_pubsub, kafka, activemq, aws_sqs, etc.
# Can be overridden via MESSAGING_SYSTEM environment variable.
messaging_system: "gcp_pubsub"
Copy link
Contributor

Choose a reason for hiding this comment

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

Category: Bug

messaging_system appears twice in this file (lines 60 and 66). Same issue in
configs/rabbitmq-example.yaml (lines 60, 66) and configs/dev-example.yaml
(lines 57, 63). Looks like the old block wasn't removed when the new
structure was added — just delete the duplicate in each file.

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.

4 participants