1
0
mirror of https://github.com/openshift/installer.git synced 2026-02-05 06:46:36 +01:00

Merge pull request #9847 from danmanor/enable-dual-stack

MGMT-21212: Enable dual-stack clusters with image-based installations
This commit is contained in:
openshift-merge-bot[bot]
2025-08-13 11:23:00 +00:00
committed by GitHub
14 changed files with 411 additions and 11 deletions

View File

@@ -0,0 +1,39 @@
# Test dual IPv4 networks validation for image-based installation
# This test verifies that two IPv4 networks are properly supported
exec openshift-install image-based create config-image --dir $WORK
exists $WORK/imagebasedconfig.iso
isoContains imagebasedconfig.iso /cluster-configuration/manifest.json
-- install-config.yaml --
apiVersion: v1
baseDomain: test.metalkube.org
controlPlane:
name: master
replicas: 1
compute:
- name: worker
replicas: 0
metadata:
name: dual-ipv4-test
networking:
clusterNetwork:
- cidr: 10.128.0.0/14
hostPrefix: 23
networkType: OVNKubernetes
machineNetwork:
- cidr: 10.0.0.0/16
- cidr: 10.10.0.0/24
serviceNetwork:
- 172.30.0.0/16
platform:
none: {}
sshKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDK6UTEydcEKzuNdPaofn8Z2DwgHqdcionLZBiPf/zIRNco++etLsat7Avv7yt04DINQd5zjxIFgG8jblaUB5E5C9ClUcMwb52GO0ay2Y9v1uBv1a4WhI3peKktAzYNk0EBMQlJtXPjRMrC9ylBPh+DsBHMu+KmDnfk7PIwyN4efC8k5kSRuPWoNdme1rz2+umU8FSmaWTHIajrbspf4GQbsntA5kuKEtDbfoNCU97o2KrRnUbeg3a8hwSjfh3u6MhlnGcg5K2Ij+zivEsWGCLKYUtE1ErqwfIzwWmJ6jnV66XCQGHf4Q1iIxqF7s2a1q24cgG2Z/iDXfqXrCIfy4P7b/Ztak3bdT9jfAdVZtdO5/r7I+O5hYhF86ayFlDWzZWP/ByiSb+q4CQbfVgK3BMmiAv2MqLHdhesmD/SmIcoOWUF6rFmRKZVFFpKpt5ATNTgUJ3JRowoXrrDruVXClUGRiCS6Zabd1rZ3VmTchaPJwtzQMdfIWISXj+Ig+C4UK0=
pullSecret: '{"auths": {"quay.io": {"auth": "c3VwZXItc2VjcmV0Cg=="}}}'
-- image-based-config.yaml --
apiVersion: v1beta1
kind: ImageBasedConfig
metadata:
name: dual-ipv4-test
hostname: dual-ipv4-test

View File

@@ -0,0 +1,40 @@
# Test dual-stack validation for image-based installation
# This test verifies that the installer correctly validates dual-stack configurations
# Test dual-stack IPv4+IPv6 configuration generates config successfully
exec openshift-install image-based create config-image --dir $WORK
exists $WORK/imagebasedconfig.iso
isoContains imagebasedconfig.iso /cluster-configuration/manifest.json
-- install-config.yaml --
apiVersion: v1
baseDomain: test.metalkube.org
controlPlane:
name: master
replicas: 1
compute:
- name: worker
replicas: 0
metadata:
name: dual-stack-test
networking:
clusterNetwork:
- cidr: 10.128.0.0/14
hostPrefix: 23
networkType: OVNKubernetes
machineNetwork:
- cidr: 10.0.0.0/24
- cidr: 2001:db8::/64
serviceNetwork:
- 172.30.0.0/16
platform:
none: {}
sshKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDK6UTEydcEKzuNdPaofn8Z2DwgHqdcionLZBiPf/zIRNco++etLsat7Avv7yt04DINQd5zjxIFgG8jblaUB5E5C9ClUcMwb52GO0ay2Y9v1uBv1a4WhI3peKktAzYNk0EBMQlJtXPjRMrC9ylBPh+DsBHMu+KmDnfk7PIwyN4efC8k5kSRuPWoNdme1rz2+umU8FSmaWTHIajrbspf4GQbsntA5kuKEtDbfoNCU97o2KrRnUbeg3a8hwSjfh3u6MhlnGcg5K2Ij+zivEsWGCLKYUtE1ErqwfIzwWmJ6jnV66XCQGHf4Q1iIxqF7s2a1q24cgG2Z/iDXfqXrCIfy4P7b/Ztak3bdT9jfAdVZtdO5/r7I+O5hYhF86ayFlDWzZWP/ByiSb+q4CQbfVgK3BMmiAv2MqLHdhesmD/SmIcoOWUF6rFmRKZVFFpKpt5ATNTgUJ3JRowoXrrDruVXClUGRiCS6Zabd1rZ3VmTchaPJwtzQMdfIWISXj+Ig+C4UK0=
pullSecret: '{"auths": {"quay.io": {"auth": "c3VwZXItc2VjcmV0Cg=="}}}'
-- image-based-config.yaml --
apiVersion: v1beta1
kind: ImageBasedConfig
metadata:
name: dual-stack-test
hostname: dual-stack-test

View File

@@ -0,0 +1,38 @@
# Test single-stack IPv6 validation for image-based installation
# This test verifies that single IPv6 networks work correctly
exec openshift-install image-based create config-image --dir $WORK
exists $WORK/imagebasedconfig.iso
isoContains imagebasedconfig.iso /cluster-configuration/manifest.json
-- install-config.yaml --
apiVersion: v1
baseDomain: test.metalkube.org
controlPlane:
name: master
replicas: 1
compute:
- name: worker
replicas: 0
metadata:
name: single-ipv6-test
networking:
clusterNetwork:
- cidr: 10.128.0.0/14
hostPrefix: 23
networkType: OVNKubernetes
machineNetwork:
- cidr: 2001:db8::/64
serviceNetwork:
- 172.30.0.0/16
platform:
none: {}
sshKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDK6UTEydcEKzuNdPaofn8Z2DwgHqdcionLZBiPf/zIRNco++etLsat7Avv7yt04DINQd5zjxIFgG8jblaUB5E5C9ClUcMwb52GO0ay2Y9v1uBv1a4WhI3peKktAzYNk0EBMQlJtXPjRMrC9ylBPh+DsBHMu+KmDnfk7PIwyN4efC8k5kSRuPWoNdme1rz2+umU8FSmaWTHIajrbspf4GQbsntA5kuKEtDbfoNCU97o2KrRnUbeg3a8hwSjfh3u6MhlnGcg5K2Ij+zivEsWGCLKYUtE1ErqwfIzwWmJ6jnV66XCQGHf4Q1iIxqF7s2a1q24cgG2Z/iDXfqXrCIfy4P7b/Ztak3bdT9jfAdVZtdO5/r7I+O5hYhF86ayFlDWzZWP/ByiSb+q4CQbfVgK3BMmiAv2MqLHdhesmD/SmIcoOWUF6rFmRKZVFFpKpt5ATNTgUJ3JRowoXrrDruVXClUGRiCS6Zabd1rZ3VmTchaPJwtzQMdfIWISXj+Ig+C4UK0=
pullSecret: '{"auths": {"quay.io": {"auth": "c3VwZXItc2VjcmV0Cg=="}}}'
-- image-based-config.yaml --
apiVersion: v1beta1
kind: ImageBasedConfig
metadata:
name: single-ipv6-test
hostname: single-ipv6-test

View File

@@ -0,0 +1,39 @@
# Test validation error for too many machine networks
# This test verifies that 3+ machine networks are properly rejected
! exec openshift-install image-based create config-image --dir $WORK
stderr 'Networking.MachineNetwork: Too many: 3: must have at most 2 items'
-- install-config.yaml --
apiVersion: v1
baseDomain: test.metalkube.org
controlPlane:
name: master
replicas: 1
compute:
- name: worker
replicas: 0
metadata:
name: too-many-networks-test
networking:
clusterNetwork:
- cidr: 10.128.0.0/14
hostPrefix: 23
networkType: OVNKubernetes
machineNetwork:
- cidr: 10.0.0.0/24
- cidr: 192.168.1.0/24
- cidr: 172.16.0.0/24
serviceNetwork:
- 172.30.0.0/16
platform:
none: {}
sshKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDK6UTEydcEKzuNdPaofn8Z2DwgHqdcionLZBiPf/zIRNco++etLsat7Avv7yt04DINQd5zjxIFgG8jblaUB5E5C9ClUcMwb52GO0ay2Y9v1uBv1a4WhI3peKktAzYNk0EBMQlJtXPjRMrC9ylBPh+DsBHMu+KmDnfk7PIwyN4efC8k5kSRuPWoNdme1rz2+umU8FSmaWTHIajrbspf4GQbsntA5kuKEtDbfoNCU97o2KrRnUbeg3a8hwSjfh3u6MhlnGcg5K2Ij+zivEsWGCLKYUtE1ErqwfIzwWmJ6jnV66XCQGHf4Q1iIxqF7s2a1q24cgG2Z/iDXfqXrCIfy4P7b/Ztak3bdT9jfAdVZtdO5/r7I+O5hYhF86ayFlDWzZWP/ByiSb+q4CQbfVgK3BMmiAv2MqLHdhesmD/SmIcoOWUF6rFmRKZVFFpKpt5ATNTgUJ3JRowoXrrDruVXClUGRiCS6Zabd1rZ3VmTchaPJwtzQMdfIWISXj+Ig+C4UK0=
pullSecret: '{"auths": {"quay.io": {"auth": "c3VwZXItc2VjcmV0Cg=="}}}'
-- image-based-config.yaml --
apiVersion: v1beta1
kind: ImageBasedConfig
metadata:
name: too-many-networks-test
hostname: too-many-networks-test

View File

@@ -25,6 +25,7 @@ hostname: change-to-hostname
releaseRegistry: quay.io
# networkConfig contains the network configuration for the host in NMState format.
# See https://nmstate.io/examples.html for examples.
# For dual-stack networking, configure both IPv4 and IPv6 addresses:
networkConfig:
interfaces:
- name: eth0
@@ -37,3 +38,9 @@ networkConfig:
- ip: 192.168.122.2
prefix-length: 23
dhcp: false
ipv6:
enabled: true
address:
- ip: 2001:db8::2
prefix-length: 64
dhcp: false

View File

@@ -0,0 +1,12 @@
# Test that dual-stack support is documented in the template
# This test verifies that the generated template mentions dual-stack networking support
exec openshift-install image-based create image-config-template --dir $WORK
exists $WORK/image-based-installation-config.yaml
# Verify template mentions dual-stack support
grep 'Dual-stack networking.*IPv4.*IPv6.*is supported' $WORK/image-based-installation-config.yaml
# Verify template has both IPv4 and IPv6 examples
grep 'ipv4:' $WORK/image-based-installation-config.yaml
grep 'ipv6:' $WORK/image-based-installation-config.yaml

View File

@@ -25,6 +25,7 @@ installationDisk: /dev/vda
pullSecret: '<your-pull-secret>'
# networkConfig is optional and contains the network configuration for the host in NMState format.
# See https://nmstate.io/examples.html for examples.
# Dual-stack networking (IPv4 + IPv6) is supported - configure both address families as needed.
# networkConfig:
# interfaces:
# - name: eth0
@@ -37,3 +38,9 @@ pullSecret: '<your-pull-secret>'
# - ip: 192.168.122.2
# prefix-length: 23
# dhcp: false
# ipv6:
# enabled: true
# address:
# - ip: 2001:db8::2
# prefix-length: 64
# dhcp: false

View File

@@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"github.com/thoas/go-funk"
k8sjson "sigs.k8s.io/json"
"github.com/openshift/installer/pkg/asset"
@@ -156,10 +157,9 @@ func (cc *ClusterConfiguration) Generate(_ context.Context, dependencies asset.P
},
}
// validation for the length of the MachineNetwork is performed in the InstallConfig
if len(installConfig.Config.Networking.MachineNetwork) > 0 {
cc.Config.MachineNetwork = installConfig.Config.Networking.MachineNetwork[0].CIDR.String()
}
funk.ForEach(installConfig.Config.Networking.MachineNetwork, func(machineNetwork *types.MachineNetworkEntry) {
cc.Config.MachineNetworks = append(cc.Config.MachineNetworks, machineNetwork.CIDR.String())
})
clusterConfigurationData, err := json.Marshal(cc.Config)
if err != nil {

View File

@@ -260,7 +260,7 @@ func TestClusterConfiguration_LoadedFromDisk(t *testing.T) {
"kubeadmin_password_hash": "a-password-hash",
"raw_nm_state_config": "interfaces:\n- ipv4:\n address:\n - ip: 192.168.122.2\n prefix-length: 23\n dhcp: false\n enabled: true\n mac-address: \"00:00:00:00:00:00\"\n name: eth0\n state: up\n type: ethernet\n",
"pull_secret": "{\"auths\":{\"cloud.openshift.com\":{\"auth\":\"b3BlUTA=\",\"email\":\"test@redhat.com\"}}}",
"machine_network": "10.10.11.0/24"
"machine_networks": ["10.10.11.0/24"]
}`,
expectedFound: true,
@@ -378,7 +378,9 @@ func clusterConfiguration() *ClusterConfigurationBuilder {
},
}
cc.Config.MachineNetwork = installConfig.Config.Networking.MachineNetwork[0].CIDR.String()
for _, machineNetwork := range installConfig.Config.Networking.MachineNetwork {
cc.Config.MachineNetworks = append(cc.Config.MachineNetworks, machineNetwork.CIDR.String())
}
ccb.ClusterConfiguration = *cc
return ccb

View File

@@ -58,6 +58,7 @@ hostname: change-to-hostname
releaseRegistry: quay.io
# networkConfig contains the network configuration for the host in NMState format.
# See https://nmstate.io/examples.html for examples.
# For dual-stack networking, configure both IPv4 and IPv6 addresses:
networkConfig:
interfaces:
- name: eth0
@@ -70,6 +71,12 @@ networkConfig:
- ip: 192.168.122.2
prefix-length: 23
dhcp: false
ipv6:
enabled: true
address:
- ip: 2001:db8::2
prefix-length: 64
dhcp: false
`
i.Template = imageBasedConfigTemplate

View File

@@ -164,10 +164,11 @@ func (i *InstallConfig) validateSNOConfiguration(installConfig *types.InstallCon
allErrs = append(allErrs, field.Invalid(fieldPath, installConfig.Networking.NetworkType, "Only OVNKubernetes network type is allowed for Single Node OpenShift (SNO) cluster"))
}
// Allow multiple machine networks for dual-stack support in image-based installer
machineNetworksCount := len(installConfig.Networking.MachineNetwork)
if machineNetworksCount > 1 {
if machineNetworksCount > 2 {
fieldPath = field.NewPath("Networking", "MachineNetwork")
allErrs = append(allErrs, field.TooMany(fieldPath, machineNetworksCount, 1))
allErrs = append(allErrs, field.TooMany(fieldPath, machineNetworksCount, 2))
}
return allErrs

View File

@@ -104,7 +104,7 @@ pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"authorization value\"}}}"
expectedError: "invalid install-config configuration: [controlPlane.fencing.credentials: Forbidden: there should be exactly two fencing credentials to support the two node cluster, instead 0 credentials were found, ControlPlane.Replicas: Required value: Only Single Node OpenShift (SNO) is supported, total number of ControlPlane.Replicas must be 1. Found 2]",
},
{
name: "invalid number of MachineNetworks",
name: "valid dual-stack configuration with two IPv4 machine networks",
data: `
apiVersion: v1
metadata:
@@ -130,9 +130,194 @@ compute:
platform:
none : {}
pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"authorization value\"}}}"
`,
expectedFound: true,
expectedConfig: &types.InstallConfig{
TypeMeta: metav1.TypeMeta{
APIVersion: types.InstallConfigVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
},
BaseDomain: "test-domain",
Networking: &types.Networking{
NetworkType: "OVNKubernetes",
MachineNetwork: []types.MachineNetworkEntry{
{CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")},
{CIDR: *ipnet.MustParseCIDR("10.10.0.0/24")},
},
},
ControlPlane: &types.MachinePool{
Name: "master",
Replicas: ptr.To[int64](1),
Hyperthreading: types.HyperthreadingEnabled,
Architecture: types.ArchitectureAMD64,
},
Compute: []types.MachinePool{
{
Name: "worker",
Replicas: ptr.To[int64](0),
Hyperthreading: types.HyperthreadingEnabled,
Architecture: types.ArchitectureAMD64,
},
},
Platform: types.Platform{None: &none.Platform{}},
PullSecret: `{"auths":{"example.com":{"auth":"authorization value"}}}`,
},
},
{
name: "valid dual-stack configuration with IPv4 and IPv6 machine networks",
data: `
apiVersion: v1
metadata:
name: test-cluster
baseDomain: test-domain
networking:
networkType: OVNKubernetes
machineNetwork:
- cidr: 10.0.0.0/24
- cidr: 2001:db8::/64
compute:
- architecture: amd64
hyperthreading: Enabled
name: worker
platform: {}
replicas: 0
controlPlane:
architecture: amd64
hyperthreading: Enabled
name: master
platform: {}
replicas: 1
platform:
none : {}
pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
`,
expectedFound: true,
expectedConfig: &types.InstallConfig{
TypeMeta: metav1.TypeMeta{
APIVersion: types.InstallConfigVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
},
BaseDomain: "test-domain",
Networking: &types.Networking{
NetworkType: "OVNKubernetes",
MachineNetwork: []types.MachineNetworkEntry{
{CIDR: *ipnet.MustParseCIDR("10.0.0.0/24")},
{CIDR: *ipnet.MustParseCIDR("2001:db8::/64")},
},
},
ControlPlane: &types.MachinePool{
Name: "master",
Replicas: ptr.To[int64](1),
Hyperthreading: types.HyperthreadingEnabled,
Architecture: types.ArchitectureAMD64,
},
Compute: []types.MachinePool{
{
Name: "worker",
Replicas: ptr.To[int64](0),
Hyperthreading: types.HyperthreadingEnabled,
Architecture: types.ArchitectureAMD64,
},
},
Platform: types.Platform{None: &none.Platform{}},
PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
},
},
{
name: "valid single-stack IPv6 configuration",
data: `
apiVersion: v1
metadata:
name: test-cluster
baseDomain: test-domain
networking:
networkType: OVNKubernetes
machineNetwork:
- cidr: 2001:db8::/64
compute:
- architecture: amd64
hyperthreading: Enabled
name: worker
platform: {}
replicas: 0
controlPlane:
architecture: amd64
hyperthreading: Enabled
name: master
platform: {}
replicas: 1
platform:
none : {}
pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
`,
expectedFound: true,
expectedConfig: &types.InstallConfig{
TypeMeta: metav1.TypeMeta{
APIVersion: types.InstallConfigVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
},
BaseDomain: "test-domain",
Networking: &types.Networking{
NetworkType: "OVNKubernetes",
MachineNetwork: []types.MachineNetworkEntry{
{CIDR: *ipnet.MustParseCIDR("2001:db8::/64")},
},
},
ControlPlane: &types.MachinePool{
Name: "master",
Replicas: ptr.To[int64](1),
Hyperthreading: types.HyperthreadingEnabled,
Architecture: types.ArchitectureAMD64,
},
Compute: []types.MachinePool{
{
Name: "worker",
Replicas: ptr.To[int64](0),
Hyperthreading: types.HyperthreadingEnabled,
Architecture: types.ArchitectureAMD64,
},
},
Platform: types.Platform{None: &none.Platform{}},
PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
},
},
{
name: "invalid configuration with 3 machine networks",
data: `
apiVersion: v1
metadata:
name: test-cluster
baseDomain: test-domain
networking:
networkType: OVNKubernetes
machineNetwork:
- cidr: 10.0.0.0/24
- cidr: 2001:db8::/64
- cidr: 192.168.1.0/24
compute:
- architecture: amd64
hyperthreading: Enabled
name: worker
platform: {}
replicas: 0
controlPlane:
architecture: amd64
hyperthreading: Enabled
name: master
platform: {}
replicas: 1
platform:
none : {}
pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
`,
expectedFound: false,
expectedError: "invalid install-config configuration: Networking.MachineNetwork: Too many: 2: must have at most 1 items",
expectedError: "invalid install-config configuration: Networking.MachineNetwork: Too many: 3: must have at most 2 items",
},
{
name: "valid configuration for none platform for sno",

View File

@@ -73,6 +73,7 @@ installationDisk: /dev/vda
pullSecret: '<your-pull-secret>'
# networkConfig is optional and contains the network configuration for the host in NMState format.
# See https://nmstate.io/examples.html for examples.
# Dual-stack networking (IPv4 + IPv6) is supported - configure both address families as needed.
# networkConfig:
# interfaces:
# - name: eth0
@@ -85,6 +86,12 @@ pullSecret: '<your-pull-secret>'
# - ip: 192.168.122.2
# prefix-length: 23
# dhcp: false
# ipv6:
# enabled: true
# address:
# - ip: 2001:db8::2
# prefix-length: 64
# dhcp: false
`
i.Template = configTemplate

View File

@@ -53,7 +53,7 @@ type SeedReconfiguration struct {
// KubeconfigCryptoRetention contains all the crypto material that is required
// for the image-based installer to ensure that the generated kubeconfigs can
// be used to access the cluster after its configuration.
// be used to access the cluster after its configuration.// Use NodeIPs instead.
KubeconfigCryptoRetention KubeConfigCryptoRetention
// MachineNetwork is the list of IP address pools for machines.
@@ -61,11 +61,27 @@ type SeedReconfiguration struct {
// be empty or match the first entry in the list.
// Default is 10.0.0.0/16 for all platforms other than Power VS.
// For Power VS, the default is 192.168.0.0/24.
// Deprecated: Use MachineNetworks instead.
MachineNetwork string `json:"machine_network,omitempty"`
// MachineNetworks is the list of subnets provided by user.
// For single stack ocp clusters, this will be a single subnet.
// For dual-stack ocp clusters, this will be a list of two subnets.
// This will be used to create the node network and choose ip addresses for the node.
// Equivalent to install-config.yaml's machineNetworks.
// If both MachineNetwork and MachineNetworks are specified, MachineNetworks takes precedence.
// Use this field instead of MachineNetwork.
MachineNetworks []string `json:"machine_networks,omitempty"`
// NodeIP is the desired IP address of the node.
// Deprecated: Use NodeIPs instead.
NodeIP string `json:"node_ip,omitempty"`
// The desired IP addresses of the SNO node. One for single stack
// clusters, and two for dual-stack clusters.
// Use instead of NodeIP.
NodeIPs []string `json:"node_ips,omitempty"`
// RawNMStateConfig contains the nmstate configuration YAML manifest as string.
// Example nmstate configurations can be found here: https://nmstate.io/examples.html.
RawNMStateConfig string `json:"raw_nm_state_config,omitempty"`