Skip to content

HYPERFLEET-331: fix TSL syntax for resource selector search parameter#31

Merged
openshift-merge-bot[bot] merged 1 commit intoopenshift-hyperfleet:mainfrom
rafabene:HYPERFLEET-331
Dec 11, 2025
Merged

HYPERFLEET-331: fix TSL syntax for resource selector search parameter#31
openshift-merge-bot[bot] merged 1 commit intoopenshift-hyperfleet:mainfrom
rafabene:HYPERFLEET-331

Conversation

@rafabene
Copy link
Contributor

@rafabene rafabene commented Dec 11, 2025

Summary

  • Fixed labelSelectorToSearchString() to generate correct TSL (Tree Search Language) syntax for HyperFleet API
  • Labels now prefixed with labels. (e.g., labels.region)
  • Values quoted with single quotes (e.g., 'us-east')
  • Multiple conditions joined with and instead of comma

Before: region=us-east,env=prod
After: labels.env='prod' and labels.region='us-east'

Test plan

  • Unit tests updated and passing for labelSelectorToSearchString()
  • Integration test TestFetchResources_WithLabelSelector updated
  • New integration test TestIntegration_TSLSyntaxMultipleLabels added
  • Verified TSL syntax matches HyperFleet API documentation and integration tests
  • Test against running HyperFleet API instance

References

  • Fixes: HYPERFLEET-331
  • HyperFleet API TSL syntax confirmed in hyperfleet-api/README.md and search_field_mapping_test.go

Summary by CodeRabbit

  • New Features

    • Added a public resource-fetching API and new resource-type categories for retrieving cluster and node-pool data.
  • Refactor

    • Label selector queries now use standardized Tree Search Language (TSL): label fields prefixed, values quoted, and single quotes escaped; multiple conditions joined with "and". Keys with hyphens/underscores supported. Documentation updated.
  • Tests

    • Unit and integration tests updated and extended to validate the new TSL selector format, including multi-label cases.

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

@openshift-ci openshift-ci bot requested review from AlexVulaj and vkareh December 11, 2025 16:39
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Warning

Rate limit exceeded

@rafabene has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7c2b8dd and 39e001a.

📒 Files selected for processing (3)
  • internal/client/client.go (1 hunks)
  • internal/client/client_test.go (3 hunks)
  • test/integration/integration_test.go (2 hunks)

Walkthrough

The PR changes labelSelectorToSearchString to emit Tree Search Language (TSL) predicates (each label -> labels.<key>='<value>', values escape single quotes, clauses joined with and). It adds a fetchResources pipeline: new ResourceType, Resource, ResourceStatus, Condition, and APIError types; fetchResourcesOnce dispatchers (fetchClusters, fetchNodePools); and a public FetchResources that wraps fetchResourcesOnce with retry/backoff and retriable error classification. Unit and integration tests were updated (and a new integration test TestIntegration_TSLSyntaxMultipleLabels added) to assert the TSL search parameter format.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Caller
participant Client
participant RemoteAPI
Note over Client: labelSelectorToSearchString -> builds TSL searchParam
Caller->>Client: FetchResources(ctx, resourceType, labelSelector)
Client->>Client: call fetchResourcesOnce(searchParam)
alt success
Client->>RemoteAPI: HTTP request with search=searchParam
RemoteAPI-->>Client: 200 + resources
Client-->>Caller: resources
else retriable error
Client->>RemoteAPI: HTTP request with search=searchParam
RemoteAPI-->>Client: transient error (e.g., 5xx / timeout)
Client->>Client: isRetriable -> backoff & retry loop
loop retries
Client->>RemoteAPI: retry request
RemoteAPI-->>Client: response (success or error)
end
alt final success
Client-->>Caller: resources
else final failure
Client-->>Caller: APIError (with Retriable=false, StatusCode)
end
else non-retriable error
Client->>RemoteAPI: HTTP request with search=searchParam
RemoteAPI-->>Client: non-retriable error (e.g., 4xx)
Client-->>Caller: APIError (non-retriable)
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Attention areas:
    • fetchResourcesOnce, fetchClusters, fetchNodePools: verify dispatch logic and searchParam usage.
    • Retry/backoff: correctness of retriable detection (isRetriable, isHTTPStatusRetriable) and retry termination conditions.
    • APIError shape and propagation (StatusCode, Retriable flag) and tests expecting specific error semantics.
    • labelSelectorToSearchString: escaping of single quotes and handling of empty/missing selectors.
    • Updated unit and integration tests (including TestIntegration_TSLSyntaxMultipleLabels) for brittleness and duplicate test definitions.

Suggested labels

approved

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main change: fixing TSL syntax for resource selector search parameters, which aligns with the core objective of correcting labelSelectorToSearchString() to generate proper TSL format.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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: 0

🧹 Nitpick comments (1)
internal/client/client.go (1)

173-181: Optionally preallocate parts slice for small efficiency win

You can preallocate parts to avoid potential reallocations when building the slice, especially if selectors grow over time:

-	var parts []string
+	parts := make([]string, 0, len(labelSelector))

Given the likely small map sizes this is minor, but it’s a cheap improvement.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f489a6 and 2b9a50f.

📒 Files selected for processing (2)
  • internal/client/client.go (1 hunks)
  • internal/client/client_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/client/client_test.go
🔇 Additional comments (1)
internal/client/client.go (1)

162-182: TSL formatting and escaping look correct

The function and its documentation align with the described TSL requirements: labels. prefix, single-quoted values with proper ' escaping, deterministic ordering via sorting, and and conjunctions. Behavior for empty/nil selectors is also well handled.

@rafabene rafabene force-pushed the HYPERFLEET-331 branch 2 times, most recently from 25048c5 to 7c2b8dd Compare December 11, 2025 16:53
@rafabene rafabene changed the title fix(client): correct TSL syntax for resource selector search parameter HYPERFLEET-331: fix TSL syntax for resource selector search parameter Dec 11, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/client/client.go (1)

173-177: Consider documenting the escaping behavior.

The single-quote escaping by doubling (''') follows SQL/TSL conventions. Consider adding a brief inline comment explaining this is the standard TSL escape sequence for single quotes, which will help future maintainers understand the intent.

 	parts := make([]string, 0, len(labelSelector))
 	for k, v := range labelSelector {
-		// Escape single quotes in values by doubling them (SQL/TSL standard)
+		// Escape single quotes by doubling them ('' is the TSL escape sequence for a literal ')
 		escapedValue := strings.ReplaceAll(v, "'", "''")
 		parts = append(parts, fmt.Sprintf("labels.%s='%s'", k, escapedValue))
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25048c5 and 7c2b8dd.

📒 Files selected for processing (3)
  • internal/client/client.go (1 hunks)
  • internal/client/client_test.go (3 hunks)
  • test/integration/integration_test.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/integration/integration_test.go (2)
test/integration/testcontainer.go (1)
  • NewHelper (122-141)
internal/config/config.go (2)
  • SentinelConfig (24-33)
  • LabelSelector (15-18)
🔇 Additional comments (5)
internal/client/client_test.go (2)

476-515: Comprehensive TSL syntax test coverage.

The test cases appropriately cover the key TSL syntax requirements:

  • Single and multiple label selectors with correct labels. prefix
  • Sorted output for deterministic testing
  • Edge cases for hyphens/underscores in keys and values
  • Proper escaping of single quotes by doubling (''')

633-636: LGTM!

The integration test expectation correctly validates the TSL format with multiple labels sorted alphabetically and joined with and.

test/integration/integration_test.go (2)

207-223: LGTM - TSL syntax matching updated correctly.

The server-side filtering logic now correctly matches the TSL format labels.shard='1' instead of the old key=value format.


274-380: Well-structured integration test for TSL syntax validation.

The test correctly:

  1. Sets up a mock server that validates the exact TSL syntax
  2. Configures multiple label selectors
  3. Validates the received search parameter matches expected TSL format
  4. Cleans up properly with context cancellation

One minor observation: receivedSearchParam is written by the HTTP handler goroutine and read by the test goroutine. While the 300ms sleep provides sufficient delay in practice, consider using a channel or sync primitive for stricter correctness if this test becomes flaky.

internal/client/client.go (1)

162-182: TSL conversion implementation is correct.

The implementation properly:

  • Prefixes keys with labels.
  • Escapes single quotes in values by doubling (standard SQL/TSL escaping)
  • Joins multiple conditions with and
  • Sorts keys for deterministic output

The edge case concern about special characters in label keys is not valid. Kubernetes label key restrictions explicitly allow only alphanumerics, dashes, underscores, and dots (plus "/" for the optional DNS subdomain prefix). Single quotes are not permitted in Kubernetes label keys, so the current implementation—which escapes values but not keys—is correct and requires no changes.

The labelSelectorToSearchString() function was generating incorrect
search parameter format that didn't match the HyperFleet API's TSL
(Tree Search Language) syntax.

Changes:
- Prefix label keys with "labels." (e.g., labels.region)
- Quote values with single quotes (e.g., 'us-east')
- Use " and " instead of comma for multiple labels
- Escape single quotes in values by doubling them (SQL/TSL standard)
- Pre-allocate slice for better performance
- Add edge case tests for special characters

Before: region=us-east,env=prod
After:  labels.env='prod' and labels.region='us-east'

This fix enables the resource selector feature to work correctly
with the HyperFleet API, unblocking multi-instance deployment
sharding functionality.
@rh-amarin
Copy link
Contributor

I had an issue with trying with real hyperfleet-api, but the code here is already following the documented syntax.

/lgtm

@openshift-ci
Copy link

openshift-ci bot commented Dec 11, 2025

[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 09df31a into openshift-hyperfleet:main Dec 11, 2025
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.

2 participants