Skip to content

Commit 2363566

Browse files
committed
fix: add dual-stack support for kubelet node-ip
1 parent eed641b commit 2363566

File tree

2 files changed

+127
-6
lines changed
  • bottlerocket-settings-models

2 files changed

+127
-6
lines changed

bottlerocket-settings-models/modeled-types/src/kubernetes.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,128 @@ mod test_cluster_dns_ip {
13151315
}
13161316
}
13171317

1318+
// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
1319+
1320+
/// KubernetesNodeIp represents the --node-ip setting for kubelet.
1321+
///
1322+
/// This model allows the value to be either a single IP (IPv4 or IPv6) or a
1323+
/// list of IPs for dual-stack configurations (one IPv4 and one IPv6).
1324+
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1325+
#[serde(untagged)]
1326+
pub enum KubernetesNodeIp {
1327+
Scalar(IpAddr),
1328+
Vector(Vec<IpAddr>),
1329+
}
1330+
1331+
impl KubernetesNodeIp {
1332+
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a IpAddr> + 'a> {
1333+
match self {
1334+
Self::Scalar(inner) => Box::new(std::iter::once(inner)),
1335+
Self::Vector(inner) => Box::new(inner.iter()),
1336+
}
1337+
}
1338+
}
1339+
1340+
impl IntoIterator for KubernetesNodeIp {
1341+
type Item = IpAddr;
1342+
type IntoIter = std::vec::IntoIter<Self::Item>;
1343+
1344+
fn into_iter(self) -> Self::IntoIter {
1345+
match self {
1346+
Self::Scalar(inner) => vec![inner],
1347+
Self::Vector(inner) => inner,
1348+
}
1349+
.into_iter()
1350+
}
1351+
}
1352+
1353+
#[cfg(test)]
1354+
mod test_kubernetes_node_ip {
1355+
use super::KubernetesNodeIp;
1356+
use std::net::IpAddr;
1357+
use std::str::FromStr;
1358+
1359+
#[test]
1360+
fn test_parse_single_ipv4() {
1361+
assert_eq!(
1362+
serde_json::from_str::<KubernetesNodeIp>(r#""192.168.1.1""#).unwrap(),
1363+
KubernetesNodeIp::Scalar(IpAddr::from_str("192.168.1.1").unwrap())
1364+
);
1365+
}
1366+
1367+
#[test]
1368+
fn test_parse_single_ipv6() {
1369+
assert_eq!(
1370+
serde_json::from_str::<KubernetesNodeIp>(r#""2001:db8::1""#).unwrap(),
1371+
KubernetesNodeIp::Scalar(IpAddr::from_str("2001:db8::1").unwrap())
1372+
);
1373+
}
1374+
1375+
#[test]
1376+
fn test_parse_dual_stack_list() {
1377+
let node_ip =
1378+
serde_json::from_str::<KubernetesNodeIp>(r#"["192.168.1.1", "2001:db8::1"]"#).unwrap();
1379+
assert_eq!(
1380+
node_ip,
1381+
KubernetesNodeIp::Vector(vec![
1382+
IpAddr::from_str("192.168.1.1").unwrap(),
1383+
IpAddr::from_str("2001:db8::1").unwrap()
1384+
])
1385+
);
1386+
}
1387+
1388+
#[test]
1389+
fn test_parse_dual_stack_reverse_order() {
1390+
let node_ip =
1391+
serde_json::from_str::<KubernetesNodeIp>(r#"["2001:db8::1", "192.168.1.1"]"#).unwrap();
1392+
assert_eq!(
1393+
node_ip,
1394+
KubernetesNodeIp::Vector(vec![
1395+
IpAddr::from_str("2001:db8::1").unwrap(),
1396+
IpAddr::from_str("192.168.1.1").unwrap()
1397+
])
1398+
);
1399+
}
1400+
1401+
#[test]
1402+
fn test_iter_scalar() {
1403+
let node_ip = KubernetesNodeIp::Scalar(IpAddr::from_str("192.168.1.1").unwrap());
1404+
assert_eq!(
1405+
node_ip.iter().collect::<Vec<&IpAddr>>(),
1406+
vec![&IpAddr::from_str("192.168.1.1").unwrap()]
1407+
);
1408+
}
1409+
1410+
#[test]
1411+
fn test_iter_vector() {
1412+
let node_ip = KubernetesNodeIp::Vector(vec![
1413+
IpAddr::from_str("192.168.1.1").unwrap(),
1414+
IpAddr::from_str("2001:db8::1").unwrap(),
1415+
]);
1416+
assert_eq!(
1417+
node_ip.iter().collect::<Vec<&IpAddr>>(),
1418+
vec![
1419+
&IpAddr::from_str("192.168.1.1").unwrap(),
1420+
&IpAddr::from_str("2001:db8::1").unwrap()
1421+
]
1422+
);
1423+
}
1424+
1425+
#[test]
1426+
fn test_serde_round_trip_scalar() {
1427+
let json = r#""192.168.1.1""#;
1428+
let node_ip: KubernetesNodeIp = serde_json::from_str(json).unwrap();
1429+
assert_eq!(serde_json::to_string(&node_ip).unwrap(), json);
1430+
}
1431+
1432+
#[test]
1433+
fn test_serde_round_trip_vector() {
1434+
let json = r#"["192.168.1.1","2001:db8::1"]"#;
1435+
let node_ip: KubernetesNodeIp = serde_json::from_str(json).unwrap();
1436+
assert_eq!(serde_json::to_string(&node_ip).unwrap(), json);
1437+
}
1438+
}
1439+
13181440
type EnvVarMap = HashMap<SingleLineString, SingleLineString>;
13191441

13201442
/// CredentialProvider contains the settings for a credential provider for use

bottlerocket-settings-models/settings-extensions/kubernetes/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ use bottlerocket_modeled_types::{
66
KubernetesCloudProvider, KubernetesClusterDnsIp, KubernetesClusterName,
77
KubernetesDurationValue, KubernetesEvictionKey, KubernetesHostnameOverrideSource,
88
KubernetesLabelKey, KubernetesLabelValue, KubernetesMemoryManagerPolicy,
9-
KubernetesMemoryReservation, KubernetesMemorySwapBehavior, KubernetesQuantityValue,
10-
KubernetesReservedResourceKey, KubernetesTaintValue, KubernetesThresholdValue,
11-
NonNegativeInteger, SingleLineString, TopologyManagerPolicy, TopologyManagerScope, Url,
12-
ValidBase64, ValidLinuxHostname,
9+
KubernetesMemoryReservation, KubernetesMemorySwapBehavior, KubernetesNodeIp,
10+
KubernetesQuantityValue, KubernetesReservedResourceKey, KubernetesTaintValue,
11+
KubernetesThresholdValue, NonNegativeInteger, SingleLineString, TopologyManagerPolicy,
12+
TopologyManagerScope, Url, ValidBase64, ValidLinuxHostname,
1313
};
1414
use bottlerocket_settings_sdk::{GenerateResult, SettingsModel};
1515

1616
use self::de::deserialize_node_taints;
1717
use std::collections::HashMap;
1818
use std::convert::Infallible;
19-
use std::net::IpAddr;
2019

2120
mod de;
2221

@@ -93,7 +92,7 @@ pub struct KubernetesSettingsV1 {
9392
max_pods: u32,
9493
cluster_dns_ip: KubernetesClusterDnsIp,
9594
cluster_domain: DNSDomain,
96-
node_ip: IpAddr,
95+
node_ip: KubernetesNodeIp,
9796
pod_infra_container_image: SingleLineString,
9897
hostname_override: ValidLinuxHostname,
9998
}

0 commit comments

Comments
 (0)