Skip to content

Commit f409769

Browse files
Merge pull request #390 from danmanor/enable-dual-stack
MGMT-21200: Enable dual-stack
2 parents 0ceda4e + 3ee6a5a commit f409769

File tree

9 files changed

+493
-150
lines changed

9 files changed

+493
-150
lines changed

hack/dummy_config.yaml

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,45 +29,54 @@ etcd_endpoint: localhost:2379
2929
extend_expiration: true
3030
force_expire: false
3131
hostname: test.hostname
32+
ip:
33+
- 192.168.126.99
34+
- 2001:db8::99
3235
# proxy: http://registry.kni-qe-0.lab.eng.rdu2.redhat.com:3128|http://registry.kni-qe-0.lab.eng.rdu2.redhat.com:3130|.cluster.local,.kni-qe-2.lab.eng.rdu2.redhat.com,.svc,127.0.0.1,2620:52:0:11c::/64,2620:52:0:11c::1,2620:52:0:11c::10,2620:52:0:11c::11,2620:52:0:199::/64,api-int.kni-qe-2.lab.eng.rdu2.redhat.com,fd01::/48,fd02::/112,localhost|http://registry.kni-qe-0.lab.eng.rdu2.redhat.com:3128|http://registry.kni-qe-0.lab.eng.rdu2.redhat.com:3130|.cluster.local,.kni-qe-2.lab.eng.rdu2.redhat.com,.svc,127.0.0.1,2620:52:0:11c::/64,2620:52:0:11c::1,2620:52:0:11c::10,2620:52:0:11c::11,2620:52:0:199::/64,api-int.kni-qe-2.lab.eng.rdu2.redhat.com,fd01::/48,fd02::/112,localhost,moreproxy
3336
install_config: |
34-
additionalTrustBundlePolicy: Proxyonly
35-
apiVersion: v1
36-
baseDomain: ibo0.redhat.com
37-
bootstrapInPlace:
38-
installationDisk: /dev/disk/by-path/pci-0000:04:00.0
39-
compute:
40-
- architecture: amd64
41-
hyperthreading: Enabled
42-
name: worker
43-
platform: {}
44-
replicas: 0
45-
controlPlane:
46-
architecture: amd64
47-
hyperthreading: Enabled
48-
name: master
49-
platform: {}
50-
replicas: 1
51-
metadata:
52-
creationTimestamp: null
53-
name: seed
54-
networking:
55-
clusterNetwork:
56-
- cidr: 10.128.0.0/14
57-
hostPrefix: 23
58-
machineNetwork:
59-
- cidr: 192.168.126.0/24
60-
networkType: OVNKubernetes
61-
serviceNetwork:
62-
- 172.30.0.0/16
63-
platform:
64-
none: {}
65-
publish: External
66-
pullSecret: ""
67-
sshKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDThIOETj6iTvbCaNv15tZg121nWLcwtJuZofc1QS5iAdw8C8fN2R39cSW/ambikl2Fr6YNBBVR3znbtmattOyWyxAOFUfdY0aw0MqZb4LWLf04q6X0KsWIYWaV3ol0KxTzgvX38i/IU42XQfJwMYFE8dQ15TZ7l+FTKKi3SUPXLuy/9CXRfaCDZ2dKMcCkelkTr0KR1HdjiKQ86rMfk9JUbAf7D29aAQq4h1WNnHMM9vnbqN7MW9L8ebn/lCTJjGQ56r0UmurgyIEMt0P+CGp1e4AUNKYsPoYFB0GNwUkr/rB8LeuCOaZcoWdYXlUJaN45GjtCDon56+AoMA9V8tYkV6HqyFwGQjoGKI1cRCHXDJnGyAbMd9OK94TWJmNvtdHkbSURHyw2G7otZpAkRuEvMP0C7R+3JmuxrDA8yaUgWvgccqGcmFl1krClksW6KrAXNlwhZ4QOAMhDrXwwPfOOQoG82zPpg+g9gZQIhkro1Cje4bmWz5z5fiuDloTq1vc=
68-
root@edge-01.edge.lab.eng.rdu2.redhat.com
69-
ip: 192.168.126.99
70-
kubeadmin_password_hash: $2a$10$20Q4iRLy7cWZkjn/D07bF.RZQZonKwstyRGH0qiYbYRkx5Pe4Ztyi
37+
additionalTrustBundlePolicy: Proxyonly
38+
apiVersion: v1
39+
baseDomain: ibo0.redhat.com
40+
bootstrapInPlace:
41+
installationDisk: /dev/disk/by-path/pci-0000:04:00.0
42+
compute:
43+
- architecture: amd64
44+
hyperthreading: Enabled
45+
name: worker
46+
platform: {}
47+
replicas: 0
48+
controlPlane:
49+
architecture: amd64
50+
hyperthreading: Enabled
51+
name: master
52+
platform: {}
53+
replicas: 1
54+
metadata:
55+
creationTimestamp: null
56+
name: seed
57+
networking:
58+
clusterNetwork:
59+
- cidr: 10.128.0.0/14
60+
hostPrefix: 23
61+
- cidr: fd01::/48
62+
hostPrefix: 64
63+
machineNetwork:
64+
- cidr: 192.168.128.0/24
65+
- cidr: 1001:db8::/120
66+
networkType: OVNKubernetes
67+
serviceNetwork:
68+
- 172.30.0.0/16
69+
- fd02::/112
70+
platform:
71+
none: {}
72+
publish: External
73+
pullSecret: ""
74+
sshKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDThIOETj6iTvbCaNv15tZg121nWLcwtJuZofc1QS5iAdw8C8fN2R39cSW/ambikl2Fr6YNBBVR3znbtmattOyWyxAOFUfdY0aw0MqZb4LWLf04q6X0KsWIYWaV3ol0KxTzgvX38i/IU42XQfJwMYFE8dQ15TZ7l+FTKKi3SUPXLuy/9CXRfaCDZ2dKMcCkelkTr0KR1HdjiKQ86rMfk9JUbAf7D29aAQq4h1WNnHMM9vnbqN7MW9L8ebn/lCTJjGQ56r0UmurgyIEMt0P+CGp1e4AUNKYsPoYFB0GNwUkr/rB8LeuCOaZcoWdYXlUJaN45GjtCDon56+AoMA9V8tYkV6HqyFwGQjoGKI1cRCHXDJnGyAbMd9OK94TWJmNvtdHkbSURHyw2G7otZpAkRuEvMP0C7R+3JmuxrDA8yaUgWvgccqGcmFl1krClksW6KrAXNlwhZ4QOAMhDrXwwPfOOQoG82zPpg+g9gZQIhkro1Cje4bmWz5z5fiuDloTq1vc=
75+
root@edge-01.edge.lab.eng.rdu2.redhat.com
76+
machine_network_cidr:
77+
- 192.168.128.0/24
78+
- 1001:db8::/120
79+
kubeadmin_password_hash: "$2a$10$20Q4iRLy7cWZkjn/D07bF.RZQZonKwstyRGH0qiYbYRkx5Pe4Ztyi"
7180
# proxy_trusted_ca_bundle: 'user-ca-bundle:'
7281
# user_ca_bundle: |
7382
# # Foo
@@ -92,7 +101,6 @@ kubeadmin_password_hash: $2a$10$20Q4iRLy7cWZkjn/D07bF.RZQZonKwstyRGH0qiYbYRkx5Pe
92101
# 42TI0UzcqRV4CWDoARMSV8yMLajZ0g1eEreUprwmFcOy17V7KCeV6E8lKb21OU8M
93102
# Ad9q3H0iXjct
94103
# -----END CERTIFICATE-----
95-
machine_network_cidr: 192.168.127.0/24
96104
postprocess_only: false
97105
pull_secret: '{"auths":{"empty_registry":{"username":"empty","password":"empty","auth":"ZW1wdHk6ZW1wdHk=","email":""}}}'
98106
summary_file: summary.yaml

src/config.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ pub(crate) struct ClusterCustomizations {
4242
pub(crate) files: Vec<ConfigPath>,
4343
pub(crate) cluster_rename: Option<ClusterNamesRename>,
4444
pub(crate) hostname: Option<String>,
45-
pub(crate) ip: Option<String>,
45+
pub(crate) ip_addresses: Vec<String>,
4646
pub(crate) proxy: Option<Proxy>,
4747
pub(crate) install_config: Option<String>,
4848
pub(crate) kubeadmin_password_hash: Option<String>,
4949
#[serde(serialize_with = "redact")]
5050
pub(crate) pull_secret: Option<String>,
5151
pub(crate) user_ca_bundle: Option<String>,
5252
pub(crate) proxy_trusted_ca_bundle: Option<ProxyAdditionalTrustBundle>,
53-
pub(crate) machine_network_cidr: Option<String>,
53+
pub(crate) machine_network_cidrs: Vec<String>,
5454
pub(crate) chrony_config: Option<String>,
5555
}
5656

@@ -155,12 +155,12 @@ impl RecertConfig {
155155
files: vec![],
156156
cluster_rename: None,
157157
hostname: None,
158-
ip: None,
158+
ip_addresses: vec![],
159159
kubeadmin_password_hash: None,
160160
pull_secret: None,
161161
proxy: None,
162162
install_config: None,
163-
machine_network_cidr: None,
163+
machine_network_cidrs: vec![],
164164
user_ca_bundle: None,
165165
proxy_trusted_ca_bundle: None,
166166
chrony_config: None,
@@ -222,9 +222,20 @@ impl RecertConfig {
222222
Some(value) => Some(value.as_str().context("hostname must be a string")?.to_string()),
223223
None => None,
224224
};
225-
let ip = match value.remove("ip") {
226-
Some(value) => Some(value.as_str().context("ip must be a string")?.to_string()),
227-
None => None,
225+
let ip_addresses: Vec<String> = match value.remove("ip") {
226+
Some(serde_json::Value::Array(array)) => {
227+
ensure!(array.len() <= 2, "ip array must up to 2 elements");
228+
array
229+
.iter()
230+
.map(|v| -> Result<String, anyhow::Error> { Ok(v.as_str().context("ip array element must be a string")?.to_string()) })
231+
.collect::<Result<Vec<_>, _>>()?
232+
}
233+
Some(serde_json::Value::String(single_ip)) => {
234+
// Handle single IP for backward compatibility
235+
vec![single_ip.to_string()]
236+
}
237+
None => vec![],
238+
_ => anyhow::bail!("ip must be a string or an array of strings"),
228239
};
229240
let pull_secret = match value.remove("pull_secret") {
230241
Some(value) => Some(value.as_str().context("pull_secret must be a string")?.to_string()),
@@ -260,9 +271,24 @@ impl RecertConfig {
260271
),
261272
None => None,
262273
};
263-
let machine_network_cidr = match value.remove("machine_network_cidr") {
264-
Some(value) => Some(value.as_str().context("machine_network_cidr must be a string")?.to_string()),
265-
None => None,
274+
let machine_network_cidrs = match value.remove("machine_network_cidr") {
275+
Some(serde_json::Value::Array(array)) => {
276+
ensure!(array.len() <= 2, "machine_network_cidr array must up to 2 elements");
277+
array
278+
.iter()
279+
.map(|v| -> Result<String, anyhow::Error> {
280+
Ok(v.as_str()
281+
.context("machine_network_cidr array element must be a string")?
282+
.to_string())
283+
})
284+
.collect::<Result<Vec<_>, _>>()?
285+
}
286+
Some(serde_json::Value::String(single_cidr)) => {
287+
// Handle single CIDR for backward compatibility
288+
vec![single_cidr.to_string()]
289+
}
290+
None => vec![],
291+
_ => anyhow::bail!("machine_network_cidr must be a string or an array of strings"),
266292
};
267293
let chrony_config = match value.remove("chrony_config") {
268294
Some(value) => Some(value.as_str().context("chrony_config must be a string")?.to_string()),
@@ -338,14 +364,14 @@ impl RecertConfig {
338364
files: cluster_customization_files,
339365
cluster_rename,
340366
hostname,
341-
ip,
367+
ip_addresses,
342368
kubeadmin_password_hash: set_kubeadmin_password_hash,
343369
pull_secret,
344370
user_ca_bundle,
345371
proxy_trusted_ca_bundle,
346372
proxy,
347373
install_config,
348-
machine_network_cidr,
374+
machine_network_cidrs,
349375
chrony_config,
350376
};
351377

@@ -429,14 +455,14 @@ impl RecertConfig {
429455
},
430456
cluster_rename: cli.cluster_rename,
431457
hostname: cli.hostname,
432-
ip: cli.ip,
458+
ip_addresses: cli.ip,
433459
proxy: cli.proxy,
434460
install_config: cli.install_config,
435461
kubeadmin_password_hash: cli.kubeadmin_password_hash,
436462
pull_secret: cli.pull_secret,
437463
user_ca_bundle: cli.user_ca_bundle,
438464
proxy_trusted_ca_bundle: cli.proxy_trusted_ca_bundle,
439-
machine_network_cidr: cli.machine_network_cidr,
465+
machine_network_cidrs: cli.machine_network_cidr,
440466
chrony_config: cli.chrony_config,
441467
},
442468
encryption_customizations: EncryptionCustomizations {

src/config/cli.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ pub(crate) struct Cli {
9797
pub(crate) hostname: Option<String>,
9898

9999
/// If given, the cluster resources that include the IP address will be modified to use this
100-
/// one instead.
100+
/// one instead. For dual stack, provide multiple IP addresses (IPv4 first).
101101
#[clap(long)]
102-
pub(crate) ip: Option<String>,
102+
pub(crate) ip: Vec<String>,
103103

104104
/// If given, the cluster's HTTP proxy configuration will be modified to use this one instead.
105105
#[clap(long, value_parser = Proxy::parse)]
@@ -150,10 +150,11 @@ pub(crate) struct Cli {
150150

151151
/// The CIDR of the machine network. If given, the machine network CIDR which appears in the
152152
/// install-config found in the cluster-config-v1 configmaps will be modified to use this
153-
/// machine CIDR. WARNING: If a different machine network CIDR is stated in the
153+
/// machine CIDR. For dual stack, provide multiple IPv4 and IPv6 CIDRs (IPv4 first).
154+
/// WARNING: If a different machine network CIDR is stated in the
154155
/// --install-config parameter, it might overwrite the one given here.
155156
#[clap(long)]
156-
pub(crate) machine_network_cidr: Option<String>,
157+
pub(crate) machine_network_cidr: Vec<String>,
157158

158159
/// If given, the cluster resources that include chrony.config be modified to have this value.
159160
#[clap(long)]

src/etcd_encoding.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::protobuf_gen::{
55
admissionregistration::v1::{MutatingWebhookConfiguration, ValidatingWebhookConfiguration},
66
apps::v1::{ControllerRevision, DaemonSet, Deployment, StatefulSet},
77
batch::v1::{CronJob, Job},
8-
core::v1::{ConfigMap, Secret},
8+
core::v1::{ConfigMap, Node, Secret},
99
},
1010
apimachinery::pkg::runtime::{TypeMeta, Unknown},
1111
},
@@ -56,6 +56,7 @@ k8s_type!(JobWithMeta, Job);
5656
k8s_type!(CronJobWithMeta, CronJob);
5757
k8s_type!(StatefulSetWithMeta, StatefulSet);
5858
k8s_type!(ConfigMapWithMeta, ConfigMap);
59+
k8s_type!(NodeWithMeta, Node);
5960
k8s_type!(SecretWithMeta, Secret);
6061
k8s_type!(ValidatingWebhookConfigurationWithMeta, ValidatingWebhookConfiguration);
6162
k8s_type!(MutatingWebhookConfigurationWithMeta, MutatingWebhookConfiguration);
@@ -86,6 +87,7 @@ pub(crate) async fn decode(data: &[u8]) -> Result<Vec<u8>> {
8687
"StatefulSet" => serde_json::to_vec(&StatefulSetWithMeta::try_from(unknown)?)?,
8788
"DaemonSet" => serde_json::to_vec(&DaemonsSetWithMeta::try_from(unknown)?)?,
8889
"ConfigMap" => serde_json::to_vec(&ConfigMapWithMeta::try_from(unknown)?)?,
90+
"Node" => serde_json::to_vec(&NodeWithMeta::try_from(unknown)?)?,
8991
"Secret" => serde_json::to_vec(&SecretWithMeta::try_from(unknown)?)?,
9092
"ValidatingWebhookConfiguration" => serde_json::to_vec(&ValidatingWebhookConfigurationWithMeta::try_from(unknown)?)?,
9193
"MutatingWebhookConfiguration" => serde_json::to_vec(&MutatingWebhookConfigurationWithMeta::try_from(unknown)?)?,

src/ocp_postprocess.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,19 @@ async fn run_cluster_customizations(
152152
.context("renaming cluster")?;
153153
}
154154

155-
if let Some(ip) = &cluster_customizations.ip {
156-
ip_rename(in_memory_etcd_client, ip, dirs, files).await.context("renaming IP")?;
155+
let ips = &cluster_customizations.ip_addresses;
156+
if ips.len() == 1 {
157+
log::info!("Processing single IP: {}", ips[0]);
158+
ip_rename(in_memory_etcd_client, &ips[0], dirs, files)
159+
.await
160+
.context(format!("renaming IP {}", ips[0]))?;
161+
} else if ips.len() == 2 {
162+
log::info!("Processing dual-stack IPs: {}", ips.join(", "));
163+
ip_rename_dual_stack(in_memory_etcd_client, ips, dirs, files)
164+
.await
165+
.context("renaming dual-stack IPs")?;
166+
} else if ips.is_empty() {
167+
log::info!("No IPs were provided, skipping IP rename");
157168
}
158169

159170
if let Some(hostname) = &cluster_customizations.hostname {
@@ -196,8 +207,11 @@ async fn run_cluster_customizations(
196207
.await
197208
.context("renaming additional trust bundle")?;
198209

199-
if let Some(machine_network_cidr) = &cluster_customizations.machine_network_cidr {
200-
fix_machine_network_cidr(in_memory_etcd_client, machine_network_cidr, dirs, files)
210+
let machine_network_cidrs = &cluster_customizations.machine_network_cidrs;
211+
if !machine_network_cidrs.is_empty() {
212+
let combined_cidrs = machine_network_cidrs.join(",");
213+
log::info!("Processing machine network CIDRs: {}", combined_cidrs);
214+
fix_machine_network_cidr(in_memory_etcd_client, &combined_cidrs, dirs, files)
201215
.await
202216
.context("fixing machine network CIDR")?;
203217
}
@@ -913,6 +927,21 @@ pub(crate) async fn ip_rename(
913927
Ok(())
914928
}
915929

930+
pub(crate) async fn ip_rename_dual_stack(
931+
in_memory_etcd_client: &Arc<InMemoryK8sEtcd>,
932+
ips: &[String],
933+
dirs: &[ConfigPath],
934+
files: &[ConfigPath],
935+
) -> Result<()> {
936+
let etcd_client = in_memory_etcd_client;
937+
938+
ip_rename::rename_all_dual_stack(etcd_client, ips, dirs, files)
939+
.await
940+
.context("renaming all dual stack")?;
941+
942+
Ok(())
943+
}
944+
916945
pub(crate) async fn pull_secret_rename(
917946
in_memory_etcd_client: &Arc<InMemoryK8sEtcd>,
918947
pull_secret: &str,

0 commit comments

Comments
 (0)