Skip to content

ROSAENG-60614 | feat: allow partial component route updates#3403

Open
reedcort wants to merge 1 commit into
openshift:masterfrom
reedcort:ROSAENG-9174-allow-partial-component-routes
Open

ROSAENG-60614 | feat: allow partial component route updates#3403
reedcort wants to merge 1 commit into
openshift:masterfrom
reedcort:ROSAENG-9174-allow-partial-component-routes

Conversation

@reedcort

@reedcort reedcort commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

PR Summary

Allow partial component route updates via rosa edit ingress --component-routes — users can now set a single route (e.g. only console) without providing all routes.

Detailed Description of the Issue

The previous change (#3376) followed the Classic CLI framework which required all routes to be set together (classic: oauth+console+downloads, HCP: console+downloads). The OCM API supports partial updates — setting only console leaves downloads unchanged — but the CLI rejected partial input with "the expected amount of component routes is N, but M have been supplied". After review feedback, the decision is to allow partial updates to match the API flexibility.

Related Issues and PRs

Type of Change

  • feat - adds a new user-facing capability.

Previous Behavior

rosa edit ingress --component-routes required all routes for the platform — classic required oauth+console+downloads (3), HCP required console+downloads (2). Passing fewer routes returned:

ERR: the expected amount of component routes is 2, but 1 have been supplied

Behavior After This Change

Users can pass any subset of valid routes. Name validation and duplicate detection are preserved.

# Set only console (HCP)
rosa edit ingress -c mycluster --component-routes 'console: hostname=console.example.com;tlsSecretRef=console-tls' <ingress-id>

# Set only downloads (HCP)
rosa edit ingress -c mycluster --component-routes 'downloads: hostname=downloads.example.com;tlsSecretRef=downloads-tls' <ingress-id>

# Set both (still works)
rosa edit ingress -c mycluster --component-routes 'console: hostname=console.example.com;tlsSecretRef=console-tls, downloads: hostname=downloads.example.com;tlsSecretRef=downloads-tls' <ingress-id>

Invalid names and duplicates are still rejected:

ERR: 'oauth' is not a valid component name. Expected include [console, downloads]
ERR: component route "console" was supplied more than once

How to Test (Step-by-Step)

Preconditions

  • A running ROSA HCP cluster with hypershift-component-routes feature toggle enabled
  • HyperShift operator >= v0.1.79
  • TLS secret in openshift-config on the hosted cluster

Test Steps

  1. Set only console: rosa edit ingress -c <cluster> --component-routes 'console: hostname=console.example.com;tlsSecretRef=console-tls' <ingress-id>
  2. Verify console route updated, downloads unchanged
  3. Set only downloads: rosa edit ingress -c <cluster> --component-routes 'downloads: hostname=downloads.example.com;tlsSecretRef=downloads-tls' <ingress-id>
  4. Verify downloads route updated, console unchanged
  5. Verify invalid name still rejected: rosa edit ingress -c <cluster> --component-routes 'oauth: hostname=x;tlsSecretRef=y' <ingress-id>
  6. Verify duplicate still rejected: rosa edit ingress -c <cluster> --component-routes 'console: hostname=a;tlsSecretRef=b, console: hostname=c;tlsSecretRef=d' <ingress-id>

Expected Results

  • Partial updates succeed
  • Invalid names and duplicates still rejected
  • Existing routes not included in the update remain unchanged on the cluster

Proof of the Fix

$ go test ./cmd/edit/ingress/... -v
Ran 14 of 14 Specs in 0.002 seconds
SUCCESS! -- 14 Passed | 0 Failed | 0 Pending | 0 Skipped

Live cluster — partial updates and clearing

1. Set console only:

$ rosa edit ingress -c creed-hcp --component-routes 'console: hostname=console.custom.example.com;tlsSecretRef=console-tls-secret' o5n4
INFO: Updated ingress 'o5n4'

API state: {"console": {"hostname": "console.custom.example.com", "tls_secret_ref": "console-tls-secret"}}

2. Add downloads (console unchanged):

$ rosa edit ingress -c creed-hcp --component-routes 'downloads: hostname=downloads.custom.example.com;tlsSecretRef=downloads-tls-secret' o5n4
INFO: Updated ingress 'o5n4'

API state: {"console": {...}, "downloads": {"hostname": "downloads.custom.example.com", "tls_secret_ref": "downloads-tls-secret"}}

3. Clear downloads only (console unchanged):

$ rosa edit ingress -c creed-hcp --component-routes 'downloads: hostname=;tlsSecretRef=' o5n4
INFO: Updated ingress 'o5n4'

API state: {"console": {"hostname": "console.custom.example.com", "tls_secret_ref": "console-tls-secret"}}

Breaking Changes

  • No breaking changes

Developer Verification Checklist

  • Commit subject/title follows [JIRA-TICKET] | [TYPE]: <MESSAGE>.
  • PR description clearly explains both what changed and why.
  • Relevant Jira/GitHub issues and related PRs are linked.
  • make install-hooks has been run in this clone.
  • Tests were added/updated where appropriate.
  • I manually tested the change.
  • make test passes.
  • make lint passes.
  • make rosa passes.
  • Documentation or repo-local agent guidance was added/updated where appropriate.
  • Any risk, limitation, or follow-up work is documented.

Summary by CodeRabbit

  • Documentation
    • Updated the component-routes CLI flag help text to clarify HCP-supported components and the required hostname + tlsSecretRef pair per route.
  • Bug Fixes
    • Improved HCP component-route validation to avoid failing solely due to a component count mismatch, while still accepting valid partial configurations.
  • Tests
    • Expanded ingress component-route parsing tests to confirm correct output when only selected routes are provided and that empty parameter values are serialized as empty strings.

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 20, 2026
@openshift-ci

openshift-ci Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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

@openshift-ci openshift-ci Bot added dco-signoff: yes needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jul 20, 2026
@openshift-ci

openshift-ci Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

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

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

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Ingress component-route parsing now accepts partial route sets instead of requiring every allowed route. The CLI help text documents HCP route constraints and the required hostname and tlsSecretRef values. Tests verify successful partial parsing for HCP console and classic oauth and downloads routes, including sparse serialization and empty values.

Possibly related issues

  • openshift/rosa#3405 — Concerns partial component-route updates and omitted-route or empty-value behavior.

Suggested reviewers: dustman9000, braetroutman

🚥 Pre-merge checks | ✅ 15
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, properly formatted, and clearly summarizes the main change: allowing partial component route updates.
Description check ✅ Passed The description follows the template well and covers the issue, change, testing, related PRs, and verification details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All modified Ginkgo titles are static strings; none include dynamic names, timestamps, IDs, or other run-specific data.
Test Structure And Quality ✅ Passed The added Ginkgo tests are narrow parser/serialization checks; no cluster resources, waits, or cleanup are involved, and each It targets one behavior.
Microshift Test Compatibility ✅ Passed Only unit/parser tests were added; they use SDK builders/marshal, not MicroShift-unsupported OpenShift APIs or assumptions.
Single Node Openshift (Sno) Test Compatibility ✅ Passed Only parser/unit tests changed in cmd/edit/ingress; no new Ginkgo e2e tests or SNO-specific node assumptions were added.
Topology-Aware Scheduling Compatibility ✅ Passed Only CLI parsing/help text and parser tests changed; no manifests, controllers, affinity, selectors, or replica logic were added.
Ote Binary Stdout Contract ✅ Passed No process-level stdout writes were added; the touched files only change parsing/tests, and suite bootstrap has RunSpecs only.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the changed tests are CLI parser/unit tests and contain no IPv4-only or external connectivity assumptions.
No-Weak-Crypto ✅ Passed Changed files only adjust ingress parsing/tests; no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret/token comparisons found.
Container-Privileges ✅ Passed PR only changes ingress flag parsing/tests; no container/K8s manifests were touched and no privileged settings appear in the diff.
No-Sensitive-Data-In-Logs ✅ Passed No new logs were added; changed code only adjusts parsing/tests and help text, with errors avoiding secret values.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@reedcort
reedcort force-pushed the ROSAENG-9174-allow-partial-component-routes branch from cdbcf23 to 7ce958b Compare July 20, 2026 22:00
@reedcort
reedcort marked this pull request as ready for review July 20, 2026 22:01
@openshift-ci openshift-ci Bot removed do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jul 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
cmd/edit/ingress/cmd.go (1)

62-69: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Keep the ingress error text consistent or update the e2e assertions
tests/e2e/test_rosacli_ingress.go still expects "Expected exactly one command line parameter containing the id of the ingress" in two places, so the lowercase change in cmd/edit/ingress/cmd.go will break those checks unless they’re updated too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/edit/ingress/cmd.go` around lines 62 - 69, Keep the validation error text
in the ingress command’s Args function consistent with the existing e2e
assertions, or update both assertions in test_rosacli_ingress.go to match the
new lowercase wording; ensure all occurrences use the same exact message.

Source: Learnings

🧹 Nitpick comments (1)
cmd/edit/ingress/flags.go (1)

96-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider mentioning partial-update support in flag help.

The updated help text lists available keys per cluster type but doesn't explicitly note that any subset of keys can be supplied and unspecified routes are left unchanged, which is the core behavior change of this PR.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/edit/ingress/flags.go` around lines 96 - 99, Update the component routes
flag help text in the flag definition near the ingress configuration to state
that any subset of supported route keys may be supplied and unspecified routes
remain unchanged. Preserve the existing key list and hostname/tlsSecretRef
format guidance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmd/edit/ingress/cmd.go`:
- Around line 62-69: Keep the validation error text in the ingress command’s
Args function consistent with the existing e2e assertions, or update both
assertions in test_rosacli_ingress.go to match the new lowercase wording; ensure
all occurrences use the same exact message.

---

Nitpick comments:
In `@cmd/edit/ingress/flags.go`:
- Around line 96-99: Update the component routes flag help text in the flag
definition near the ingress configuration to state that any subset of supported
route keys may be supplied and unspecified routes remain unchanged. Preserve the
existing key list and hostname/tlsSecretRef format guidance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 52dd1458-85cf-4d6f-8ce4-725f016b194a

📥 Commits

Reviewing files that changed from the base of the PR and between d2ebc87 and cdbcf23.

📒 Files selected for processing (3)
  • cmd/edit/ingress/cmd.go
  • cmd/edit/ingress/cmd_test.go
  • cmd/edit/ingress/flags.go

@amandahla

Copy link
Copy Markdown
Contributor

@reedcort thanks for submitting the PR.

How is expected to delete an existing route, for example, I added console + download and now I want only console. What would be the flow?

@coderabbitai pinging you in case this already exists in the code, check. Also check if we have other commands working in a similar way (partial updates/keeps things unchanged).

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Relevant ingress files ==='
fd -t f . cmd/edit/ingress | sort

echo '=== Ingress source outline ==='
ast-grep outline cmd/edit/ingress --items all 2>/dev/null || true

echo '=== Component-route symbols and request/update construction ==='
rg -n -C 5 --glob '*.go' \
  'parseComponentRoutes|component-routes|ComponentRoutes|componentRoutes|TlsSecretRef|Hostname|Ingress' \
  cmd/edit/ingress cmd 2>/dev/null || true

echo '=== Edit commands that explicitly clear/reset/remove fields ==='
rg -n -i -C 4 --glob '*.go' \
  '(clear|unset|remove|delete|reset|nil).*flag|flag.*(clear|unset|remove|delete|reset)|Set[A-Za-z0-9_]*\(\s*(nil|""|false|0)\s*\)' \
  cmd/edit 2>/dev/null || true

echo '=== Partial-update mechanisms and PATCH/update calls across edit commands ==='
rg -n -C 4 --glob '*.go' \
  '\.(Patch|Update|Edit)\(|New.*Patch|patch\.|Set[A-Za-z0-9_]+\(' \
  cmd/edit 2>/dev/null || true

echo '=== PR metadata and changed files ==='
gh pr view 3403 --repo openshift/rosa --json files,body,url 2>/dev/null || true

Length of output: 174516


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== OCM UpdateIngress wrapper and its tests ==='
rg -n -C 8 --glob '*.go' \
  'func \(.*\) UpdateIngress\b|UpdateIngress\(|ComponentRoutes\(' \
  pkg internal cmd 2>/dev/null || true

echo '=== Ingress command request tests (method, payload, and component routes) ==='
rg -n -C 10 --glob '*test.go' \
  'UpdateIngress|ComponentRoutes|component-routes|MethodPatch|MethodPost|MethodPut' \
  cmd/edit/ingress pkg 2>/dev/null || true

echo '=== OCM SDK version and locally cached ingress service implementation ==='
rg -n 'openshift-online/ocm-sdk-go' go.mod go.sum 2>/dev/null || true
sdk_dir="$(go env GOPATH 2>/dev/null)/pkg/mod/github.com/openshift-online"
if [ -d "$sdk_dir" ]; then
  fd -t f . "$sdk_dir" | rg 'clustersmgmt.*/v1/.+(ingress|component_route)|ingress.*\.go|component_route.*\.go' | head -80
fi

echo '=== Focused comparable partial-update command code ==='
for f in cmd/edit/service/cmd.go cmd/edit/cluster/cmd.go; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    ast-grep outline "$f" --items all 2>/dev/null || true
  fi
done
sed -n '80,120p' cmd/edit/service/cmd.go 2>/dev/null || true
sed -n '790,830p' cmd/edit/cluster/cmd.go 2>/dev/null || true

echo '=== Explicit command-level removal interfaces in edit commands ==='
rg -n -i -C 3 --glob '*.go' \
  '(remove-|delete-|clear-|unset-|disable-).*(flag|option)|flag.*(remove|delete|clear|unset|disable)' \
  cmd/edit 2>/dev/null || true

Length of output: 56753


@amandahla I checked the implementation. This PR supports partial updates only, not deletion:

rosa edit ingress -c <cluster> \
  --component-routes 'console: hostname=console.example.com;tlsSecretRef=console-tls' \
  <ingress-id>

With existing console + downloads, that updates console and intentionally leaves downloads unchanged. Omitting downloads is not a removal operation.

There is currently no --remove-component-route downloads-style option, nor a supported empty/null route representation—the parser requires both hostname and tlsSecretRef for each route, and empty input does not construct an update payload. So the flow “console + downloads → console only” is not available through rosa edit ingress in this PR.

This partial/unchanged behavior matches other edit flows, notably rosa edit service, which sends only explicitly changed parameters. Where ROSA needs an inverse action, it is generally explicit; for example, rosa edit cluster --enable-delete-protection=false.

So I’d recommend documenting this limitation or tracking route removal separately if the underlying OCM API supports a deletion representation.

@reedcort

reedcort commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@amandahla Assuming both console and download routes are set to remove a single route you just need to pass it with empty values.

rosa edit ingress -c <cluster> --component-routes 'downloads: hostname=;tlsSecretRef=' <ingress-id>

This clears downloads while leaving console unchanged.`

Comment thread cmd/edit/ingress/flags.go
@amandahla

Copy link
Copy Markdown
Contributor

@coderabbitai do we have a e2e test for component routes? Should we update something?

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Candidate test directories/files ==='
fd -t f -i '(e2e|integration|acceptance|functional|test)' . \
  | rg -i '(^|/)(e2e|integration|acceptance|functional|test)(/|_|\.|$)|ingress|route' \
  | sort \
  | head -300

echo '=== All component-route and ingress edit test references ==='
rg -n -i -C 5 --glob '*_test.go' --glob '*.feature' --glob '*.yaml' --glob '*.yml' \
  '(component-routes|component routes|ComponentRoutes|edit ingress|UpdateIngress|TlsSecretRef)' \
  . 2>/dev/null || true

echo '=== Test framework/dependency clues ==='
rg -n -i -C 3 --glob 'go.mod' --glob '*.go' --glob '*.feature' \
  '(ginkgo|gomega|e2e|integration|acceptance|cucumber|godog)' \
  . 2>/dev/null | head -400 || true

echo '=== Ingress command test layout and focused contents ==='
ast-grep outline cmd/edit/ingress --items all 2>/dev/null || true
sed -n '1,280p' cmd/edit/ingress/cmd_test.go 2>/dev/null || true

echo '=== Test-related CI/workflow entries ==='
rg -n -i -C 3 --glob '.github/workflows/*' --glob 'Makefile' --glob 'makefile' \
  '(e2e|integration|acceptance|functional)' \
  . 2>/dev/null || true

Length of output: 57742


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== E2E ingress test structure ==='
ast-grep outline tests/e2e/test_rosacli_ingress.go --items all 2>/dev/null || true

echo '=== E2E ingress test: default and edit contexts ==='
sed -n '1,540p' tests/e2e/test_rosacli_ingress.go

echo '=== CLI ingress helper definitions and use sites ==='
rg -n -C 5 --glob '*.go' \
  '(type .*Ingress|func \(.*\) (Edit|Describe|List|Create|Delete).*Ingress|EditIngress\(|DescribeIngress\()' \
  tests 2>/dev/null || true

echo '=== Component-routes references specifically in executable E2E tests ==='
rg -n -i -C 5 --glob '*.go' \
  '(component-routes|ComponentRoutes|TlsSecretRef)' \
  tests/e2e tests/utils/exec 2>/dev/null || true

echo '=== Ingress feature labels and E2E configuration guards ==='
rg -n -C 4 --glob '*.go' \
  '(Ingress|Hypershift|HCP|PrivateLink|STS)' \
  tests/ci/labels tests/e2e/e2e_pre_check.go tests/e2e/e2e_setup_test.go tests/utils/config 2>/dev/null || true

Length of output: 51932


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== E2E ingress reflected model and describe parsing ==='
sed -n '52,112p' tests/utils/exec/rosacli/ingress_service.go

echo '=== Canonical component-routes describe output and assertions ==='
ast-grep outline cmd/describe/ingress/cmd_test.go --items all 2>/dev/null || true
sed -n '15,145p' cmd/describe/ingress/cmd_test.go

echo '=== Other E2E helpers that parse nested YAML maps ==='
rg -n -C 6 --glob '*.go' \
  '(YamlToMap\(\)|map\[string\].*yaml|yaml:".*Routes|yaml:".*Config)' \
  tests/utils/exec tests/e2e 2>/dev/null | head -280 || true

Length of output: 32267


@amandahla Yes—there is an ingress E2E suite in tests/e2e/test_rosacli_ingress.go, but it currently has no coverage for --component-routes. The only coverage in this PR is parser-level in cmd/edit/ingress/cmd_test.go.

I recommend adding, at minimum, a command/request-level test in this PR that verifies a partial invocation—for example, only console—sends only the console entry in the ingress update payload. That directly protects the key behavior change: omitted routes must not be included or cleared by the CLI.

An E2E test would also be valuable, but needs a small amount of test infrastructure first:

  • Extend tests/utils/exec/rosacli/ingress_service.go’s Ingress model to parse the nested Component Routes data returned by rosa describe ingress.
  • Add an HCP ingress scenario that:
    1. records both existing console and downloads routes,
    2. updates only one route,
    3. verifies that route changed while the omitted route is unchanged,
    4. restores the original route.
  • Gate/skip it unless the E2E cluster has valid configurable component-route hostnames and TLS secret references. The current E2E profile/config does not provide those inputs.

So: no existing E2E test; add the sparse-payload test now, and add the E2E scenario when the test environment can safely supply and restore valid route values.

@amandahla

amandahla commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@amandahla Assuming both console and download routes are set to remove a single route you just need to pass it with empty values.

rosa edit ingress -c <cluster> --component-routes 'downloads: hostname=;tlsSecretRef=' <ingress-id>

This clears downloads while leaving console unchanged.`

@reedcort
Thanks for the clarification on partial updates and empty clears!

Could you paste a short before/after from a live cluster into the PR description (or a comment)? Something like rosa describe ingress (or the OCM/API equivalent you used) showing:

  1. Both console + downloads set → update only console → downloads unchanged
  2. Clear only one route with empty values → the other route unchanged

That would make the OCM merge behavior easy for reviewers to verify alongside the unit tests. Appreciate it!

@amandahla

Copy link
Copy Markdown
Contributor

@coderabbitai create an issue referencing this PR
2 main points: evaluate adding a new flag --remove-component-route and everage standard key-value map representations (--component-route key=value or JSON/YAML payloads

Details from Cursor:
UX follow-up (non-blocking for this PR): consider an explicit remove path later.
Clearing today requires knowing the internal key shape and empty-value convention:

rosa edit ingress -c <cluster> \
  --component-routes 'downloads: hostname=;tlsSecretRef=' <ingress-id>
That is hard to discover, easy to mistype, and fragile in shells because the DSL uses ; (command separator) unless the whole value is quoted.

A clearer follow-up would be something like:

rosa edit ingress <ingress-id> -c <cluster> \
  --component-route name=console,hostname=console.example.com,tlsSecretRef=console-tls
rosa edit ingress <ingress-id> -c <cluster> \
  --remove-component-route downloads

Why this is better UX than empty keys:
Intent is explicit (remove vs update), so omit ≠ delete stays unambiguous.
Users don’t need to remember hostname / tlsSecretRef just to clear a route.
Avoids shell breakage from unquoted ; / trailing = in the clear path.
Matches other ROSA edit patterns where inverse actions are explicit (e.g. boolean toggles), rather than “pass empty  values”.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="openshift/rosa"
pr_number="3403"
pr_url="https://github.com/${repo}/pull/${pr_number}"

comment_url="$(
  gh api --paginate "repos/${repo}/issues/${pr_number}/comments" \
    --jq '.[] | select(.user.login == "amandahla" and (.body | contains("create an issue referencing this PR"))) | .html_url' \
  | tail -n 1
)"

if [ -z "$comment_url" ]; then
  comment_url="${pr_url}`#issuecomment-unavailable`"
fi

body="$(cat <<EOF
## Summary

Follow up on the component-route UX introduced in #${pr_number}. Evaluate a clearer, explicit interface for updating and removing individual ingress component routes.

## Current behavior and problem

\`rosa edit ingress --component-routes\` supports partial updates: routes omitted from an invocation remain unchanged. Removing a route currently relies on supplying the existing DSL with empty values, for example:

\`\`\`bash
rosa edit ingress -c <cluster> \\
  --component-routes 'downloads: hostname=;tlsSecretRef=' <ingress-id>
\`\`\`

This is difficult to discover, requires users to know the internal route fields, and can be easy to mistype or misquote because the DSL uses semicolons.

## Requested evaluation

1. Evaluate an explicit removal option, for example:

   \`\`\`bash
   rosa edit ingress <ingress-id> -c <cluster> \\
     --remove-component-route downloads
   \`\`\`

2. Evaluate more standard component-route update representations, such as:
   - repeatable key-value input, for example \`--component-route name=console,hostname=console.example.com,tlsSecretRef=console-tls\`;
   - a JSON or YAML payload alternative, if consistent with existing ROSA CLI conventions.

The final interface should preserve the distinction between:
- omitting a route: leave it unchanged;
- updating a route: set the supplied values;
- removing a route: explicit deletion/clear operation.

## Affected areas

- \`rosa edit ingress\` command and its flag parsing under \`cmd/edit/ingress\`
- CLI help text and parser/request tests
- Any applicable ingress E2E coverage or documentation

## Acceptance criteria

- [ ] The removal flow is explicit and documented; it does not require users to provide empty \`hostname\` and \`tlsSecretRef\` values.
- [ ] Partial update semantics remain unchanged: routes not named by update or removal options are preserved.
- [ ] The chosen update input format is evaluated for consistency, discoverability, shell safety, validation, and backward compatibility.
- [ ] Unit/request tests cover updating one route, removing one route while preserving another, invalid route names, and duplicate/conflicting input.
- [ ] CLI help documents update versus removal behavior and examples.

## References

- Originating pull request: ${pr_url}
- Originating discussion: ${comment_url}
- Requested by: `@amandahla`
EOF
)"

gh issue create \
  --repo "$repo" \
  --title "Improve ingress component-route update and removal UX" \
  --body "$body" \
  --assignee "amandahla"

Length of output: 3150


@amandahla Created #3405: Improve ingress component-route update and removal UX.

It is assigned to you and includes the requested evaluation of:

  • An explicit --remove-component-route flow.
  • More standard update inputs, including repeatable key-value flags and JSON/YAML payloads.
  • Preservation of the distinction between omitted (unchanged), updated, and removed routes.
  • Required documentation and test coverage.

@amandahla

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain
🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Candidate test directories/files ==='
fd -t f -i '(e2e|integration|acceptance|functional|test)' . \
  | rg -i '(^|/)(e2e|integration|acceptance|functional|test)(/|_|\.|$)|ingress|route' \
  | sort \
  | head -300

echo '=== All component-route and ingress edit test references ==='
rg -n -i -C 5 --glob '*_test.go' --glob '*.feature' --glob '*.yaml' --glob '*.yml' \
  '(component-routes|component routes|ComponentRoutes|edit ingress|UpdateIngress|TlsSecretRef)' \
  . 2>/dev/null || true

echo '=== Test framework/dependency clues ==='
rg -n -i -C 3 --glob 'go.mod' --glob '*.go' --glob '*.feature' \
  '(ginkgo|gomega|e2e|integration|acceptance|cucumber|godog)' \
  . 2>/dev/null | head -400 || true

echo '=== Ingress command test layout and focused contents ==='
ast-grep outline cmd/edit/ingress --items all 2>/dev/null || true
sed -n '1,280p' cmd/edit/ingress/cmd_test.go 2>/dev/null || true

echo '=== Test-related CI/workflow entries ==='
rg -n -i -C 3 --glob '.github/workflows/*' --glob 'Makefile' --glob 'makefile' \
  '(e2e|integration|acceptance|functional)' \
  . 2>/dev/null || true

Length of output: 57742

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== E2E ingress test structure ==='
ast-grep outline tests/e2e/test_rosacli_ingress.go --items all 2>/dev/null || true

echo '=== E2E ingress test: default and edit contexts ==='
sed -n '1,540p' tests/e2e/test_rosacli_ingress.go

echo '=== CLI ingress helper definitions and use sites ==='
rg -n -C 5 --glob '*.go' \
  '(type .*Ingress|func \(.*\) (Edit|Describe|List|Create|Delete).*Ingress|EditIngress\(|DescribeIngress\()' \
  tests 2>/dev/null || true

echo '=== Component-routes references specifically in executable E2E tests ==='
rg -n -i -C 5 --glob '*.go' \
  '(component-routes|ComponentRoutes|TlsSecretRef)' \
  tests/e2e tests/utils/exec 2>/dev/null || true

echo '=== Ingress feature labels and E2E configuration guards ==='
rg -n -C 4 --glob '*.go' \
  '(Ingress|Hypershift|HCP|PrivateLink|STS)' \
  tests/ci/labels tests/e2e/e2e_pre_check.go tests/e2e/e2e_setup_test.go tests/utils/config 2>/dev/null || true

Length of output: 51932

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== E2E ingress reflected model and describe parsing ==='
sed -n '52,112p' tests/utils/exec/rosacli/ingress_service.go

echo '=== Canonical component-routes describe output and assertions ==='
ast-grep outline cmd/describe/ingress/cmd_test.go --items all 2>/dev/null || true
sed -n '15,145p' cmd/describe/ingress/cmd_test.go

echo '=== Other E2E helpers that parse nested YAML maps ==='
rg -n -C 6 --glob '*.go' \
  '(YamlToMap\(\)|map\[string\].*yaml|yaml:".*Routes|yaml:".*Config)' \
  tests/utils/exec tests/e2e 2>/dev/null | head -280 || true

Length of output: 32267

@amandahla Yes—there is an ingress E2E suite in tests/e2e/test_rosacli_ingress.go, but it currently has no coverage for --component-routes. The only coverage in this PR is parser-level in cmd/edit/ingress/cmd_test.go.

I recommend adding, at minimum, a command/request-level test in this PR that verifies a partial invocation—for example, only console—sends only the console entry in the ingress update payload. That directly protects the key behavior change: omitted routes must not be included or cleared by the CLI.

An E2E test would also be valuable, but needs a small amount of test infrastructure first:

  • Extend tests/utils/exec/rosacli/ingress_service.go’s Ingress model to parse the nested Component Routes data returned by rosa describe ingress.

  • Add an HCP ingress scenario that:

    1. records both existing console and downloads routes,
    2. updates only one route,
    3. verifies that route changed while the omitted route is unchanged,
    4. restores the original route.
  • Gate/skip it unless the E2E cluster has valid configurable component-route hostnames and TLS secret references. The current E2E profile/config does not provide those inputs.

So: no existing E2E test; add the sparse-payload test now, and add the E2E scenario when the test environment can safely supply and restore valid route values.

@jerichokeyne wdyt? should we add one?

@reedcort
reedcort force-pushed the ROSAENG-9174-allow-partial-component-routes branch from 7ce958b to 38aebc7 Compare July 21, 2026 14:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cmd/edit/ingress/cmd_test.go (1)

98-105: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Verify the values produced by sparse route parsing.

The new tests prove only that route keys are present, not that the builders contain the supplied values. Assert each returned route’s Hostname() and TlsSecretRef(); add a focused case for clearing a route with empty values.

  • cmd/edit/ingress/cmd_test.go#L98-L105: build and validate the partial console route.
  • cmd/edit/ingress/cmd_test.go#L119-L127: build and validate the partial oauth and downloads routes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/edit/ingress/cmd_test.go` around lines 98 - 105, Strengthen the sparse
route parsing tests in cmd/edit/ingress/cmd_test.go at lines 98-105 by asserting
the returned console route’s Hostname() and TlsSecretRef() match the supplied
values, and add a focused empty-values case that verifies clearing the route. At
lines 119-127, assert the partial oauth and downloads routes’ Hostname() and
TlsSecretRef() values as well.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cmd/edit/ingress/cmd_test.go`:
- Around line 98-105: Strengthen the sparse route parsing tests in
cmd/edit/ingress/cmd_test.go at lines 98-105 by asserting the returned console
route’s Hostname() and TlsSecretRef() match the supplied values, and add a
focused empty-values case that verifies clearing the route. At lines 119-127,
assert the partial oauth and downloads routes’ Hostname() and TlsSecretRef()
values as well.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 8282d7cd-0218-49b8-931d-5cc3a553d9b9

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce958b and 38aebc7.

📒 Files selected for processing (2)
  • cmd/edit/ingress/cmd_test.go
  • cmd/edit/ingress/flags.go

Comment thread cmd/edit/ingress/flags.go
@@ -107,13 +109,6 @@ func parseComponentRoutesForAllowed(input string, allowedRoutes []string) (map[s
result := map[string]*cmv1.ComponentRouteBuilder{}
input = strings.TrimSpace(input)
components := strings.Split(input, ",")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Now that componentRoutes can be sparse, this full-map DeepEqual no longer matches the CLI contract. A console-only update against an ingress that already has unchanged console plus existing downloads will still PATCH and report success, because the omitted key makes the maps unequal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed — added sparseComponentRoutesEqual to fix no-op detection for partial updates.

Verified on live cluster:

# Set console
$ rosa edit ingress --component-routes 'console: hostname=console.custom.example.com;tlsSecretRef=console-tls-secret' j3t8
INFO: Updated ingress 'j3t8'

# Same console again — no-op detected
$ rosa edit ingress --component-routes 'console: hostname=console.custom.example.com;tlsSecretRef=console-tls-secret' j3t8
WARN: No need to update ingress as there are no changes

# Different console value — update sent
$ rosa edit ingress --component-routes 'console: hostname=console2.custom.example.com;tlsSecretRef=console-tls-secret' j3t8
INFO: Updated ingress 'j3t8'

Comment thread cmd/edit/ingress/cmd_test.go
@olucasfreitas

Copy link
Copy Markdown
Contributor

The PR description now includes both cases this thread asked for: a console-only update that leaves downloads unchanged and a downloads clear with empty values that leaves console unchanged. That gives reviewers the backend-merge evidence needed for the behavior change.

@olucasfreitas

Copy link
Copy Markdown
Contributor

I wouldn't block this PR on a new live HCP E2E, but I would block on adding one focused command/runtime PATCH assertion now. The current diff only updates parser tests, so we still need a test that proves omitted routes are not serialized and empty values do serialize as a clear.

@reedcort
reedcort force-pushed the ROSAENG-9174-allow-partial-component-routes branch 2 times, most recently from 6f0413c to 400cf5f Compare July 21, 2026 18:30
@openshift-ci

openshift-ci Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@reedcort: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/security 400cf5f link false /test security

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@olucasfreitas

Copy link
Copy Markdown
Contributor

Now that componentRoutes can be sparse, this full-map DeepEqual no longer matches the CLI contract. A console-only update against an ingress that already has unchanged console plus existing downloads will still PATCH and report success, because the omitted key makes the maps unequal.

@olucasfreitas

Copy link
Copy Markdown
Contributor

Given the new serialization tests plus the live-cluster proof in the PR description, I don't think this PR needs a new HCP E2E before merge. A follow-up E2E is still worthwhile once tests/utils/exec/rosacli/ingress_service.go can parse Component Routes from rosa describe ingress.

Remove the count check in parseComponentRoutesForAllowed so users can
update a single component route without providing all routes. The OCM
API supports partial updates and requiring the full set was overly
restrictive.

Name validation and duplicate detection are preserved.

Signed-off-by: Cortney Reed <creed@redhat.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@reedcort
reedcort force-pushed the ROSAENG-9174-allow-partial-component-routes branch from 400cf5f to 6492237 Compare July 23, 2026 14:42
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