Skip to content

Commit 0ce73df

Browse files
Merge pull request #299 from mkowalski/OCPBUGS-23308
OCPBUGS-23308: Set IPFamilyPriority and ExcludeNetworkSubnetCIDR for vSphere IPv6-only
2 parents 4673eec + c64a5ba commit 0ce73df

2 files changed

Lines changed: 120 additions & 20 deletions

File tree

pkg/cloud/vsphere/vsphere_config_transformer.go

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ func CloudConfigTransformer(source string, infra *configv1.Infrastructure, netwo
4343
// https://github.com/openshift/enhancements/blob/f6b33eb0cd4ba060af71fee6192297cf6bc31e5a/enhancements/installer/vsphere-ipi-zonal.md
4444
// https://github.com/openshift/api/pull/1278
4545
if infra.Spec.PlatformSpec.VSphere != nil {
46-
setDualStack(cpiCfg, infra.Status.PlatformStatus.VSphere, &infra.Spec.PlatformSpec.VSphere.NodeNetworking, network)
46+
setIPFamilies(cpiCfg, infra.Status.PlatformStatus.VSphere, &infra.Spec.PlatformSpec.VSphere.NodeNetworking, network)
47+
setExcludeNetworkSubnetCIDR(cpiCfg, infra.Status.PlatformStatus.VSphere, &infra.Spec.PlatformSpec.VSphere.NodeNetworking, network)
4748
setNodes(cpiCfg, &infra.Spec.PlatformSpec.VSphere.NodeNetworking)
4849
setVirtualCenters(cpiCfg, infra.Spec.PlatformSpec.VSphere)
4950

@@ -102,31 +103,68 @@ func setVirtualCenters(cfg *ccmConfig.CPIConfig, vSphereSpec *configv1.VSpherePl
102103
}
103104
}
104105

105-
// setDualStack updates the configuration required by the cloud-provider-vsphere to explicitly set
106+
// setIPFamilies updates the configuration required by the cloud-provider-vsphere to explicitly set
106107
// value of IPFamilyPriority instead of using the default which is IPv4. This is needed by the
107108
// cloud provider in order to properly filter IP addresses that feed the instance metadata.
108109
//
109-
// We rely on the Service Networks configuration that initially comes from o/installer and later
110-
// from the Cluster Network Operator as those two components take care of validating that clusters
111-
// with dual-stack configuration have exactly 2 of them and that they match the required order.
110+
// We use Service Networks as a way to determine IP stack of the cluster as this field is already
111+
// well-defined and validated by o/installer in the following way
112112
//
113-
// We are mangling with the ExcludeNetworkSubnetCIDR param here because VM agent by default detects
114-
// also IP addresses that are used by us internally and which should never be exposed as node IPs
115-
// (i.e. API VIP and Ingress VIP for IPI installations and fd69::2 which is internal to OVN-K8s).
113+
// - for single Service Network, its IP stack determines IP stack of the cluster
114+
// - for 2 entries in Service Network list, cluster is a dual-stack cluster; order of networks
115+
// determines order of IP stacks for the cluster
116+
// - number of subnets in Service Network list must be 1 or 2
116117
//
117118
// Ref.: https://issues.redhat.com/browse/OCPBUGS-18641
118-
func setDualStack(cfg *ccmConfig.CPIConfig, status *configv1.VSpherePlatformStatus, nodeNetworking *configv1.VSpherePlatformNodeNetworking, network *configv1.Network) {
119-
if network != nil && len(network.Spec.ServiceNetwork) == 2 {
119+
func setIPFamilies(cfg *ccmConfig.CPIConfig, status *configv1.VSpherePlatformStatus, nodeNetworking *configv1.VSpherePlatformNodeNetworking, network *configv1.Network) {
120+
if network != nil {
120121
// Extensive validations are performed by o/installer so that here we already know that
121122
// if the configuration is dual-stack, we will have exactly 2 service networks and if
122123
// single-stack then 1 service network. Simplified logic here is applied to avoid code
123124
// duplication.
124125
//
125126
// Ref.: https://github.com/openshift/installer/blob/6471b31/pkg/types/validation/installconfig.go#L241
126-
if net.IsIPv4CIDRString(network.Spec.ServiceNetwork[0]) {
127-
cfg.Global.IPFamilyPriority = []string{"ipv4", "ipv6"}
128-
} else {
129-
cfg.Global.IPFamilyPriority = []string{"ipv6", "ipv4"}
127+
if len(network.Spec.ServiceNetwork) == 1 {
128+
if net.IsIPv6CIDRString(network.Spec.ServiceNetwork[0]) {
129+
cfg.Global.IPFamilyPriority = []string{"ipv6"}
130+
}
131+
return
132+
}
133+
if len(network.Spec.ServiceNetwork) == 2 {
134+
if net.IsIPv4CIDRString(network.Spec.ServiceNetwork[0]) {
135+
cfg.Global.IPFamilyPriority = []string{"ipv4", "ipv6"}
136+
} else {
137+
cfg.Global.IPFamilyPriority = []string{"ipv6", "ipv4"}
138+
}
139+
return
140+
}
141+
}
142+
}
143+
144+
// setExcludeNetworkSubnetCIDR updates ExcludeNetworkSubnetCIDR param because VM agent by default
145+
// uses IP addresses that are used by internally and which should never be exposed as node IPs
146+
// (i.e. API VIP and Ingress VIP for IPI installations and fd69::2 which is internal to OVN-K8s).
147+
//
148+
// For comaptibility reasons we are running this only for IPv6-only and dual-stack clusters. We
149+
// could run it also for IPv4-only clusters for completeness but this issue was never observed in
150+
// those, so to avoid any potential regression we are not changing IPv4-only setups
151+
//
152+
// Ref.: https://issues.redhat.com/browse/OCPBUGS-18641
153+
func setExcludeNetworkSubnetCIDR(cfg *ccmConfig.CPIConfig, status *configv1.VSpherePlatformStatus, nodeNetworking *configv1.VSpherePlatformNodeNetworking, network *configv1.Network) {
154+
ipv6 := false
155+
if network != nil {
156+
for _, addr := range network.Spec.ServiceNetwork {
157+
if net.IsIPv6CIDRString(addr) {
158+
ipv6 = true
159+
break
160+
}
161+
}
162+
163+
// If none of Service Networks is an IPv6 subnet then the cluster is IPv4-only then we do
164+
// not change any configuration. We simply stop and remaning code will run only for dual-stack
165+
// and IPv6-only setups.
166+
if !ipv6 {
167+
return
130168
}
131169

132170
if status != nil {

pkg/cloud/vsphere/vsphere_config_transformer_test.go

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (b infraBuilder) withPlatform(platform configv1.PlatformType) infraBuilder
5757
return b
5858
}
5959

60-
func (b infraBuilder) withVSphereNodeNetworking() infraBuilder {
60+
func (b infraBuilder) withVSphereDefaultNodeNetworking() infraBuilder {
6161
vspereSpecRef := b.platformSpec.VSphere
6262

6363
vspereSpecRef.NodeNetworking.External.Network = "external-network"
@@ -72,6 +72,21 @@ func (b infraBuilder) withVSphereNodeNetworking() infraBuilder {
7272
return b
7373
}
7474

75+
func (b infraBuilder) withVSphereIPv6onlyNodeNetworking() infraBuilder {
76+
vspereSpecRef := b.platformSpec.VSphere
77+
78+
vspereSpecRef.NodeNetworking.External.Network = "external-network"
79+
vspereSpecRef.NodeNetworking.Internal.Network = "internal-network"
80+
81+
vspereSpecRef.NodeNetworking.External.NetworkSubnetCIDR = []string{"fe80::3/128"}
82+
vspereSpecRef.NodeNetworking.External.ExcludeNetworkSubnetCIDR = []string{"fe80::2/128"}
83+
84+
vspereSpecRef.NodeNetworking.Internal.ExcludeNetworkSubnetCIDR = []string{"fe80::1/128"}
85+
vspereSpecRef.NodeNetworking.Internal.NetworkSubnetCIDR = []string{"fe80::4/128"}
86+
87+
return b
88+
}
89+
7590
func (b infraBuilder) withVSphereZones() infraBuilder {
7691
vcenterSpec := configv1.VSpherePlatformVCenterSpec{
7792
Server: "test-server",
@@ -139,6 +154,12 @@ func (b infraBuilder) withPrimaryIPv6VIP() infraBuilder {
139154
return b
140155
}
141156

157+
func (b infraBuilder) withIPv6onlyVIP() infraBuilder {
158+
b.platformStatus.VSphere.APIServerInternalIPs = []string{"fd65:a1a8:60ad:271c::200"}
159+
b.platformStatus.VSphere.IngressIPs = []string{"fd65:a1a8:60ad:271c::201"}
160+
return b
161+
}
162+
142163
func makeDummyNetworkConfig() *configv1.Network {
143164
return &configv1.Network{}
144165
}
@@ -175,6 +196,20 @@ func withDualStackPrimaryIPv6NetworkConfig() *configv1.Network {
175196
}
176197
}
177198

199+
func withIPv6onlyNetworkConfig() *configv1.Network {
200+
return &configv1.Network{
201+
Spec: configv1.NetworkSpec{
202+
ClusterNetwork: []configv1.ClusterNetworkEntry{
203+
{CIDR: "fd65:10:128::/56", HostPrefix: 64},
204+
},
205+
NetworkType: "OVNKubernetes",
206+
ServiceNetwork: []string{
207+
"fd65:172:16::/112",
208+
},
209+
},
210+
}
211+
}
212+
178213
const iniConfigWithWorkspace = `
179214
[Global]
180215
secret-name = "vsphere-creds"
@@ -300,6 +335,26 @@ nodes:
300335
excludeInternalNetworkSubnetCidr: 192.0.2.0/24,fe80::1/128,fd65:a1a8:60ad:271c::200/128,192.168.96.3/32,fd65:a1a8:60ad:271c::201/128,192.168.96.4/32,fd69::2/128
301336
excludeExternalNetworkSubnetCidr: 192.1.2.0/24,fe80::2/128,fd65:a1a8:60ad:271c::200/128,192.168.96.3/32,fd65:a1a8:60ad:271c::201/128,192.168.96.4/32,fd69::2/128`
302337

338+
const yamlConfigNodeNetworkingIPv6only = `
339+
global:
340+
insecureFlag: true
341+
secretName: vsphere-creds
342+
secretNamespace: kube-system
343+
vcenter:
344+
test-server:
345+
server: test-server
346+
datacenters:
347+
- DC1
348+
ipFamily:
349+
- ipv6
350+
nodes:
351+
internalNetworkSubnetCidr: fe80::4/128
352+
externalNetworkSubnetCidr: fe80::3/128
353+
internalVmNetworkName: internal-network
354+
externalVmNetworkName: external-network
355+
excludeInternalNetworkSubnetCidr: fe80::1/128,fd65:a1a8:60ad:271c::200/128,fd65:a1a8:60ad:271c::201/128,fd69::2/128
356+
excludeExternalNetworkSubnetCidr: fe80::2/128,fd65:a1a8:60ad:271c::200/128,fd65:a1a8:60ad:271c::201/128,fd69::2/128`
357+
303358
const iniConfigZonal = `
304359
[Global]
305360
secret-name = "vsphere-creds"
@@ -348,7 +403,7 @@ func TestCloudConfigTransformer(t *testing.T) {
348403
},
349404
{
350405
name: "in-tree to external with node networking",
351-
infraBuilder: newVsphereInfraBuilder().withVSphereNodeNetworking(),
406+
infraBuilder: newVsphereInfraBuilder().withVSphereDefaultNodeNetworking(),
352407
networkBuilder: makeDummyNetworkConfig(),
353408
inputConfig: iniConfigWithWorkspace,
354409
equivalentConfig: iniConfigNodeNetworking,
@@ -383,14 +438,14 @@ func TestCloudConfigTransformer(t *testing.T) {
383438
},
384439
{
385440
name: "yaml and ini config parsing results should be the same, node networking",
386-
infraBuilder: newVsphereInfraBuilder().withVSphereNodeNetworking(),
441+
infraBuilder: newVsphereInfraBuilder().withVSphereDefaultNodeNetworking(),
387442
networkBuilder: makeDummyNetworkConfig(),
388443
inputConfig: yamlConfigNodeNetworking,
389444
equivalentConfig: iniConfigNodeNetworking,
390445
},
391446
{
392447
name: "yaml config should contain node networking if it's specified in infra",
393-
infraBuilder: newVsphereInfraBuilder().withVSphereNodeNetworking(),
448+
infraBuilder: newVsphereInfraBuilder().withVSphereDefaultNodeNetworking(),
394449
networkBuilder: makeDummyNetworkConfig(),
395450
inputConfig: yamlConfig,
396451
equivalentConfig: yamlConfigNodeNetworking,
@@ -404,18 +459,25 @@ func TestCloudConfigTransformer(t *testing.T) {
404459
},
405460
{
406461
name: "yaml config should contain ipv4-primary dual-stack config and correct excluded subnets",
407-
infraBuilder: newVsphereInfraBuilder().withVSphereNodeNetworking().withPrimaryIPv4VIP(),
462+
infraBuilder: newVsphereInfraBuilder().withVSphereDefaultNodeNetworking().withPrimaryIPv4VIP(),
408463
networkBuilder: withDualStackPrimaryIPv4NetworkConfig(),
409464
inputConfig: yamlConfig,
410465
equivalentConfig: yamlConfigNodeNetworkingDualStackPrimaryIPv4,
411466
},
412467
{
413468
name: "yaml config should contain ipv6-primary dual-stack config and correct excluded subnets",
414-
infraBuilder: newVsphereInfraBuilder().withVSphereNodeNetworking().withPrimaryIPv6VIP(),
469+
infraBuilder: newVsphereInfraBuilder().withVSphereDefaultNodeNetworking().withPrimaryIPv6VIP(),
415470
networkBuilder: withDualStackPrimaryIPv6NetworkConfig(),
416471
inputConfig: yamlConfig,
417472
equivalentConfig: yamlConfigNodeNetworkingDualStackPrimaryIPv6,
418473
},
474+
{
475+
name: "yaml config should contain ipv6-only config and correct excluded subnets",
476+
infraBuilder: newVsphereInfraBuilder().withVSphereIPv6onlyNodeNetworking().withIPv6onlyVIP(),
477+
networkBuilder: withIPv6onlyNetworkConfig(),
478+
inputConfig: yamlConfig,
479+
equivalentConfig: yamlConfigNodeNetworkingIPv6only,
480+
},
419481
{
420482
name: "empty input",
421483
infraBuilder: newVsphereInfraBuilder(),

0 commit comments

Comments
 (0)