Skip to content

CORS-3897: Dual stack support for cluster manifest & VNet#10319

Open
jhixson74 wants to merge 4 commits intoopenshift:mainfrom
jhixson74:main-ipv6-azure
Open

CORS-3897: Dual stack support for cluster manifest & VNet#10319
jhixson74 wants to merge 4 commits intoopenshift:mainfrom
jhixson74:main-ipv6-azure

Conversation

@jhixson74
Copy link
Member

@jhixson74 jhixson74 commented Feb 17, 2026

Summary by CodeRabbit

  • New Features
    • Dual-stack (IPv4/IPv6) networking support for Azure cluster deployments.
    • Address-family aware subnet construction with mixed IPv4/IPv6 subnet generation.
    • New public network utilities to manage paired IPv4/IPv6 subnets and derive control-plane/compute CIDRs.
    • Improved automatic CIDR collection from install config to simplify network inputs.
    • Per-subnet inbound security rules for API server scenarios with incremental priorities.
    • Enhanced load balancer, NAT and IPv6 outbound handling for dual-stack and IPv4-only setups.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 17, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 17, 2026

@jhixson74: This pull request references CORS-3901 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot requested review from rna-afk and sadasu February 17, 2026 00:14
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 17, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign tthvo 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

@jhixson74 jhixson74 force-pushed the main-ipv6-azure branch 3 times, most recently from bc4a130 to 53acac8 Compare February 20, 2026 14:03
@jhixson74 jhixson74 changed the title CORS-3901: [WIP] Dual stack support for cluster manifest & VNet CORS-3901: Dual stack support for cluster manifest & VNet Feb 20, 2026
@jhixson74 jhixson74 force-pushed the main-ipv6-azure branch 3 times, most recently from 135e207 to fa580ef Compare February 20, 2026 14:51
@jhixson74 jhixson74 changed the title CORS-3901: Dual stack support for cluster manifest & VNet CORS-3897: Dual stack support for cluster manifest & VNet Feb 27, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 27, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

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 openshift-eng/jira-lifecycle-plugin repository.

@jhixson74 jhixson74 force-pushed the main-ipv6-azure branch 2 times, most recently from 37cc589 to 44ded41 Compare February 27, 2026 03:16
@jhixson74
Copy link
Member Author

/retest-required

PrivateIPAddress: lbip,
PrivateIPAddress: lbip4,
},
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move virtualNetworkID from ln 229 to here? It is updated in the following block of code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not following. Can you be more specific?

@jhixson74 jhixson74 force-pushed the main-ipv6-azure branch 5 times, most recently from a880127 to e6b588b Compare March 5, 2026 03:36
@jhixson74 jhixson74 requested review from rna-afk and sadasu March 5, 2026 03:37
Copy link
Contributor

@patrickdillon patrickdillon left a comment

Choose a reason for hiding this comment

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

A few things I noticed when reviewing:

  • aso crds are included, but can be removed
  • this doesn't make use of the IPFamilies field that was introduced, which isn't strictly necessary in this pr, but perhaps there are some places where it would be handy?

}

// DeepCopyInto deep copies an AddressFamilySubnet struct.
func (in *AddressFamilySubnet) DeepCopyInto(out *AddressFamilySubnet) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't look right. I think the DeepCopyInto function should be in a zz generated file?

Copy link
Member Author

Choose a reason for hiding this comment

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

The generated file does not generate the correct code. This is why I've manually done this here.

// If we are using Internal publishing, we need a security rule for each CIDR
var securityRules []capz.SecurityRule
var securityRulePriority int32 = 100
if addressFamilySubnets.IPv4Count() > 0 && installConfig.Config.Publish == types.InternalPublishingStrategy {
Copy link
Contributor

Choose a reason for hiding this comment

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

you want to use the install config's PublicAPI() function here to account for the MixedPublishing strategy use case

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do.

Copy link
Member Author

@jhixson74 jhixson74 Mar 9, 2026

Choose a reason for hiding this comment

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

So, looking at the existing code, we aren't accounting for the MixedPublishing strategy, which is what this code is based on.

source := "*" if installConfig.Config.Publish == types.InternalPublishingStrategy { source = mainCIDR }

Since we aren't currently handling it, does that mean this is a bug?

return nil, err
// IPv6 outbound load balancer for worker subnet(s)
var nodeOutboundLB *capz.LoadBalancerSpec
if addressFamilySubnets.IsDualStack() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you provide more context for why we need this outbound lb for ipv6? Can we not use the same pattern we've been using for single stack ipv4?

Copy link
Member Author

Choose a reason for hiding this comment

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

We are currently using a NAT gateway for the default outbound networking for the worker subnet. v1 NAT gatways do not support IPv6 addresses. So, we need a way for IPv6 to get outbound, that is what this is for. This isn't necessary with v2 NAT gateways. However, CAPZ does not currently support v2 NAT gateways.

@jhixson74
Copy link
Member Author

A few things I noticed when reviewing:

* aso crds are included, but can be removed

I don't understand what this means. You want this PR to remove ASO CRD's somewhere?

* this doesn't make use of the `IPFamilies` field that was introduced, which isn't strictly necessary in this pr, but perhaps there are some places where it would be handy?

Where would you like it to be used?

@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

Walkthrough

Replaces flat CIDR handling with address‑family (IPv4/IPv6) aware subnets via new AddressFamilySubnets, updates cluster manifest generation to emit mixed-stack subnet specs and per‑CIDR security rules, revises NAT/LB provisioning for dual‑stack, and adds helpers to collect MachineNetwork CIDRs and VNet prefixes.

Changes

Cohort / File(s) Summary
Azure Network Types
pkg/types/azure/network.go
Adds AddressFamilySubnet and AddressFamilySubnets types and API: parsing CIDRs into IPv4/IPv6 pairs, subnet-role assignment, compute-subnet splitting, and numerous accessors for CIDR blocks and subnet groups.
Azure Cluster Manifest
pkg/asset/manifests/azure/cluster.go
Switches manifest generation to use AddressFamilySubnets: derive AF subnets from CIDRs, compute virtual network address prefixes, emit mixed IPv4/IPv6 subnet_NN specs, generate per‑CIDR security rules with incremental priorities, update NAT gateway / LB provisioning for dual‑stack, and change getSubnetSpec signature to accept AF subnets.
CAPI Helpers
pkg/asset/manifests/capiutils/helpers.go
Adds CIDRsFromInstallConfig(installConfig *installconfig.InstallConfig) []ipnet.IPNet to collect MachineNetwork CIDRs, falling back to a default IPv4 CIDR when none provided.
Public helper
pkg/asset/manifests/azure/cluster.go (new function)
Adds getVirtualNetworkAddressPrefixes(cidrs []ipnet.IPNet) []string and integrates it into VNet/address-prefix derivation.
Misc / Signatures
pkg/asset/manifests/azure/cluster.go
Updates error messages and control flow to reflect address‑family workflow; replaces direct calls to IPv4-only split helpers with AF-aware methods.
Build / Manifests
go.mod, manifest metadata files
Module/manifest metadata updated to account for new types and imports.

Sequence Diagram(s)

sequenceDiagram
  participant InstallConfig
  participant AFParser as AddressFamilySubnets
  participant ManifestGen as ClusterManifestGenerator
  participant AzureVNet as AzureVNet
  participant AzureNSG as AzureNSG
  participant AzureNAT as AzureNATGateway
  participant AzureLB as AzureLoadBalancer

  InstallConfig->>AFParser: Provide MachineNetwork CIDRs (CIDRsFromInstallConfig)
  AFParser-->>InstallConfig: Return AddressFamilySubnets (paired IPv4/IPv6, roles)
  InstallConfig->>ManifestGen: Provide install config + AF subnets
  ManifestGen->>AzureVNet: getVirtualNetworkAddressPrefixes -> Create VNet with IPv4/IPv6 prefixes
  ManifestGen->>AzureVNet: Create subnets (subnet_01, subnet_02...) from AF subnets
  ManifestGen->>AzureNSG: Generate per‑CIDR security rules (apiserver_in/ssh_in) with incremental priorities
  ManifestGen->>AzureNAT: Decide/attach NAT gateway(s) per-address-family and per-subnet as needed
  ManifestGen->>AzureLB: Create API server LB frontends (IPv4 and optional IPv6) and outbound LBs for IPv6 outbound when required
  AzureLB-->>ManifestGen: Return frontend IP assignments
  AzureNAT-->>ManifestGen: Confirm NAT gateway assignments
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ❓ Inconclusive No Ginkgo test files were found to be modified or added in this PR; changes appear limited to implementation files in azure, capiutils, and network packages. Clarify whether test files were modified as part of this PR and provide paths to test files requiring review against Ginkgo quality standards.
✅ 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 'CORS-3897: Dual stack support for cluster manifest & VNet' directly and specifically describes the main objective of the changeset—adding dual-stack (IPv4/IPv6) support for Azure cluster manifests and virtual network configuration.
Stable And Deterministic Test Names ✅ Passed PR modifies only production code files with no new Ginkgo tests containing unstable or non-deterministic test names.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


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

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 9, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary by CodeRabbit

  • New Features
  • Dual-stack IPv4/IPv6 networking support for Azure cluster deployments
  • Build system enhancements enabling configurable parallelism for faster compilation
  • Optimized artifact updates reducing unnecessary rebuild time

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 openshift-eng/jira-lifecycle-plugin repository.

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

🧹 Nitpick comments (5)
hack/build.sh (1)

30-36: Consider separating build parallelization changes.

Per the PR discussion, the reviewer (patrickdillon) suggested that build-related changes should be handled separately from the dual-stack functional changes. The author acknowledged these changes "slipped into the PR."

The changes themselves are functionally reasonable for parallelization, but may benefit from being in a dedicated PR for cleaner review and change tracking.

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

In `@hack/build.sh` around lines 30 - 36, The build parallelization changes
(export GOBUILDFLAGS, export MAKEFLAGS and the make ${MAKEFLAGS} -C cluster-api
all invocation) were introduced alongside dual-stack functional changes and
should be moved into a separate, focused commit/PR; revert or remove the
parallelization exports and the modified make invocation from this branch
(restore prior behavior), then create a new branch/PR that adds
GOBUILDFLAGS/MAKEFLAGS exports and updates the make call (make ${MAKEFLAGS} -C
cluster-api all) with a clear description of the parallelization intent so
reviewers can evaluate that change independently of the dual-stack code.
hack/build-cluster-api.sh (2)

12-12: Clarify intent of -j1 flag.

The flag -j1 combines -j (junk directory names) with -1 (fastest compression). If the intent is just to junk paths without specifying compression level, use -j alone. If fastest compression is desired, this is fine but could be clearer as separate flags -j -1 for readability.

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

In `@hack/build-cluster-api.sh` at line 12, The ZIPFLAGS value currently set as
ZIPFLAGS="-j1" conflates the -j (junk paths) and -1 (fastest compression) flags;
update ZIPFLAGS to make intent explicit by either using ZIPFLAGS="-j" if you
only want to junk paths, or ZIPFLAGS="-j -1" (or ZIPFLAGS="-j" and ZIPLEVEL="-1"
if you prefer separate variables) if you intend both junking paths and fastest
compression, and update any code that consumes ZIPFLAGS accordingly.

33-36: Fix inconsistent indentation.

Line 35 uses 8-space indentation while the rest of the file uses 2-space indentation.

🧹 Proposed fix
   # Nothing to do
   if [ "${RET}" = "12" ]; then
-          return 0
+    return 0
   fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/build-cluster-api.sh` around lines 33 - 36, Fix the inconsistent
indentation in the conditional block that checks the RET variable: align the
indentation for the "return 0" line to use 2 spaces like the rest of the script
(the block starting with if [ "${RET}" = "12" ]; then and containing return 0)
so all lines in that conditional use consistent 2-space indentation.
pkg/types/azure/network.go (1)

169-347: LGTM with optional refactoring note.

The accessor methods are consistent and correct. There's noticeable duplication between IPv4 and IPv6 variants (e.g., GetIPv4ComputeSubnets vs GetIPv6ComputeSubnets), which could potentially be consolidated using generics or a helper function, but this is acceptable for clarity and type safety.

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

In `@pkg/types/azure/network.go` around lines 169 - 347, The subnet accessor
methods duplicate IPv4/IPv6 logic; refactor by extracting a small helper used by
GetIPv4ComputeSubnets/GetIPv6ComputeSubnets,
GetIPv4AdditionalSubnets/GetIPv6AdditionalSubnets, and the CIDR getters to
reduce repetition: create a helper (e.g., collectIPNets or collectCIDRs) that
accepts a slice of AddressFamilySubnet and a selector/flag to return either
[]*net.IPNet or []string, then call that helper from the named methods
(GetComputeSubnets, GetAdditionalSubnets, GetIPv4ComputeCIDRBlocks,
GetIPv6ComputeCIDRBlocks, etc.) to centralize the filtering and nil checks.
pkg/asset/manifests/azure/cluster.go (1)

46-135: LGTM with minor inconsistency note.

The security rule generation correctly handles both default (public) and per-CIDR (internal) publishing strategies. The priority management is sound.

Minor: The code mixes ptr.To (k8s.io/utils/ptr, line 52) and to.Ptr (Azure SDK, line 82) for creating pointers. Consider standardizing on one for consistency.

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

In `@pkg/asset/manifests/azure/cluster.go` around lines 46 - 135, The code mixes
two pointer helpers (ptr.To and to.Ptr) causing inconsistency; standardize to
the k8s helper used in defaultSecurityRules by replacing to.Ptr(...) with
ptr.To(...) in the per-CIDR security rule construction (the blocks that append
to securityRules for IPv4/IPv6 where Name is formatted like
"apiserver_in_ipv4_%02d" / "ssh_in_ipv6_%02d"), and then update imports
accordingly (remove or adjust the azure/sdk to alias if it becomes unused) so
all SecurityRule Source/Destination/Ports use ptr.To.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hack/build.sh`:
- Around line 23-28: The regex used to parse MAKEFLAGS into MAKEJOBS in
hack/build.sh is incorrect: change the sed pattern to accept any digits and
optional space after -j, e.g., update the capture from [1-9]+ to [0-9]+ and from
\s+? to \s*; also remove the extra quotes around ${MAKEFLAGS} in the echo and
quote ${MAKEJOBS} in the numeric test to safely check its value before appending
to GOBUILDFLAGS (refer to MAKEFLAGS, MAKEJOBS, and GOBUILDFLAGS in the script).

In `@pkg/asset/manifests/azure/cluster.go`:
- Around line 680-692: The getVirtualNetworkAddressPrefixes function currently
appends hard-coded "10.1.0.0/16" and "10.2.0.0/16" which is undocumented and can
overlap user CIDRs; remove these magic CIDRs from
getVirtualNetworkAddressPrefixes and instead (a) if they are required by CAPZ
behavior, surface them as configurable constants or fields (e.g.,
DefaultNodeSubnet/AdditionalVNetPrefixes) with clear comments and/or config
flags, (b) add validation in the same function to detect and deduplicate
overlaps against the provided cidrs slice (return an error or log and skip
conflicting prefixes), and (c) update documentation/comments to explain the
source/purpose if kept. Ensure references to getVirtualNetworkAddressPrefixes
and any new constant/field names are used so reviewers can find the change.

In `@pkg/types/azure/network.go`:
- Around line 156-167: The counters are incremented by the full number of new
split subnets but the original compute subnet is removed, so adjust the
increments to add (N - 1) instead of N: in the method that performs the
replacement (the block that updates a.addressFamilySubnets and then updates
a.length, a.ipv4Count, a.ipv6Count), change the increments to subtract 1 from
the added counts (e.g., use len(ipv4ComputeSubnets) - 1 and ipv4Count - 1 /
ipv6Count - 1 as appropriate) so the removed original subnet is not
double-counted.
- Around line 356-363: The loop that classifies cidrs into ipv4Cidrs and
ipv6Cidrs uses len(cidr.IP) which is unreliable; change the classification to
use cidr.IP.To4() != nil for IPv4 detection and append to ipv4Cidrs, otherwise
append to ipv6Cidrs (replace the switch over net.IPv4len/net.IPv6len); reference
the variables ipv4Cidrs, ipv6Cidrs and the cidr.IP.To4() method when making the
change.

---

Nitpick comments:
In `@hack/build-cluster-api.sh`:
- Line 12: The ZIPFLAGS value currently set as ZIPFLAGS="-j1" conflates the -j
(junk paths) and -1 (fastest compression) flags; update ZIPFLAGS to make intent
explicit by either using ZIPFLAGS="-j" if you only want to junk paths, or
ZIPFLAGS="-j -1" (or ZIPFLAGS="-j" and ZIPLEVEL="-1" if you prefer separate
variables) if you intend both junking paths and fastest compression, and update
any code that consumes ZIPFLAGS accordingly.
- Around line 33-36: Fix the inconsistent indentation in the conditional block
that checks the RET variable: align the indentation for the "return 0" line to
use 2 spaces like the rest of the script (the block starting with if [ "${RET}"
= "12" ]; then and containing return 0) so all lines in that conditional use
consistent 2-space indentation.

In `@hack/build.sh`:
- Around line 30-36: The build parallelization changes (export GOBUILDFLAGS,
export MAKEFLAGS and the make ${MAKEFLAGS} -C cluster-api all invocation) were
introduced alongside dual-stack functional changes and should be moved into a
separate, focused commit/PR; revert or remove the parallelization exports and
the modified make invocation from this branch (restore prior behavior), then
create a new branch/PR that adds GOBUILDFLAGS/MAKEFLAGS exports and updates the
make call (make ${MAKEFLAGS} -C cluster-api all) with a clear description of the
parallelization intent so reviewers can evaluate that change independently of
the dual-stack code.

In `@pkg/asset/manifests/azure/cluster.go`:
- Around line 46-135: The code mixes two pointer helpers (ptr.To and to.Ptr)
causing inconsistency; standardize to the k8s helper used in
defaultSecurityRules by replacing to.Ptr(...) with ptr.To(...) in the per-CIDR
security rule construction (the blocks that append to securityRules for
IPv4/IPv6 where Name is formatted like "apiserver_in_ipv4_%02d" /
"ssh_in_ipv6_%02d"), and then update imports accordingly (remove or adjust the
azure/sdk to alias if it becomes unused) so all SecurityRule
Source/Destination/Ports use ptr.To.

In `@pkg/types/azure/network.go`:
- Around line 169-347: The subnet accessor methods duplicate IPv4/IPv6 logic;
refactor by extracting a small helper used by
GetIPv4ComputeSubnets/GetIPv6ComputeSubnets,
GetIPv4AdditionalSubnets/GetIPv6AdditionalSubnets, and the CIDR getters to
reduce repetition: create a helper (e.g., collectIPNets or collectCIDRs) that
accepts a slice of AddressFamilySubnet and a selector/flag to return either
[]*net.IPNet or []string, then call that helper from the named methods
(GetComputeSubnets, GetAdditionalSubnets, GetIPv4ComputeCIDRBlocks,
GetIPv6ComputeCIDRBlocks, etc.) to centralize the filtering and nil checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4fe704f-85ec-4c68-943d-30deca68aa7c

📥 Commits

Reviewing files that changed from the base of the PR and between 4006847 and c7da64a.

⛔ Files ignored due to path filters (1)
  • pkg/types/azure/zz_generated.deepcopy.go is excluded by !**/zz_generated*
📒 Files selected for processing (7)
  • cluster-api/Makefile
  • data/data/cluster-api/azureaso-infrastructure-components.yaml
  • hack/build-cluster-api.sh
  • hack/build.sh
  • pkg/asset/manifests/azure/cluster.go
  • pkg/asset/manifests/capiutils/helpers.go
  • pkg/types/azure/network.go

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 10, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary by CodeRabbit

  • New Features
  • Added dual-stack (IPv4/IPv6) networking support for Azure cluster deployments.
  • Introduced address-family aware subnet construction and management for flexible network configuration.
  • Enhanced API server load balancer with improved IPv4 address handling and dual-stack awareness.
  • Added per-CIDR inbound security rules for internal API server configurations.
  • Enabled dynamic subnet generation supporting mixed IPv4/IPv6 deployments.

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 openshift-eng/jira-lifecycle-plugin repository.

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

🧹 Nitpick comments (1)
pkg/types/azure/network.go (1)

39-43: Variable shadowing reduces readability.

Lines 40-42 shadow the receiver parameter names in and out with local variables. While this is a common Go deep-copy pattern, it can be confusing. Consider using distinct names like srcRole and dstRole.

♻️ Suggested refactor
 	if in.SubnetRole != nil {
-		in, out := &in.SubnetRole, &out.SubnetRole
-		*out = new(capz.SubnetRole)
-		**out = **in
+		out.SubnetRole = new(capz.SubnetRole)
+		*out.SubnetRole = *in.SubnetRole
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/types/azure/network.go` around lines 39 - 43, The local variables in the
deep-copy block inside the DeepCopyInto (or similar) method shadow the receiver
params `in` and `out`; rename the locals to avoid shadowing (e.g., use `srcRole`
and `dstRole`) and update the assignments accordingly so the logic still
allocates a new capz.SubnetRole and copies from the source to destination
(replace the `in, out := &in.SubnetRole, &out.SubnetRole` pattern and the
subsequent `*out = *in` with the new names to improve readability).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/asset/manifests/azure/cluster.go`:
- Around line 408-413: The code initializes controlPlaneCIDRBlocks with
ipv4ControlPlaneCIDRBlocks even when it's an empty string, producing
[]string{""}; change the logic in the block that builds controlPlaneCIDRBlocks
(using addressFamilySubnets.GetIPv4ControlPlaneCIDRBlocks() and
GetIPv6ControlPlaneCIDRBlocks()) to start with an empty slice and append
ipv4ControlPlaneCIDRBlocks and ipv6ControlPlaneCIDRBlocks only if each string is
non-empty (i.e., check != ""), ensuring no empty-string elements are added to
controlPlaneCIDRBlocks.

---

Nitpick comments:
In `@pkg/types/azure/network.go`:
- Around line 39-43: The local variables in the deep-copy block inside the
DeepCopyInto (or similar) method shadow the receiver params `in` and `out`;
rename the locals to avoid shadowing (e.g., use `srcRole` and `dstRole`) and
update the assignments accordingly so the logic still allocates a new
capz.SubnetRole and copies from the source to destination (replace the `in, out
:= &in.SubnetRole, &out.SubnetRole` pattern and the subsequent `*out = *in` with
the new names to improve readability).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8c264781-8575-4946-a276-bbd93be09873

📥 Commits

Reviewing files that changed from the base of the PR and between c7da64a and 11e4b6f.

⛔ Files ignored due to path filters (1)
  • pkg/types/azure/zz_generated.deepcopy.go is excluded by !**/zz_generated*
📒 Files selected for processing (4)
  • data/data/cluster-api/azureaso-infrastructure-components.yaml
  • pkg/asset/manifests/azure/cluster.go
  • pkg/asset/manifests/capiutils/helpers.go
  • pkg/types/azure/network.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/asset/manifests/capiutils/helpers.go

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 10, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary by CodeRabbit

  • New Features
  • Added dual-stack (IPv4/IPv6) networking support for Azure cluster deployments.
  • Introduced address-family aware subnet construction and mixed IPv4/IPv6 subnet generation.
  • Enhanced API server load balancer and NAT gateway handling for dual-stack and IPv4-only paths.
  • Added per-CIDR inbound security rules for internal API server configurations.
  • Added automatic CIDR collection from install configuration to simplify network inputs.

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 10, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary by CodeRabbit

  • New Features
  • Added dual-stack (IPv4/IPv6) networking support for Azure cluster deployments.
  • Address-family aware subnet construction with mixed IPv4/IPv6 subnet generation.
  • Improved automatic CIDR collection from install config to simplify network inputs.
  • Per-CIDR inbound security rules for internal API server scenarios with incremental priorities.
  • Enhanced load balancer and NAT handling for dual-stack and IPv4-only deployments.

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 openshift-eng/jira-lifecycle-plugin repository.

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

♻️ Duplicate comments (1)
pkg/asset/manifests/azure/cluster.go (1)

71-133: ⚠️ Potential issue | 🟠 Major

Use API visibility, not Config.Publish, to decide whether to restrict ingress.

Line 74 and Line 103 still key off installConfig.Config.Publish == types.InternalPublishingStrategy. A MixedPublishing install with an internal API will miss these per-CIDR rules and fall through to the Source: "*" defaults on Lines 132-133, which is broader than intended.

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

In `@pkg/asset/manifests/azure/cluster.go` around lines 71 - 133, The code
currently gates per-CIDR security rules on installConfig.Config.Publish ==
types.InternalPublishingStrategy (seen in the IPv4 and IPv6 blocks); change
those conditions to check the API publishing/visibility setting instead (i.e.,
whether the API is configured as internal) so MixedPublishing with an internal
API still gets per-CIDR rules. Update the two checks that reference
installConfig.Config.Publish to use the API visibility/publishing config (or the
installer helper that determines "is API internal") and leave the rest of the
logic that builds securityRules, securityRulePriority, addressFamilySubnets, and
defaultSecurityRules unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/asset/manifests/azure/cluster.go`:
- Around line 219-229: The IPv6 node outbound LB is being created
unconditionally for dual-stack clusters; update the logic so nodeOutboundLB is
not created when installConfig.Config.Platform.Azure.OutboundType equals
azure.UserDefinedRoutingOutboundType (same check used to set
controlPlaneOutboundLB to nil). In practice, wrap the
addressFamilySubnets.IsDualStack() nodeOutboundLB creation with the
outbound-type check (or add a guard returning nil when OutboundType ==
azure.UserDefinedRoutingOutboundType) so that nodeOutboundLB remains nil for
user-defined routing; reference
installConfig.Config.Platform.Azure.OutboundType,
azure.UserDefinedRoutingOutboundType, nodeOutboundLB, and
addressFamilySubnets.IsDualStack() when making the change.

---

Duplicate comments:
In `@pkg/asset/manifests/azure/cluster.go`:
- Around line 71-133: The code currently gates per-CIDR security rules on
installConfig.Config.Publish == types.InternalPublishingStrategy (seen in the
IPv4 and IPv6 blocks); change those conditions to check the API
publishing/visibility setting instead (i.e., whether the API is configured as
internal) so MixedPublishing with an internal API still gets per-CIDR rules.
Update the two checks that reference installConfig.Config.Publish to use the API
visibility/publishing config (or the installer helper that determines "is API
internal") and leave the rest of the logic that builds securityRules,
securityRulePriority, addressFamilySubnets, and defaultSecurityRules unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4b05b7a1-4eb8-4a0a-b784-1f4a710278ee

📥 Commits

Reviewing files that changed from the base of the PR and between 91dd9be and 465a5df.

⛔ Files ignored due to path filters (1)
  • pkg/types/azure/zz_generated.deepcopy.go is excluded by !**/zz_generated*
📒 Files selected for processing (3)
  • data/data/cluster-api/azureaso-infrastructure-components.yaml
  • pkg/asset/manifests/azure/cluster.go
  • pkg/asset/manifests/capiutils/helpers.go

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 10, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary by CodeRabbit

  • New Features
  • Dual-stack (IPv4/IPv6) networking support for Azure cluster deployments.
  • Address-family aware subnet construction with mixed IPv4/IPv6 subnet generation.
  • New public network utilities to manage paired IPv4/IPv6 subnets and derive control-plane/compute CIDRs.
  • Improved automatic CIDR collection from install config to simplify network inputs.
  • Per-CIDR inbound security rules for internal API server scenarios with incremental priorities.
  • Enhanced load balancer and NAT handling for dual-stack and IPv4-only deployments.

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 openshift-eng/jira-lifecycle-plugin repository.

@jhixson74
Copy link
Member Author

@coderabbitai resume

@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

✅ Actions performed

Reviews resumed.

@jhixson74
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 10, 2026

@jhixson74: This pull request references CORS-3897 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary by CodeRabbit

  • New Features
  • Dual-stack (IPv4/IPv6) networking support for Azure cluster deployments.
  • Address-family aware subnet construction with mixed IPv4/IPv6 subnet generation.
  • New public network utilities to manage paired IPv4/IPv6 subnets and derive control-plane/compute CIDRs.
  • Improved automatic CIDR collection from install config to simplify network inputs.
  • Per-subnet inbound security rules for API server scenarios with incremental priorities.
  • Enhanced load balancer, NAT and IPv6 outbound handling for dual-stack and IPv4-only setups.

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 openshift-eng/jira-lifecycle-plugin repository.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/asset/manifests/azure/cluster.go`:
- Around line 448-465: The loop that builds extra node subnets (building
capz.SubnetSpec entries in subnetSpec) omits assigning the SecurityGroup, so the
generated subnet_%02d entries lack the NSG; update the code that constructs spec
(the capz.SubnetSpec with SubnetClassSpec{Name: fmt.Sprintf("subnet_%02d", i),
Role: capz.SubnetNode}) to set spec.SecurityGroup = securityGroup (or the
appropriate securityGroup variable used elsewhere) before appending to
subnetSpec so these autogenerated extra node subnets inherit the same NSG as the
other installer-generated subnets.
- Around line 70-134: The per-CIDR NSG rule blocks are currently gated by
installConfig.Config.PublicAPI(), which is inverted: change both guards (the
IPv4 block using addressFamilySubnets.IPv4Count() &&
installConfig.Config.PublicAPI() and the IPv6 block using
addressFamilySubnets.IPv6Count() && installConfig.Config.PublicAPI()) to run
when the API is NOT public (use !installConfig.Config.PublicAPI() or
installConfig.Config.PublicAPI() == false) so that the restrictive per-CIDR
capz.SecurityRule entries (apiserver_in_ipv4_*, ssh_in_ipv4_*,
apiserver_in_ipv6_*, ssh_in_ipv6_*) are created for internal publishing and
public/mixed clusters fall back to defaultSecurityRules.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e217051a-4701-449d-8182-818c9d9d6b09

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba1cf4 and 6377401.

⛔ Files ignored due to path filters (1)
  • pkg/types/azure/zz_generated.deepcopy.go is excluded by !**/zz_generated*
📒 Files selected for processing (3)
  • data/data/cluster-api/azureaso-infrastructure-components.yaml
  • pkg/asset/manifests/azure/cluster.go
  • pkg/asset/manifests/capiutils/helpers.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/asset/manifests/capiutils/helpers.go

Comment on lines +70 to +134
// If we are using Internal publishing, we need a security rule for each CIDR
var securityRules []capz.SecurityRule
var securityRulePriority int32 = 100
//if addressFamilySubnets.IPv4Count() > 0 && installConfig.Config.Publish == types.InternalPublishingStrategy {
if addressFamilySubnets.IPv4Count() > 0 && installConfig.Config.PublicAPI() {
for i := 0; i < addressFamilySubnets.IPv4Count(); i++ {
securityRules = append(securityRules, capz.SecurityRule{
Name: fmt.Sprintf("apiserver_in_ipv4_%02d", i),
Protocol: capz.SecurityGroupProtocolTCP,
Direction: capz.SecurityRuleDirectionInbound,
SourcePorts: ptr.To("*"),
DestinationPorts: ptr.To("6443"),
Source: ptr.To(addressFamilySubnets.GetIPv4Subnets()[i].String()),
Destination: ptr.To("*"),
Priority: securityRulePriority,
Action: capz.SecurityRuleActionAllow,
})
securityRulePriority += 10

securityRules = append(securityRules, capz.SecurityRule{
Name: fmt.Sprintf("ssh_in_ipv4_%02d", i),
Protocol: capz.SecurityGroupProtocolTCP,
Direction: capz.SecurityRuleDirectionInbound,
SourcePorts: ptr.To("*"),
DestinationPorts: ptr.To("22"),
Source: ptr.To(addressFamilySubnets.GetIPv4Subnets()[i].String()),
Destination: ptr.To("*"),
Priority: securityRulePriority,
Action: capz.SecurityRuleActionAllow,
})
securityRulePriority += 10
}
}
if addressFamilySubnets.IPv6Count() > 0 && installConfig.Config.PublicAPI() {
for i := 0; i < addressFamilySubnets.IPv6Count(); i++ {
securityRules = append(securityRules, capz.SecurityRule{
Name: fmt.Sprintf("apiserver_in_ipv6_%02d", i),
Protocol: capz.SecurityGroupProtocolTCP,
Direction: capz.SecurityRuleDirectionInbound,
SourcePorts: ptr.To("*"),
DestinationPorts: ptr.To("6443"),
Source: ptr.To(addressFamilySubnets.GetIPv6Subnets()[i].String()),
Destination: ptr.To("*"),
Priority: securityRulePriority,
Action: capz.SecurityRuleActionAllow,
})
securityRulePriority += 10

securityRules = append(securityRules, capz.SecurityRule{
Name: fmt.Sprintf("ssh_in_ipv6_%02d", i),
Protocol: capz.SecurityGroupProtocolTCP,
Direction: capz.SecurityRuleDirectionInbound,
SourcePorts: ptr.To("*"),
DestinationPorts: ptr.To("22"),
Source: ptr.To(addressFamilySubnets.GetIPv6Subnets()[i].String()),
Destination: ptr.To("*"),
Priority: securityRulePriority,
Action: capz.SecurityRuleActionAllow,
})
securityRulePriority += 10
}
}
if len(securityRules) == 0 {
securityRules = append(securityRules, defaultSecurityRules...)
}
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

Invert the PublicAPI() guard on the CIDR-scoped NSG rules.

Lines 74 and 103 currently build the restrictive per-CIDR ingress rules when PublicAPI() is true, which flips the intended behavior: public/mixed clusters lose the * ingress rules, while internal clusters fall back to the open defaults below. That can break public API reachability and weakens the internal-publishing path.

🐛 Proposed fix
-	if addressFamilySubnets.IPv4Count() > 0 && installConfig.Config.PublicAPI() {
+	if addressFamilySubnets.IPv4Count() > 0 && !installConfig.Config.PublicAPI() {
 		for i := 0; i < addressFamilySubnets.IPv4Count(); i++ {
 			securityRules = append(securityRules, capz.SecurityRule{
 				Name:             fmt.Sprintf("apiserver_in_ipv4_%02d", i),
@@
-	if addressFamilySubnets.IPv6Count() > 0 && installConfig.Config.PublicAPI() {
+	if addressFamilySubnets.IPv6Count() > 0 && !installConfig.Config.PublicAPI() {
 		for i := 0; i < addressFamilySubnets.IPv6Count(); i++ {
 			securityRules = append(securityRules, capz.SecurityRule{
 				Name:             fmt.Sprintf("apiserver_in_ipv6_%02d", i),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/asset/manifests/azure/cluster.go` around lines 70 - 134, The per-CIDR NSG
rule blocks are currently gated by installConfig.Config.PublicAPI(), which is
inverted: change both guards (the IPv4 block using
addressFamilySubnets.IPv4Count() && installConfig.Config.PublicAPI() and the
IPv6 block using addressFamilySubnets.IPv6Count() &&
installConfig.Config.PublicAPI()) to run when the API is NOT public (use
!installConfig.Config.PublicAPI() or installConfig.Config.PublicAPI() == false)
so that the restrictive per-CIDR capz.SecurityRule entries (apiserver_in_ipv4_*,
ssh_in_ipv4_*, apiserver_in_ipv6_*, ssh_in_ipv6_*) are created for internal
publishing and public/mixed clusters fall back to defaultSecurityRules.

Comment on lines 448 to +465
subnetSpec := []capz.SubnetSpec{}
subnetsCount := max(len(ipv4Subnets), len(ipv6Subnets))
for i := 0; i < subnetsCount; i++ {
spec := capz.SubnetSpec{
SubnetClassSpec: capz.SubnetClassSpec{
Name: fmt.Sprintf("subnet_%02d", i),
Role: capz.SubnetNode,
},
}
var cidrBlocks []string
if i < len(ipv4Subnets) {
cidrBlocks = append(cidrBlocks, ipv4Subnets[i].String())
}
if i < len(ipv6Subnets) {
cidrBlocks = append(cidrBlocks, ipv6Subnets[i].String())
}
spec.CIDRBlocks = cidrBlocks
subnetSpec = append(subnetSpec, spec)
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

Attach the generated NSG to the autogenerated extra node subnets.

Line 451 creates subnet_%02d without SecurityGroup: securityGroup. Every other installer-generated subnet in this function carries the NSG, so this path emits manifests where the extra generated node subnets do not inherit the security rules assembled above.

🐛 Proposed fix
 		spec := capz.SubnetSpec{
 			SubnetClassSpec: capz.SubnetClassSpec{
 				Name: fmt.Sprintf("subnet_%02d", i),
 				Role: capz.SubnetNode,
 			},
+			SecurityGroup: securityGroup,
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/asset/manifests/azure/cluster.go` around lines 448 - 465, The loop that
builds extra node subnets (building capz.SubnetSpec entries in subnetSpec) omits
assigning the SecurityGroup, so the generated subnet_%02d entries lack the NSG;
update the code that constructs spec (the capz.SubnetSpec with
SubnetClassSpec{Name: fmt.Sprintf("subnet_%02d", i), Role: capz.SubnetNode}) to
set spec.SecurityGroup = securityGroup (or the appropriate securityGroup
variable used elsewhere) before appending to subnetSpec so these autogenerated
extra node subnets inherit the same NSG as the other installer-generated
subnets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants