From bcce9343c2a2b98695fda17c0fc02e7a5d33cdc4 Mon Sep 17 00:00:00 2001 From: Joe Doss Date: Wed, 26 Feb 2025 01:20:05 -0600 Subject: [PATCH 1/5] proxmoxve: Add support for static network configuration from cloud-init. --- src/initrd/mod.rs | 2 + src/providers/proxmoxve/cloudconfig.rs | 163 +++++++++++++++++++++---- src/providers/proxmoxve/configdrive.rs | 33 ++++- 3 files changed, 173 insertions(+), 25 deletions(-) diff --git a/src/initrd/mod.rs b/src/initrd/mod.rs index 9cd4466a..98cdf607 100644 --- a/src/initrd/mod.rs +++ b/src/initrd/mod.rs @@ -6,6 +6,7 @@ use crate::providers::vmware::VmwareProvider; use crate::providers::MetadataProvider; +use crate::providers::proxmoxve::ProxmoxVEConfigDrive; use anyhow::{Context, Result}; use std::fs::File; use std::io::Write; @@ -17,6 +18,7 @@ static KARGS_PATH: &str = "/etc/cmdline.d/50-afterburn-network-kargs.conf"; pub(crate) fn fetch_network_kargs(provider: &str) -> Result> { match provider { "vmware" => VmwareProvider::try_new()?.rd_network_kargs(), + "proxmoxve" => ProxmoxVEConfigDrive::try_new()?.rd_network_kargs(), _ => Ok(None), } } diff --git a/src/providers/proxmoxve/cloudconfig.rs b/src/providers/proxmoxve/cloudconfig.rs index 6be26763..2f62c5fb 100644 --- a/src/providers/proxmoxve/cloudconfig.rs +++ b/src/providers/proxmoxve/cloudconfig.rs @@ -2,7 +2,7 @@ use crate::{ network::{self, DhcpSetting, NetworkRoute}, providers::MetadataProvider, }; -use anyhow::Result; +use anyhow::{Context, Result}; use ipnetwork::IpNetwork; use openssh_keys::PublicKey; use pnet_base::MacAddr; @@ -20,6 +20,7 @@ use std::{ pub struct ProxmoxVECloudConfig { pub meta_data: ProxmoxVECloudMetaData, pub user_data: Option, + #[allow(dead_code)] pub vendor_data: ProxmoxVECloudVendorData, pub network_config: ProxmoxVECloudNetworkConfig, } @@ -33,26 +34,15 @@ pub struct ProxmoxVECloudMetaData { #[derive(Debug, Deserialize)] pub struct ProxmoxVECloudUserData { pub hostname: String, - pub manage_etc_hosts: bool, - pub fqdn: String, - pub chpasswd: ProxmoxVECloudChpasswdConfig, - pub users: Vec, - pub package_upgrade: bool, #[serde(default)] pub ssh_authorized_keys: Vec, } -#[derive(Debug, Deserialize)] -pub struct ProxmoxVECloudChpasswdConfig { - pub expire: bool, -} - #[derive(Debug, Deserialize)] pub struct ProxmoxVECloudVendorData {} #[derive(Debug, Deserialize)] pub struct ProxmoxVECloudNetworkConfig { - pub version: u32, pub config: Vec, } @@ -65,8 +55,6 @@ pub struct ProxmoxVECloudNetworkConfigEntry { #[serde(default)] pub address: Vec, #[serde(default)] - pub search: Vec, - #[serde(default)] pub subnets: Vec, } @@ -82,25 +70,31 @@ pub struct ProxmoxVECloudNetworkConfigSubnet { impl ProxmoxVECloudConfig { pub fn try_new(path: &Path) -> Result { let mut user_data = None; - let raw_user_data = std::fs::read_to_string(path.join("user-data"))?; + let raw_user_data = std::fs::read_to_string(path.join("user-data")) + .context("failed to read user-data file")?; if let Some(first_line) = raw_user_data.split('\n').next() { if first_line.starts_with("#cloud-config") { - user_data = serde_yaml::from_str(&raw_user_data)?; + user_data = serde_yaml::from_str(&raw_user_data) + .context("failed to parse user-data as YAML")?; } } if user_data.is_none() { - warn!( - "user-data does not have the expected header `#cloud-config`, ignoring this file" - ); + warn!("user-data does not have the expected header `#cloud-config`, ignoring this file"); } Ok(Self { user_data, - meta_data: serde_yaml::from_reader(File::open(path.join("meta-data"))?)?, - vendor_data: serde_yaml::from_reader(File::open(path.join("vendor-data"))?)?, - network_config: serde_yaml::from_reader(File::open(path.join("network-config"))?)?, + meta_data: serde_yaml::from_reader(File::open(path.join("meta-data")) + .context("failed to open meta-data file")?) + .context("failed to parse meta-data as YAML")?, + vendor_data: serde_yaml::from_reader(File::open(path.join("vendor-data")) + .context("failed to open vendor-data file")?) + .context("failed to parse vendor-data as YAML")?, + network_config: serde_yaml::from_reader(File::open(path.join("network-config")) + .context("failed to open network-config file")?) + .context("failed to parse network-config as YAML")?, }) } } @@ -183,6 +177,131 @@ impl MetadataProvider for ProxmoxVECloudConfig { Ok(interfaces) } + + fn rd_network_kargs(&self) -> Result> { + let mut kargs = Vec::new(); + + if let Ok(networks) = self.networks() { + for iface in networks { + // Add IP configuration if static + for addr in iface.ip_addresses { + match addr { + IpNetwork::V4(network) => { + if let Some(gateway) = iface.routes.iter().find(|r| r.destination.is_ipv4() && r.destination.prefix() == 0) { + kargs.push(format!("ip={}::{}:{}", + network.ip(), + gateway.gateway, + network.mask() + )); + } else { + kargs.push(format!("ip={}:::{}", + network.ip(), + network.mask() + )); + } + } + IpNetwork::V6(network) => { + if let Some(gateway) = iface.routes.iter().find(|r| r.destination.is_ipv6() && r.destination.prefix() == 0) { + kargs.push(format!("ip={}::{}:{}", + network.ip(), + gateway.gateway, + network.prefix() + )); + } else { + kargs.push(format!("ip={}:::{}", + network.ip(), + network.prefix() + )); + } + } + } + } + + // Add DHCP configuration + if let Some(dhcp) = iface.dhcp { + match dhcp { + DhcpSetting::V4 => kargs.push("ip=dhcp".to_string()), + DhcpSetting::V6 => kargs.push("ip=dhcp6".to_string()), + DhcpSetting::Both => kargs.push("ip=dhcp,dhcp6".to_string()), + } + } + + // Add nameservers + if !iface.nameservers.is_empty() { + let nameservers = iface.nameservers + .iter() + .map(|ns| ns.to_string()) + .collect::>() + .join(","); + kargs.push(format!("nameserver={}", nameservers)); + } + } + } + + if kargs.is_empty() { + Ok(None) + } else { + Ok(Some(kargs.join(" "))) + } + } + + fn netplan_config(&self) -> Result> { + // Convert network config to netplan format + if let Ok(networks) = self.networks() { + let mut netplan = serde_yaml::Mapping::new(); + let mut network = serde_yaml::Mapping::new(); + let mut ethernets = serde_yaml::Mapping::new(); + + for iface in networks { + let mut eth_config = serde_yaml::Mapping::new(); + + // Add DHCP settings + if let Some(dhcp) = iface.dhcp { + match dhcp { + DhcpSetting::V4 => { eth_config.insert("dhcp4".into(), true.into()); } + DhcpSetting::V6 => { eth_config.insert("dhcp6".into(), true.into()); } + DhcpSetting::Both => { + eth_config.insert("dhcp4".into(), true.into()); + eth_config.insert("dhcp6".into(), true.into()); + } + } + } + + // Add static addresses if any + if !iface.ip_addresses.is_empty() { + let addresses: Vec = iface.ip_addresses + .iter() + .map(|addr| addr.to_string()) + .collect(); + eth_config.insert("addresses".into(), addresses.into()); + } + + // Add nameservers if any + if !iface.nameservers.is_empty() { + let nameservers: Vec = iface.nameservers + .iter() + .map(|ns| ns.to_string()) + .collect(); + eth_config.insert("nameservers".into(), + serde_yaml::Value::Mapping(serde_yaml::Mapping::from_iter(vec![ + ("addresses".into(), nameservers.into()) + ])) + ); + } + + if let Some(name) = iface.name { + ethernets.insert(name.into(), eth_config.into()); + } + } + + network.insert("ethernets".into(), ethernets.into()); + netplan.insert("network".into(), network.into()); + + Ok(Some(serde_yaml::to_string(&netplan)?)) + } else { + Ok(None) + } + } } impl ProxmoxVECloudNetworkConfigEntry { diff --git a/src/providers/proxmoxve/configdrive.rs b/src/providers/proxmoxve/configdrive.rs index 4b31a76d..c122eda4 100644 --- a/src/providers/proxmoxve/configdrive.rs +++ b/src/providers/proxmoxve/configdrive.rs @@ -3,7 +3,7 @@ use crate::{network, providers::MetadataProvider}; use anyhow::{Context, Result}; use openssh_keys::PublicKey; use slog_scope::error; -use std::{collections::HashMap, path::Path}; +use std::{collections::HashMap, path::Path, process::Command}; use tempfile::TempDir; const CONFIG_DRIVE_LABEL: &str = "cidata"; @@ -16,21 +16,40 @@ pub struct ProxmoxVEConfigDrive { } impl ProxmoxVEConfigDrive { + fn find_cidata_device() -> Option { + let output = Command::new("blkid") + .args(["--cache-file", "/dev/null", "-L", CONFIG_DRIVE_LABEL]) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + Some(String::from_utf8_lossy(&output.stdout).trim().to_string()) + } + pub fn try_new() -> Result { let mount_dir = tempfile::Builder::new() .prefix("afterburn-") .tempdir() .context("failed to create temporary directory")?; + let device_path = Self::find_cidata_device() + .ok_or_else(|| anyhow::anyhow!("could not find cidata device"))?; + crate::util::mount_ro( - &Path::new("/dev/disk/by-label/").join(CONFIG_DRIVE_LABEL), + Path::new(&device_path), mount_dir.path(), TARGET_FS, 3, )?; + let config = ProxmoxVECloudConfig::try_new(mount_dir.path()) + .context("failed to read ProxmoxVE cloud config")?; + Ok(Self { - config: ProxmoxVECloudConfig::try_new(mount_dir.path())?, + config, mount_dir, }) } @@ -52,6 +71,14 @@ impl MetadataProvider for ProxmoxVEConfigDrive { fn networks(&self) -> Result> { self.config.networks() } + + fn rd_network_kargs(&self) -> Result> { + self.config.rd_network_kargs() + } + + fn netplan_config(&self) -> Result> { + self.config.netplan_config() + } } impl Drop for ProxmoxVEConfigDrive { From 9a44d7995c296bb9d30cf4d98411b26e971a9ee4 Mon Sep 17 00:00:00 2001 From: Joe Doss Date: Wed, 26 Feb 2025 01:22:01 -0600 Subject: [PATCH 2/5] proxmoxve: Add more context to log message. --- src/providers/proxmoxve/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/providers/proxmoxve/mod.rs b/src/providers/proxmoxve/mod.rs index 1ce543fa..1c9043ea 100644 --- a/src/providers/proxmoxve/mod.rs +++ b/src/providers/proxmoxve/mod.rs @@ -29,8 +29,9 @@ mod tests; pub fn try_config_drive_else_leave() -> Result> { match ProxmoxVEConfigDrive::try_new() { Ok(config_drive) => Ok(Box::new(config_drive)), - Err(_) => { - warn!("failed to locate config-drive - aborting ProxmoxVE provider"); + Err(e) => { + warn!("failed to locate config-drive: {}", e); + warn!("aborting ProxmoxVE provider"); Ok(Box::new(NoopProvider::try_new()?)) } } From d5250fd1334001354535786edcc19151545f8402 Mon Sep 17 00:00:00 2001 From: Joe Doss Date: Wed, 26 Feb 2025 01:22:45 -0600 Subject: [PATCH 3/5] proxmoxve: Add tests for static network configuration from cloud-init. --- src/providers/proxmoxve/tests.rs | 112 ++++++++++++++++++ .../proxmoxve/static-no-gateway/meta-data | 1 + .../static-no-gateway/network-config | 13 ++ .../proxmoxve/static-no-gateway/user-data | 13 ++ .../proxmoxve/static-no-gateway/vendor-data | 0 5 files changed, 139 insertions(+) create mode 100644 tests/fixtures/proxmoxve/static-no-gateway/meta-data create mode 100644 tests/fixtures/proxmoxve/static-no-gateway/network-config create mode 100644 tests/fixtures/proxmoxve/static-no-gateway/user-data create mode 100644 tests/fixtures/proxmoxve/static-no-gateway/vendor-data diff --git a/src/providers/proxmoxve/tests.rs b/src/providers/proxmoxve/tests.rs index b4b764e3..5708ed75 100644 --- a/src/providers/proxmoxve/tests.rs +++ b/src/providers/proxmoxve/tests.rs @@ -154,3 +154,115 @@ fn test_invalid_user_data() { assert!(config.hostname().unwrap().is_none()); assert_eq!(config.ssh_keys().unwrap(), vec![]); } + +#[test] +fn test_network_kargs() { + let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static")) + .expect("cannot parse config"); + + let kargs = config.rd_network_kargs().expect("cannot get network kargs"); + assert!(kargs.is_some()); + let kargs = kargs.unwrap(); + + // Check static IP configuration with gateway + assert!(kargs.contains("ip=192.168.1.1::192.168.1.254:255.255.255.0")); + assert!(kargs.contains("ip=2001:db8:85a3::8a2e:370:0::2001:db8:85a3::8a2e:370:9999:24")); + + // Check nameservers + assert!(kargs.contains("nameserver=1.1.1.1,8.8.8.8")); +} + +#[test] +fn test_network_kargs_dhcp() { + let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/dhcp")) + .expect("cannot parse config"); + + let kargs = config.rd_network_kargs().expect("cannot get network kargs"); + assert!(kargs.is_some()); + let kargs = kargs.unwrap(); + + // Check DHCP configuration + assert!(kargs.contains("ip=dhcp")); + + // Check nameservers + assert!(kargs.contains("nameserver=1.1.1.1,8.8.8.8")); +} + +#[test] +fn test_network_kargs_no_gateway() { + let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static-no-gateway")) + .expect("cannot parse config"); + + let kargs = config.rd_network_kargs().expect("cannot get network kargs"); + assert!(kargs.is_some()); + let kargs = kargs.unwrap(); + + // Check static IP configuration without gateway + assert!(kargs.contains("ip=192.168.1.1:::255.255.255.0")); + + // Check nameservers + assert!(kargs.contains("nameserver=1.1.1.1,8.8.8.8")); +} + +#[test] +fn test_netplan_config_static() { + let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static")) + .expect("cannot parse config"); + + let netplan = config.netplan_config().expect("cannot get netplan config"); + assert!(netplan.is_some()); + let netplan = netplan.unwrap(); + + // Parse the YAML to verify its structure + let parsed: serde_yaml::Value = serde_yaml::from_str(&netplan).expect("invalid YAML"); + + // Check network configuration + let network = &parsed["network"]; + assert!(network.is_mapping()); + + // Check ethernet interfaces + let ethernets = &network["ethernets"]; + assert!(ethernets.is_mapping()); + + // Check eth0 configuration + let eth0 = ðernets["eth0"]; + assert!(eth0.is_mapping()); + + // Verify static addresses + let addresses = eth0["addresses"].as_sequence().unwrap(); + assert!(addresses.contains(&serde_yaml::Value::String("192.168.1.1/24".into()))); + assert!(addresses.contains(&serde_yaml::Value::String( + "2001:db8:85a3::8a2e:370:0/24".into() + ))); + + // Verify nameservers + let nameservers = ð0["nameservers"]["addresses"]; + assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("1.1.1.1".into()))); + assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("8.8.8.8".into()))); +} + +#[test] +fn test_netplan_config_dhcp() { + let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/dhcp")) + .expect("cannot parse config"); + + let netplan = config.netplan_config().expect("cannot get netplan config"); + assert!(netplan.is_some()); + let netplan = netplan.unwrap(); + + // Parse the YAML to verify its structure + let parsed: serde_yaml::Value = serde_yaml::from_str(&netplan).expect("invalid YAML"); + + // Check network configuration + let network = &parsed["network"]; + let ethernets = &network["ethernets"]; + let eth0 = ðernets["eth0"]; + + // Verify DHCP configuration + assert_eq!(eth0["dhcp4"], serde_yaml::Value::Bool(true)); + + // Verify nameservers + let nameservers = ð0["nameservers"]["addresses"]; + assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("1.1.1.1".into()))); + assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("8.8.8.8".into()))); +} diff --git a/tests/fixtures/proxmoxve/static-no-gateway/meta-data b/tests/fixtures/proxmoxve/static-no-gateway/meta-data new file mode 100644 index 00000000..bd5926b3 --- /dev/null +++ b/tests/fixtures/proxmoxve/static-no-gateway/meta-data @@ -0,0 +1 @@ +instance-id: 15a9919cb91024fbd1d70fa07f0efa749cbba03b diff --git a/tests/fixtures/proxmoxve/static-no-gateway/network-config b/tests/fixtures/proxmoxve/static-no-gateway/network-config new file mode 100644 index 00000000..dfef8824 --- /dev/null +++ b/tests/fixtures/proxmoxve/static-no-gateway/network-config @@ -0,0 +1,13 @@ +version: 1 +config: + - type: physical + name: eth0 + mac_address: "01:23:45:67:89:00" + subnets: + - type: static + address: "192.168.1.1" + netmask: "255.255.255.0" + - type: nameserver + address: + - "1.1.1.1" + - "8.8.8.8" diff --git a/tests/fixtures/proxmoxve/static-no-gateway/user-data b/tests/fixtures/proxmoxve/static-no-gateway/user-data new file mode 100644 index 00000000..e62a3e6a --- /dev/null +++ b/tests/fixtures/proxmoxve/static-no-gateway/user-data @@ -0,0 +1,13 @@ +#cloud-config +hostname: dummy +manage_etc_hosts: true +fqdn: dummy.local.com +user: dummy-user +password: $5$6LDowW6p$.RyFu8lVH7Cw3AB.pPS/K2lmB8IczVs99A7gbcUCLV2 +ssh_authorized_keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDd1hElre4j44sbmULXyO5j6dRnkRFCMjEGtRSy2SuvFD8WyB5uectcEMvz7ORhQIVbPlz94wFjpSX5wl/gmSKL/7GOyerJo0Y2cvyjJJahuDn+JnIL0tT0HS1pJ5iJqQpxXeOAzMK5Heum+uGw9BzbiUHnRzjJr8Ltx4CAGMfubevD4SX32Q8BTQiaU4ZnGtdHo16pWwRsq1f6/UtL4gDCni9vm8QmmGDRloi/pBn1csjKw+volFyu/kSEmGLWow6NuT6TrhGAbMKas5HfYq0Mn3LGPZL7XjqJQ6CO0TzkG/BNplZT2tiwHtsvXsbePTp4ZUi4dkCMz2xR4eikaI1V dummy@dummy.local +chpasswd: + expire: False +users: + - default +package_upgrade: true diff --git a/tests/fixtures/proxmoxve/static-no-gateway/vendor-data b/tests/fixtures/proxmoxve/static-no-gateway/vendor-data new file mode 100644 index 00000000..e69de29b From fb908486d985ff9b38e85f3e05776440a2438fe1 Mon Sep 17 00:00:00 2001 From: Joe Doss Date: Wed, 26 Feb 2025 01:23:08 -0600 Subject: [PATCH 4/5] Update release notes. --- docs/release-notes.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 251df6a6..7723e365 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,8 @@ nav_order: 8 Major changes: +- ProxmoxVE: Add support for static IP configuration from cloud-init + Minor changes: - ProxmoxVE: Fixed instance boot without config drive @@ -160,8 +162,8 @@ Changes: - providers/gcp: access GCP metadata service by IP address - providers/packet: access metadata service over HTTPS - cli: don't report an error when --help or --version is specified -- cli: correctly print version when --version specified -- providers: Add PowerVS +- cli: correctly print version when --version specified +- providers: Add PowerVS - workflows: bump toolchains; restrict repository access @@ -173,7 +175,7 @@ Changes: - cargo: update all dependencies - *: remove cl-legacy feature - ibmcloud: don't ignore I/O error when parsing metadata -- providers: fix clippy::unnecessary_wraps lint on 1.50 +- providers: fix clippy::unnecessary_wraps lint on 1.50 - workflows: update pinned lint toolchain to 1.50.0 - *: switch from `error-chain` to `anyhow` - cli: stop wrapping command-line parse errors @@ -266,7 +268,7 @@ Changes: - sshkeys: send structured info to journald - ci: test a secondary arch on Travis -- ci: rust version from 1.39.0 to 1.40.0 +- ci: rust version from 1.39.0 to 1.40.0 - makefile: tweak install step - providers: add vmware - util/cmdline: add helpers for detecting network kargs @@ -278,7 +280,7 @@ Changes: Changes: -- cargo: relax dependencies micro versions +- cargo: relax dependencies micro versions - cargo: switch from deprecated tempdir crate to tempfile - providers: add exoscale - providers: add ibmcloud-classic as a separate platform @@ -361,7 +363,7 @@ Changes: Changes: -- providers/azure: fetch hostname from metadata +- providers/azure: fetch hostname from metadata - add checkin service files for Azure and Packet - metadata: accept "ec2" provider name only in legacy mode - bump minimum toolchain to 1.31 @@ -384,7 +386,7 @@ Bugfixes: - providers/gce: fix panic fetching metadata -Misc: +Misc: - providers/gce: add basic hostname mock-test - rustfmt whole project @@ -491,4 +493,4 @@ and behavior for all providers should be identical to the previous golang version. If it's not, please file a bug in our bug tracker, https://github.com/coreos/bugs (or submit a pr!). -Additionally, `coreos-metadata` now supports ssh keys for azure. +Additionally, `coreos-metadata` now supports ssh keys for azure. From a493156710dbe8c5f00e6d0bb672b6e0167904fc Mon Sep 17 00:00:00 2001 From: Joe Doss Date: Wed, 26 Feb 2025 11:07:18 -0600 Subject: [PATCH 5/5] Run cargo fmt. --- src/initrd/mod.rs | 2 +- src/providers/proxmoxve/cloudconfig.rs | 84 +++++++++++++++----------- src/providers/proxmoxve/configdrive.rs | 12 +--- src/providers/proxmoxve/tests.rs | 25 ++++++-- 4 files changed, 72 insertions(+), 51 deletions(-) diff --git a/src/initrd/mod.rs b/src/initrd/mod.rs index 98cdf607..baf92606 100644 --- a/src/initrd/mod.rs +++ b/src/initrd/mod.rs @@ -4,9 +4,9 @@ //! services are configured, so it may not be able to use all usual metadata //! fetcher. +use crate::providers::proxmoxve::ProxmoxVEConfigDrive; use crate::providers::vmware::VmwareProvider; use crate::providers::MetadataProvider; -use crate::providers::proxmoxve::ProxmoxVEConfigDrive; use anyhow::{Context, Result}; use std::fs::File; use std::io::Write; diff --git a/src/providers/proxmoxve/cloudconfig.rs b/src/providers/proxmoxve/cloudconfig.rs index 2f62c5fb..4d420931 100644 --- a/src/providers/proxmoxve/cloudconfig.rs +++ b/src/providers/proxmoxve/cloudconfig.rs @@ -81,20 +81,26 @@ impl ProxmoxVECloudConfig { } if user_data.is_none() { - warn!("user-data does not have the expected header `#cloud-config`, ignoring this file"); + warn!( + "user-data does not have the expected header `#cloud-config`, ignoring this file" + ); } Ok(Self { user_data, - meta_data: serde_yaml::from_reader(File::open(path.join("meta-data")) - .context("failed to open meta-data file")?) - .context("failed to parse meta-data as YAML")?, - vendor_data: serde_yaml::from_reader(File::open(path.join("vendor-data")) - .context("failed to open vendor-data file")?) - .context("failed to parse vendor-data as YAML")?, - network_config: serde_yaml::from_reader(File::open(path.join("network-config")) - .context("failed to open network-config file")?) - .context("failed to parse network-config as YAML")?, + meta_data: serde_yaml::from_reader( + File::open(path.join("meta-data")).context("failed to open meta-data file")?, + ) + .context("failed to parse meta-data as YAML")?, + vendor_data: serde_yaml::from_reader( + File::open(path.join("vendor-data")).context("failed to open vendor-data file")?, + ) + .context("failed to parse vendor-data as YAML")?, + network_config: serde_yaml::from_reader( + File::open(path.join("network-config")) + .context("failed to open network-config file")?, + ) + .context("failed to parse network-config as YAML")?, }) } } @@ -187,31 +193,35 @@ impl MetadataProvider for ProxmoxVECloudConfig { for addr in iface.ip_addresses { match addr { IpNetwork::V4(network) => { - if let Some(gateway) = iface.routes.iter().find(|r| r.destination.is_ipv4() && r.destination.prefix() == 0) { - kargs.push(format!("ip={}::{}:{}", + if let Some(gateway) = iface + .routes + .iter() + .find(|r| r.destination.is_ipv4() && r.destination.prefix() == 0) + { + kargs.push(format!( + "ip={}::{}:{}", network.ip(), gateway.gateway, network.mask() )); } else { - kargs.push(format!("ip={}:::{}", - network.ip(), - network.mask() - )); + kargs.push(format!("ip={}:::{}", network.ip(), network.mask())); } } IpNetwork::V6(network) => { - if let Some(gateway) = iface.routes.iter().find(|r| r.destination.is_ipv6() && r.destination.prefix() == 0) { - kargs.push(format!("ip={}::{}:{}", + if let Some(gateway) = iface + .routes + .iter() + .find(|r| r.destination.is_ipv6() && r.destination.prefix() == 0) + { + kargs.push(format!( + "ip={}::{}:{}", network.ip(), gateway.gateway, network.prefix() )); } else { - kargs.push(format!("ip={}:::{}", - network.ip(), - network.prefix() - )); + kargs.push(format!("ip={}:::{}", network.ip(), network.prefix())); } } } @@ -228,7 +238,8 @@ impl MetadataProvider for ProxmoxVECloudConfig { // Add nameservers if !iface.nameservers.is_empty() { - let nameservers = iface.nameservers + let nameservers = iface + .nameservers .iter() .map(|ns| ns.to_string()) .collect::>() @@ -258,8 +269,12 @@ impl MetadataProvider for ProxmoxVECloudConfig { // Add DHCP settings if let Some(dhcp) = iface.dhcp { match dhcp { - DhcpSetting::V4 => { eth_config.insert("dhcp4".into(), true.into()); } - DhcpSetting::V6 => { eth_config.insert("dhcp6".into(), true.into()); } + DhcpSetting::V4 => { + eth_config.insert("dhcp4".into(), true.into()); + } + DhcpSetting::V6 => { + eth_config.insert("dhcp6".into(), true.into()); + } DhcpSetting::Both => { eth_config.insert("dhcp4".into(), true.into()); eth_config.insert("dhcp6".into(), true.into()); @@ -269,7 +284,8 @@ impl MetadataProvider for ProxmoxVECloudConfig { // Add static addresses if any if !iface.ip_addresses.is_empty() { - let addresses: Vec = iface.ip_addresses + let addresses: Vec = iface + .ip_addresses .iter() .map(|addr| addr.to_string()) .collect(); @@ -278,14 +294,14 @@ impl MetadataProvider for ProxmoxVECloudConfig { // Add nameservers if any if !iface.nameservers.is_empty() { - let nameservers: Vec = iface.nameservers - .iter() - .map(|ns| ns.to_string()) - .collect(); - eth_config.insert("nameservers".into(), - serde_yaml::Value::Mapping(serde_yaml::Mapping::from_iter(vec![ - ("addresses".into(), nameservers.into()) - ])) + let nameservers: Vec = + iface.nameservers.iter().map(|ns| ns.to_string()).collect(); + eth_config.insert( + "nameservers".into(), + serde_yaml::Value::Mapping(serde_yaml::Mapping::from_iter(vec![( + "addresses".into(), + nameservers.into(), + )])), ); } diff --git a/src/providers/proxmoxve/configdrive.rs b/src/providers/proxmoxve/configdrive.rs index c122eda4..d45537f4 100644 --- a/src/providers/proxmoxve/configdrive.rs +++ b/src/providers/proxmoxve/configdrive.rs @@ -38,20 +38,12 @@ impl ProxmoxVEConfigDrive { let device_path = Self::find_cidata_device() .ok_or_else(|| anyhow::anyhow!("could not find cidata device"))?; - crate::util::mount_ro( - Path::new(&device_path), - mount_dir.path(), - TARGET_FS, - 3, - )?; + crate::util::mount_ro(Path::new(&device_path), mount_dir.path(), TARGET_FS, 3)?; let config = ProxmoxVECloudConfig::try_new(mount_dir.path()) .context("failed to read ProxmoxVE cloud config")?; - Ok(Self { - config, - mount_dir, - }) + Ok(Self { config, mount_dir }) } } diff --git a/src/providers/proxmoxve/tests.rs b/src/providers/proxmoxve/tests.rs index 5708ed75..d07946a0 100644 --- a/src/providers/proxmoxve/tests.rs +++ b/src/providers/proxmoxve/tests.rs @@ -190,8 +190,9 @@ fn test_network_kargs_dhcp() { #[test] fn test_network_kargs_no_gateway() { - let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static-no-gateway")) - .expect("cannot parse config"); + let config = + ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static-no-gateway")) + .expect("cannot parse config"); let kargs = config.rd_network_kargs().expect("cannot get network kargs"); assert!(kargs.is_some()); @@ -237,8 +238,14 @@ fn test_netplan_config_static() { // Verify nameservers let nameservers = ð0["nameservers"]["addresses"]; - assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("1.1.1.1".into()))); - assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("8.8.8.8".into()))); + assert!(nameservers + .as_sequence() + .unwrap() + .contains(&serde_yaml::Value::String("1.1.1.1".into()))); + assert!(nameservers + .as_sequence() + .unwrap() + .contains(&serde_yaml::Value::String("8.8.8.8".into()))); } #[test] @@ -263,6 +270,12 @@ fn test_netplan_config_dhcp() { // Verify nameservers let nameservers = ð0["nameservers"]["addresses"]; - assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("1.1.1.1".into()))); - assert!(nameservers.as_sequence().unwrap().contains(&serde_yaml::Value::String("8.8.8.8".into()))); + assert!(nameservers + .as_sequence() + .unwrap() + .contains(&serde_yaml::Value::String("1.1.1.1".into()))); + assert!(nameservers + .as_sequence() + .unwrap() + .contains(&serde_yaml::Value::String("8.8.8.8".into()))); }