api: add ingress visibility to ARM API and wire to Cluster Service#5643
api: add ingress visibility to ARM API and wire to Cluster Service#5643zgalor wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for configuring cluster Ingress visibility for HCP OpenShift clusters across internal types, validation, API version conversions, and OCM cluster build output.
Changes:
- Introduces
CustomerIngressProfile/IngressProfilewithvisibilityand defaulting behavior. - Adds validation for ingress visibility (required, enum, immutable on update) and updates unit tests.
- Plumbs ingress visibility through OCM conversion and updates OpenAPI/spec + example payloads.
Reviewed changes
Copilot reviewed 21 out of 24 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/validation/validate_cluster.go | Adds ingress validation hook and implements validateCustomerIngressProfile. |
| internal/validation/hcpopenshiftcluster_test.go | Extends required-field validation test coverage for ingress visibility. |
| internal/ocm/convert.go | Maps ingress visibility to OCM “Ingresses[].Listening”. |
| internal/ocm/convert_test.go | Updates expected OCM cluster builders to include default ingress listening. |
| internal/api/v20251223preview/hcpopenshiftclusters_methods.go | Defaults, (de)normalization, and versioned shape for ingress profile. |
| internal/api/v20240610preview/hcpopenshiftclusters_methods.go | Preserves new ingress field across older API version conversions. |
| internal/api/v20240610preview/conversion_fuzz_test.go | Adjusts fuzz round-trip behavior to account for new ingress field. |
| internal/api/types_cluster.go | Adds internal CustomerIngressProfile and defaults in constructors/EnsureDefaults. |
| api/.../preview/2025-12-23-preview/openapi.json | Adds IngressProfile definition + property, updates Visibility enum descriptions. |
| api/.../preview/2024-06-10-preview/openapi.json | Updates Visibility enum descriptions. |
| api/.../hcpCluster-models.tsp | Adds ingress to the TypeSpec model and refines Visibility doc text. |
| api/.../examples/**/2025-12-23-preview/*.json | Updates example payloads to include properties.ingress.visibility. |
| ingressListening, err := convertVisibilityToListening(hcpCluster.CustomerProperties.Ingress.Visibility) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| clusterBuilder.Ingresses(arohcpv1alpha1.NewIngressList().Items( | ||
| arohcpv1alpha1.NewIngress().Default(true).Listening(ingressListening), | ||
| )) |
| }, | ||
| "ingress": { | ||
| "$ref": "#/definitions/IngressProfile", | ||
| "description": "Shows the cluster ingress profile", |
There was a problem hiding this comment.
Agree with the suggestion.
0288fe3 to
f9d01c6
Compare
f9d01c6 to
d5a3a6c
Compare
| "ingress": { | ||
| "$ref": "#/definitions/IngressProfile", | ||
| "description": "Shows the cluster ingress profile", | ||
| "x-ms-mutability": [ | ||
| "read", | ||
| "create" | ||
| ] |
e80486f to
4acc332
Compare
| if obj.Properties.Ingress == nil { | ||
| obj.Properties.Ingress = &generated.IngressProfile{} | ||
| } | ||
| if obj.Properties.Ingress.Visibility == nil { | ||
| obj.Properties.Ingress.Visibility = ptr.To(generated.VisibilityPublic) | ||
| } |
There was a problem hiding this comment.
I'm less familiar with patching behavior than I used to be, but this is following the same pattern as APIProfile. I don't see a problem.
| }, | ||
| "ingress": { | ||
| "$ref": "#/definitions/IngressProfile", | ||
| "description": "Shows the cluster ingress profile", |
e0bde11 to
5bd837f
Compare
| "IngressProfile": { | ||
| "type": "object", | ||
| "description": "Information about the Ingress of a cluster.", | ||
| "properties": { | ||
| "visibility": { | ||
| "type": "string", | ||
| "description": "The internet visibility of the OpenShift Ingress.", | ||
| "default": "Public", | ||
| "enum": [ | ||
| "Public", | ||
| "Private" | ||
| ], | ||
| "x-ms-enum": { | ||
| "name": "Visibility", | ||
| "modelAsString": true, | ||
| "values": [ | ||
| { | ||
| "name": "Public", | ||
| "value": "Public", | ||
| "description": "The endpoint is visible from the internet." | ||
| }, | ||
| { | ||
| "name": "Private", | ||
| "value": "Private", | ||
| "description": "The endpoint is not visible from the internet." | ||
| } | ||
| ] | ||
| }, | ||
| "x-ms-mutability": [ | ||
| "read", | ||
| "create" | ||
| ] | ||
| } | ||
| } | ||
| }, |
|
Note: The |
| "visibility": "Public", | ||
| "url": "https://api.test.shrd.usw3test.hcp.osadev.cloud:443" | ||
| }, | ||
| "ingress": { |
There was a problem hiding this comment.
Does this control the default ingress visibility, or the visibility of all ingresses created over time? Depending on the answer, the naming can be confusing.
Are we always going to offer a default ingress, or is it possible or being discussed that that will be on end-users responsibility?
There was a problem hiding this comment.
This controls the default ingress visibility - the single default OpenShift ingress that's created with the cluster. It maps to CS's ingresses[0].default=true with the corresponding listening method (external/internal).
ARO HCP clusters have a single ingress configuration exposed through the ARM API. The field is immutable after creation. The naming follows the existing ApiProfile pattern (singular) since it represents a single cluster-level configuration, not a collection.
There was a problem hiding this comment.
Should we call it defaultIngress then?
There was a problem hiding this comment.
Isn't default ingress an OpenShift concept (https://github.com/openshift/cluster-ingress-operator#installing) ?
mbarnes
left a comment
There was a problem hiding this comment.
Two issues:
- This should be added to the
2026-06-30-previewversion, not2025-12-23-preview, although I realize that's not a fair ask at this moment since we haven't yet merged the scaffolding for the new version yet. - Either way, we'll need some new test cases for the new "public" ingress default in
/internal/database/convert_defaults_consistency_test.goand I think a couple of the integration tests. There's a DDR in-repo about default handling across API versions, with a detailed checklist for new fields.
| }, | ||
| "ingress": { | ||
| "$ref": "#/definitions/IngressProfile", | ||
| "description": "Shows the cluster ingress profile", |
There was a problem hiding this comment.
Agree with the suggestion.
| if obj.Properties.Ingress == nil { | ||
| obj.Properties.Ingress = &generated.IngressProfile{} | ||
| } | ||
| if obj.Properties.Ingress.Visibility == nil { | ||
| obj.Properties.Ingress.Visibility = ptr.To(generated.VisibilityPublic) | ||
| } |
There was a problem hiding this comment.
I'm less familiar with patching behavior than I used to be, but this is following the same pattern as APIProfile. I don't see a problem.
5bd837f to
68fcfbb
Compare
Add IngressProfile with a visibility field (Public/Private) to the v2025-12-23-preview ARM API, allowing customers to configure private ingress at cluster creation time. The field is immutable after creation and defaults to Public. The ingress visibility is converted to CS ingresses[0].listening (external/internal) and sent during cluster creation, reusing the existing convertVisibilityToListening mapping. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
68fcfbb to
cc01099
Compare
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: zgalor The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Regenerate v2024 and v2025 openapi.json to pick up the updated
Visibility description ("endpoint" instead of "API server").
Fix APIProfile field ordering in v2026 generated models to match
CI's autorest output.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Add v2026→v2024 and v2026→v2025 cross-version PUT and PATCH tests to verify that ingress visibility (a v2026-only field) is preserved when a cluster is updated via older API versions. Also add ingress to the v2026 cluster create payload with visibility=Private. Addresses DDR checklist item 6f from docs/api-version-defaults-and-storage.md. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 47 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- internal/api/zz_generated.deepcopy.go: Generated file
Comments suppressed due to low confidence (6)
test-integration/frontend/cross_version_roundtrip_test.go:1
- These 4 new integration tests each create a fresh v2026 cluster, which can significantly increase CI time and flakiness for the integration suite. Consider reducing cluster creates by combining PUT+PATCH assertions per version pair (create once, snapshot before, run both mutations sequentially with separate comparisons) or by reusing a single created cluster across subtests where isolation isn’t required.
test-integration/frontend/cross_version_roundtrip_test.go:1 - These 4 new integration tests each create a fresh v2026 cluster, which can significantly increase CI time and flakiness for the integration suite. Consider reducing cluster creates by combining PUT+PATCH assertions per version pair (create once, snapshot before, run both mutations sequentially with separate comparisons) or by reusing a single created cluster across subtests where isolation isn’t required.
test-integration/frontend/cross_version_roundtrip_test.go:1 - These 4 new integration tests each create a fresh v2026 cluster, which can significantly increase CI time and flakiness for the integration suite. Consider reducing cluster creates by combining PUT+PATCH assertions per version pair (create once, snapshot before, run both mutations sequentially with separate comparisons) or by reusing a single created cluster across subtests where isolation isn’t required.
test-integration/frontend/cross_version_roundtrip_test.go:1 - These 4 new integration tests each create a fresh v2026 cluster, which can significantly increase CI time and flakiness for the integration suite. Consider reducing cluster creates by combining PUT+PATCH assertions per version pair (create once, snapshot before, run both mutations sequentially with separate comparisons) or by reusing a single created cluster across subtests where isolation isn’t required.
test-integration/frontend/cross_version_roundtrip_test.go:1 - The new cross-version cluster tests are largely copy/paste (v2024 vs v2025, PUT vs PATCH). This repetition makes it easy for future version additions to drift (e.g., different logging, missed steps). Consider factoring the shared flow into a helper that takes (roundTripVersion, method PUT/PATCH, mutation body) and uses table-driven cases to generate the test permutations.
test-integration/frontend/cross_version_roundtrip_test.go:1 - The new cross-version cluster tests are largely copy/paste (v2024 vs v2025, PUT vs PATCH). This repetition makes it easy for future version additions to drift (e.g., different logging, missed steps). Consider factoring the shared flow into a helper that takes (roundTripVersion, method PUT/PATCH, mutation body) and uses table-driven cases to generate the test permutations.
| /** Shows the cluster ingress profile */ | ||
| @added(Versions.v2026_06_30_preview) | ||
| @visibility(Lifecycle.Read, Lifecycle.Create) | ||
| ingress?: IngressProfile; |
| /** Information about the Ingress of a cluster. */ | ||
| @added(Versions.v2026_06_30_preview) | ||
| model IngressProfile { | ||
| /** The internet visibility of the OpenShift Ingress. */ | ||
| @added(Versions.v2026_06_30_preview) | ||
| @visibility(Lifecycle.Read, Lifecycle.Create) | ||
| visibility?: Visibility = Visibility.Public; | ||
| } |
Change "Shows the cluster ingress profile" to "The cluster ingress configuration" since this is a configurable property (Read+Create), not a status field. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
@mbarnes, I addressed your comments. Moved the ned field to Please take another look |
|
For the ci/prow/api-validation error, it looks like you might just need to run Overall LGTM. I'll give formal approval once api-validation is fixed. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 47 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- internal/api/zz_generated.deepcopy.go: Generated file
Comments suppressed due to low confidence (1)
test-integration/frontend/cross_version_roundtrip_test.go:1
- These new cross-version cluster tests are highly repetitive (only the 'older version' and operation differ). Consider consolidating into a small table-driven helper (e.g., parameters: olderVersion, method PUT/PATCH, createVersion=v2026, expectedPreservedFields) to reduce duplication and make it easier to extend coverage for future API versions.
| if obj.Properties.API.Visibility == nil { | ||
| obj.Properties.API.Visibility = ptr.To(generated.VisibilityPublic) | ||
| } | ||
| if obj.Properties.Ingress == nil { | ||
| obj.Properties.Ingress = &generated.IngressProfile{} | ||
| } | ||
| if obj.Properties.Ingress.Visibility == nil { | ||
| obj.Properties.Ingress.Visibility = ptr.To(generated.VisibilityPublic) | ||
| } |
| func normalizeIngress(p *generated.IngressProfile, out *api.CustomerIngressProfile) { | ||
| out.Visibility = api.Visibility(api.Deref(p.Visibility)) | ||
| } |
| if obj.Properties.Ingress == nil { | ||
| obj.Properties.Ingress = &generated.IngressProfile{} | ||
| } |
Sync the comment in generated models.go with the TypeSpec description
change ("The cluster ingress configuration").
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
6938933 to
57891df
Compare
|
@zgalor: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions 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. |
ARO-27757
Summary
IngressProfilewithvisibilityfield (Public/Private) to thev2026-06-30-previewARM APIingresses[0].listening(external/internal) during cluster creationChanges
IngressProfilemodel with@added(Versions.v2026_06_30_preview), updatedVisibilityunion descriptionsCustomerIngressProfilestruct, defaults inNewDefaultHCPOpenShiftClusterandEnsureDefaultsnewIngressProfile/normalizeIngress, wired intoConvertToExternal/ConvertToInternal/SetDefaultspreserveUnknownClusterFieldsin both v20240610preview and v20251223previewvalidateCustomerIngressProfilewith required, immutable, and enum checksconvertVisibilityToListeningto buildIngresses(NewIngressList().Items(NewIngress().Default(true).Listening(...)))inBuildCSClusterTest plan
cd api && make generate— TypeSpec compiles, OpenAPI + Go models generate (v2026 only)cd api && make validate-examples— example validation passes (all versions)go test ./internal/ocm/...— CS conversion tests passgo test ./internal/api/...— all API version tests pass (v2024, v2025, v2026 including fuzz roundtrip)go test ./internal/validation/...— validation tests passgo test ./internal/database/...— database + defaults consistency tests passgo test ./frontend/...— frontend tests passgo test ./test-integration/frontend/...— integration tests passmake verify-deepcopy verify-json-format— verify checks passgo build ./internal/... ./frontend/... ./backend/...— full build succeeds🤖 Generated with Claude Code