diff --git a/build_library/vm_image_util.sh b/build_library/vm_image_util.sh index ecaa792d5a5..38033ef998b 100644 --- a/build_library/vm_image_util.sh +++ b/build_library/vm_image_util.sh @@ -25,6 +25,7 @@ VALID_IMG_TYPES=( openstack_mini packet parallels + proxmoxve pxe qemu_uefi rackspace @@ -57,6 +58,7 @@ VALID_OEM_PACKAGES=( kubevirt openstack packet + proxmoxve qemu rackspace rackspace-onmetal @@ -332,6 +334,13 @@ IMG_akamai_OEM_PACKAGE=common-oem-files IMG_akamai_OEM_USE=akamai IMG_akamai_OEM_SYSEXT=oem-akamai +# proxmoxve +IMG_proxmoxve_DISK_FORMAT=qcow2 +IMG_proxmoxve_DISK_LAYOUT=vm +IMG_proxmoxve_OEM_PACKAGE=common-oem-files +IMG_proxmoxve_OEM_USE=proxmoxve +IMG_proxmoxve_OEM_SYSEXT=oem-proxmoxve + ########################################################### # Print the default vm type for the specified board diff --git a/changelog/changes/2024-08-17-proxmoxve.md b/changelog/changes/2024-08-17-proxmoxve.md new file mode 100644 index 00000000000..2470ec3616a --- /dev/null +++ b/changelog/changes/2024-08-17-proxmoxve.md @@ -0,0 +1 @@ +- Added Proxmox Virtual Environment images ([scripts#1783](https://github.com/flatcar/scripts/pull/1783)) diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-5.6.0-r1.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-5.6.0-r2.ebuild similarity index 100% rename from sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-5.6.0-r1.ebuild rename to sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-5.6.0-r2.ebuild diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-9999.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-9999.ebuild index 02247d4006e..d26a5ccc11b 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-9999.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/afterburn-9999.ebuild @@ -371,6 +371,7 @@ PATCHES=( # already upstreamed. "${FILESDIR}"/0004-providers-hetzner-fix-duplicate-attribute-prefix.patch "${FILESDIR}"/0005-providers-hetzner-private-ipv4-addresses-in-attribut.patch + "${FILESDIR}"/1023.patch ) src_unpack() { diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/1023.patch b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/1023.patch new file mode 100644 index 00000000000..96edaf68c5f --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/1023.patch @@ -0,0 +1,1031 @@ +From a92c78d32f7cb2c2296937bb04f89176fbef5778 Mon Sep 17 00:00:00 2001 +From: Arthur Chaloin +Date: Wed, 19 Jun 2024 18:06:09 +0000 +Subject: [PATCH] proxmoxve: implement proxmoxve provider + +allow reading cloud-init drive from proxmoxve. supports writing hostname, sshkeys and networkd configuration. +--- + dracut/30afterburn/afterburn-hostname.service | 1 + + src/metadata.rs | 2 + + src/network.rs | 62 ++++ + src/providers/digitalocean/mod.rs | 1 + + src/providers/ibmcloud_classic/mod.rs | 1 + + src/providers/mod.rs | 1 + + src/providers/packet/mod.rs | 3 + + src/providers/proxmoxve/cloudconfig.rs | 275 ++++++++++++++++++ + src/providers/proxmoxve/configdrive.rs | 63 ++++ + src/providers/proxmoxve/mod.rs | 22 ++ + src/providers/proxmoxve/tests.rs | 156 ++++++++++ + systemd/afterburn-sshkeys@.service.in | 1 + + tests/fixtures/proxmoxve/dhcp/meta-data | 1 + + tests/fixtures/proxmoxve/dhcp/network-config | 13 + + tests/fixtures/proxmoxve/dhcp/user-data | 13 + + tests/fixtures/proxmoxve/dhcp/vendor-data | 0 + .../proxmoxve/invalid-user-data/meta-data | 1 + + .../invalid-user-data/network-config | 30 ++ + .../proxmoxve/invalid-user-data/user-data | 5 + + .../proxmoxve/invalid-user-data/vendor-data | 0 + tests/fixtures/proxmoxve/static/meta-data | 1 + + .../fixtures/proxmoxve/static/network-config | 30 ++ + tests/fixtures/proxmoxve/static/user-data | 13 + + tests/fixtures/proxmoxve/static/vendor-data | 0 + 27 files changed, 707 insertions(+) + create mode 100644 src/providers/proxmoxve/cloudconfig.rs + create mode 100644 src/providers/proxmoxve/configdrive.rs + create mode 100644 src/providers/proxmoxve/mod.rs + create mode 100644 src/providers/proxmoxve/tests.rs + create mode 100644 tests/fixtures/proxmoxve/dhcp/meta-data + create mode 100644 tests/fixtures/proxmoxve/dhcp/network-config + create mode 100644 tests/fixtures/proxmoxve/dhcp/user-data + create mode 100644 tests/fixtures/proxmoxve/dhcp/vendor-data + create mode 100644 tests/fixtures/proxmoxve/invalid-user-data/meta-data + create mode 100644 tests/fixtures/proxmoxve/invalid-user-data/network-config + create mode 100644 tests/fixtures/proxmoxve/invalid-user-data/user-data + create mode 100644 tests/fixtures/proxmoxve/invalid-user-data/vendor-data + create mode 100644 tests/fixtures/proxmoxve/static/meta-data + create mode 100644 tests/fixtures/proxmoxve/static/network-config + create mode 100644 tests/fixtures/proxmoxve/static/user-data + create mode 100644 tests/fixtures/proxmoxve/static/vendor-data + +diff --git a/dracut/30afterburn/afterburn-hostname.service b/dracut/30afterburn/afterburn-hostname.service +index 485cd82a..268522bd 100644 +--- a/dracut/30afterburn/afterburn-hostname.service ++++ b/dracut/30afterburn/afterburn-hostname.service +@@ -12,6 +12,7 @@ ConditionKernelCommandLine=|ignition.platform.id=exoscale + ConditionKernelCommandLine=|ignition.platform.id=hetzner + ConditionKernelCommandLine=|ignition.platform.id=ibmcloud + ConditionKernelCommandLine=|ignition.platform.id=kubevirt ++ConditionKernelCommandLine=|ignition.platform.id=proxmoxve + ConditionKernelCommandLine=|ignition.platform.id=scaleway + ConditionKernelCommandLine=|ignition.platform.id=vultr + +diff --git a/src/metadata.rs b/src/metadata.rs +index b89bf5b3..94f9238b 100644 +--- a/src/metadata.rs ++++ b/src/metadata.rs +@@ -33,6 +33,7 @@ use crate::providers::openstack; + use crate::providers::openstack::network::OpenstackProviderNetwork; + use crate::providers::packet::PacketProvider; + use crate::providers::powervs::PowerVSProvider; ++use crate::providers::proxmoxve::ProxmoxVEConfigDrive; + use crate::providers::scaleway::ScalewayProvider; + use crate::providers::vmware::VmwareProvider; + use crate::providers::vultr::VultrProvider; +@@ -70,6 +71,7 @@ pub fn fetch_metadata(provider: &str) -> Result box_result!(OpenstackProviderNetwork::try_new()?), + "packet" => box_result!(PacketProvider::try_new()?), + "powervs" => box_result!(PowerVSProvider::try_new()?), ++ "proxmoxve" => box_result!(ProxmoxVEConfigDrive::try_new()?), + "scaleway" => box_result!(ScalewayProvider::try_new()?), + "vmware" => box_result!(VmwareProvider::try_new()?), + "vultr" => box_result!(VultrProvider::try_new()?), +diff --git a/src/network.rs b/src/network.rs +index 77957352..26c0a6fb 100644 +--- a/src/network.rs ++++ b/src/network.rs +@@ -79,6 +79,8 @@ pub struct Interface { + pub priority: u8, + pub nameservers: Vec, + pub ip_addresses: Vec, ++ // Optionally enable DHCP ++ pub dhcp: Option, + pub routes: Vec, + pub bond: Option, + pub unmanaged: bool, +@@ -128,6 +130,31 @@ impl NetDevKind { + } + } + ++/// Optional use of DHCP. ++#[allow(dead_code)] ++#[derive(Clone, Debug, PartialEq, Eq)] ++pub enum DhcpSetting { ++ Both, ++ V4, ++ V6, ++} ++ ++impl DhcpSetting { ++ /// Return DHCP setting according to `systemd.network` ++ /// ++ /// See [systemd documentation](dhcp) for the full list. ++ /// ++ /// dhcp: https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#DHCP= ++ fn sd_dhcp_setting(&self) -> String { ++ let setting = match *self { ++ DhcpSetting::Both => "yes", ++ DhcpSetting::V4 => "ipv4", ++ DhcpSetting::V6 => "ipv6", ++ }; ++ setting.to_string() ++ } ++} ++ + impl Interface { + /// Return a deterministic `systemd.network` unit name for this device. + pub fn sd_network_unit_name(&self) -> Result { +@@ -158,6 +185,9 @@ impl Interface { + + // [Network] section + writeln!(config, "\n[Network]").unwrap(); ++ if let Some(dhcp) = &self.dhcp { ++ writeln!(config, "DHCP={}", dhcp.sd_dhcp_setting()).unwrap(); ++ } + for ns in &self.nameservers { + writeln!(config, "DNS={ns}").unwrap() + } +@@ -246,6 +276,7 @@ mod tests { + priority: 20, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -261,6 +292,7 @@ mod tests { + priority: 10, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -276,6 +308,7 @@ mod tests { + priority: 20, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -291,6 +324,7 @@ mod tests { + priority: 20, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -306,6 +340,7 @@ mod tests { + priority: 20, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -330,6 +365,7 @@ mod tests { + priority: 20, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -387,6 +423,7 @@ mod tests { + Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 128).unwrap(), + ), + ], ++ dhcp: None, + routes: vec![NetworkRoute { + destination: IpNetwork::V4( + Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 8).unwrap(), +@@ -428,6 +465,7 @@ Gateway=127.0.0.1 + priority: 10, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -447,6 +485,7 @@ Gateway=127.0.0.1 + priority: 10, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: false, +@@ -470,6 +509,7 @@ RequiredForOnline=no + priority: 10, + nameservers: vec![], + ip_addresses: vec![], ++ dhcp: None, + routes: vec![], + bond: None, + unmanaged: true, +@@ -482,6 +522,28 @@ Name=* + + [Link] + Unmanaged=yes ++", ++ ), ++ // test the DHCP setting ++ ( ++ Interface { ++ name: Some("*".to_owned()), ++ mac_address: None, ++ path: None, ++ priority: 10, ++ nameservers: vec![], ++ ip_addresses: vec![], ++ dhcp: Some(DhcpSetting::V4), ++ routes: vec![], ++ bond: None, ++ unmanaged: false, ++ required_for_online: None, ++ }, ++ "[Match] ++Name=* ++ ++[Network] ++DHCP=ipv4 + ", + ), + ]; +diff --git a/src/providers/digitalocean/mod.rs b/src/providers/digitalocean/mod.rs +index 25836967..dd817dc4 100644 +--- a/src/providers/digitalocean/mod.rs ++++ b/src/providers/digitalocean/mod.rs +@@ -156,6 +156,7 @@ impl DigitalOceanProvider { + mac_address: Some(mac), + nameservers: self.dns.nameservers.clone(), + ip_addresses: addrs, ++ dhcp: None, + routes, + bond: None, + name: None, +diff --git a/src/providers/ibmcloud_classic/mod.rs b/src/providers/ibmcloud_classic/mod.rs +index 7c31707d..88c5483e 100644 +--- a/src/providers/ibmcloud_classic/mod.rs ++++ b/src/providers/ibmcloud_classic/mod.rs +@@ -251,6 +251,7 @@ impl IBMClassicProvider { + priority: 10, + nameservers: nameservers.clone(), + ip_addresses: vec![ip_net], ++ dhcp: None, + routes, + bond: None, + unmanaged: false, +diff --git a/src/providers/mod.rs b/src/providers/mod.rs +index ab9698a7..e17d5519 100644 +--- a/src/providers/mod.rs ++++ b/src/providers/mod.rs +@@ -38,6 +38,7 @@ pub mod microsoft; + pub mod openstack; + pub mod packet; + pub mod powervs; ++pub mod proxmoxve; + pub mod scaleway; + pub mod vmware; + pub mod vultr; +diff --git a/src/providers/packet/mod.rs b/src/providers/packet/mod.rs +index 314c6420..4a8ec351 100644 +--- a/src/providers/packet/mod.rs ++++ b/src/providers/packet/mod.rs +@@ -216,6 +216,7 @@ impl PacketProvider { + priority: 10, + nameservers: Vec::new(), + ip_addresses: Vec::new(), ++ dhcp: None, + routes: Vec::new(), + // the interface should be unmanaged if it doesn't have a bond + // section +@@ -241,6 +242,7 @@ impl PacketProvider { + path: None, + bond: None, + ip_addresses: Vec::new(), ++ dhcp: None, + routes: Vec::new(), + unmanaged: false, + required_for_online: Some("degraded-carrier".to_owned()), +@@ -334,6 +336,7 @@ impl PacketProvider { + bond: None, + nameservers: Vec::new(), + ip_addresses: Vec::new(), ++ dhcp: None, + routes: Vec::new(), + required_for_online: None, + }; +diff --git a/src/providers/proxmoxve/cloudconfig.rs b/src/providers/proxmoxve/cloudconfig.rs +new file mode 100644 +index 00000000..6be26763 +--- /dev/null ++++ b/src/providers/proxmoxve/cloudconfig.rs +@@ -0,0 +1,275 @@ ++use crate::{ ++ network::{self, DhcpSetting, NetworkRoute}, ++ providers::MetadataProvider, ++}; ++use anyhow::Result; ++use ipnetwork::IpNetwork; ++use openssh_keys::PublicKey; ++use pnet_base::MacAddr; ++use serde::Deserialize; ++use slog_scope::warn; ++use std::{ ++ collections::HashMap, ++ fs::File, ++ net::{AddrParseError, IpAddr}, ++ path::Path, ++ str::FromStr, ++}; ++ ++#[derive(Debug)] ++pub struct ProxmoxVECloudConfig { ++ pub meta_data: ProxmoxVECloudMetaData, ++ pub user_data: Option, ++ pub vendor_data: ProxmoxVECloudVendorData, ++ pub network_config: ProxmoxVECloudNetworkConfig, ++} ++ ++#[derive(Debug, Deserialize)] ++pub struct ProxmoxVECloudMetaData { ++ #[serde(rename = "instance-id")] ++ pub instance_id: String, ++} ++ ++#[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, ++} ++ ++#[derive(Debug, Deserialize)] ++pub struct ProxmoxVECloudNetworkConfigEntry { ++ #[serde(rename = "type")] ++ pub network_type: String, ++ pub name: Option, ++ pub mac_address: Option, ++ #[serde(default)] ++ pub address: Vec, ++ #[serde(default)] ++ pub search: Vec, ++ #[serde(default)] ++ pub subnets: Vec, ++} ++ ++#[derive(Debug, Deserialize)] ++pub struct ProxmoxVECloudNetworkConfigSubnet { ++ #[serde(rename = "type")] ++ pub subnet_type: String, ++ pub address: Option, ++ pub netmask: Option, ++ pub gateway: Option, ++} ++ ++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"))?; ++ ++ 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)?; ++ } ++ } ++ ++ if user_data.is_none() { ++ 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"))?)?, ++ }) ++ } ++} ++ ++impl MetadataProvider for ProxmoxVECloudConfig { ++ fn attributes(&self) -> Result> { ++ let mut out = HashMap::new(); ++ ++ out.insert( ++ "PROXMOXVE_INSTANCE_ID".to_owned(), ++ self.meta_data.instance_id.clone(), ++ ); ++ ++ if let Some(hostname) = self.hostname()? { ++ out.insert("PROXMOXVE_HOSTNAME".to_owned(), hostname); ++ } ++ ++ if let Some(first_interface) = self.networks()?.first() { ++ first_interface.ip_addresses.iter().for_each(|ip| match ip { ++ IpNetwork::V4(network) => { ++ out.insert("PROXMOXVE_IPV4".to_owned(), network.ip().to_string()); ++ } ++ IpNetwork::V6(network) => { ++ out.insert("PROXMOXVE_IPV6".to_owned(), network.ip().to_string()); ++ } ++ }); ++ } ++ ++ Ok(out) ++ } ++ ++ fn hostname(&self) -> Result> { ++ Ok(self ++ .user_data ++ .as_ref() ++ .map(|user_data| user_data.hostname.clone())) ++ } ++ ++ fn ssh_keys(&self) -> Result> { ++ if let Some(user_data) = &self.user_data { ++ return Ok(user_data ++ .ssh_authorized_keys ++ .iter() ++ .map(|key| PublicKey::from_str(key)) ++ .collect::, _>>()?); ++ } ++ ++ Ok(vec![]) ++ } ++ ++ fn networks(&self) -> Result> { ++ let nameservers = self ++ .network_config ++ .config ++ .iter() ++ .filter(|config| config.network_type == "nameserver") ++ .collect::>(); ++ ++ if nameservers.len() > 1 { ++ return Err(anyhow::anyhow!("too many nameservers, only one supported")); ++ } ++ ++ let mut interfaces = self ++ .network_config ++ .config ++ .iter() ++ .filter(|config| config.network_type == "physical") ++ .map(|entry| entry.to_interface()) ++ .collect::, _>>()?; ++ ++ if let Some(iface) = interfaces.first_mut() { ++ if let Some(nameserver) = nameservers.first() { ++ iface.nameservers = nameserver ++ .address ++ .iter() ++ .map(|ip| IpAddr::from_str(ip)) ++ .collect::, AddrParseError>>()?; ++ } ++ } ++ ++ Ok(interfaces) ++ } ++} ++ ++impl ProxmoxVECloudNetworkConfigEntry { ++ pub fn to_interface(&self) -> Result { ++ if self.network_type != "physical" { ++ return Err(anyhow::anyhow!( ++ "cannot convert config to interface: unsupported config type \"{}\"", ++ self.network_type ++ )); ++ } ++ ++ let mut iface = network::Interface { ++ name: self.name.clone(), ++ ++ // filled later ++ nameservers: vec![], ++ // filled below ++ ip_addresses: vec![], ++ // filled below ++ routes: vec![], ++ // filled below ++ dhcp: None, ++ // filled below because Option::try_map doesn't exist yet ++ mac_address: None, ++ ++ // unsupported by proxmox ve ++ bond: None, ++ ++ // default values ++ path: None, ++ priority: 20, ++ unmanaged: false, ++ required_for_online: None, ++ }; ++ ++ for subnet in &self.subnets { ++ if subnet.subnet_type.contains("static") { ++ if subnet.address.is_none() { ++ return Err(anyhow::anyhow!( ++ "cannot convert static subnet to interface: missing address" ++ )); ++ } ++ ++ if let Some(netmask) = &subnet.netmask { ++ iface.ip_addresses.push(IpNetwork::with_netmask( ++ IpAddr::from_str(subnet.address.as_ref().unwrap())?, ++ IpAddr::from_str(netmask)?, ++ )?); ++ } else { ++ iface ++ .ip_addresses ++ .push(IpNetwork::from_str(subnet.address.as_ref().unwrap())?); ++ } ++ ++ if let Some(gateway) = &subnet.gateway { ++ let gateway = IpAddr::from_str(gateway)?; ++ ++ let destination = if gateway.is_ipv6() { ++ IpNetwork::from_str("::/0")? ++ } else { ++ IpNetwork::from_str("0.0.0.0/0")? ++ }; ++ ++ iface.routes.push(NetworkRoute { ++ destination, ++ gateway, ++ }); ++ } else { ++ warn!("found subnet type \"static\" without gateway"); ++ } ++ } ++ ++ if subnet.subnet_type == "dhcp" || subnet.subnet_type == "dhcp4" { ++ iface.dhcp = Some(DhcpSetting::V4) ++ } ++ if subnet.subnet_type == "dhcp6" { ++ iface.dhcp = Some(DhcpSetting::V6) ++ } ++ if subnet.subnet_type == "ipv6_slaac" { ++ warn!("subnet type \"ipv6_slaac\" not supported, ignoring"); ++ } ++ } ++ ++ if let Some(mac) = &self.mac_address { ++ iface.mac_address = Some(MacAddr::from_str(mac)?); ++ } ++ ++ Ok(iface) ++ } ++} +diff --git a/src/providers/proxmoxve/configdrive.rs b/src/providers/proxmoxve/configdrive.rs +new file mode 100644 +index 00000000..4b31a76d +--- /dev/null ++++ b/src/providers/proxmoxve/configdrive.rs +@@ -0,0 +1,63 @@ ++use super::ProxmoxVECloudConfig; ++use crate::{network, providers::MetadataProvider}; ++use anyhow::{Context, Result}; ++use openssh_keys::PublicKey; ++use slog_scope::error; ++use std::{collections::HashMap, path::Path}; ++use tempfile::TempDir; ++ ++const CONFIG_DRIVE_LABEL: &str = "cidata"; ++const TARGET_FS: &str = "iso9660"; ++ ++#[derive(Debug)] ++pub struct ProxmoxVEConfigDrive { ++ mount_dir: TempDir, ++ config: ProxmoxVECloudConfig, ++} ++ ++impl ProxmoxVEConfigDrive { ++ pub fn try_new() -> Result { ++ let mount_dir = tempfile::Builder::new() ++ .prefix("afterburn-") ++ .tempdir() ++ .context("failed to create temporary directory")?; ++ ++ crate::util::mount_ro( ++ &Path::new("/dev/disk/by-label/").join(CONFIG_DRIVE_LABEL), ++ mount_dir.path(), ++ TARGET_FS, ++ 3, ++ )?; ++ ++ Ok(Self { ++ config: ProxmoxVECloudConfig::try_new(mount_dir.path())?, ++ mount_dir, ++ }) ++ } ++} ++ ++impl MetadataProvider for ProxmoxVEConfigDrive { ++ fn attributes(&self) -> Result> { ++ self.config.attributes() ++ } ++ ++ fn hostname(&self) -> Result> { ++ self.config.hostname() ++ } ++ ++ fn ssh_keys(&self) -> Result> { ++ self.config.ssh_keys() ++ } ++ ++ fn networks(&self) -> Result> { ++ self.config.networks() ++ } ++} ++ ++impl Drop for ProxmoxVEConfigDrive { ++ fn drop(&mut self) { ++ if let Err(e) = crate::util::unmount(self.mount_dir.path(), 3) { ++ error!("failed to cleanup Proxmox VE config-drive: {:?}", e); ++ }; ++ } ++} +diff --git a/src/providers/proxmoxve/mod.rs b/src/providers/proxmoxve/mod.rs +new file mode 100644 +index 00000000..14146b0e +--- /dev/null ++++ b/src/providers/proxmoxve/mod.rs +@@ -0,0 +1,22 @@ ++// Copyright 2017 CoreOS, Inc. ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++mod configdrive; ++pub use configdrive::*; ++ ++mod cloudconfig; ++pub use cloudconfig::*; ++ ++#[cfg(test)] ++mod tests; +diff --git a/src/providers/proxmoxve/tests.rs b/src/providers/proxmoxve/tests.rs +new file mode 100644 +index 00000000..b4b764e3 +--- /dev/null ++++ b/src/providers/proxmoxve/tests.rs +@@ -0,0 +1,156 @@ ++use super::ProxmoxVECloudConfig; ++use crate::{ ++ network::{self, DhcpSetting, NetworkRoute}, ++ providers::MetadataProvider, ++}; ++use ipnetwork::IpNetwork; ++use openssh_keys::PublicKey; ++use pnet_base::MacAddr; ++use std::{net::IpAddr, path::Path, str::FromStr}; ++ ++#[test] ++fn test_attributes() { ++ let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static")) ++ .expect("cannot parse config"); ++ let attributes = config.attributes().expect("cannot get hostname"); ++ ++ assert_eq!(attributes["PROXMOXVE_HOSTNAME"], "dummy".to_string()); ++ ++ assert_eq!( ++ attributes["PROXMOXVE_INSTANCE_ID"], ++ "15a9919cb91024fbd1d70fa07f0efa749cbba03b".to_string() ++ ); ++ ++ assert_eq!(attributes["PROXMOXVE_IPV4"], "192.168.1.1".to_string()); ++ ++ assert_eq!( ++ attributes["PROXMOXVE_IPV6"], ++ "2001:db8:85a3::8a2e:370:0".to_string() ++ ); ++} ++ ++#[test] ++fn test_hostname() { ++ let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/dhcp")) ++ .expect("cannot parse config"); ++ ++ assert_eq!( ++ config.hostname().expect("cannot get hostname"), ++ Some("dummy".to_string()) ++ ); ++} ++ ++#[test] ++fn test_ssh_keys() { ++ let test_ssh_key = PublicKey::from_str("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDd1hElre4j44sbmULXyO5j6dRnkRFCMjEGtRSy2SuvFD8WyB5uectcEMvz7ORhQIVbPlz94wFjpSX5wl/gmSKL/7GOyerJo0Y2cvyjJJahuDn+JnIL0tT0HS1pJ5iJqQpxXeOAzMK5Heum+uGw9BzbiUHnRzjJr8Ltx4CAGMfubevD4SX32Q8BTQiaU4ZnGtdHo16pWwRsq1f6/UtL4gDCni9vm8QmmGDRloi/pBn1csjKw+volFyu/kSEmGLWow6NuT6TrhGAbMKas5HfYq0Mn3LGPZL7XjqJQ6CO0TzkG/BNplZT2tiwHtsvXsbePTp4ZUi4dkCMz2xR4eikaI1V dummy@dummy.local").unwrap(); ++ let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/dhcp")) ++ .expect("cannot parse config"); ++ ++ assert_eq!( ++ config.ssh_keys().expect("cannot get ssh keys"), ++ vec![test_ssh_key] ++ ); ++} ++ ++#[test] ++fn test_network_dhcp() { ++ let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/dhcp")) ++ .expect("cannot parse config"); ++ ++ assert_eq!( ++ config.networks().expect("cannot get networks"), ++ vec![network::Interface { ++ name: Some("eth0".to_owned()), ++ mac_address: Some(MacAddr::from_str("01:23:45:67:89:00").unwrap()), ++ path: None, ++ priority: 20, ++ dhcp: Some(DhcpSetting::V4), ++ nameservers: vec![ ++ IpAddr::from_str("1.1.1.1").unwrap(), ++ IpAddr::from_str("8.8.8.8").unwrap() ++ ], ++ ip_addresses: vec![], ++ routes: vec![], ++ bond: None, ++ unmanaged: false, ++ required_for_online: None ++ }] ++ ); ++} ++ ++#[test] ++fn test_network_static() { ++ let config = ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/static")) ++ .expect("cannot parse config"); ++ ++ assert_eq!( ++ config.networks().expect("cannot get networks"), ++ vec![ ++ network::Interface { ++ name: Some("eth0".to_owned()), ++ mac_address: Some(MacAddr::from_str("01:23:45:67:89:00").unwrap()), ++ path: None, ++ priority: 20, ++ nameservers: vec![ ++ IpAddr::from_str("1.1.1.1").unwrap(), ++ IpAddr::from_str("8.8.8.8").unwrap() ++ ], ++ ip_addresses: vec![ ++ IpNetwork::from_str("192.168.1.1/24").unwrap(), ++ IpNetwork::from_str("2001:0db8:85a3:0000:0000:8a2e:0370:0/24").unwrap(), ++ ], ++ dhcp: None, ++ routes: vec![ ++ NetworkRoute { ++ destination: IpNetwork::from_str("0.0.0.0/0").unwrap(), ++ gateway: IpAddr::from_str("192.168.1.254").unwrap(), ++ }, ++ NetworkRoute { ++ destination: IpNetwork::from_str("::/0").unwrap(), ++ gateway: IpAddr::from_str("2001:0db8:85a3:0000:0000:8a2e:0370:9999") ++ .unwrap(), ++ }, ++ ], ++ bond: None, ++ unmanaged: false, ++ required_for_online: None ++ }, ++ network::Interface { ++ name: Some("eth1".to_owned()), ++ mac_address: Some(MacAddr::from_str("01:23:45:67:89:99").unwrap()), ++ path: None, ++ priority: 20, ++ nameservers: vec![], ++ ip_addresses: vec![ ++ IpNetwork::from_str("192.168.42.1/24").unwrap(), ++ IpNetwork::from_str("2001:0db8:85a3:0000:0000:8a2e:4242:0/24").unwrap(), ++ ], ++ dhcp: None, ++ routes: vec![ ++ NetworkRoute { ++ destination: IpNetwork::from_str("0.0.0.0/0").unwrap(), ++ gateway: IpAddr::from_str("192.168.42.254").unwrap(), ++ }, ++ NetworkRoute { ++ destination: IpNetwork::from_str("::/0").unwrap(), ++ gateway: IpAddr::from_str("2001:0db8:85a3:0000:0000:8a2e:4242:9999") ++ .unwrap(), ++ }, ++ ], ++ bond: None, ++ unmanaged: false, ++ required_for_online: None ++ }, ++ ] ++ ); ++} ++ ++#[test] ++fn test_invalid_user_data() { ++ let config = ++ ProxmoxVECloudConfig::try_new(Path::new("tests/fixtures/proxmoxve/invalid-user-data")) ++ .expect("cannot parse config"); ++ ++ assert!(config.hostname().unwrap().is_none()); ++ assert_eq!(config.ssh_keys().unwrap(), vec![]); ++} +diff --git a/systemd/afterburn-sshkeys@.service.in b/systemd/afterburn-sshkeys@.service.in +index 5709131e..911a57e6 100644 +--- a/systemd/afterburn-sshkeys@.service.in ++++ b/systemd/afterburn-sshkeys@.service.in +@@ -16,6 +16,7 @@ ConditionKernelCommandLine=|ignition.platform.id=gcp + ConditionKernelCommandLine=|ignition.platform.id=hetzner + ConditionKernelCommandLine=|ignition.platform.id=ibmcloud + ConditionKernelCommandLine=|ignition.platform.id=openstack ++ConditionKernelCommandLine=|ignition.platform.id=proxmoxve + ConditionKernelCommandLine=|ignition.platform.id=scaleway + ConditionKernelCommandLine=|ignition.platform.id=packet + ConditionKernelCommandLine=|ignition.platform.id=powervs +diff --git a/tests/fixtures/proxmoxve/dhcp/meta-data b/tests/fixtures/proxmoxve/dhcp/meta-data +new file mode 100644 +index 00000000..bd5926b3 +--- /dev/null ++++ b/tests/fixtures/proxmoxve/dhcp/meta-data +@@ -0,0 +1 @@ ++instance-id: 15a9919cb91024fbd1d70fa07f0efa749cbba03b +diff --git a/tests/fixtures/proxmoxve/dhcp/network-config b/tests/fixtures/proxmoxve/dhcp/network-config +new file mode 100644 +index 00000000..4031d025 +--- /dev/null ++++ b/tests/fixtures/proxmoxve/dhcp/network-config +@@ -0,0 +1,13 @@ ++version: 1 ++config: ++ - type: physical ++ name: eth0 ++ mac_address: '01:23:45:67:89:00' ++ subnets: ++ - type: dhcp4 ++ - type: nameserver ++ address: ++ - '1.1.1.1' ++ - '8.8.8.8' ++ search: ++ - 'local.com' +diff --git a/tests/fixtures/proxmoxve/dhcp/user-data b/tests/fixtures/proxmoxve/dhcp/user-data +new file mode 100644 +index 00000000..e62a3e6a +--- /dev/null ++++ b/tests/fixtures/proxmoxve/dhcp/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/dhcp/vendor-data b/tests/fixtures/proxmoxve/dhcp/vendor-data +new file mode 100644 +index 00000000..e69de29b +diff --git a/tests/fixtures/proxmoxve/invalid-user-data/meta-data b/tests/fixtures/proxmoxve/invalid-user-data/meta-data +new file mode 100644 +index 00000000..bd5926b3 +--- /dev/null ++++ b/tests/fixtures/proxmoxve/invalid-user-data/meta-data +@@ -0,0 +1 @@ ++instance-id: 15a9919cb91024fbd1d70fa07f0efa749cbba03b +diff --git a/tests/fixtures/proxmoxve/invalid-user-data/network-config b/tests/fixtures/proxmoxve/invalid-user-data/network-config +new file mode 100644 +index 00000000..e708ca1d +--- /dev/null ++++ b/tests/fixtures/proxmoxve/invalid-user-data/network-config +@@ -0,0 +1,30 @@ ++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' ++ gateway: '192.168.1.254' ++ - type: static6 ++ address: '2001:0db8:85a3:0000:0000:8a2e:0370:0/24' ++ gateway: '2001:0db8:85a3:0000:0000:8a2e:0370:9999' ++ - type: physical ++ name: eth1 ++ mac_address: '01:23:45:67:89:99' ++ subnets: ++ - type: static ++ address: '192.168.42.1' ++ netmask: '255.255.255.0' ++ gateway: '192.168.42.254' ++ - type: static6 ++ address: '2001:0db8:85a3:0000:0000:8a2e:4242:0/24' ++ gateway: '2001:0db8:85a3:0000:0000:8a2e:4242:9999' ++ - type: nameserver ++ address: ++ - '1.1.1.1' ++ - '8.8.8.8' ++ search: ++ - 'local.com' +diff --git a/tests/fixtures/proxmoxve/invalid-user-data/user-data b/tests/fixtures/proxmoxve/invalid-user-data/user-data +new file mode 100644 +index 00000000..c1baeda3 +--- /dev/null ++++ b/tests/fixtures/proxmoxve/invalid-user-data/user-data +@@ -0,0 +1,5 @@ ++{ ++ "some": { ++ "ignition": "config" ++ } ++} +diff --git a/tests/fixtures/proxmoxve/invalid-user-data/vendor-data b/tests/fixtures/proxmoxve/invalid-user-data/vendor-data +new file mode 100644 +index 00000000..e69de29b +diff --git a/tests/fixtures/proxmoxve/static/meta-data b/tests/fixtures/proxmoxve/static/meta-data +new file mode 100644 +index 00000000..bd5926b3 +--- /dev/null ++++ b/tests/fixtures/proxmoxve/static/meta-data +@@ -0,0 +1 @@ ++instance-id: 15a9919cb91024fbd1d70fa07f0efa749cbba03b +diff --git a/tests/fixtures/proxmoxve/static/network-config b/tests/fixtures/proxmoxve/static/network-config +new file mode 100644 +index 00000000..e708ca1d +--- /dev/null ++++ b/tests/fixtures/proxmoxve/static/network-config +@@ -0,0 +1,30 @@ ++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' ++ gateway: '192.168.1.254' ++ - type: static6 ++ address: '2001:0db8:85a3:0000:0000:8a2e:0370:0/24' ++ gateway: '2001:0db8:85a3:0000:0000:8a2e:0370:9999' ++ - type: physical ++ name: eth1 ++ mac_address: '01:23:45:67:89:99' ++ subnets: ++ - type: static ++ address: '192.168.42.1' ++ netmask: '255.255.255.0' ++ gateway: '192.168.42.254' ++ - type: static6 ++ address: '2001:0db8:85a3:0000:0000:8a2e:4242:0/24' ++ gateway: '2001:0db8:85a3:0000:0000:8a2e:4242:9999' ++ - type: nameserver ++ address: ++ - '1.1.1.1' ++ - '8.8.8.8' ++ search: ++ - 'local.com' +diff --git a/tests/fixtures/proxmoxve/static/user-data b/tests/fixtures/proxmoxve/static/user-data +new file mode 100644 +index 00000000..e62a3e6a +--- /dev/null ++++ b/tests/fixtures/proxmoxve/static/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/vendor-data b/tests/fixtures/proxmoxve/static/vendor-data +new file mode 100644 +index 00000000..e69de29b diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service index facc01224db..ad64b983b47 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service @@ -25,6 +25,8 @@ ConditionKernelCommandLine=|flatcar.oem.id=kubevirt ConditionKernelCommandLine=|flatcar.oem.id=akamai +ConditionKernelCommandLine=|flatcar.oem.id=proxmoxve + Description=Flatcar Metadata Agent [Service] diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/common-oem-files/common-oem-files-0-r8.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/common-oem-files/common-oem-files-0-r8.ebuild index 9c69deac343..a53c261d12d 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/common-oem-files/common-oem-files-0-r8.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/common-oem-files/common-oem-files-0-r8.ebuild @@ -35,6 +35,7 @@ COMMON_OEMIDS=( hetzner openstack packet + proxmoxve qemu scaleway kubevirt diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-0.0.1-r195.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-0.0.1-r196.ebuild similarity index 100% rename from sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-0.0.1-r195.ebuild rename to sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-0.0.1-r196.ebuild diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-proxmoxve/metadata.xml b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-proxmoxve/metadata.xml new file mode 100644 index 00000000000..097975e3adc --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-proxmoxve/metadata.xml @@ -0,0 +1,4 @@ + + + + diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-proxmoxve/oem-proxmoxve-0.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-proxmoxve/oem-proxmoxve-0.ebuild new file mode 100644 index 00000000000..bb7fa6e0629 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-proxmoxve/oem-proxmoxve-0.ebuild @@ -0,0 +1,15 @@ +# Copyright (c) 2013 CoreOS, Inc.. All rights reserved. +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DESCRIPTION="OEM suite for Proxmox VE" +HOMEPAGE="https://www.proxmox.com/en/proxmox-virtual-environment/" +SRC_URI="" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="amd64 arm64" +IUSE="" + +OEM_NAME="Proxmox VE" diff --git a/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/coreos-base/coreos-init/115.patch b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/coreos-base/coreos-init/115.patch new file mode 100644 index 00000000000..9a2c8ebdb21 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/coreos-base/coreos-init/115.patch @@ -0,0 +1,27 @@ +From 67ac31d0fc9ed22d83f2fc5a6449c6e35a83a1df Mon Sep 17 00:00:00 2001 +From: Kai Lueke +Date: Fri, 22 Mar 2024 20:58:23 +0900 +Subject: [PATCH] Draft for Proxmox support + +This makes use of https://github.com/coreos/afterburn/pull/1023 +to set up the ssh keys from the metadata. + +Signed-off-by: Mathieu Tortuyaux +--- + systemd/system/sshkeys.service | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/systemd/system/sshkeys.service b/systemd/system/sshkeys.service +index 7727185..9edf5cc 100644 +--- a/systemd/system/sshkeys.service ++++ b/systemd/system/sshkeys.service +@@ -30,6 +30,9 @@ ConditionKernelCommandLine=|flatcar.oem.id=hetzner + ConditionKernelCommandLine=|ignition.platform.id=akamai + ConditionKernelCommandLine=|flatcar.oem.id=akamai + ++ConditionKernelCommandLine=|ignition.platform.id=proxmoxve ++ConditionKernelCommandLine=|flatcar.oem.id=proxmoxve ++ + [Service] + Type=oneshot + RemainAfterExit=yes diff --git a/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/sys-kernel/bootengine/91.patch b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/sys-kernel/bootengine/91.patch new file mode 100644 index 00000000000..7f1a8154aa4 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/sys-kernel/bootengine/91.patch @@ -0,0 +1,40 @@ +From 10357a387459fc16a381f70601c44313668076ed Mon Sep 17 00:00:00 2001 +From: Kai Lueke +Date: Fri, 22 Mar 2024 20:23:49 +0900 +Subject: [PATCH] Draft for Proxmox support + +This makes use of https://github.com/coreos/afterburn/pull/1023 +to set up any static networking from the initrd (for Ignition) and the +hostname (early enough so that Ignition could overwrite it). +--- + dracut/30ignition/flatcar-metadata-hostname.service | 2 ++ + dracut/30ignition/ignition-generator | 2 +- + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dracut/30ignition/flatcar-metadata-hostname.service b/dracut/30ignition/flatcar-metadata-hostname.service +index fc75732..323c62e 100644 +--- a/dracut/30ignition/flatcar-metadata-hostname.service ++++ b/dracut/30ignition/flatcar-metadata-hostname.service +@@ -33,8 +33,10 @@ ConditionKernelCommandLine=|flatcar.oem.id=vultr + # Addition: + ConditionKernelCommandLine=|coreos.oem.id=packet + ConditionKernelCommandLine=|flatcar.oem.id=packet ++ + ConditionKernelCommandLine=|flatcar.oem.id=hetzner + ConditionKernelCommandLine=|flatcar.oem.id=kubevirt ++ConditionKernelCommandLine=|flatcar.oem.id=proxmoxve + + OnFailure=emergency.target + OnFailureJobMode=isolate +diff --git a/dracut/30ignition/ignition-generator b/dracut/30ignition/ignition-generator +index 59bdf80..c015ff5 100755 +--- a/dracut/30ignition/ignition-generator ++++ b/dracut/30ignition/ignition-generator +@@ -146,6 +146,6 @@ if [ "${nopxe}" = 1 ]; then + add_requires "disk-uuid.service" initrd.target + fi + +-if [[ $(cmdline_arg flatcar.oem.id) == "digitalocean" ]] || [[ $(cmdline_arg coreos.oem.id) == "digitalocean" ]]; then ++if [[ $(cmdline_arg flatcar.oem.id) == "digitalocean" ]] || [[ $(cmdline_arg coreos.oem.id) == "digitalocean" ]] || [[ $(cmdline_arg flatcar.oem.id) == "proxmoxve" ]]; then + add_requires flatcar-digitalocean-network.service initrd.target + fi diff --git a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/1910.patch b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/1910.patch new file mode 100644 index 00000000000..d79547bf169 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/1910.patch @@ -0,0 +1,172 @@ +From c91991cac13982e595e1f25a8e783accfbd63403 Mon Sep 17 00:00:00 2001 +From: Arthur Chaloin +Date: Mon, 29 Jul 2024 08:57:23 +0000 +Subject: [PATCH] providers: support for proxmox ve + +Co-authored-by: bri <284789+b-@users.noreply.github.com> +--- + internal/providers/proxmoxve/proxmoxve.go | 142 ++++++++++++++++++++++ + internal/register/providers.go | 1 + + 4 files changed, 146 insertions(+) + create mode 100644 internal/providers/proxmoxve/proxmoxve.go + +diff --git a/internal/providers/proxmoxve/proxmoxve.go b/internal/providers/proxmoxve/proxmoxve.go +new file mode 100644 +index 000000000..58fa4d570 +--- /dev/null ++++ b/internal/providers/proxmoxve/proxmoxve.go +@@ -0,0 +1,142 @@ ++// Copyright 2019 Red Hat, Inc. ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++// The OpenStack provider fetches configurations from the userdata available in ++// both the config-drive as well as the network metadata service. Whichever ++// responds first is the config that is used. ++// NOTE: This provider is still EXPERIMENTAL. ++ ++package proxmoxve ++ ++import ( ++ "bytes" ++ "context" ++ "fmt" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "time" ++ ++ "github.com/flatcar/ignition/v2/config/v3_5_experimental/types" ++ "github.com/flatcar/ignition/v2/internal/distro" ++ "github.com/flatcar/ignition/v2/internal/log" ++ "github.com/flatcar/ignition/v2/internal/platform" ++ "github.com/flatcar/ignition/v2/internal/providers/util" ++ "github.com/flatcar/ignition/v2/internal/resource" ++ ut "github.com/flatcar/ignition/v2/internal/util" ++ ++ "github.com/coreos/vcontext/report" ++) ++ ++const ( ++ cidataPath = "/user-data" ++ deviceLabel = "cidata" ++) ++ ++func init() { ++ platform.Register( ++ platform.Provider{ ++ Name: "proxmoxve", ++ Fetch: fetchConfig, ++ }, ++ ) ++} ++ ++func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { ++ var data []byte ++ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ++ ++ dispatch := func(name string, fn func() ([]byte, error)) { ++ raw, err := fn() ++ if err != nil { ++ switch err { ++ case context.Canceled: ++ case context.DeadlineExceeded: ++ f.Logger.Err("timed out while fetching config from %s", name) ++ default: ++ f.Logger.Err("failed to fetch config from %s: %v", name, err) ++ } ++ return ++ } ++ ++ data = raw ++ cancel() ++ } ++ ++ go dispatch( ++ "config drive (cidata)", func() ([]byte, error) { ++ return fetchConfigFromDevice(f.Logger, ctx, filepath.Join(distro.DiskByLabelDir(), deviceLabel)) ++ }, ++ ) ++ ++ <-ctx.Done() ++ if ctx.Err() == context.DeadlineExceeded { ++ f.Logger.Info("cidata drive was not available in time. Continuing without a config...") ++ } ++ ++ return util.ParseConfig(f.Logger, data) ++} ++ ++func fileExists(path string) bool { ++ _, err := os.Stat(path) ++ return (err == nil) ++} ++ ++func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, path string) ([]byte, error) { ++ for !fileExists(path) { ++ logger.Debug("config drive (%q) not found. Waiting...", path) ++ select { ++ case <-time.After(time.Second): ++ case <-ctx.Done(): ++ return nil, ctx.Err() ++ } ++ } ++ ++ logger.Debug("creating temporary mount point") ++ mnt, err := os.MkdirTemp("", "ignition-configdrive") ++ if err != nil { ++ return nil, fmt.Errorf("failed to create temp directory: %v", err) ++ } ++ defer os.Remove(mnt) ++ ++ cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", path, mnt) ++ if _, err := logger.LogCmd(cmd, "mounting config drive"); err != nil { ++ return nil, err ++ } ++ defer func() { ++ _ = logger.LogOp( ++ func() error { ++ return ut.UmountPath(mnt) ++ }, ++ "unmounting %q at %q", path, mnt, ++ ) ++ }() ++ ++ if !fileExists(filepath.Join(mnt, cidataPath)) { ++ return nil, nil ++ } ++ ++ contents, err := os.ReadFile(filepath.Join(mnt, cidataPath)) ++ if err != nil { ++ return nil, err ++ } ++ ++ header := []byte("#cloud-config\n") ++ if bytes.HasPrefix(contents, header) { ++ logger.Debug("config drive (%q) contains a cloud-config configuration, ignoring", path) ++ return nil, nil ++ } ++ ++ return contents, nil ++} +diff --git a/internal/register/providers.go b/internal/register/providers.go +index 5382e2f3a..bda4b7cfe 100644 +--- a/internal/register/providers.go ++++ b/internal/register/providers.go +@@ -35,6 +35,7 @@ import ( + _ "github.com/flatcar/ignition/v2/internal/providers/openstack" + _ "github.com/flatcar/ignition/v2/internal/providers/packet" + _ "github.com/flatcar/ignition/v2/internal/providers/powervs" ++ _ "github.com/flatcar/ignition/v2/internal/providers/proxmoxve" + _ "github.com/flatcar/ignition/v2/internal/providers/qemu" + _ "github.com/flatcar/ignition/v2/internal/providers/scaleway" + _ "github.com/flatcar/ignition/v2/internal/providers/virtualbox" diff --git a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-2.19.0-r1.ebuild b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-2.19.0-r2.ebuild similarity index 100% rename from sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-2.19.0-r1.ebuild rename to sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-2.19.0-r2.ebuild diff --git a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild index 735ca6979d1..12e98531d5f 100644 --- a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild @@ -62,6 +62,7 @@ PATCHES=( "${FILESDIR}/0020-internal-exec-stages-mount-Mount-oem.patch" "${FILESDIR}/0021-sgdisk-Run-partprobe-after-partition-changes.patch" "${FILESDIR}/0022-akamai-fix-base64-decoding.patch" + "${FILESDIR}/1910.patch" ) src_compile() { diff --git a/sdk_container/src/third_party/coreos-overlay/sys-kernel/bootengine/bootengine-0.0.38-r31.ebuild b/sdk_container/src/third_party/coreos-overlay/sys-kernel/bootengine/bootengine-0.0.38-r32.ebuild similarity index 100% rename from sdk_container/src/third_party/coreos-overlay/sys-kernel/bootengine/bootengine-0.0.38-r31.ebuild rename to sdk_container/src/third_party/coreos-overlay/sys-kernel/bootengine/bootengine-0.0.38-r32.ebuild