Skip to content

Conversation

@xueli181114
Copy link
Contributor

@xueli181114 xueli181114 commented Dec 30, 2025

Summary by CodeRabbit

  • New Features

    • Parameter type conversion for string, int/int64, float/float64, and bool with defaults and conversion error handling.
  • Improvements

    • Two-phase validation: structural then semantic, with clearer, more descriptive validation errors.
    • Config surface refactor: unified action wrapper for preconditions/post-actions and unified capture (field-or-expression) definitions.
  • Documentation

    • Expanded docs and template examples showing supported parameter types and validation guidance.
  • Tests

    • Test suites updated to match refactored config shapes and validation behavior.

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

@openshift-ci openshift-ci bot requested review from AlexVulaj and jsell-rh December 30, 2025 10:51
@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

Walkthrough

The PR restructures config loading and validation into a phased Validator (structure, file references, semantic) with loader.go delegating to it and propagating baseDir for secure path resolution. It replaces ad-hoc struct validation with go-playground/validator integration, adds struct-level and custom validators, introduces FieldExpressionDef and ActionBase in config types, and implements parameter type conversion helpers in the executor. Many tests and READMEs were updated to reflect new types, validation entry points, and error messages. Several legacy validator_schema components were removed and replaced by the new validator/struct_validator modules.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as Loader
    participant V as Validator
    participant FS as FileSystem
    participant CEL as CEL/Semantic Engine

    CLI->>V: NewValidator(config, baseDir)
    CLI->>V: ValidateStructure()
    V-->>CLI: structural errors / OK
    CLI->>V: ValidateFileReferences()
    V->>FS: resolvePath(ref) / loadYAMLFile(ref)
    FS-->>V: file contents / error
    V-->>CLI: file reference errors / OK
    CLI->>V: ValidateSemantic()
    V->>CEL: init env, evaluate CEL/templates
    CEL-->>V: semantic errors / OK
    V-->>CLI: aggregated validation result
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

lgtm, approved

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% 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 accurately describes the main change: a refactoring of config_loader with improvements to the validator component, which is evident throughout the changeset.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
internal/config_loader/struct_validator.go (1)

30-48: Consider logging custom validation registration errors.

The custom validation registrations (lines 35-36) ignore errors using _. While these registrations are unlikely to fail, consider logging any errors to aid debugging during development:

🔎 Optional improvement to log registration errors
 func getStructValidator() *validator.Validate {
 	structValidatorOnce.Do(func() {
 		structValidator = validator.New()
 
 		// Register custom validations
-		_ = structValidator.RegisterValidation("resourcename", validateResourceName)
-		_ = structValidator.RegisterValidation("validoperator", validateOperator)
+		if err := structValidator.RegisterValidation("resourcename", validateResourceName); err != nil {
+			panic(fmt.Sprintf("failed to register resourcename validation: %v", err))
+		}
+		if err := structValidator.RegisterValidation("validoperator", validateOperator); err != nil {
+			panic(fmt.Sprintf("failed to register validoperator validation: %v", err))
+		}
 
 		// Use yaml tag names for field names in errors
 		structValidator.RegisterTagNameFunc(func(fld reflect.StructField) string {

Note: Using panic in sync.Once initialization is acceptable since these registrations should never fail in production and indicate a programming error if they do.

internal/config_loader/README.md (1)

117-124: Add language specifier to fenced code block.

The error message example code block at line 119 is missing a language specifier. This was flagged by markdownlint.

🔎 Proposed fix
 ### Error Messages
 
 Validation errors are descriptive:
-```
+```text
 spec.params[0].name is required
 spec.preconditions[1].apiCall.method "INVALID" is invalid (allowed: GET, POST, PUT, PATCH, DELETE)
 spec.resources[0].name "my-resource": must start with lowercase letter and contain only letters, numbers, underscores (no hyphens)
 spec.preconditions[0].capture[0]: must have either 'field' or 'expression' set
</details>

</blockquote></details>
<details>
<summary>internal/config_loader/validator.go (1)</summary><blockquote>

`606-620`: **Fragile error filtering using substring matching.**

The `validateResources` helper filters errors by checking if the message contains "resources" or "Resources". This approach is brittle and may incorrectly include/exclude errors as message formats evolve.



Consider tracking error paths more explicitly rather than relying on substring matching, or document this as a temporary backward-compatibility shim that should be removed once callers migrate to the new API.

</blockquote></details>
<details>
<summary>internal/config_loader/types.go (2)</summary><blockquote>

`143-149`: **LGTM!**

Good extraction of common fields into a shared base type, reducing duplication between `Precondition` and `PostAction`.

Minor: Consider adding `validate:"omitempty"` to `Log` field for consistency with `APICall`, though it's not strictly required for a pointer field.

---

`160-169`: **LGTM!**

Appropriate validation for `Method` (restricted to valid HTTP verbs) and `URL` (required).

Consider adding format validation for `URL` (e.g., `validate:"required,url"`) to catch malformed URLs early, though this may be intentionally flexible to allow templated URLs.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: Organization UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2acf251f6d62a3dcc6582452be5fb53a713c25a5 and c02962c68a1df52b85803bbea1ef3b27d4d1e3f5.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `go.sum` is excluded by `!**/*.sum`

</details>

<details>
<summary>📒 Files selected for processing (21)</summary>

* `configs/adapter-config-template.yaml`
* `go.mod`
* `internal/config_loader/README.md`
* `internal/config_loader/loader.go`
* `internal/config_loader/loader_test.go`
* `internal/config_loader/struct_validator.go`
* `internal/config_loader/types.go`
* `internal/config_loader/validator.go`
* `internal/config_loader/validator_schema.go`
* `internal/config_loader/validator_test.go`
* `internal/criteria/README.md`
* `internal/executor/README.md`
* `internal/executor/executor_test.go`
* `internal/executor/param_extractor.go`
* `internal/executor/param_extractor_test.go`
* `internal/executor/post_action_executor_test.go`
* `internal/executor/precondition_executor.go`
* `test/integration/config-loader/testdata/adapter_config_valid.yaml`
* `test/integration/executor/executor_integration_test.go`
* `test/integration/executor/executor_k8s_integration_test.go`
* `test/testdata/adapter_config_valid.yaml`

</details>

<details>
<summary>💤 Files with no reviewable changes (3)</summary>

* test/integration/config-loader/testdata/adapter_config_valid.yaml
* test/testdata/adapter_config_valid.yaml
* internal/config_loader/validator_schema.go

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (2)</summary>

<details>
<summary>📓 Common learnings</summary>

Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with env.* source, Required=true, and no default) occurs at config load time in internal/config_loader/validator_schema.go. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.


</details>
<details>
<summary>📚 Learning: 2025-12-11T02:06:33.975Z</summary>

Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with env.* source, Required=true, and no default) occurs at config load time in internal/config_loader/validator_schema.go. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.


**Applied to files:**
- `internal/config_loader/struct_validator.go`
- `internal/executor/param_extractor.go`
- `internal/config_loader/validator.go`
- `internal/config_loader/README.md`
- `internal/config_loader/validator_test.go`
- `internal/config_loader/loader.go`
- `internal/config_loader/types.go`

</details>

</details><details>
<summary>🧬 Code graph analysis (8)</summary>

<details>
<summary>internal/executor/precondition_executor.go (2)</summary><blockquote>

<details>
<summary>internal/criteria/types.go (2)</summary>

* `Operator` (10-10)
* `OperatorExists` (28-28)

</details>
<details>
<summary>internal/config_loader/constants.go (1)</summary>

* `FieldValue` (72-72)

</details>

</blockquote></details>
<details>
<summary>internal/executor/param_extractor.go (1)</summary><blockquote>

<details>
<summary>internal/executor/types.go (2)</summary>

* `NewExecutorError` (374-381)
* `PhaseParamExtraction` (21-21)

</details>

</blockquote></details>
<details>
<summary>test/integration/executor/executor_k8s_integration_test.go (1)</summary><blockquote>

<details>
<summary>internal/config_loader/types.go (4)</summary>

* `ActionBase` (145-149)
* `APICall` (161-169)
* `CaptureField` (182-185)
* `FieldExpressionDef` (13-18)

</details>

</blockquote></details>
<details>
<summary>internal/executor/post_action_executor_test.go (1)</summary><blockquote>

<details>
<summary>internal/config_loader/types.go (2)</summary>

* `ActionBase` (145-149)
* `LogAction` (261-264)

</details>

</blockquote></details>
<details>
<summary>internal/config_loader/validator.go (5)</summary><blockquote>

<details>
<summary>internal/config_loader/types.go (2)</summary>

* `AdapterConfig` (69-74)
* `ValidationErrors` (286-288)

</details>
<details>
<summary>internal/config_loader/struct_validator.go (1)</summary>

* `ValidateStruct` (62-82)

</details>
<details>
<summary>internal/config_loader/loader.go (1)</summary>

* `SupportedAPIVersions` (26-28)

</details>
<details>
<summary>internal/config_loader/constants.go (13)</summary>

* `FieldSpec` (9-9)
* `FieldParams` (18-18)
* `FieldResources` (20-20)
* `FieldPost` (21-21)
* `FieldPayloads` (41-41)
* `FieldBuildRef` (43-43)
* `FieldManifest` (78-78)
* `FieldRef` (86-86)
* `FieldRefs` (87-87)
* `FieldPreconditions` (19-19)
* `FieldConditions` (50-50)
* `FieldCapture` (49-49)
* `FieldExpression` (51-51)

</details>
<details>
<summary>internal/criteria/types.go (2)</summary>

* `Operator` (10-10)
* `OperatorExists` (28-28)

</details>

</blockquote></details>
<details>
<summary>test/integration/executor/executor_integration_test.go (1)</summary><blockquote>

<details>
<summary>internal/config_loader/types.go (5)</summary>

* `ActionBase` (145-149)
* `APICall` (161-169)
* `CaptureField` (182-185)
* `FieldExpressionDef` (13-18)
* `LogAction` (261-264)

</details>

</blockquote></details>
<details>
<summary>internal/config_loader/loader.go (3)</summary><blockquote>

<details>
<summary>internal/config_loader/validator.go (2)</summary>

* `NewValidator` (34-40)
* `ValidateAdapterVersion` (572-584)

</details>
<details>
<summary>internal/config_loader/types.go (1)</summary>

* `AdapterConfig` (69-74)

</details>
<details>
<summary>internal/config_loader/constants.go (8)</summary>

* `FieldSpec` (9-9)
* `FieldResources` (20-20)
* `FieldManifest` (78-78)
* `FieldRef` (86-86)
* `FieldRefs` (87-87)
* `FieldPost` (21-21)
* `FieldPayloads` (41-41)
* `FieldBuildRef` (43-43)

</details>

</blockquote></details>
<details>
<summary>internal/executor/executor_test.go (2)</summary><blockquote>

<details>
<summary>internal/config_loader/types.go (3)</summary>

* `ActionBase` (145-149)
* `LogAction` (261-264)
* `Precondition` (153-158)

</details>
<details>
<summary>internal/executor/types.go (1)</summary>

* `StatusSuccess` (35-35)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>internal/executor/README.md</summary>

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | `float`, `float64` | Floating point value | Strings parsed | | `bool` | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

<details>
<summary>internal/config_loader/README.md</summary>

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

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (40)</summary><blockquote>

<details>
<summary>go.mod (2)</summary><blockquote>

`55-55`: **Validator v10.30.1 is not listed in official releases—verify this version exists.**

The specified validator/v10 v10.30.1 does not appear in the official release history; the latest release is v10.28.0 (Oct 5, 2025). Confirm whether v10.30.1 is a pre-release, development version, or version string error, and consider updating to the latest stable v10.28.0 unless v10.30.1 is intentionally pinned for a specific reason.

The golang.org/x package versions (crypto v0.46.0, net v0.47.0, term v0.38.0) appear current and align with or exceed the 2025 security fixes (CVE-2025-22869, CVE-2025-22870, CVE-2025-22872, CVE-2025-47911, CVE-2025-58190).

Also applies to: 62-64, 78-78

---

`129-129`: **Versions are stable and mutually compatible.**

The golang.org/x library updates are routine maintenance versions with no documented breaking changes. Notably, golang.org/x/net v0.47.0 explicitly requires golang.org/x/sys v0.39.0 in its go.mod, confirming compatibility. These minor version bumps contain security fixes and maintenance updates with no intentional API-breaking changes.

</blockquote></details>
<details>
<summary>configs/adapter-config-template.yaml (1)</summary><blockquote>

`76-118`: **LGTM! Clear documentation of parameter type support.**

The documentation additions clearly explain the supported parameter types (string, int, float, bool) and provide helpful examples. The commented-out examples for `nodeCount` and `enableFeature` demonstrate practical usage patterns.

</blockquote></details>
<details>
<summary>internal/executor/README.md (1)</summary><blockquote>

`155-165`: **LGTM! Clear and comprehensive parameter type documentation.**

The new section clearly documents supported parameter types and their conversion behavior. The table format is easy to read and the note about unspecified types is helpful.

</blockquote></details>
<details>
<summary>internal/criteria/README.md (2)</summary><blockquote>

`181-203`: **LGTM! Clear documentation of the new GetField method.**

The documentation thoroughly explains the new `GetField` method's behavior, return values, and error conditions. The table format for error conditions is particularly helpful.

---

`45-45`: All `NewEvaluator` call sites across the codebase have been properly updated to include the `context.Context` parameter as the first argument. Verified 40+ call sites in test files and production code—no outdated signatures remain.

</blockquote></details>
<details>
<summary>internal/executor/post_action_executor_test.go (1)</summary><blockquote>

`328-369`: **LGTM! Consistent ActionBase refactoring applied.**

The test cases have been correctly updated to use the new `ActionBase` wrapper structure. The nested `ActionBase{Name: ..., Log: ...}` pattern is consistently applied across all test cases and matches the type definition.

</blockquote></details>
<details>
<summary>internal/executor/precondition_executor.go (1)</summary><blockquote>

`186-200`: **LGTM! Improved logging for exists operator.**

The conditional logging based on operator type improves log clarity. For the `exists` operator, logging the field value (when matched) or "nil or empty" (when failed) is more semantically correct than showing an "expected value", since `exists` checks presence rather than equality.

</blockquote></details>
<details>
<summary>internal/config_loader/loader_test.go (1)</summary><blockquote>

`341-341`: **LGTM! Test assertions updated for new validation messages.**

The shortened error message expectations using `assert.Contains` are more robust than exact string matches. The substring matching will continue to work even if the full error message format changes slightly.

</blockquote></details>
<details>
<summary>internal/executor/executor_test.go (2)</summary><blockquote>

`453-497`: **LGTM! Consistent ActionBase refactoring in test cases.**

All test cases have been correctly updated to use the `ActionBase` wrapper structure. The pattern `ActionBase{Name: "..."}` with `Expression` at the `Precondition` level is consistently applied throughout the test file.

---

`660-662`: **LGTM! PostAction ActionBase usage is correct.**

The `PostAction` test cases correctly nest `Log` under `ActionBase`, matching the type definition and the pattern used throughout the codebase.

</blockquote></details>
<details>
<summary>internal/config_loader/struct_validator.go (4)</summary><blockquote>

`24-27`: **LGTM! Well-designed resource name pattern for CEL compatibility.**

The regex pattern correctly enforces CEL-compatible resource names by requiring a lowercase letter start and excluding hyphens (which would conflict with CEL's minus operator). The comment clearly explains the rationale.

---

`50-58`: **LGTM! Clean custom validation implementations.**

Both custom validation functions are simple, correct, and delegate to appropriate validation logic (regex pattern and criteria package). They follow the validator function signature correctly.

---

`60-82`: **LGTM! Robust ValidateStruct implementation.**

The function correctly handles validation errors with proper type assertions and fallback handling. The use of `formatFullErrorMessage` ensures consistent error formatting across the validation system.

---

`84-155`: **LGTM! Comprehensive error formatting with clear messages.**

The error formatting functions provide user-friendly, consistent error messages. The `formatFieldPath` function correctly handles embedded structs by removing uppercase type names from the path (e.g., `ActionBase` is removed from the path).

The complexity in `formatFieldPath` is justified by the need to handle embedded structs properly for clean error messages.

</blockquote></details>
<details>
<summary>internal/executor/param_extractor_test.go (1)</summary><blockquote>

`1-203`: **Well-structured test coverage for type conversion utilities.**

The table-driven tests comprehensively cover conversion paths with both success and error cases. Good use of `assert.InDelta` for float comparisons at line 154 to handle floating-point precision.

</blockquote></details>
<details>
<summary>test/integration/executor/executor_k8s_integration_test.go (2)</summary><blockquote>

`180-199`: **Precondition and CaptureField structures correctly updated to new API shape.**

The test configuration properly uses the new `ActionBase` wrapper for preconditions and the `FieldExpressionDef` embedding for capture fields, aligning with the type definitions in `internal/config_loader/types.go`.

---

`283-296`: **PostAction structure correctly updated with ActionBase wrapper.**

The PostAction now properly nests the `APICall` within `ActionBase`, consistent with the refactored type structure.

</blockquote></details>
<details>
<summary>internal/config_loader/README.md (1)</summary><blockquote>

`1-232`: **Comprehensive documentation for the refactored validation architecture.**

The README clearly documents the two-phase validation approach, struct embedding patterns, and validation tag usage. The package structure table and usage examples are helpful.

</blockquote></details>
<details>
<summary>internal/config_loader/loader.go (2)</summary><blockquote>

`239-266`: **Good path traversal prevention in resolvePath.**

The security check correctly uses `filepath.Rel` and checks for `..` prefix to prevent escaping the base directory. This protects against malicious file references in configurations.

---

`128-163`: **Well-structured validation pipeline with clear phase separation.**

The pipeline correctly orders validation phases: structural first (fail-fast), then adapter version, file references, and finally semantic validation. The optional `skipSemanticValidation` flag is useful for performance-sensitive contexts.

</blockquote></details>
<details>
<summary>internal/config_loader/validator.go (2)</summary><blockquote>

`117-129`: **Environment variable validation at config load time aligns with design intent.**

This fail-fast validation for required environment parameters (those with `env.*` source, `Required=true`, and no default) ensures clear error messages before deployment. Based on learnings, this is intentional design.

---

`230-251`: **Condition value validation handles operator-specific requirements correctly.**

The validation properly enforces that `exists` operator should not have a value set, while `in`/`notIn` require array values. Good separation between structural validation (operator enum) and semantic validation (value requirements).

</blockquote></details>
<details>
<summary>internal/config_loader/validator_test.go (2)</summary><blockquote>

`97-109`: **Good addition of tests for `exists` operator value validation.**

These new tests verify that the `exists` operator correctly rejects both single values and list values, ensuring the semantic validation catches invalid configurations.

---

`557-603`: **Capture field validation tests properly updated for FieldExpressionDef.**

Tests correctly use the new `FieldExpressionDef` structure and validate both valid cases (field-only, expression-only) and invalid cases (both set, neither set, missing name).

</blockquote></details>
<details>
<summary>test/integration/executor/executor_integration_test.go (3)</summary><blockquote>

`429-447`: **CEL expression evaluation test correctly uses new type structure.**

The precondition configuration properly uses `ActionBase` wrapper and `FieldExpressionDef` for capture fields. The CEL expression `clusterPhase == "Ready" && nodeCount >= 3` provides good coverage for CEL-based precondition evaluation.

---

`865-927`: **Log action test configuration properly structured.**

The test creates a comprehensive configuration with multiple log actions at different levels (info, debug, warning) in both preconditions and post-actions. The `ActionBase` wrapper correctly nests `Log` actions.

---

`1-1378`: **Comprehensive integration test coverage for executor functionality.**

The test file covers:
- Full success flow with preconditions, resources, and post-actions
- Precondition not met scenarios
- API failures at various phases
- CEL expression evaluation
- Log actions
- Context cancellation
- Missing/invalid event handling
- Payload build failures

Good test isolation and proper cleanup patterns.

</blockquote></details>
<details>
<summary>internal/executor/param_extractor.go (1)</summary><blockquote>

`322-329`: The comparison `v != 0` in multi-type cases works correctly. Go's comparison rules successfully handle `interface{}` compared to an untyped `0` for all numeric types (int, int8, int16, int32, int64, uint, uint8, float32, float64). The code does not have a type conversion bug and functions as intended.



> Likely an incorrect or invalid review comment.

</blockquote></details>
<details>
<summary>internal/config_loader/types.go (11)</summary><blockquote>

`10-18`: **LGTM!**

Clean implementation of mutual exclusion using `required_without` and `excluded_with` validation tags. The documentation clearly explains the intent.

---

`35-38`: **LGTM!**

Good use of composition by embedding `FieldExpressionDef` inline. This ensures consistent validation behavior across all types that need field/expression extraction.

---

`68-81`: **LGTM!**

Appropriate validation constraints for the top-level config structure. The `eq=AdapterConfig` check ensures proper resource kind validation.

---

`83-92`: **LGTM!**

Correct use of `validate:"dive"` for slice fields to ensure each element is validated. The `omitempty` on the optional `Post` pointer is appropriate.

---

`112-121`: **LGTM!**

The validation constraints align with the expected behavior where `Name` and `Source` are required. The `omitempty` YAML tag for serialization doesn't conflict with the `required` validation tag.

---

`130-141`: **LGTM!**

Consistent mutual exclusion pattern for `Build` and `BuildRef`. The documentation clearly explains the constraint.

---

`177-185`: **LGTM!**

Clean use of embedded `FieldExpressionDef` ensures consistent validation behavior with other types that need field/expression extraction.

---

`249-264`: **LGTM!**

Appropriate validation for `PostConfig` using `dive` for slice fields. `PostAction` cleanly reuses `ActionBase` through embedding.

---

`275-327`: **LGTM!**

Clean validation error handling utilities. The `Extend` method's nil check is good defensive programming.

Minor observation: `First()` returns only the message without the path. If callers need the full context, they should access `ve.Errors[0]` directly or consider returning the full `ValidationError`.

---

`227-235`: > Likely an incorrect or invalid review comment.

---

`187-192`: The custom validator `validoperator` is already properly registered in `internal/config_loader/struct_validator.go` (line 36) as part of the singleton validator initialization using `sync.Once`. The corresponding validator function `validateOperator()` exists and delegates to `criteria.IsValidOperator()`. No action is required.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
internal/config_loader/types.go (1)

237-247: DiscoveryConfig validation enforces ByName or BySelectors requirement.

The required_without tags correctly enforce that at least one of ByName or BySelectors must be provided. As confirmed in past review discussion, this validation works correctly despite the omitempty tag.

🧹 Nitpick comments (4)
internal/config_loader/loader_test.go (1)

341-341: Consider more specific error message assertions.

The error message assertion changed from checking the full message to just "must specify". While this is more flexible, it may miss regressions where the error message becomes less helpful. Consider asserting on a more specific substring like "must specify apiCall" or "expression" to ensure the error message remains informative.

Same applies to lines 1187 and 1208.

internal/config_loader/struct_validator.go (2)

62-78: Consider handling RegisterValidation errors.

While the custom validations are unlikely to fail registration, explicitly handling or logging errors from RegisterValidation would make debugging easier if issues arise during initialization.

🔎 Potential improvement
 	structValidatorOnce.Do(func() {
 		structValidator = validator.New()
 
 		// Register custom validations
-		_ = structValidator.RegisterValidation("resourcename", validateResourceName)
-		_ = structValidator.RegisterValidation("validoperator", validateOperator)
+		if err := structValidator.RegisterValidation("resourcename", validateResourceName); err != nil {
+			panic(fmt.Sprintf("failed to register resourcename validator: %v", err))
+		}
+		if err := structValidator.RegisterValidation("validoperator", validateOperator); err != nil {
+			panic(fmt.Sprintf("failed to register validoperator validator: %v", err))
+		}
 
 		// Use yaml tag names for field names in errors
 		structValidator.RegisterTagNameFunc(extractYamlTagName)

181-202: Verify embedded struct name removal logic.

The formatFieldPath function removes embedded struct names (uppercase parts without indices) to clean up error paths. This works for the current ActionBase embedding pattern, but ensure it handles all current and future embedded structs correctly.

Consider adding a unit test that verifies paths like:

  • AdapterConfig.Spec.Preconditions[0].ActionBase.Namespec.preconditions[0].name
  • AdapterConfig.Spec.Resources[0].Discovery.BySelectors.LabelSelector → appropriate output
internal/config_loader/validator.go (1)

606-619: String-based error filtering in validateResources is fragile.

The error filtering on lines 612-614 uses strings.Contains to detect resource-related errors, which could miss errors with different wording or falsely match unrelated errors containing "resources".

🔎 Consider returning structured errors or using error types
 func validateResources(config *AdapterConfig) error {
 	v := NewValidator(config, "")
 	// Run struct validation for resources first
 	if errs := ValidateStruct(config); errs != nil && errs.HasErrors() {
-		// Filter for resource-related errors
-		for _, e := range errs.Errors {
-			if strings.Contains(e.Message, "resources") || strings.Contains(e.Message, "Resources") {
-				return fmt.Errorf("%s", e.Message)
-			}
-		}
+		// Return first resource-related error by path prefix
+		for _, e := range errs.Errors {
+			if strings.HasPrefix(e.Path, "spec.resources") {
+				return fmt.Errorf("%s: %s", e.Path, e.Message)
+			}
+		}
 	}
 	// Then check for duplicate resource names
 	return v.validateDuplicateResourceNames()
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c02962c and e6b1886.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • configs/adapter-config-template.yaml
  • go.mod
  • internal/config_loader/README.md
  • internal/config_loader/loader.go
  • internal/config_loader/loader_test.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/types.go
  • internal/config_loader/validator.go
  • internal/config_loader/validator_schema.go
  • internal/config_loader/validator_test.go
  • internal/criteria/README.md
  • internal/executor/README.md
  • internal/executor/executor_test.go
  • internal/executor/param_extractor.go
  • internal/executor/param_extractor_test.go
  • internal/executor/post_action_executor_test.go
  • internal/executor/precondition_executor.go
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/integration/executor/executor_integration_test.go
  • test/integration/executor/executor_k8s_integration_test.go
  • test/testdata/adapter_config_valid.yaml
💤 Files with no reviewable changes (3)
  • test/testdata/adapter_config_valid.yaml
  • internal/config_loader/validator_schema.go
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
✅ Files skipped from review due to trivial changes (1)
  • configs/adapter-config-template.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • go.mod
  • test/integration/executor/executor_k8s_integration_test.go
  • internal/executor/param_extractor_test.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.
📚 Learning: 2025-12-11T02:06:33.975Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.

Applied to files:

  • internal/executor/param_extractor.go
  • internal/config_loader/README.md
  • internal/config_loader/loader.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/validator_test.go
  • internal/config_loader/types.go
  • internal/config_loader/validator.go
🧬 Code graph analysis (8)
internal/executor/param_extractor.go (1)
internal/executor/types.go (2)
  • NewExecutorError (374-381)
  • PhaseParamExtraction (21-21)
internal/executor/precondition_executor.go (2)
internal/criteria/types.go (2)
  • Operator (10-10)
  • OperatorExists (28-28)
internal/config_loader/constants.go (1)
  • FieldValue (72-72)
internal/config_loader/loader.go (3)
internal/config_loader/validator.go (2)
  • NewValidator (34-40)
  • ValidateAdapterVersion (572-584)
internal/config_loader/types.go (1)
  • AdapterConfig (69-74)
internal/config_loader/constants.go (8)
  • FieldSpec (9-9)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (86-86)
  • FieldRefs (87-87)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
internal/executor/post_action_executor_test.go (1)
internal/config_loader/types.go (2)
  • ActionBase (145-149)
  • LogAction (261-264)
internal/config_loader/struct_validator.go (2)
internal/config_loader/types.go (2)
  • AdapterConfig (69-74)
  • ValidationErrors (286-288)
internal/criteria/types.go (2)
  • IsValidOperator (44-51)
  • OperatorStrings (54-60)
internal/executor/executor_test.go (2)
internal/config_loader/types.go (3)
  • ActionBase (145-149)
  • LogAction (261-264)
  • Precondition (153-158)
internal/executor/types.go (1)
  • StatusSuccess (35-35)
test/integration/executor/executor_integration_test.go (1)
internal/config_loader/types.go (5)
  • ActionBase (145-149)
  • APICall (161-169)
  • CaptureField (182-185)
  • FieldExpressionDef (13-18)
  • LogAction (261-264)
internal/config_loader/types.go (2)
internal/criteria/types.go (1)
  • Operator (10-10)
internal/k8s_client/discovery.go (2)
  • Discovery (14-29)
  • DiscoveryConfig (32-42)
🪛 LanguageTool
internal/executor/README.md

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | float, float64 | Floating point value | Strings parsed | | bool | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
internal/config_loader/README.md

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

(MD040, fenced-code-language)

🔇 Additional comments (28)
internal/executor/precondition_executor.go (1)

188-199: LGTM! Improved logging for exists operator.

The logging changes appropriately handle the exists operator which doesn't have an expected value. For matched conditions, it now logs only the field value, and for failed conditions, it provides a clearer "field is nil or empty" message instead of confusing comparisons.

internal/executor/post_action_executor_test.go (1)

330-364: LGTM! Tests updated for ActionBase embedding.

The test data consistently reflects the new structure where PostAction embeds ActionBase. The changes are mechanical and maintain test coverage.

internal/executor/README.md (1)

144-165: LGTM! Clear parameter type documentation.

The new "Supported Parameter Types" section provides clear guidance on type conversion behavior. The table is well-structured and the note about retaining original types is helpful.

Minor: Consider "floating-point value" instead of "Floating point value" for consistency (though this is a nitpick).

internal/executor/param_extractor.go (2)

32-56: LGTM! Clean defaulting and type conversion flow.

The defaulting logic correctly handles nil and empty string cases, and the type conversion is appropriately gated on non-nil values with proper error handling for required vs. non-required parameters.

Note: The duplicate defaulting logic mentioned in past reviews appears to have been resolved in this refactor.


165-330: Type conversion utilities look solid.

The conversion functions handle a comprehensive set of type cases with proper error messages. Key strengths:

  • uint64 overflow check (lines 209-211) prevents silent truncation
  • Bool conversion handles common truthy/falsy strings beyond strconv.ParseBool
  • Consistent error messages reference the input value and type
internal/executor/executor_test.go (1)

453-497: LGTM! Tests consistently updated for ActionBase embedding.

All precondition test literals now use the ActionBase wrapper, maintaining test coverage while reflecting the new type structure. The changes are mechanical and consistent.

internal/config_loader/struct_validator.go (1)

26-29: Good: Resource name pattern enforces CEL compatibility.

The regex ^[a-z][a-zA-Z0-9_]*$ correctly prevents hyphens which would conflict with CEL's minus operator. The pattern is well-commented.

internal/config_loader/README.md (2)

78-124: Excellent validation documentation!

The two-phase validation explanation with detailed examples and error message formats is very helpful for users. The custom validators table and validation tag examples are clear and comprehensive.

Minor: Line 119 has a fenced code block without a language specifier (static analysis hint). Consider adding the language for proper rendering.


141-173: Clear documentation of new patterns.

The struct embedding section and ValidationErrors usage examples provide excellent guidance for understanding the refactored architecture. The code examples are practical and easy to follow.

internal/config_loader/loader.go (3)

129-163: Well-structured validation pipeline with clear phase separation.

The refactored validation pipeline cleanly separates concerns: structural validation (fail-fast), adapter version validation, file reference validation, and semantic validation. This modular approach improves maintainability and allows selective validation.


170-200: File reference loading handles single vs multiple refs correctly.

The logic correctly differentiates between single ref (backward compatible, populates resource.Manifest) and multiple refs (populates resource.ManifestItems). The error messages include appropriate path context for debugging.


239-266: Path containment check doesn't account for symlinks.

The resolvePath function validates that the logical path stays within the base directory using filepath.Rel, but this check operates on path strings without resolving symlinks. A symlink within the base directory could resolve to a location outside it, allowing os.Stat to access files beyond the intended boundary.

If symlink-following is a security concern in your deployment, consider using filepath.EvalSymlinks to resolve the target path before the containment check:

// Resolve symlinks after cleaning
targetPath, err := filepath.EvalSymlinks(targetPath)
if err != nil {
    return "", fmt.Errorf("path %q cannot be resolved: %w", refPath, err)
}

// Then check if the resolved path is within base directory
rel, err := filepath.Rel(baseClean, targetPath)

However, this approach will fail on broken symlinks. If your use case requires handling broken symlinks gracefully, consider using os.Lstat instead of os.Stat in the calling code to avoid following symlinks entirely.

internal/config_loader/validator_test.go (3)

97-109: Good addition of edge case tests for the exists operator.

These tests verify that the exists operator correctly rejects configurations where a value is provided, ensuring proper validation of operator semantics.


477-490: Updated test assertions for ValidateStruct return type.

The test correctly handles the new *ValidationErrors return type from ValidateStruct, checking for nil before accessing HasErrors().


606-648: New tests verify yaml field name mapping and cache population.

These tests ensure the field name cache is correctly populated during validator initialization, which is essential for accurate error path reporting in validation messages.

internal/criteria/README.md (2)

45-57: Documentation updated for two-value NewEvaluator return.

The example correctly shows the updated NewEvaluator signature returning (evaluator, error). Note that the example ignores the error with _ - in production code, this should be handled.


181-203: Clear documentation of GetField error semantics.

The table clearly distinguishes between parse errors (returned as second value), field-not-found (nil Value, no error), and runtime extraction failures (result.Error). This distinction is important for callers to handle appropriately.

test/integration/executor/executor_integration_test.go (3)

429-447: Integration test correctly uses ActionBase wrapper for preconditions.

The test properly constructs Precondition with the new ActionBase wrapper containing Name and APICall, and CaptureField entries using FieldExpressionDef for field extraction.


865-927: LogAction integration tests demonstrate new ActionBase structure.

The tests correctly construct log-only preconditions and post-actions using ActionBase.Log, validating that log actions work independently of API calls.


1110-1127: Precondition with CEL expression and captures correctly structured.

The test properly combines ActionBase (with APICall), Capture (with FieldExpressionDef), and Conditions in a single precondition, demonstrating the flexible validation model.

internal/config_loader/validator.go (4)

42-65: Structural validation with fail-fast behavior is well-designed.

The two-phase approach (struct tag validation followed by custom validators) provides clear separation. Returning on first error is appropriate for structural issues that indicate fundamental configuration problems.


116-129: Environment variable validation at load time is intentional design.

Based on learnings, this fail-fast validation ensures environment variables are set before deployment, providing clear error messages early. This is the correct approach for required env.* parameters.


230-251: Improved exists operator validation rejects values correctly.

The updated logic correctly ensures that the exists operator doesn't accept values, matching the semantic that it only checks for field presence. This addresses the edge case where users might incorrectly provide a value.


410-444: CEL environment initialization handles dynamic variables well.

The logic correctly deduplicates root variables and adds fallback declarations for resources and adapter if not already defined. The capacity estimation at line 411 is a minor optimization detail.

internal/config_loader/types.go (4)

10-18: FieldExpressionDef provides clean mutual exclusivity pattern.

The inline embedding pattern with required_without and excluded_with tags enforces that exactly one of Field or Expression must be set. This is reused effectively in ValueDef and CaptureField.


270-327: ValidationErrors provides comprehensive error aggregation utilities.

The ValidationErrors type with Add, Extend, First, Count, and HasErrors methods provides a clean API for collecting and reporting multiple validation errors. The formatted Error() output is user-friendly.


255-258: PostAction simplified to embed ActionBase.

The refactored PostAction now only embeds ActionBase, eliminating duplication with Precondition while maintaining the same capabilities (Name, APICall, Log).


143-158: Nested field references in validation tags are correctly supported.

The go-playground/validator library supports nested field references through embedded structs using dotted notation (e.g., ActionBase.APICall). The required_without_all validator correctly resolves these references. Integration tests in the codebase (executor_k8s_integration_test.go, config_criteria_integration_test.go) validate Precondition structs with these validation rules, and they pass successfully.

Likely an incorrect or invalid review 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: 2

🤖 Fix all issues with AI agents
In `@internal/config_loader/README.md`:
- Around line 117-124: Update the fenced code block that shows example
validation errors in internal/config_loader/README.md to include a language
specifier (e.g., "text") for proper rendering; locate the block containing the
four validation lines starting with "spec.params[0].name is required" and change
the opening fence from ``` to ```text so the errors render with the correct
formatting.

In `@internal/executor/param_extractor.go`:
- Around line 221-222: The uint conversion branch in the function handling
numeric type extraction (the switch case handling "case uint") lacks an overflow
check and can silently wrap on 64-bit platforms; modify the case for uint in the
same function (the conversion logic that returns int64) to check if v >
uint(math.MaxInt64) and return an overflow error (matching the existing uint64
branch behavior), otherwise cast to int64 and return the value.
♻️ Duplicate comments (1)
internal/config_loader/struct_validator.go (1)

204-204: Extra blank line.

There's an extra blank line at the end of the file.

🧹 Nitpick comments (6)
internal/executor/README.md (1)

155-164: Consider clarifying type equivalence and addressing the grammar suggestion.

The type conversion documentation is helpful, but could benefit from minor improvements:

  1. Type equivalence: The table lists int, int64 and float, float64 together, but it's unclear if they're equivalent or have different behaviors. The example comment "Convert to int64" when using type: "int" suggests they're treated the same. Consider clarifying: "Both int and int64 result in int64 values; both float and float64 result in float64 values."

  2. Grammar (static analysis): "Floating point value" should be "Floating-point value" (compound adjective).

  3. Optional: Consider briefly mentioning error handling behavior (e.g., "Invalid conversions result in validation errors at config load time").

📝 Suggested documentation improvements
 #### Supported Parameter Types
 
 | Type | Description | Conversion Notes |
 |------|-------------|-----------------|
 | `string` | String value (default) | Any value converted to string |
-| `int`, `int64` | Integer value | Strings parsed, floats truncated |
-| `float`, `float64` | Floating point value | Strings parsed |
+| `int`, `int64` | Integer value (both result in int64) | Strings parsed, floats truncated |
+| `float`, `float64` | Floating-point value (both result in float64) | Strings parsed |
 | `bool` | Boolean value | Supports: `true/false`, `yes/no`, `on/off`, `1/0` |
 
-If `type` is not specified, the value retains its original type from the source.
+If `type` is not specified, the value retains its original type from the source. Invalid type conversions result in validation errors at config load time.
internal/config_loader/struct_validator.go (2)

68-69: Consider handling RegisterValidation errors.

Silently ignoring errors from RegisterValidation could hide issues if custom validators fail to register (e.g., duplicate tag names, invalid validator functions). Consider at minimum logging these errors during initialization.

Proposed fix
-		_ = structValidator.RegisterValidation("resourcename", validateResourceName)
-		_ = structValidator.RegisterValidation("validoperator", validateOperator)
+		if err := structValidator.RegisterValidation("resourcename", validateResourceName); err != nil {
+			panic(fmt.Sprintf("failed to register resourcename validator: %v", err))
+		}
+		if err := structValidator.RegisterValidation("validoperator", validateOperator); err != nil {
+			panic(fmt.Sprintf("failed to register validoperator validator: %v", err))
+		}

181-202: Embedded struct name filtering is effective but fragile.

The logic to skip embedded struct names (uppercase, no index brackets) works for current patterns like ActionBase, but could incorrectly filter out legitimate field names in edge cases. Consider documenting this assumption or using a more explicit approach.

internal/config_loader/loader.go (1)

169-203: Consider adding size limits for loaded YAML files.

The loadYAMLFile function reads entire files into memory without size constraints. For defense-in-depth, consider adding a maximum file size check to prevent potential resource exhaustion if a referenced file is unexpectedly large.

💡 Optional: Add file size limit
 func loadYAMLFile(baseDir, refPath string) (map[string]interface{}, error) {
 	fullPath, err := resolvePath(baseDir, refPath)
 	if err != nil {
 		return nil, err
 	}

+	// Check file size to prevent resource exhaustion
+	info, err := os.Stat(fullPath)
+	if err != nil {
+		return nil, fmt.Errorf("failed to stat file %q: %w", fullPath, err)
+	}
+	const maxFileSize = 10 * 1024 * 1024 // 10 MB
+	if info.Size() > maxFileSize {
+		return nil, fmt.Errorf("file %q exceeds maximum size of %d bytes", fullPath, maxFileSize)
+	}
+
 	data, err := os.ReadFile(fullPath)
internal/config_loader/validator.go (1)

596-609: Backward compatibility helper has fragile string matching.

The validateResources function filters errors by checking if the message contains "resources" or "Resources". This string-based filtering is fragile and could miss relevant errors or include unrelated ones.

💡 Consider using error path instead of message content
 func validateResources(config *AdapterConfig) error {
 	v := NewValidator(config, "")
 	// Run struct validation for resources first
 	if errs := ValidateStruct(config); errs != nil && errs.HasErrors() {
 		// Filter for resource-related errors
 		for _, e := range errs.Errors {
-			if strings.Contains(e.Message, "resources") || strings.Contains(e.Message, "Resources") {
+			if strings.Contains(e.Path, "resources") || strings.Contains(e.Path, "Resources") {
 				return fmt.Errorf("%s", e.Message)
 			}
 		}
 	}
 	// Then check for duplicate resource names
 	return v.validateDuplicateResourceNames()
 }
internal/config_loader/types.go (1)

143-155: Potential inconsistency: Manual Validate() vs struct tags.

The Payload.Validate() method duplicates the mutual exclusivity logic already expressed in the struct validation tags on Lines 135 and 138. This could lead to inconsistent error messages.

Consider removing the manual Validate() method since the struct tags (required_without, excluded_with) already enforce this constraint, or ensure the method is not called when struct validation is used.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6b1886 and c3051bc.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (20)
  • configs/adapter-config-template.yaml
  • go.mod
  • internal/config_loader/README.md
  • internal/config_loader/loader.go
  • internal/config_loader/loader_test.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/types.go
  • internal/config_loader/validator.go
  • internal/config_loader/validator_schema.go
  • internal/config_loader/validator_test.go
  • internal/criteria/README.md
  • internal/executor/README.md
  • internal/executor/executor_test.go
  • internal/executor/param_extractor.go
  • internal/executor/param_extractor_test.go
  • internal/executor/post_action_executor_test.go
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/integration/executor/executor_integration_test.go
  • test/integration/executor/executor_k8s_integration_test.go
  • test/testdata/adapter_config_valid.yaml
💤 Files with no reviewable changes (3)
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/testdata/adapter_config_valid.yaml
  • internal/config_loader/validator_schema.go
✅ Files skipped from review due to trivial changes (1)
  • configs/adapter-config-template.yaml
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/config_loader/loader_test.go
  • test/integration/executor/executor_k8s_integration_test.go
  • test/integration/executor/executor_integration_test.go
  • internal/executor/param_extractor_test.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.
📚 Learning: 2025-12-11T02:06:33.975Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.

Applied to files:

  • go.mod
  • internal/config_loader/validator.go
  • internal/executor/param_extractor.go
  • internal/config_loader/README.md
  • internal/config_loader/struct_validator.go
  • internal/config_loader/validator_test.go
  • internal/config_loader/loader.go
  • internal/config_loader/types.go
🧬 Code graph analysis (7)
internal/executor/post_action_executor_test.go (1)
internal/config_loader/types.go (2)
  • ActionBase (159-163)
  • LogAction (273-276)
internal/executor/executor_test.go (2)
internal/config_loader/types.go (3)
  • ActionBase (159-163)
  • LogAction (273-276)
  • Precondition (167-172)
internal/executor/types.go (1)
  • StatusSuccess (35-35)
internal/executor/param_extractor.go (1)
internal/executor/types.go (2)
  • NewExecutorError (375-382)
  • PhaseParamExtraction (21-21)
internal/config_loader/struct_validator.go (2)
internal/config_loader/types.go (2)
  • AdapterConfig (69-74)
  • ValidationErrors (298-300)
internal/criteria/types.go (2)
  • IsValidOperator (44-51)
  • OperatorStrings (54-60)
internal/config_loader/validator_test.go (2)
internal/config_loader/types.go (7)
  • ActionBase (159-163)
  • Condition (202-206)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
  • Precondition (167-172)
  • Payload (130-141)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (92-112)
internal/config_loader/loader.go (3)
internal/config_loader/validator.go (2)
  • NewValidator (34-40)
  • ValidateAdapterVersion (562-574)
internal/config_loader/types.go (1)
  • AdapterConfig (69-74)
internal/config_loader/constants.go (7)
  • FieldSpec (9-9)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (85-85)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
internal/config_loader/types.go (3)
internal/criteria/types.go (1)
  • Operator (10-10)
internal/k8s_client/discovery.go (2)
  • Discovery (14-29)
  • DiscoveryConfig (32-42)
pkg/errors/error.go (1)
  • Errors (77-95)
🪛 LanguageTool
internal/executor/README.md

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | float, float64 | Floating point value | Strings parsed | | bool | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
internal/config_loader/README.md

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

(MD040, fenced-code-language)

🔇 Additional comments (34)
internal/executor/README.md (1)

144-150: LGTM! Clear examples of type conversion.

The parameter examples effectively demonstrate the new type conversion feature with practical use cases.

internal/executor/param_extractor.go (7)

6-8: LGTM!

The new imports are appropriate for the type conversion functionality - math for overflow bounds checking and strconv for string parsing.


32-39: LGTM!

The defaulting logic correctly handles both nil values and empty strings, applying the default when appropriate.


41-56: LGTM!

The type conversion integration is well-structured with appropriate error handling that distinguishes between required and non-required parameters.


165-181: LGTM!

The type router function is clean, provides good error messages, and sensibly maps both int/int64 and float/float64 to consistent implementations.


183-201: LGTM!

The string conversion handles all numeric types appropriately, using strconv.FormatFloat for floats to preserve precision and providing a sensible %v fallback for unknown types.


253-294: LGTM!

The float64 conversion handles all numeric types correctly. The inherent precision loss for large integers (beyond 2^53) is a standard limitation of float64 representation.


319-322: Critical bug: Multi-type switch cases break zero comparison.

When a type switch case lists multiple types (int, int8, int16, int32, int64), the variable v retains type interface{}. The comparison v != 0 compares an interface value against int(0), which only matches when the underlying type is exactly int. For other integer types like int8(0), the comparison returns true (types differ) instead of false.

Example: int8(0) will incorrectly convert to true because interface{int8}(0) != int(0) evaluates to true due to type mismatch.

🐛 Proposed fix - separate cases for each integer type
-	case int, int8, int16, int32, int64:
-		return v != 0, nil
-	case uint, uint8, uint16, uint32, uint64:
-		return v != 0, nil
+	case int:
+		return v != 0, nil
+	case int8:
+		return v != 0, nil
+	case int16:
+		return v != 0, nil
+	case int32:
+		return v != 0, nil
+	case int64:
+		return v != 0, nil
+	case uint:
+		return v != 0, nil
+	case uint8:
+		return v != 0, nil
+	case uint16:
+		return v != 0, nil
+	case uint32:
+		return v != 0, nil
+	case uint64:
+		return v != 0, nil

Alternatively, use reflection or convert to a common type:

-	case int, int8, int16, int32, int64:
-		return v != 0, nil
-	case uint, uint8, uint16, uint32, uint64:
-		return v != 0, nil
+	case int:
+		return v != 0, nil
+	case int8:
+		return v != 0, nil
+	case int16:
+		return v != 0, nil
+	case int32:
+		return v != 0, nil
+	case int64:
+		return v != 0, nil
+	case uint:
+		return v != 0, nil
+	case uint8:
+		return v != 0, nil
+	case uint16:
+		return v != 0, nil
+	case uint32:
+		return v != 0, nil
+	case uint64:
+		return v != 0, nil

Likely an incorrect or invalid review comment.

go.mod (2)

62-84: Indirect dependencies look appropriate.

The added indirect dependencies (mimetype, locales, universal-translator, go-urn) are standard transitive dependencies for go-playground/validator/v10. The golang.org/x/* updates are minor version bumps.


8-8: No action required; validator version is current.

Version v10.30.1 is the latest release (December 24, 2025) and is valid.

internal/criteria/README.md (2)

45-45: Documentation correctly updated for new API signature.

The NewEvaluator examples properly reflect the new signature with context.Background() as the first parameter and the two-value return pattern (evaluator, _).


181-203: Clear and comprehensive GetField documentation.

The error condition table effectively documents the distinction between parse errors (2nd return), runtime extraction errors (result.Error), and field-not-found cases. This clarity will help developers handle edge cases correctly.

internal/executor/post_action_executor_test.go (2)

328-347: Tests correctly updated for ActionBase wrapper pattern.

The test cases properly construct PostAction with the embedded ActionBase struct, correctly placing Name and Log fields within ActionBase. The pattern is consistent across all test cases.


362-369: Test with payloads correctly structured.

The "with payloads" test case properly uses the ActionBase wrapper while also testing Payloads at the PostConfig level.

internal/config_loader/struct_validator.go (2)

42-60: Recursive field cache building handles edge cases well.

The buildFieldNameCache function properly handles pointers, slices, arrays, maps, and structs recursively with cycle detection via the visited map.


92-112: ValidateStruct implementation is clean.

The function correctly uses the singleton validator, handles both validator.ValidationErrors and generic errors, and returns nil for valid structs. The error aggregation into ValidationErrors aligns with the broader validation architecture.

internal/config_loader/README.md (2)

78-107: Excellent two-phase validation documentation.

The documentation clearly explains the structural vs. semantic validation phases, provides practical examples of validation tags, and documents mutual exclusivity patterns. This will help users understand how to properly configure their adapters.


141-173: Struct embedding and ValidationErrors documentation is comprehensive.

The documentation for ActionBase, FieldExpressionDef, and ValidationErrors API is clear and includes practical code examples.

internal/config_loader/loader.go (2)

129-163: LGTM! Well-structured validation pipeline.

The four-phase validation approach (structural → adapter version → file references → semantic) is clean and follows fail-fast principles appropriately. The conditional file reference validation when baseDir is set is a good design choice.


225-252: Path traversal protection has a potential bypass for absolute paths.

When refPath is absolute (Line 235-236), the code cleans it but doesn't verify it's within the base directory before the relative path check. An absolute path like /etc/passwd would pass filepath.Rel successfully if it doesn't start with .. relative to baseDir.

However, the subsequent check on Line 247-248 correctly catches this since an absolute path outside baseDir will produce a relative path starting with ... The logic is sound.

internal/executor/executor_test.go (2)

453-456: LGTM! Test literals correctly updated for ActionBase wrapper.

The precondition test cases are properly updated to use the new ActionBase embedded struct syntax. The test coverage for sequential execution behavior remains intact.


656-658: LGTM! PostAction test literals correctly use nested LogAction.

The LogAction is now correctly nested inside ActionBase.Log within PostAction, matching the new type structure defined in types.go.

internal/config_loader/validator_test.go (4)

26-35: LGTM! Test helper correctly uses ActionBase wrapper.

The withCondition helper function properly constructs preconditions with the new ActionBase{Name: ...} syntax.


493-526: LGTM! Comprehensive coverage for CaptureField validation.

The test cases properly cover:

  • Valid field-only captures
  • Valid expression-only captures
  • Invalid both field and expression set
  • Invalid neither field nor expression set
  • Missing capture name

The updated assertions match the new error messages from struct validation tags.


528-570: Good addition: Tests for YAML field name resolution.

These new tests verify that the yamlFieldName function correctly maps Go field names to their YAML equivalents, and that the field name cache is properly populated during validator initialization.


554-569: The flakiness concern is unfounded. The code uses sync.Once to guarantee thread-safe, single initialization of fieldNameCache via getStructValidator(), even with parallel test execution. After initialization, the cache is read-only, and the field mappings are deterministic (derived from the static AdapterConfig struct).

The actual coupling concern—that tests access a private unexported variable—is a code design issue, not a flakiness risk.

Likely an incorrect or invalid review comment.

internal/config_loader/validator.go (4)

22-40: LGTM! Clean Validator struct design.

The Validator struct encapsulates all validation state cleanly, including config, baseDir, error collection, defined variables, and CEL environment. The constructor properly initializes the error collector.


116-129: Environment variable validation at config load time aligns with design intent.

This fail-fast validation for required environment variables is intentional per the project's design - environment variables must be set before deployment. Based on learnings, this is the expected behavior.


228-234: Good enhancement: Validation that exists operator should not have a value.

The new validation correctly enforces that the exists operator checks for field presence only, and should not have a value or values set. This prevents configuration mistakes.


405-438: CEL environment initialization handles variable roots correctly.

The logic properly extracts root variable names from dotted paths and avoids duplicate declarations. The fallback additions for resources and adapter variables ensure CEL expressions can reference these standard contexts.

internal/config_loader/types.go (4)

10-18: LGTM! Clean FieldExpressionDef type for DRY field/expression handling.

The mutual exclusivity is well-enforced via required_without and excluded_with validation tags. This type is reused appropriately across ValueDef and CaptureField.


287-339: LGTM! Well-designed ValidationErrors type.

The ValidationErrors type provides useful methods:

  • Add for collecting errors with paths
  • Extend for merging error collections
  • First for fail-fast scenarios
  • HasErrors for conditional checks
  • Clear error formatting with path context

242-258: DiscoveryConfig validation for BySelectors has been discussed.

Per past review comments, the omitempty on BySelectors validation tag was tested and works correctly - the required_without check still enforces that either ByName or BySelectors must be set. The current implementation is correct.


157-172: Validation tags are correctly implemented.

The dotted notation ActionBase.APICall is the correct and supported syntax for referencing fields in an embedded struct within go-playground/validator v10. The validator expects exported Go field names joined with dots to reference nested/embedded fields, and this pattern works correctly with required_without_all validation tags.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 117 to 124

Validation errors are descriptive:
```
spec.params[0].name is required
spec.preconditions[1].apiCall.method must be one of: GET, POST, PUT, PATCH, DELETE
spec.preconditions[1].apiCall.method "INVALID" is invalid (allowed: GET, POST, PUT, PATCH, DELETE)
spec.resources[0].name "my-resource": must start with lowercase letter and contain only letters, numbers, underscores (no hyphens)
spec.preconditions[0].capture[0]: must have either 'field' or 'expression' set
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language specifier to code block.

The fenced code block for error message examples should have a language specifier for proper rendering.

Proposed fix
 Validation errors are descriptive:
-```
+```text
 spec.params[0].name is required
 spec.preconditions[1].apiCall.method "INVALID" is invalid (allowed: GET, POST, PUT, PATCH, DELETE)
 spec.resources[0].name "my-resource": must start with lowercase letter and contain only letters, numbers, underscores (no hyphens)
 spec.preconditions[0].capture[0]: must have either 'field' or 'expression' set
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

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

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @internal/config_loader/README.md around lines 117 - 124, Update the fenced
code block that shows example validation errors in
internal/config_loader/README.md to include a language specifier (e.g., "text")
for proper rendering; locate the block containing the four validation lines
starting with "spec.params[0].name is required" and change the opening fence
from totext so the errors render with the correct formatting.


</details>

<!-- fingerprinting:phantom:poseidon:ocelot -->

<!-- This is an auto-generated comment by CodeRabbit -->

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 `@internal/executor/param_extractor.go`:
- Around line 319-326: The switch in internal/executor/param_extractor.go is
comparing interface values to an int literal which fails for non-int numeric
types; update the multi-type cases (the block handling int, int8, int16, int32,
int64; uint variants; float32; float64) to use separate case arms or explicit
type assertions so each branch binds a typed variable (e.g., for case int8: v :=
v.(int8)) and compare to a zero of that exact type (e.g., v != int8(0)); ensure
the same for uint/float branches so the comparisons check the concrete typed
value rather than comparing interface types.
♻️ Duplicate comments (3)
internal/executor/param_extractor.go (1)

221-222: Missing overflow check for uint on 64-bit platforms.

On 64-bit systems, uint is 64-bit and can hold values exceeding math.MaxInt64. The uint64 case correctly checks for overflow (lines 209-211), but this case does not, leading to silent overflow when converting large uint values.

🔧 Proposed fix - add overflow check for uint
 	case uint:
+		if uint64(v) > math.MaxInt64 {
+			return 0, fmt.Errorf("uint value %d overflows int64", v)
+		}
 		return int64(v), nil
internal/config_loader/README.md (1)

118-124: Add a language specifier to the fenced error block.

This is already flagged previously (MD040); keeping consistent formatting helps rendering.

internal/config_loader/types.go (1)

165-172: Embedded-field references in validation tags may not resolve.

required_without_all=ActionBase.APICall might be ignored by go-playground/validator because embedded fields are typically referenced by promoted name (APICall). If so, the “at least one of APICall/Expression/Conditions” constraint can be weakened. Consider switching to APICall in the tag.

🔧 Suggested adjustment
-	Conditions []Condition    `yaml:"conditions,omitempty" validate:"dive,required_without_all=ActionBase.APICall Expression"`
-	Expression string         `yaml:"expression,omitempty" validate:"required_without_all=ActionBase.APICall Conditions"`
+	Conditions []Condition    `yaml:"conditions,omitempty" validate:"dive,required_without_all=APICall Expression"`
+	Expression string         `yaml:"expression,omitempty" validate:"required_without_all=APICall Conditions"`
Does go-playground/validator v10 allow validation tag references to embedded fields using "ActionBase.APICall", or should embedded fields be referenced by the promoted name "APICall"?
🧹 Nitpick comments (2)
internal/config_loader/struct_validator.go (1)

69-70: Consider handling RegisterValidation errors.

The errors from RegisterValidation are silently ignored. While these calls are unlikely to fail in practice (valid function, unique name), logging or returning an error would help diagnose issues if the validator setup ever fails unexpectedly.

Suggested improvement
-		_ = structValidator.RegisterValidation("resourcename", validateResourceName)
-		_ = structValidator.RegisterValidation("validoperator", validateOperator)
+		if err := structValidator.RegisterValidation("resourcename", validateResourceName); err != nil {
+			panic(fmt.Sprintf("failed to register resourcename validation: %v", err))
+		}
+		if err := structValidator.RegisterValidation("validoperator", validateOperator); err != nil {
+			panic(fmt.Sprintf("failed to register validoperator validation: %v", err))
+		}
internal/config_loader/loader.go (1)

225-251: Consider symlink-aware baseDir confinement if this is a security boundary.

resolvePath blocks .. traversal, but a symlink inside baseDir could still point outside. If untrusted configs are a concern, consider resolving symlinks before the Rel check or documenting the trust model.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3051bc and d352d3d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • configs/adapter-config-template.yaml
  • go.mod
  • internal/config_loader/README.md
  • internal/config_loader/loader.go
  • internal/config_loader/loader_test.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/types.go
  • internal/config_loader/validator.go
  • internal/config_loader/validator_schema.go
  • internal/config_loader/validator_test.go
  • internal/criteria/README.md
  • internal/executor/README.md
  • internal/executor/executor_test.go
  • internal/executor/param_extractor.go
  • internal/executor/param_extractor_test.go
  • internal/executor/post_action_executor_test.go
  • pkg/logger/with_error_field_test.go
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/integration/executor/executor_integration_test.go
  • test/integration/executor/executor_k8s_integration_test.go
  • test/testdata/adapter_config_valid.yaml
💤 Files with no reviewable changes (3)
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/testdata/adapter_config_valid.yaml
  • internal/config_loader/validator_schema.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/logger/with_error_field_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/executor/param_extractor_test.go
  • internal/executor/post_action_executor_test.go
  • internal/executor/executor_test.go
  • configs/adapter-config-template.yaml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.
📚 Learning: 2025-12-11T02:06:33.975Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.

Applied to files:

  • internal/config_loader/struct_validator.go
  • go.mod
  • internal/config_loader/loader.go
  • internal/config_loader/loader_test.go
  • internal/config_loader/README.md
  • internal/config_loader/validator.go
  • internal/config_loader/types.go
  • internal/config_loader/validator_test.go
  • internal/executor/param_extractor.go
🧬 Code graph analysis (7)
internal/config_loader/loader.go (3)
internal/config_loader/validator.go (2)
  • NewValidator (34-40)
  • ValidateAdapterVersion (525-537)
internal/config_loader/types.go (1)
  • AdapterConfig (69-74)
internal/config_loader/constants.go (7)
  • FieldSpec (9-9)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (85-85)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
test/integration/executor/executor_k8s_integration_test.go (1)
internal/config_loader/types.go (4)
  • ActionBase (159-163)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
internal/config_loader/validator.go (5)
internal/config_loader/types.go (2)
  • AdapterConfig (69-74)
  • ValidationErrors (298-300)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/config_loader/loader.go (1)
  • SupportedAPIVersions (26-28)
internal/config_loader/constants.go (10)
  • FieldSpec (9-9)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (85-85)
  • FieldPreconditions (19-19)
  • FieldCapture (49-49)
  • FieldExpression (51-51)
internal/criteria/types.go (2)
  • Operator (10-10)
  • OperatorExists (28-28)
test/integration/executor/executor_integration_test.go (1)
internal/config_loader/types.go (5)
  • ActionBase (159-163)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
  • LogAction (273-276)
internal/config_loader/types.go (2)
internal/criteria/types.go (1)
  • Operator (10-10)
internal/k8s_client/discovery.go (2)
  • Discovery (14-29)
  • DiscoveryConfig (32-42)
internal/config_loader/validator_test.go (2)
internal/config_loader/types.go (7)
  • ActionBase (159-163)
  • Condition (202-206)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
  • Precondition (167-172)
  • Payload (130-141)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/executor/param_extractor.go (1)
internal/executor/types.go (2)
  • NewExecutorError (375-382)
  • PhaseParamExtraction (21-21)
🪛 LanguageTool
internal/executor/README.md

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | float, float64 | Floating point value | Strings parsed | | bool | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
internal/config_loader/README.md

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

(MD040, fenced-code-language)

🔇 Additional comments (29)
internal/executor/param_extractor.go (6)

1-13: LGTM!

The new math and strconv imports are appropriate for the type conversion functionality being added.


32-56: LGTM!

The defaulting logic correctly handles both nil and empty string cases. The type conversion integration follows the same error-handling pattern as extraction failures—required parameters return errors while non-required parameters fall back to defaults. This is consistent and well-structured.


165-181: LGTM!

The convertParamType dispatcher is clean and clearly documents supported types in the error message. The supported type set (string, int/int64, float/float64, bool) covers common configuration parameter needs.


183-201: LGTM!

String conversion handles all numeric types correctly. Using strconv.FormatFloat preserves precision, and the %v fallback for unknown types is a reasonable safety net.


253-294: LGTM!

Float conversion handles all numeric types correctly. The potential precision loss for very large int64/uint64 values (>2^53) converting to float64 is inherent to IEEE 754 and acceptable for typical configuration parameter use cases.


301-318: LGTM!

The string-to-bool conversion provides good coverage: strconv.ParseBool handles standard representations (true/false, 1/0, t/f), and the fallback handles common alternatives (yes/no, y/n, on/off). The empty string returning false is a sensible default.

go.mod (2)

8-8: v10.30.1 is the current stable version and has no known security vulnerabilities.

This is the latest release (Dec 24, 2025). While the validator library may depend on golang.org/x/crypto, your PR already includes v0.46.0, which exceeds the v0.31.0 threshold required to mitigate CVE-2024-45337.


136-142: No security concerns identified. The versions specified for golang.org/x/crypto, golang.org/x/net, golang.org/x/sys, and golang.org/x/term are current and do not contain known vulnerabilities. These appear to be routine transitive updates pulled in by go mod tidy.

test/integration/executor/executor_integration_test.go (7)

427-443: LGTM!

The ActionBase wrapper pattern and FieldExpressionDef embedding are correctly applied. The CEL expression properly references the captured fields defined in the Capture slice.


864-899: LGTM!

The test properly covers three precondition patterns:

  1. Log-only action (diagnostic logging)
  2. Log before API call
  3. Full API call with capture and conditions

All patterns correctly use the ActionBase wrapper with appropriate optional fields.


901-926: LGTM!

Post-actions correctly use ActionBase wrapper for log-only actions, testing both info and warning log levels.


1109-1125: LGTM!

The precondition correctly uses the ActionBase wrapper and FieldExpressionDef embedding patterns. The intentional failure setup (via mockAPI.SetFailPrecondition(true)) properly tests the adapter.executionError CEL access.


1159-1171: LGTM!

Post-action correctly uses ActionBase wrapper. The test effectively validates that CEL expressions can access adapter.executionError fields in payload building.


1299-1311: LGTM!

Post-action correctly uses ActionBase wrapper. The test properly validates that payload build failures block subsequent post-action execution (verified by assertion on line 1348).


1278-1284: The precondition pattern without APICall/Capture is valid.

This precondition correctly evaluates conditions against parameters. The clusterId parameter is extracted from event data during the parameter extraction phase and made available to the condition evaluator via GetCELVariables(). Conditions are designed to evaluate against parameters, captured fields, and other CEL variables—not exclusively from API responses. No changes needed.

internal/executor/README.md (1)

144-164: Documentation updates look good.

The new parameter examples and "Supported Parameter Types" section clearly document the type conversion capabilities. This aligns well with the broader parameter extraction enhancements in this PR.

Minor nit from static analysis: Consider hyphenating "Floating point" to "Floating-point" on line 161 for grammatical correctness (compound adjective modifying "value").

test/integration/executor/executor_k8s_integration_test.go (2)

180-199: Test updates correctly reflect new type structure.

The precondition construction properly uses the new ActionBase wrapper for Name and APICall, and CaptureField with embedded FieldExpressionDef. This aligns with the type definitions in internal/config_loader/types.go.


283-295: PostAction construction updated correctly.

The PostAction now properly wraps Name and APICall within ActionBase, consistent with the refactored type structure.

internal/criteria/README.md (2)

45-57: API documentation updated for new NewEvaluator signature.

The example correctly shows the updated signature with context.Background() as the first parameter. The error handling pattern is clear.


181-203: Well-documented GetField method and error handling semantics.

The table clearly distinguishes between parse errors (returned as second value) vs runtime extraction errors (in result.Error), and the "field not found" case returning nil values. This is helpful for understanding the API contract.

internal/config_loader/struct_validator.go (3)

94-111: Environment variable validation at config load time is correct.

This aligns with the intentional design for fail-fast behavior - environment variables must be set before deployment. Based on learnings, this validation occurs at config load time to ensure clear error messages when required env vars are missing.


137-186: Comprehensive error message formatting.

The formatFullErrorMessage function handles various validation tags with clear, user-friendly messages. The handling of embedded struct prefixes (e.g., ActionBase.APICallapiCall) in required_without_all is particularly well thought out.


215-251: Clean handling of embedded struct names in error paths.

The embeddedStructNames map correctly removes internal Go struct names (ActionBase, FieldExpressionDef) from error paths, ensuring YAML-friendly paths are shown to users.

internal/config_loader/loader_test.go (3)

341-342: Error message expectation updated for new validation format.

The expected error message now matches the new required_without_all formatting from struct_validator.go. This correctly reflects the two-phase validation architecture.


1052-1063: Good use of helper function for test config construction.

The configWithResources helper reduces boilerplate and makes the test cases more readable by focusing on the resources being tested rather than full config scaffolding.


1193-1204: Test assertions properly updated for new validation API.

The tests now correctly use ValidateStruct(config) and check errs.HasErrors() for error presence, with specific error message assertions maintained. This maintains test quality while adapting to the new validation entry points.

internal/config_loader/loader.go (1)

128-203: Clear, staged validation flow with explicit file-reference loading.

The phased pipeline and the dedicated load step make the execution order and error context easy to follow.

internal/config_loader/validator_test.go (1)

528-569: Nice coverage for YAML field-name mapping and cache population.

These tests help lock down error-path stability after the refactor.

internal/config_loader/validator.go (1)

68-90: The required env.* parameter validation already exists and is functioning correctly. The validateParameterEnvRequired() function in struct_validator.go (lines 94-111) validates that required parameters sourced from env.* have their environment variables set at load time. This validation is registered as a struct-level validator and is invoked during the ValidateStructure() phase, which is the first step in the validation pipeline called from loader.go. The validation properly checks for Required=true combined with Source starting with env., uses os.Getenv() to verify the variable exists, and reports an error if the environment variable is not set and no default is provided. No action is needed.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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)
internal/executor/param_extractor.go (1)

20-55: Convert defaults through the same type coercion path.

Defaults applied after extraction/conversion failures bypass convertParamType, so params can remain the wrong type (e.g., default "3" stays string even when type: int). Consider funnelling defaults through the conversion logic for consistency.

♻️ Proposed fix
-		if err != nil {
-			if param.Required {
-				return NewExecutorError(PhaseParamExtraction, param.Name,
-					fmt.Sprintf("failed to extract required parameter '%s' from source '%s'", param.Name, param.Source), err)
-			}
-			// Use default for non-required params if extraction fails
-			if param.Default != nil {
-				execCtx.Params[param.Name] = param.Default
-			}
-			continue
-		}
+		if err != nil {
+			if param.Required {
+				return NewExecutorError(PhaseParamExtraction, param.Name,
+					fmt.Sprintf("failed to extract required parameter '%s' from source '%s'", param.Name, param.Source), err)
+			}
+			// Use default for non-required params if extraction fails
+			value = param.Default
+			if value == nil {
+				continue
+			}
+		}
@@
-			if convErr != nil {
+			if convErr != nil {
 				if param.Required {
 					return NewExecutorError(PhaseParamExtraction, param.Name,
 						fmt.Sprintf("failed to convert parameter '%s' to type '%s'", param.Name, param.Type), convErr)
 				}
-				// Use default for non-required params if conversion fails
-				if param.Default != nil {
-					execCtx.Params[param.Name] = param.Default
-				}
-				continue
+				// Use default for non-required params if conversion fails
+				if param.Default == nil {
+					continue
+				}
+				value = param.Default
+				converted, convErr = convertParamType(value, param.Type)
+				if convErr != nil {
+					continue
+				}
 			}
 			value = converted
🤖 Fix all issues with AI agents
In `@internal/config_loader/loader.go`:
- Around line 144-153: Parse() currently skips ValidateFileReferences() and
loadFileReferences() when cfg.baseDir is empty, silently ignoring file
references; update Parse() to detect when the validator reports any file
references exist (use validator.HasFileReferences() or call
ValidateFileReferences() in a non-fatal/inspection mode) and if file references
are present but cfg.baseDir == "" return an explicit error stating baseDir is
required for file references. Specifically, add a guard in the Parse() path
before skipping file handling that checks for file refs and returns a clear
error (mentioning Parse(), WithBaseDir, cfg.baseDir, ValidateFileReferences(),
loadFileReferences(), and Load()) so callers must set WithBaseDir or use Load()
to proceed.

In `@internal/config_loader/types.go`:
- Around line 168-172: The Conditions field's validator currently uses
"dive,required_without_all=ActionBase.APICall Expression" which runs element
validation before the field-level required_without_all check; change the tag so
the field-level constraint runs first (e.g., put required_without_all before
dive) on the Conditions field in the struct (the Conditions field that
references CaptureField and uses validate tags) so an empty slice triggers the
required_without_all validation before element-level diving.

In `@internal/config_loader/validator.go`:
- Around line 15-16: The templateVarRegex currently doesn't account for Go
template trim delimiters (`{{-` and `-}}`); update the regexp assigned to
templateVarRegex so it allows an optional hyphen immediately after the opening
braces and/or immediately before the closing braces while preserving capturing
of the variable path and optional pipe/filter segment (i.e., make the openings
`\{\{\-?\s*` and closings `\s*\-?\}\}` and keep the existing capture group
`([a-zA-Z_][a-zA-Z0-9_\.]*)` and optional pipe part intact).
♻️ Duplicate comments (2)
internal/executor/param_extractor.go (1)

322-329: Fix numeric bool conversion for non-int types.

In a multi-type type switch case, v is interface{}; v != 0 compares different dynamic types and returns true for zero values of int8, uint8, etc. Split the cases so each branch compares the concrete type.

🐛 Proposed fix
-	case int, int8, int16, int32, int64:
-		return v != 0, nil
-	case uint, uint8, uint16, uint32, uint64:
-		return v != 0, nil
-	case float32:
-		return v != 0, nil
-	case float64:
-		return v != 0, nil
+	case int:
+		return v != 0, nil
+	case int8:
+		return v != 0, nil
+	case int16:
+		return v != 0, nil
+	case int32:
+		return v != 0, nil
+	case int64:
+		return v != 0, nil
+	case uint:
+		return v != 0, nil
+	case uint8:
+		return v != 0, nil
+	case uint16:
+		return v != 0, nil
+	case uint32:
+		return v != 0, nil
+	case uint64:
+		return v != 0, nil
+	case float32:
+		return v != 0, nil
+	case float64:
+		return v != 0, nil
internal/config_loader/README.md (1)

118-124: Add a language specifier to the fenced error block.

This keeps markdownlint happy and ensures consistent rendering.

Proposed fix
-```
+```text
 spec.params[0].name is required
 spec.preconditions[1].apiCall.method "INVALID" is invalid (allowed: GET, POST, PUT, PATCH, DELETE)
 spec.resources[0].name "my-resource": must start with lowercase letter and contain only letters, numbers, underscores (no hyphens)
 spec.preconditions[0].capture[0]: must have either 'field' or 'expression' set
🧹 Nitpick comments (2)
internal/config_loader/loader_test.go (1)

1053-1058: Prefer the canonical API version constant in test helpers.

Using APIVersionV1Alpha1 here avoids future drift if API version validation becomes stricter.

♻️ Proposed tweak
-			APIVersion: "hyperfleet.openshift.io/v1alpha1",
+			APIVersion: APIVersionV1Alpha1,
internal/config_loader/struct_validator.go (1)

42-59: Avoid field-name collisions in fieldNameCache.

The cache is keyed only by Go field name, so later entries can overwrite earlier ones when different structs reuse the same field name with different YAML tags, leading to misleading error messages. Consider keying by fully-qualified type + field (or storing per-type maps) and updating yamlFieldName accordingly.

Also applies to: 198-213

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d352d3d and bd2624c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • configs/adapter-config-template.yaml
  • go.mod
  • internal/config_loader/README.md
  • internal/config_loader/loader.go
  • internal/config_loader/loader_test.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/types.go
  • internal/config_loader/validator.go
  • internal/config_loader/validator_schema.go
  • internal/config_loader/validator_test.go
  • internal/criteria/README.md
  • internal/executor/README.md
  • internal/executor/executor_test.go
  • internal/executor/param_extractor.go
  • internal/executor/param_extractor_test.go
  • internal/executor/post_action_executor_test.go
  • pkg/logger/with_error_field_test.go
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/integration/executor/executor_integration_test.go
  • test/integration/executor/executor_k8s_integration_test.go
  • test/testdata/adapter_config_valid.yaml
💤 Files with no reviewable changes (3)
  • test/testdata/adapter_config_valid.yaml
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • internal/config_loader/validator_schema.go
✅ Files skipped from review due to trivial changes (1)
  • internal/executor/param_extractor_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/executor/post_action_executor_test.go
  • internal/criteria/README.md
  • test/integration/executor/executor_k8s_integration_test.go
  • go.mod
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.
📚 Learning: 2025-12-11T02:06:33.975Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.

Applied to files:

  • internal/executor/param_extractor.go
  • internal/config_loader/README.md
  • internal/config_loader/struct_validator.go
  • internal/config_loader/validator_test.go
  • internal/config_loader/validator.go
  • internal/config_loader/types.go
  • internal/config_loader/loader.go
📚 Learning: 2025-12-04T14:06:51.656Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 13
File: internal/executor/post_action_executor.go:198-205
Timestamp: 2025-12-04T14:06:51.656Z
Learning: Logger refactor is tracked in HYPERFLEET-304 for the hyperfleet-adapter repository, which will address how CEL evaluation failures and similar errors are handled and logged.

Applied to files:

  • internal/config_loader/types.go
🧬 Code graph analysis (9)
internal/executor/param_extractor.go (1)
internal/executor/types.go (2)
  • NewExecutorError (375-382)
  • PhaseParamExtraction (21-21)
internal/config_loader/struct_validator.go (2)
internal/config_loader/types.go (3)
  • Parameter (114-121)
  • AdapterConfig (69-74)
  • ValidationErrors (298-300)
internal/criteria/types.go (2)
  • IsValidOperator (44-51)
  • OperatorStrings (54-60)
internal/config_loader/validator_test.go (2)
internal/config_loader/types.go (7)
  • ActionBase (159-163)
  • Condition (202-206)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
  • Precondition (167-172)
  • Payload (130-141)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/config_loader/validator.go (4)
internal/config_loader/types.go (2)
  • AdapterConfig (69-74)
  • ValidationErrors (298-300)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/config_loader/loader.go (1)
  • SupportedAPIVersions (26-28)
internal/config_loader/constants.go (11)
  • FieldSpec (9-9)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (85-85)
  • FieldPreconditions (19-19)
  • FieldConditions (50-50)
  • FieldCapture (49-49)
  • FieldExpression (51-51)
internal/executor/executor_test.go (2)
internal/config_loader/types.go (3)
  • ActionBase (159-163)
  • LogAction (273-276)
  • Precondition (167-172)
internal/executor/types.go (1)
  • StatusSuccess (35-35)
test/integration/executor/executor_integration_test.go (1)
internal/config_loader/types.go (5)
  • ActionBase (159-163)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
  • LogAction (273-276)
internal/config_loader/loader_test.go (3)
internal/config_loader/types.go (7)
  • Resource (242-247)
  • AdapterConfig (69-74)
  • Metadata (77-81)
  • AdapterConfigSpec (84-92)
  • AdapterInfo (95-97)
  • DiscoveryConfig (250-254)
  • SelectorConfig (257-259)
internal/k8s_client/discovery.go (2)
  • Discovery (14-29)
  • DiscoveryConfig (32-42)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/config_loader/types.go (2)
internal/criteria/types.go (1)
  • Operator (10-10)
internal/k8s_client/discovery.go (2)
  • Discovery (14-29)
  • DiscoveryConfig (32-42)
internal/config_loader/loader.go (3)
internal/config_loader/validator.go (2)
  • NewValidator (34-40)
  • ValidateAdapterVersion (525-537)
internal/config_loader/types.go (1)
  • AdapterConfig (69-74)
internal/config_loader/constants.go (7)
  • FieldSpec (9-9)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (85-85)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
🪛 LanguageTool
internal/executor/README.md

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | float, float64 | Floating point value | Strings parsed | | bool | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🔇 Additional comments (17)
pkg/logger/with_error_field_test.go (1)

86-91: Formatting-only change looks good.

No behavioral impact noticed.

configs/adapter-config-template.yaml (1)

76-117: Docs update is clear and consistent with the new typing behavior.

Thanks for adding supported type guidance and examples.

internal/executor/README.md (1)

144-164: Nice documentation update for typed params.

The examples and type table align well with the new conversion helpers.

internal/executor/executor_test.go (1)

453-491: ActionBase-wrapped test fixtures look consistent.

The updated literals align with the embedded ActionBase shape for preconditions/post-actions and skip-reason coverage.

Also applies to: 656-658, 727-739

internal/config_loader/validator_test.go (2)

31-33: Test updates align with ActionBase and FieldExpressionDef refactor.

The fixture updates and structural-validation expectations look consistent with the new embedded shapes.

Also applies to: 40-46, 52-62, 97-109, 127-142, 170-175, 193-193, 321-323, 484-525


387-409: Nice coverage for payload validation errors and YAML name caching.

The added assertions and cache tests strengthen behavior and error-message fidelity.

Also applies to: 450-457, 475-476, 528-569

internal/config_loader/validator.go (4)

22-92: Validator orchestration is clear and cohesive.
The structural → file-reference → semantic split and fail-fast vs aggregation choices read well.


112-167: File-reference validation is robust.
Resolved-path checks and aggregated error formatting look solid.


181-239: Semantic validation coverage is comprehensive.
Nice handling across condition values, capture expressions, template vars (incl. payload builds), CEL parsing, and K8s manifest checks.

Also applies to: 245-335, 368-425, 465-508


514-556: Helper APIs are tidy and useful.

test/integration/executor/executor_integration_test.go (2)

427-439: Test fixtures updated cleanly for ActionBase/FieldExpressionDef.

Also applies to: 866-922, 1111-1169


1250-1310: No action required—the test will correctly detect the payload build failure.

The config is created directly in the test, bypassing semantic validation entirely. At runtime, renderTemplate() uses Go's html/template with missingkey=error, which will catch the undefined variable {{ .nonExistentParam }} and fail during payload building, exactly as the test expects. The test assertions are correct: failure in the PhasePostActions phase with zero post actions executed.

internal/config_loader/types.go (5)

10-66: FieldExpressionDef/ValueDef refactor is clean.
Inline embedding keeps value-extraction definitions consistent and easy to parse.


68-155: Core config validation tags look well-scoped.
The tightened requirements for key adapter fields and payload exclusivity read well.


157-163: ActionBase consolidation + stricter APICall/operator validation look good.

Also applies to: 173-199, 201-207


241-282: Resource/Discovery/Post config validation updates look good.


283-339: ValidationErrors utilities are clean and reusable.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +144 to 153
// 3. File reference validation and loading (only if baseDir is set)
if cfg.baseDir != "" {
if err := validateFileReferences(config, cfg.baseDir); err != nil {
if err := validator.ValidateFileReferences(); err != nil {
return fmt.Errorf("file reference validation failed: %w", err)
}

// Load file references (manifest.ref, buildRef) after validation passes
if err := loadFileReferences(config, cfg.baseDir); err != nil {
return fmt.Errorf("failed to load file references: %w", err)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n --type=go '\bconfig_loader\.Parse\s*\(' -A 3

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 65


🏁 Script executed:

# Also search for Parse calls within the same package (might be just Parse without qualifier)
rg -n --type=go -C 3 '\.Parse\s*\(' internal/config_loader/

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 548


🏁 Script executed:

# Check the actual implementation of Parse in loader.go
cat internal/config_loader/loader.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 7749


🏁 Script executed:

# Search for all Parse calls (not just config_loader.Parse)
rg -n 'Parse\(' --type=go -B 2 -A 2 | head -100

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 5744


🏁 Script executed:

# Specifically look for imports and uses of the loader package
rg -n 'config_loader' --type=go | head -50

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 5965


🏁 Script executed:

# Check if Parse is exported (capital P) vs internal
rg -n '^func Parse' internal/config_loader/

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 272


🏁 Script executed:

# Check the Parse() calls in loader_test.go to see if any test cases include file references
grep -A 50 'func TestParse' internal/config_loader/loader_test.go | head -80

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 1733


🏁 Script executed:

# Look at one of the test cases that calls Parse
sed -n '160,180p' internal/config_loader/loader_test.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 497


🏁 Script executed:

# Search for tests that might include buildRef or manifest.ref
rg -n 'buildRef|manifest\.ref|ManifestRef' internal/config_loader/loader_test.go -B 3 -A 3

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 4837


🏁 Script executed:

# Check if there are any Parse calls outside of tests
rg -n 'config_loader\.Parse\(' --type=go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 65


🏁 Script executed:

# Look at the actual test to see if Parse calls with file refs include WithBaseDir
sed -n '750,950p' internal/config_loader/loader_test.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 4550


🏁 Script executed:

# Specifically look for Parse calls with file references in tests
rg -B 10 'buildRef|ManifestRef' internal/config_loader/loader_test.go | grep -A 10 'Parse('

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 65


🏁 Script executed:

# Check if Parse is documented or exported in godoc
rg -n 'Parse' internal/config_loader/loader.go | head -20

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 252


🏁 Script executed:

# Look at the complete Parse function and comments
sed -n '100,130p' internal/config_loader/loader.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 1062


🏁 Script executed:

# Check if there are any comments on Parse documenting its usage
sed -n '95,110p' internal/config_loader/loader.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 580


Consider failing fast when file references are present but baseDir is empty.

Parse() without WithBaseDir skips ValidateFileReferences() and loadFileReferences(). While current code correctly uses Load() (which auto-sets baseDir) for actual file loading, the API allows direct Parse() calls without baseDir where file refs would be silently skipped. Add a guard to fail explicitly when refs are detected but baseDir is empty:

Suggested fix
-	// 3. File reference validation and loading (only if baseDir is set)
-	if cfg.baseDir != "" {
+	// 3. File reference validation and loading
+	if cfg.baseDir != "" {
 		if err := validator.ValidateFileReferences(); err != nil {
 			return fmt.Errorf("file reference validation failed: %w", err)
 		}
 
 		if err := loadFileReferences(config, cfg.baseDir); err != nil {
 			return fmt.Errorf("failed to load file references: %w", err)
 		}
+	} else if hasFileReferences(config) {
+		return fmt.Errorf("file references require baseDir; pass WithBaseDir or use Load()")
 	}
+// hasFileReferences reports whether config contains manifest.ref or buildRef entries.
+func hasFileReferences(config *AdapterConfig) bool {
+	for i := range config.Spec.Resources {
+		if config.Spec.Resources[i].GetManifestRef() != "" {
+			return true
+		}
+	}
+	if config.Spec.Post != nil {
+		for i := range config.Spec.Post.Payloads {
+			if config.Spec.Post.Payloads[i].BuildRef != "" {
+				return true
+			}
+		}
+	}
+	return false
+}
🤖 Prompt for AI Agents
In `@internal/config_loader/loader.go` around lines 144 - 153, Parse() currently
skips ValidateFileReferences() and loadFileReferences() when cfg.baseDir is
empty, silently ignoring file references; update Parse() to detect when the
validator reports any file references exist (use validator.HasFileReferences()
or call ValidateFileReferences() in a non-fatal/inspection mode) and if file
references are present but cfg.baseDir == "" return an explicit error stating
baseDir is required for file references. Specifically, add a guard in the
Parse() path before skipping file handling that checks for file refs and returns
a clear error (mentioning Parse(), WithBaseDir, cfg.baseDir,
ValidateFileReferences(), loadFileReferences(), and Load()) so callers must set
WithBaseDir or use Load() to proceed.

Comment on lines +168 to 172
ActionBase `yaml:",inline"`
Capture []CaptureField `yaml:"capture,omitempty" validate:"dive"`
Conditions []Condition `yaml:"conditions,omitempty" validate:"dive,required_without_all=ActionBase.APICall Expression"`
Expression string `yaml:"expression,omitempty" validate:"required_without_all=ActionBase.APICall Conditions"`
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the actual code at the specified location
cd internal/config_loader && sed -n '165,175p' types.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 657


🏁 Script executed:

# Let's see more context around these lines to understand the struct
cd internal/config_loader && sed -n '155,180p' types.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 1297


🏁 Script executed:

# Let's find where the validator package is defined and check its behavior
rg "required_without_all" --type go -A 2 -B 2

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 1192


🏁 Script executed:

# Check if there are any tests or examples showing validator tag ordering
fd -e go | xargs rg "validate:.*dive.*required" --type go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 238


🏁 Script executed:

# Search for the validator package to understand version and behavior
rg "github.com/go-playground/validator" --type go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 167


🏁 Script executed:

# Let's check the struct_validator.go to understand how required_without_all is implemented
cat -n internal/config_loader/struct_validator.go | head -150

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 6067


🏁 Script executed:

# Search for how the validation is actually executed
rg "Validate\|StructValidator" internal/config_loader/ -A 5 -B 2

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 65


🏁 Script executed:

# Check if there are any tests showing the expected validation behavior
fd -e go -path "*test*" | xargs rg "Precondition\|required_without_all" 2>/dev/null | head -20

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 251


🌐 Web query:

go-playground validator v10 dive tag behavior with required_without_all order

💡 Result:

Short answer: tag order matters. The validator parses tags left-to-right, so place the conditional/element logic before the rule you want to apply.

  • To apply a rule to slice/array/map elements, put dive before the rule(s) you want run on each element (e.g. "dive,required"). [1]
  • For conditional required variants, the position relative to omitempty matters — required (and required_* rules) must come before omitempty. [2]
  • Combine both concerns by ordering tags so the conditional check or dive appears before the target validator (e.g. for elements: "dive,required_without_all=OtherField", or for optional fields: "required_without_all=A B,omitempty" as appropriate). [1][2]

Sources:

  • go-playground/validator docs (dive + required/conditional required). [1]
  • Stack Overflow answer noting required must come before omitempty. [2]

References:
[1] https://pkg.go.dev/github.com/go-playground/validator/v10
[2] https://stackoverflow.com/questions/75193648/need-to-validate-two-fields-in-go-playground-validator-v10


🏁 Script executed:

# Let's see more of struct_validator.go to understand how required_without_all is handled
sed -n '143,200p' internal/config_loader/struct_validator.go

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 2796


🏁 Script executed:

# Look for tests or validation examples that show how the validator handles these tags
find . -name "*.go" -type f | xargs rg "required_without_all" -B 5 -A 5

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 2406


🏁 Script executed:

# Check if there's any documentation or comments about tag ordering in the validator implementation
rg "dive" internal/config_loader/ -B 3 -A 3

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 2122


🏁 Script executed:

# Let's look for any tests or usage examples of Precondition to understand the expected behavior
find . -name "*.go" -type f | xargs rg "Precondition" -B 2 -A 2 | head -80

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 6339


🏁 Script executed:

# Check if there are YAML test files that show valid/invalid Precondition configs
find . -name "*.yaml" -o -name "*.yml" | xargs rg -l "precondition" 2>/dev/null | head -5

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 364


🏁 Script executed:

# Let's understand the semantics: is required_without_all meant for field-level or element-level validation?
# Check if there are similar patterns elsewhere in the code
rg "validate:.*required_without_all" -B 2 -A 2

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 619


🏁 Script executed:

# Let's check the go-playground/validator documentation or source understanding by looking at actual validator behavior
# Create a minimal test to understand how dive + required_without_all works
python3 << 'EOF'
# The question is: does "dive,required_without_all" apply the required_without_all check at the field level or element level?
# According to go-playground/validator docs:
# - dive: tells validator to recursively apply tags to each element in slice/array/map
# - required_without_all: checks if the field is required when all specified other fields are absent

# The semantic intent here is:
# "The Conditions field is required if both APICall and Expression are absent"
# This is a FIELD-LEVEL check, not an element-level check

# With "dive,required_without_all=...":
#   1. dive runs first, processing elements
#   2. required_without_all would try to run on each element (semantically wrong)
#   3. On an empty slice, dive has no elements to process, so required_without_all might not run at the field level

# With "required_without_all=...,dive":
#   1. required_without_all runs first at field level (correct)
#   2. dive runs on elements
#   3. Empty slice would still trigger the field-level check

print("Analysis confirms: the reviewer is identifying a real tag ordering bug")
print("Current: dive,required_without_all=... (wrong order for field-level constraint)")
print("Should be: required_without_all=...,dive (field-level check first, then element processing)")
EOF

Repository: openshift-hyperfleet/hyperfleet-adapter

Length of output: 325


Reorder field-level constraint before element processing.

The required_without_all check enforces that Conditions is required if both APICall and Expression are absent—a field-level constraint. With dive,required_without_all=..., the validator processes elements first; on an empty Conditions slice, the field-level check never executes, allowing a Precondition with no APICall, Expression, or Conditions to pass validation.

🔧 Fix: apply field-level constraint before dive
-	Conditions []Condition    `yaml:"conditions,omitempty" validate:"dive,required_without_all=ActionBase.APICall Expression"`
+	Conditions []Condition    `yaml:"conditions,omitempty" validate:"required_without_all=ActionBase.APICall Expression,dive"`
🤖 Prompt for AI Agents
In `@internal/config_loader/types.go` around lines 168 - 172, The Conditions
field's validator currently uses "dive,required_without_all=ActionBase.APICall
Expression" which runs element validation before the field-level
required_without_all check; change the tag so the field-level constraint runs
first (e.g., put required_without_all before dive) on the Conditions field in
the struct (the Conditions field that references CaptureField and uses validate
tags) so an empty slice triggers the required_without_all validation before
element-level diving.

Comment on lines +15 to +16
// templateVarRegex matches Go template variables like {{ .varName }} or {{ .nested.var }}
var templateVarRegex = regexp.MustCompile(`\{\{\s*\.([a-zA-Z_][a-zA-Z0-9_\.]*)\s*(?:\|[^}]*)?\}\}`)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Template variable regex misses trim delimiters ({{- / -}}).

The current regex doesn't match Go's whitespace-trimming syntax. Since {{- expr and expr -}} are standard Go template syntax for controlling whitespace, templates using these forms won't be validated. Update the regex to allow optional - after {{ and before }}:

-var templateVarRegex = regexp.MustCompile(`\{\{\s*\.([a-zA-Z_][a-zA-Z0-9_\.]*)\s*(?:\|[^}]*)?\}\}`)
+var templateVarRegex = regexp.MustCompile(`\{\{\-?\s*\.([a-zA-Z_][a-zA-Z0-9_\.]*)\s*(?:\|[^}]*)?\s*\-?\}\}`)
🤖 Prompt for AI Agents
In `@internal/config_loader/validator.go` around lines 15 - 16, The
templateVarRegex currently doesn't account for Go template trim delimiters
(`{{-` and `-}}`); update the regexp assigned to templateVarRegex so it allows
an optional hyphen immediately after the opening braces and/or immediately
before the closing braces while preserving capturing of the variable path and
optional pipe/filter segment (i.e., make the openings `\{\{\-?\s*` and closings
`\s*\-?\}\}` and keep the existing capture group `([a-zA-Z_][a-zA-Z0-9_\.]*)`
and optional pipe part intact).

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

Caution

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

⚠️ Outside diff range comments (1)
internal/executor/param_extractor.go (1)

20-55: Convert defaults through param.Type on error paths.

Line 21-53: when extraction or conversion fails for non‑required params, param.Default is written directly, skipping type conversion and producing inconsistent types vs the success path. Consider routing defaults through convertParamType (or centralizing default+conversion) so param.Type is always honored.

🔧 Suggested fix
-        if err != nil {
-            if param.Required {
-                return NewExecutorError(PhaseParamExtraction, param.Name,
-                    fmt.Sprintf("failed to extract required parameter '%s' from source '%s'", param.Name, param.Source), err)
-            }
-            // Use default for non-required params if extraction fails
-            if param.Default != nil {
-                execCtx.Params[param.Name] = param.Default
-            }
-            continue
-        }
+        if err != nil {
+            if param.Required {
+                return NewExecutorError(PhaseParamExtraction, param.Name,
+                    fmt.Sprintf("failed to extract required parameter '%s' from source '%s'", param.Name, param.Source), err)
+            }
+            // Fall back to default (type conversion below keeps param.Type consistent)
+            value = param.Default
+        }
@@
-        if value != nil && param.Type != "" {
-            converted, convErr := convertParamType(value, param.Type)
-            if convErr != nil {
-                if param.Required {
-                    return NewExecutorError(PhaseParamExtraction, param.Name,
-                        fmt.Sprintf("failed to convert parameter '%s' to type '%s'", param.Name, param.Type), convErr)
-                }
-                // Use default for non-required params if conversion fails
-                if param.Default != nil {
-                    execCtx.Params[param.Name] = param.Default
-                }
-                continue
-            }
-            value = converted
-        }
+        if value != nil && param.Type != "" {
+            converted, convErr := convertParamType(value, param.Type)
+            if convErr != nil {
+                if param.Required {
+                    return NewExecutorError(PhaseParamExtraction, param.Name,
+                        fmt.Sprintf("failed to convert parameter '%s' to type '%s'", param.Name, param.Type), convErr)
+                }
+                if param.Default != nil {
+                    converted, convErr = convertParamType(param.Default, param.Type)
+                    if convErr != nil {
+                        return NewExecutorError(PhaseParamExtraction, param.Name,
+                            fmt.Sprintf("failed to convert default for parameter '%s' to type '%s'", param.Name, param.Type), convErr)
+                    }
+                    value = converted
+                } else {
+                    value = nil
+                }
+            } else {
+                value = converted
+            }
+        }
🤖 Fix all issues with AI agents
In `@internal/config_loader/loader_test.go`:
- Around line 1052-1062: The helper configWithResources returns an AdapterConfig
with APIVersion set to "hyperfleet.openshift.io/v1alpha1" which doesn't match
the supported constant; update the APIVersion value in configWithResources to
the supported "hyperfleet.redhat.com/v1alpha1" so AdapterConfig (and its
validation in ValidateStruct) uses the correct API group; locate the APIVersion
field inside the configWithResources function and replace the incorrect string
with the supported constant/string.

In `@internal/config_loader/loader.go`:
- Around line 225-249: The resolvePath function must prevent symlink escapes by
resolving symlinks before the containment check: after computing baseAbs (and
before computing targetPath) call filepath.EvalSymlinks on baseAbs (store e.g.
baseResolved), and after computing targetPath call filepath.EvalSymlinks on
targetPath (store targetResolved); then compute filepath.Rel between
baseResolved and targetResolved and reject paths that escape (rel starts with
".." or an error). Update error messages to reference refPath and propagate
EvalSymlinks errors appropriately; keep using resolvePath, baseDir, refPath,
baseAbs/baseClean and targetPath as anchors when editing.

In `@internal/config_loader/README.md`:
- Around line 145-150: The example ActionBase struct in the README is missing
the Log field used by post-actions; update the ActionBase definition in the
documentation to include the Log field (e.g., Log *LogConfig or similar
name/type used elsewhere) so examples and embedding references to ActionBase.Log
compile and make sense—locate the ActionBase struct in the README and add the
Log field with the correct YAML tag and type consistent with the codebase so
post-actions that reference ActionBase.Log are documented accurately.

In `@internal/executor/README.md`:
- Around line 144-162: Update the "Supported Parameter Types" table row for
`float`, `float64` to hyphenate the compound adjective: change the Description
text from "Floating point value" to "Floating-point value" (locate the table
under the "Supported Parameter Types" heading and the `float`, `float64` row).
♻️ Duplicate comments (4)
internal/config_loader/README.md (1)

118-124: Add a language hint to the validation-error example block.
This still trips fenced-code language linting.

🧹 Suggested fix
-```
+```text
 spec.params[0].name is required
 spec.preconditions[1].apiCall.method "INVALID" is invalid (allowed: GET, POST, PUT, PATCH, DELETE)
 spec.resources[0].name "my-resource": must start with lowercase letter and contain only letters, numbers, underscores (no hyphens)
 spec.preconditions[0].capture[0]: must have either 'field' or 'expression' set
</details>

</blockquote></details>
<details>
<summary>internal/config_loader/loader.go (1)</summary><blockquote>

`144-153`: **Avoid silently skipping file-reference handling when baseDir is empty.**  
`Parse()` callers can supply buildRef/manifest.ref; with an empty baseDir the pipeline skips validation/loading, leaving unresolved refs.  


<details>
<summary>🛡️ Suggested guard</summary>

```diff
-	// 3. File reference validation and loading (only if baseDir is set)
-	if cfg.baseDir != "" {
+	// 3. File reference validation and loading
+	if cfg.baseDir != "" {
 		if err := validator.ValidateFileReferences(); err != nil {
 			return fmt.Errorf("file reference validation failed: %w", err)
 		}
 
 		if err := loadFileReferences(config, cfg.baseDir); err != nil {
 			return fmt.Errorf("failed to load file references: %w", err)
 		}
+	} else if hasFileReferences(config) {
+		return fmt.Errorf("file references require baseDir; pass WithBaseDir or use Load()")
 	}
+func hasFileReferences(config *AdapterConfig) bool {
+	for i := range config.Spec.Resources {
+		if config.Spec.Resources[i].GetManifestRef() != "" {
+			return true
+		}
+	}
+	if config.Spec.Post != nil {
+		for i := range config.Spec.Post.Payloads {
+			if config.Spec.Post.Payloads[i].BuildRef != "" {
+				return true
+			}
+		}
+	}
+	return false
+}
internal/config_loader/validator.go (1)

15-16: Support Go template trim delimiters ({{- / -}}).
Templates using whitespace trimming won’t be validated by the current regex.

🔧 Proposed fix
-var templateVarRegex = regexp.MustCompile(`\{\{\s*\.([a-zA-Z_][a-zA-Z0-9_\.]*)\s*(?:\|[^}]*)?\}\}`)
+var templateVarRegex = regexp.MustCompile(`\{\{\-?\s*\.([a-zA-Z_][a-zA-Z0-9_\.]*)\s*(?:\|[^}]*)?\s*\-?\}\}`)
internal/config_loader/types.go (1)

157-172: Ensure field-level required_without_all runs before dive.
With dive,required_without_all=..., an empty slice can bypass the field-level constraint.

🔧 Proposed fix
-	Conditions []Condition    `yaml:"conditions,omitempty" validate:"dive,required_without_all=ActionBase.APICall Expression"`
+	Conditions []Condition    `yaml:"conditions,omitempty" validate:"required_without_all=ActionBase.APICall Expression,dive"`
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd2624c and 70ce5c7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • configs/adapter-config-template.yaml
  • go.mod
  • internal/config_loader/README.md
  • internal/config_loader/loader.go
  • internal/config_loader/loader_test.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/types.go
  • internal/config_loader/validator.go
  • internal/config_loader/validator_schema.go
  • internal/config_loader/validator_test.go
  • internal/criteria/README.md
  • internal/executor/README.md
  • internal/executor/executor_test.go
  • internal/executor/param_extractor.go
  • internal/executor/param_extractor_test.go
  • internal/executor/post_action_executor_test.go
  • pkg/logger/with_error_field_test.go
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • test/integration/executor/executor_integration_test.go
  • test/integration/executor/executor_k8s_integration_test.go
  • test/testdata/adapter_config_valid.yaml
💤 Files with no reviewable changes (3)
  • test/integration/config-loader/testdata/adapter_config_valid.yaml
  • internal/config_loader/validator_schema.go
  • test/testdata/adapter_config_valid.yaml
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/logger/with_error_field_test.go
  • internal/executor/post_action_executor_test.go
  • internal/executor/param_extractor_test.go
  • configs/adapter-config-template.yaml
  • test/integration/executor/executor_integration_test.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.
📚 Learning: 2025-12-11T02:06:33.975Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 17
File: internal/config_loader/validator_schema.go:126-133
Timestamp: 2025-12-11T02:06:33.975Z
Learning: In the hyperfleet-adapter repository (Go), environment variable validation for required parameters (those with `env.*` source, `Required=true`, and no default) occurs at config load time in `internal/config_loader/validator_schema.go`. This is intentional design - environment variables must be set before deployment, and the validation ensures fail-fast behavior with clear error messages.

Applied to files:

  • internal/executor/param_extractor.go
  • go.mod
  • internal/config_loader/README.md
  • internal/config_loader/loader_test.go
  • internal/config_loader/loader.go
  • internal/config_loader/struct_validator.go
  • internal/config_loader/validator_test.go
  • internal/config_loader/validator.go
  • internal/config_loader/types.go
📚 Learning: 2025-12-04T14:06:51.656Z
Learnt from: xueli181114
Repo: openshift-hyperfleet/hyperfleet-adapter PR: 13
File: internal/executor/post_action_executor.go:198-205
Timestamp: 2025-12-04T14:06:51.656Z
Learning: Logger refactor is tracked in HYPERFLEET-304 for the hyperfleet-adapter repository, which will address how CEL evaluation failures and similar errors are handled and logged.

Applied to files:

  • internal/config_loader/types.go
🧬 Code graph analysis (6)
internal/executor/param_extractor.go (1)
internal/executor/types.go (2)
  • NewExecutorError (375-382)
  • PhaseParamExtraction (21-21)
test/integration/executor/executor_k8s_integration_test.go (1)
internal/config_loader/types.go (4)
  • ActionBase (159-163)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
internal/executor/executor_test.go (2)
internal/config_loader/types.go (3)
  • ActionBase (159-163)
  • LogAction (273-276)
  • Precondition (167-172)
internal/executor/types.go (1)
  • StatusSuccess (35-35)
internal/config_loader/validator_test.go (2)
internal/config_loader/types.go (7)
  • ActionBase (159-163)
  • Condition (202-206)
  • APICall (175-183)
  • CaptureField (196-199)
  • FieldExpressionDef (13-18)
  • Precondition (167-172)
  • Payload (130-141)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/config_loader/validator.go (5)
internal/config_loader/types.go (2)
  • AdapterConfig (69-74)
  • ValidationErrors (298-300)
internal/config_loader/struct_validator.go (1)
  • ValidateStruct (115-135)
internal/config_loader/loader.go (1)
  • SupportedAPIVersions (26-28)
internal/config_loader/constants.go (10)
  • FieldSpec (9-9)
  • FieldPost (21-21)
  • FieldPayloads (41-41)
  • FieldBuildRef (43-43)
  • FieldResources (20-20)
  • FieldManifest (78-78)
  • FieldRef (85-85)
  • FieldPreconditions (19-19)
  • FieldCapture (49-49)
  • FieldExpression (51-51)
internal/criteria/types.go (2)
  • Operator (10-10)
  • OperatorExists (28-28)
internal/config_loader/types.go (2)
internal/criteria/types.go (1)
  • Operator (10-10)
internal/k8s_client/discovery.go (2)
  • Discovery (14-29)
  • DiscoveryConfig (32-42)
🪛 LanguageTool
internal/executor/README.md

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | float, float64 | Floating point value | Strings parsed | | bool | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
internal/config_loader/README.md

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

(MD040, fenced-code-language)

🔇 Additional comments (33)
test/integration/executor/executor_k8s_integration_test.go (2)

182-195: ActionBase + FieldExpressionDef wiring matches new config schema.

Line 182-195 aligns Precondition/CaptureField literals with the new ActionBase and FieldExpressionDef types, which should satisfy struct validation.


285-292: PostAction ActionBase update looks correct.

Line 285-292 mirrors the new ActionBase structure for post-actions and should keep validation consistent.

internal/criteria/README.md (2)

45-45: Evaluator examples reflect the context-aware constructor.

Line 45, 227, and 257 show NewEvaluator with an explicit context, consistent with the updated API.

Also applies to: 227-227, 257-257


144-155: ExtractValueResult/GetField error semantics are clearly documented.

Line 144-155 and 181-203 document parse vs runtime extraction errors and the GetField contract clearly.

Also applies to: 181-203

internal/executor/param_extractor.go (1)

165-354: Conversion helpers are comprehensive and readable.

Line 165-354 covers string/int/float/bool conversions with overflow guarding for unsigned ints; this is clear and robust.

go.mod (1)

8-8: Dependency versions are acceptable and confirmed as current.

The validator/v10 v10.30.1 addition at line 8 is the latest available release (24 Dec 2025) with no published security advisories. The transitive dependencies (mimetype v1.4.12, locales v0.14.1, universal-translator v0.18.1, go-urn v1.4.0 at lines 62–70 and 84) are standard validator transitive deps. The golang.org/x/ version bumps (crypto v0.46.0, net v0.47.0, sys v0.39.0, term v0.38.0 at lines 136–142) are current releases from post-security-fix branches with no identified issues. All versions are reflected in go.mod.

internal/executor/executor_test.go (3)

451-492: ActionBase wrapping keeps precondition tests aligned with updated config types.
The updated fixtures mirror the embedded ActionBase shape while preserving the sequential-stop assertions.


655-658: Post-action fixtures correctly use ActionBase embedding.
Log actions are still exercised while matching the new struct layout.


725-739: Skip-reason scenarios updated appropriately for ActionBase.
These cases remain consistent with the new precondition representation.

internal/config_loader/struct_validator.go (1)

94-110: Env-required parameter validation stays fail-fast.
The check correctly enforces required env params when no default is provided. Based on learnings, this preserves the intended fail-fast behavior.

internal/config_loader/validator_test.go (8)

30-109: Good coverage for operator validation paths, including exists semantics.


119-186: Template variable tests align with ActionBase refactor and capture-variable flow.


189-213: CEL expression tests look consistent with the new validator entry point.


317-335: Multi-error aggregation path is exercised appropriately.


356-413: Payload struct validation coverage looks solid.


444-476: Parse-level payload validation assertions are consistent with new errors.


479-525: Capture field tests correctly reflect FieldExpressionDef mutual exclusivity.


528-569: YAML field-name cache tests add useful guardrails.

internal/config_loader/validator.go (8)

22-92: Validator phases and error handling look consistent with the refactor.


112-167: File reference validation is clear and well-scoped to baseDir.


181-213: Condition value validation (incl. exists) is correctly enforced.


227-239: Capture-field CEL validation path is clear and targeted.


245-341: Template variable validation flow reads well after ActionBase changes.


368-425: CEL environment initialization and validation traversal look solid.


465-508: K8s manifest structural validation paths look consistent.


514-556: Helper APIs and test shims are clean and focused.

internal/config_loader/types.go (7)

10-38: FieldExpressionDef + ValueDef embedding is a clean simplification.


68-121: Top-level struct validation tags look consistent and helpful.


130-155: Payload validation rules and error messages are clear.


175-205: APICall/CaptureField/Condition validation tightening looks good.


243-265: Resource/Discovery/Post validation tags look consistent with the new rules.


269-270: PostAction embedding via ActionBase is clean.


283-339: ValidationErrors container is straightforward and useful.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +1052 to +1062
// Helper to create a valid config with given resources
configWithResources := func(resources []Resource) *AdapterConfig {
return &AdapterConfig{
APIVersion: "hyperfleet.openshift.io/v1alpha1",
Kind: "AdapterConfig",
Metadata: Metadata{Name: "test-adapter"},
Spec: AdapterConfigSpec{
Adapter: AdapterInfo{Version: "1.0.0"},
Resources: resources,
},
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

APIVersion value doesn’t match the supported constant.
hyperfleet.openshift.io/v1alpha1 is inconsistent with the supported hyperfleet.redhat.com/v1alpha1, which can cause ValidateStruct to fail before the discovery assertions run.

✅ Suggested fix
-        APIVersion: "hyperfleet.openshift.io/v1alpha1",
+        APIVersion: APIVersionV1Alpha1,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Helper to create a valid config with given resources
configWithResources := func(resources []Resource) *AdapterConfig {
return &AdapterConfig{
APIVersion: "hyperfleet.openshift.io/v1alpha1",
Kind: "AdapterConfig",
Metadata: Metadata{Name: "test-adapter"},
Spec: AdapterConfigSpec{
Adapter: AdapterInfo{Version: "1.0.0"},
Resources: resources,
},
}
// Helper to create a valid config with given resources
configWithResources := func(resources []Resource) *AdapterConfig {
return &AdapterConfig{
APIVersion: APIVersionV1Alpha1,
Kind: "AdapterConfig",
Metadata: Metadata{Name: "test-adapter"},
Spec: AdapterConfigSpec{
Adapter: AdapterInfo{Version: "1.0.0"},
Resources: resources,
},
}
🤖 Prompt for AI Agents
In `@internal/config_loader/loader_test.go` around lines 1052 - 1062, The helper
configWithResources returns an AdapterConfig with APIVersion set to
"hyperfleet.openshift.io/v1alpha1" which doesn't match the supported constant;
update the APIVersion value in configWithResources to the supported
"hyperfleet.redhat.com/v1alpha1" so AdapterConfig (and its validation in
ValidateStruct) uses the correct API group; locate the APIVersion field inside
the configWithResources function and replace the incorrect string with the
supported constant/string.

Comment on lines +225 to +249
// resolvePath resolves a relative path against the base directory and validates
// that the resolved path does not escape the base directory.
func resolvePath(baseDir, refPath string) (string, error) {
baseAbs, err := filepath.Abs(baseDir)
if err != nil {
return "", fmt.Errorf("failed to resolve base directory: %w", err)
}
baseClean := filepath.Clean(baseAbs)

var targetPath string
if filepath.IsAbs(refPath) {
targetPath = filepath.Clean(refPath)
} else {
targetPath = filepath.Clean(filepath.Join(baseClean, refPath))
}

// Check if target path is within base directory
rel, err := filepath.Rel(baseClean, targetPath)
if err != nil {
return "", fmt.Errorf("path %q escapes base directory", refPath)
}

if strings.HasPrefix(rel, "..") {
return "", fmt.Errorf("path %q escapes base directory", refPath)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden resolvePath against symlink escapes.
Lexical Clean/Rel checks don’t prevent a ref from pointing at a symlink inside baseDir that resolves outside.

🔒 Suggested hardening
-	baseAbs, err := filepath.Abs(baseDir)
+	baseAbs, err := filepath.EvalSymlinks(baseDir)
 	if err != nil {
 		return "", fmt.Errorf("failed to resolve base directory: %w", err)
 	}
 	baseClean := filepath.Clean(baseAbs)
@@
-	// Check if target path is within base directory
-	rel, err := filepath.Rel(baseClean, targetPath)
+	targetReal, err := filepath.EvalSymlinks(targetPath)
+	if err != nil {
+		return "", err
+	}
+	// Check if target path is within base directory
+	rel, err := filepath.Rel(baseClean, targetReal)
 	if err != nil {
 		return "", fmt.Errorf("path %q escapes base directory", refPath)
 	}
@@
-	return targetPath, nil
+	return targetReal, nil
🤖 Prompt for AI Agents
In `@internal/config_loader/loader.go` around lines 225 - 249, The resolvePath
function must prevent symlink escapes by resolving symlinks before the
containment check: after computing baseAbs (and before computing targetPath)
call filepath.EvalSymlinks on baseAbs (store e.g. baseResolved), and after
computing targetPath call filepath.EvalSymlinks on targetPath (store
targetResolved); then compute filepath.Rel between baseResolved and
targetResolved and reject paths that escape (rel starts with ".." or an error).
Update error messages to reference refPath and propagate EvalSymlinks errors
appropriately; keep using resolvePath, baseDir, refPath, baseAbs/baseClean and
targetPath as anchors when editing.

Comment on lines +145 to +150
```go
// ActionBase - common fields for actions (preconditions, post-actions)
type ActionBase struct {
Name string `yaml:"name" validate:"required"`
APICall *APICall `yaml:"apiCall,omitempty"`
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document ActionBase.Log in the struct embedding example.
The snippet omits the Log field that post-actions rely on.

📝 Suggested update
 type ActionBase struct {
     Name    string   `yaml:"name" validate:"required"`
     APICall *APICall `yaml:"apiCall,omitempty"`
+    Log     *LogAction `yaml:"log,omitempty"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```go
// ActionBase - common fields for actions (preconditions, post-actions)
type ActionBase struct {
Name string `yaml:"name" validate:"required"`
APICall *APICall `yaml:"apiCall,omitempty"`
}
🤖 Prompt for AI Agents
In `@internal/config_loader/README.md` around lines 145 - 150, The example
ActionBase struct in the README is missing the Log field used by post-actions;
update the ActionBase definition in the documentation to include the Log field
(e.g., Log *LogConfig or similar name/type used elsewhere) so examples and
embedding references to ActionBase.Log compile and make sense—locate the
ActionBase struct in the README and add the Log field with the correct YAML tag
and type consistent with the codebase so post-actions that reference
ActionBase.Log are documented accurately.

Comment on lines +144 to +162
- name: "nodeCount"
source: "event.spec.nodes"
type: "int" # Convert to int64
- name: "enableFeature"
source: "env.ENABLE_FEATURE"
type: "bool" # Convert to bool
default: false
```

</details>

#### Supported Parameter Types

| Type | Description | Conversion Notes |
|------|-------------|-----------------|
| `string` | String value (default) | Any value converted to string |
| `int`, `int64` | Integer value | Strings parsed, floats truncated |
| `float`, `float64` | Floating point value | Strings parsed |
| `bool` | Boolean value | Supports: `true/false`, `yes/no`, `on/off`, `1/0` |
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hyphenate compound adjective in type table.

Line 161: “Floating point value” should be “Floating‑point value.”

✏️ Suggested edit
-| `float`, `float64` | Floating point value | Strings parsed |
+| `float`, `float64` | Floating-point value | Strings parsed |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: "nodeCount"
source: "event.spec.nodes"
type: "int" # Convert to int64
- name: "enableFeature"
source: "env.ENABLE_FEATURE"
type: "bool" # Convert to bool
default: false
```
</details>
#### Supported Parameter Types
| Type | Description | Conversion Notes |
|------|-------------|-----------------|
| `string` | String value (default) | Any value converted to string |
| `int`, `int64` | Integer value | Strings parsed, floats truncated |
| `float`, `float64` | Floating point value | Strings parsed |
| `bool` | Boolean value | Supports: `true/false`, `yes/no`, `on/off`, `1/0` |
| Type | Description | Conversion Notes |
|------|-------------|-----------------|
| `string` | String value (default) | Any value converted to string |
| `int`, `int64` | Integer value | Strings parsed, floats truncated |
| `float`, `float64` | Floating-point value | Strings parsed |
| `bool` | Boolean value | Supports: `true/false`, `yes/no`, `on/off`, `1/0` |
🧰 Tools
🪛 LanguageTool

[uncategorized] ~161-~161: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...oats truncated | | float, float64 | Floating point value | Strings parsed | | bool | Boo...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🤖 Prompt for AI Agents
In `@internal/executor/README.md` around lines 144 - 162, Update the "Supported
Parameter Types" table row for `float`, `float64` to hyphenate the compound
adjective: change the Description text from "Floating point value" to
"Floating-point value" (locate the table under the "Supported Parameter Types"
heading and the `float`, `float64` row).

// Validator
// -----------------------------------------------------------------------------
// Phase 2: Custom validations that can't be expressed via struct tags
if err := v.validateAPIVersionSupported(); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this check belongs here within ValidateStructure or if it similar to the ValidateAdapterVersion in the runValidationPipeline since is checking more about the semantics than the required fields/format

If that is the case, the validateStructure function could be removed for a direct call to ValidateStruct

}

// Semantic validation (optional, can be skipped for performance)
// 4. Semantic validation (optional, can be skipped for performance)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the performance cost of this semantic validation?
Is it performed only on starting the adapter?

If it is small I would prefer to validate always and reduce cognitive load of knowing if this can be skipped or not

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. We should. I can cover this in my config standardizing PR.

@rh-amarin
Copy link
Contributor

/lgtm

@openshift-ci
Copy link

openshift-ci bot commented Jan 20, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rh-amarin

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

The pull request process is described here

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

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

@openshift-merge-bot openshift-merge-bot bot merged commit abca166 into openshift-hyperfleet:main Jan 20, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants