[PATCH installer v3 32/38] post-hook: switch to types in proxmox-installer-types

Christoph Heiss
2026-04-03T16:57:10Z

No functional changes.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
Changes v2 -> v3:
* new patch

Cargo.toml | 2 +-
proxmox-installer-common/src/dmi.rs | 43 ++
proxmox-installer-common/src/lib.rs | 1 +
proxmox-post-hook/Cargo.toml | 2 +-
proxmox-post-hook/src/main.rs | 682 +++++++++++-----------------
5 files changed, 313 insertions(+), 417 deletions(-)
create mode 100644 proxmox-installer-common/src/dmi.rs

diff --git a/Cargo.toml b/Cargo.toml
index 9d95796..2466822 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ toml = "0.8"
proxmox-auto-installer.path = "./proxmox-auto-installer"
proxmox-installer-common.path = "./proxmox-installer-common"
proxmox-network-types = "1.0"
-proxmox-installer-types = "0.1"
+proxmox-installer-types = { version = "0.1", features = ["legacy"] }

# Local path overrides
# NOTE: You must run `cargo update` after changing this for it to take effect!
diff --git a/proxmox-installer-common/src/dmi.rs b/proxmox-installer-common/src/dmi.rs
new file mode 100644
index 0000000..76ae4a5
--- /dev/null
+++ b/proxmox-installer-common/src/dmi.rs
@@ -0,0 +1,43 @@
+use std::{collections::HashMap, fs};
+
+use anyhow::{Result, bail};
+use proxmox_installer_types::SystemDMI;
+
+const DMI_PATH: &str = "/sys/devices/virtual/dmi/id";
+
+pub fn get() -> Result<SystemDMI> {
+ let system_files = [
+ "product_serial",
+ "product_sku",
+ "product_uuid",
+ "product_name",
+ ];
+ let baseboard_files = ["board_asset_tag", "board_serial", "board_name"];
+ let chassis_files = ["chassis_serial", "chassis_sku", "chassis_asset_tag"];
+
+ Ok(SystemDMI {
+ system: get_dmi_infos_for(&system_files)?,
+ baseboard: get_dmi_infos_for(&baseboard_files)?,
+ chassis: get_dmi_infos_for(&chassis_files)?,
+ })
+}
+
+fn get_dmi_infos_for(files: &[&str]) -> Result<HashMap<String, String>> {
+ let mut res: HashMap<String, String> = HashMap::new();
+
+ for file in files {
+ let path = format!("{DMI_PATH}/{file}");
+ let content = match fs::read_to_string(&path) {
+ Err(ref err) if err.kind() == std::io::ErrorKind::NotFound => continue,
+ Err(ref err) if err.kind() == std::io::ErrorKind::PermissionDenied => {
+ bail!("Could not read data. Are you running as root or with sudo?")
+ }
+ Err(err) => bail!("Error: '{err}' on '{path}'"),
+ Ok(content) => content.trim().into(),
+ };
+ let key = file.splitn(2, '_').last().unwrap();
+ res.insert(key.into(), content);
+ }
+
+ Ok(res)
+}
diff --git a/proxmox-installer-common/src/lib.rs b/proxmox-installer-common/src/lib.rs
index 7cdb1de..05445d5 100644
--- a/proxmox-installer-common/src/lib.rs
+++ b/proxmox-installer-common/src/lib.rs
@@ -1,4 +1,5 @@
pub mod disk_checks;
+pub mod dmi;
pub mod options;
pub mod setup;
pub mod sysinfo;
diff --git a/proxmox-post-hook/Cargo.toml b/proxmox-post-hook/Cargo.toml
index f0c344e..748b922 100644
--- a/proxmox-post-hook/Cargo.toml
+++ b/proxmox-post-hook/Cargo.toml
@@ -12,9 +12,9 @@ homepage = "https://www.proxmox.com"

[dependencies]
anyhow.workspace = true
-proxmox-auto-installer.workspace = true
proxmox-installer-common = { workspace = true, features = ["http"] }
proxmox-network-types.workspace = true
proxmox-installer-types.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
+toml.workspace = true
diff --git a/proxmox-post-hook/src/main.rs b/proxmox-post-hook/src/main.rs
index a05b30f..9025c01 100644
--- a/proxmox-post-hook/src/main.rs
+++ b/proxmox-post-hook/src/main.rs
@@ -9,212 +9,49 @@
//! Relies on `proxmox-chroot` as an external dependency to (bind-)mount the
//! previously installed system.

-use anyhow::{Context, Result, anyhow, bail};
-use proxmox_auto_installer::{
- answer::{
- Answer, FqdnConfig, FqdnExtendedConfig, FqdnSourceMode, PostNotificationHookInfo,
- RebootMode,
- },
- udevinfo::{UdevInfo, UdevProperties},
-};
-use proxmox_installer_common::http::{self, header::HeaderMap};
-use proxmox_installer_common::{
- options::{Disk, NetworkOptions},
- setup::{
- BootType, InstallConfig, IsoInfo, ProxmoxProduct, RuntimeInfo, SetupInfo,
- load_installer_setup_files,
- },
- sysinfo::SystemDMI,
-};
-use proxmox_installer_types::answer::FilesystemType;
-use proxmox_network_types::ip_address::Cidr;
-use serde::Serialize;
+use anyhow::{Context, Result, bail};
+use proxmox_installer_types::answer::{AutoInstallerConfig, PostNotificationHookInfo};
use std::{
- collections::HashSet,
- ffi::CStr,
- fs::{self, File},
- io::BufReader,
- os::unix::fs::FileExt,
- path::PathBuf,
+ fs,
+ io::Read,
process::{Command, ExitCode},
};

-/// Information about the system boot status.
-#[derive(Serialize)]
-struct BootInfo {
- /// Whether the system is booted using UEFI or legacy BIOS.
- mode: BootType,
- /// Whether SecureBoot is enabled for the installation.
- #[serde(skip_serializing_if = "bool_is_false")]
- secureboot: bool,
-}
+use proxmox_installer_common::http::{self, header::HeaderMap};

-/// Holds all the public keys for the different algorithms available.
-#[derive(Serialize)]
-struct SshPublicHostKeys {
- // ECDSA-based public host key
- ecdsa: String,
- // ED25519-based public host key
- ed25519: String,
- // RSA-based public host key
- rsa: String,
-}
+/// Current version of the schema sent by this implementation.
+const POST_HOOK_SCHEMA_VERSION: &str = "1.2";

-/// Holds information about a single disk in the system.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct DiskInfo {
- /// Size in bytes
- size: usize,
- /// Set to true if the disk is used for booting.
- #[serde(skip_serializing_if = "bool_is_false")]
- is_bootdisk: bool,
- /// Properties about the device as given by udev.
- udev_properties: UdevProperties,
-}
+mod detail {
+ use anyhow::{Context, Result, anyhow, bail};
+ use std::{
+ collections::HashSet,
+ ffi::CStr,
+ fs::{self, File},
+ io::BufReader,
+ os::unix::fs::FileExt,
+ path::PathBuf,
+ process::Command,
+ };

-/// Holds information about the management network interface.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct NetworkInterfaceInfo {
- /// Name of the interface
- name: String,
- /// MAC address of the interface
- mac: String,
- /// (Designated) IP address of the interface
- #[serde(skip_serializing_if = "Option::is_none")]
- address: Option<Cidr>,
- /// Set to true if the interface is the chosen management interface during
- /// installation.
- #[serde(skip_serializing_if = "bool_is_false")]
- is_management: bool,
- /// Set to true if the network interface name was pinned based on the MAC
- /// address during the installation.
- #[serde(skip_serializing_if = "bool_is_false")]
- is_pinned: bool,
- /// Properties about the device as given by udev.
- udev_properties: UdevProperties,
-}
+ use proxmox_installer_common::{
+ options::{Disk, NetworkOptions},
+ setup::{
+ InstallConfig, ProxmoxProduct, RuntimeInfo, SetupInfo, load_installer_setup_files,
+ },
+ };
+ use proxmox_installer_types::{
+ BootType, IsoInfo, UdevInfo,
+ answer::{AutoInstallerConfig, FqdnConfig, FqdnFromDhcpConfig, FqdnSourceMode},
+ post_hook::{
+ BootInfo, CpuInfo, DiskInfo, KernelVersionInformation, NetworkInterfaceInfo,
+ PostHookInfo, PostHookInfoSchema, ProductInfo, SshPublicHostKeys,
+ },
+ };

-fn bool_is_false(value: &bool) -> bool {
- !value
-}
+ /// Defines the size of a gibibyte in bytes.
+ const SIZE_GIB: usize = 1024 * 1024 * 1024;

-/// Information about the installed product itself.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct ProductInfo {
- /// Full name of the product
- fullname: String,
- /// Product abbreviation
- short: ProxmoxProduct,
- /// Version of the installed product
- version: String,
-}
-
-/// The current kernel version.
-/// Aligns with the format as used by the `/nodes/<node>/status` API of each product.
-#[derive(Serialize)]
-struct KernelVersionInformation {
- /// The systemname/nodename
- pub sysname: String,
- /// The kernel release number
- pub release: String,
- /// The kernel version
- pub version: String,
- /// The machine architecture
- pub machine: String,
-}
-
-/// Information about the CPU(s) installed in the system
-#[derive(Serialize)]
-struct CpuInfo {
- /// Number of physical CPU cores.
- cores: usize,
- /// Number of logical CPU cores aka. threads.
- cpus: usize,
- /// CPU feature flag set as a space-delimited list.
- flags: String,
- /// Whether hardware-accelerated virtualization is supported.
- hvm: bool,
- /// Reported model of the CPU(s)
- model: String,
- /// Number of physical CPU sockets
- sockets: usize,
-}
-
-/// Metadata of the hook, such as schema version of the document.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct PostHookInfoSchema {
- /// major.minor version describing the schema version of this document, in a semanticy-version
- /// way.
- ///
- /// major: Incremented for incompatible/breaking API changes, e.g. removing an existing
- /// field.
- /// minor: Incremented when adding functionality in a backwards-compatible matter, e.g.
- /// adding a new field.
- version: String,
-}
-
-impl PostHookInfoSchema {
- const SCHEMA_VERSION: &str = "1.2";
-}
-
-impl Default for PostHookInfoSchema {
- fn default() -> Self {
- Self {
- version: Self::SCHEMA_VERSION.to_owned(),
- }
- }
-}
-
-/// All data sent as request payload with the post-installation-webhook POST request.
-///
-/// NOTE: The format is versioned through `schema.version` (`$schema.version` in the
-/// resulting JSON), ensure you update it when this struct or any of its members gets modified.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct PostHookInfo {
- // This field is prefixed by `$` on purpose, to indicate that it is document metadata and not
- // part of the actual content itself. (E.g. JSON Schema uses a similar naming scheme)
- #[serde(rename = "$schema")]
- schema: PostHookInfoSchema,
- /// major.minor version of Debian as installed, retrieved from /etc/debian_version
- debian_version: String,
- /// PVE/PMG/PBS/PDM version as reported by `pveversion`, `pmgversion`,
- /// `proxmox-backup-manager version` or `proxmox-datacenter-manager version`, respectively.
- product: ProductInfo,
- /// Release information for the ISO used for the installation.
- iso: IsoInfo,
- /// Installed kernel version
- kernel_version: KernelVersionInformation,
- /// Describes the boot mode of the machine and the SecureBoot status.
- boot_info: BootInfo,
- /// Information about the installed CPU(s)
- cpu_info: CpuInfo,
- /// DMI information about the system
- dmi: SystemDMI,
- /// Filesystem used for boot disk(s)
- filesystem: FilesystemType,
- /// Fully qualified domain name of the installed system
- fqdn: String,
- /// Unique systemd-id128 identifier of the installed system (128-bit, 16 bytes)
- machine_id: String,
- /// All disks detected on the system.
- disks: Vec<DiskInfo>,
- /// All network interfaces detected on the system.
- network_interfaces: Vec<NetworkInterfaceInfo>,
- /// Public parts of SSH host keys of the installed system
- ssh_public_host_keys: SshPublicHostKeys,
- /// Action to will be performed, i.e. either reboot or power off the machine.
- reboot_mode: RebootMode,
-}
-
-/// Defines the size of a gibibyte in bytes.
-const SIZE_GIB: usize = 1024 * 1024 * 1024;
-
-impl PostHookInfo {
/// Gathers all needed information about the newly installed system for sending
/// it to a specified server.
///
@@ -222,7 +59,7 @@ impl PostHookInfo {
///
/// * `target_path` - Path to where the chroot environment root is mounted
/// * `answer` - Answer file as provided by the user
- fn gather(target_path: &str, answer: &Answer) -> Result<Self> {
+ pub fn gather(target_path: &str, answer: &AutoInstallerConfig) -> Result<PostHookInfo> {
println!("Gathering installed system data ...");

let config: InstallConfig =
@@ -265,34 +102,42 @@ impl PostHookInfo {

let fqdn = match &answer.global.fqdn {
FqdnConfig::Simple(name) => name.to_string(),
- FqdnConfig::Extended(FqdnExtendedConfig {
+ FqdnConfig::FromDhcp(FqdnFromDhcpConfig {
source: FqdnSourceMode::FromDhcp,
domain,
}) => NetworkOptions::construct_fqdn(
&run_env.network,
- setup_info.config.product.default_hostname(),
+ &setup_info.config.product.to_string(),
domain.as_deref(),
)
.to_string(),
};

- Ok(Self {
- schema: PostHookInfoSchema::default(),
+ Ok(PostHookInfo {
+ schema: PostHookInfoSchema {
+ version: super::POST_HOOK_SCHEMA_VERSION.to_owned(),
+ },
debian_version: read_file("/etc/debian_version")?,
- product: Self::gather_product_info(&setup_info, &run_cmd)?,
- iso: setup_info.iso_info.clone(),
- kernel_version: Self::gather_kernel_version(&run_cmd, &open_file)?,
+ product: gather_product_info(&setup_info, &run_cmd)?,
+ iso: IsoInfo {
+ release: setup_info.iso_info.release,
+ isorelease: setup_info.iso_info.isorelease,
+ },
+ kernel_version: gather_kernel_version(&run_cmd, &open_file)?,
boot_info: BootInfo {
- mode: run_env.boot_type,
+ mode: match run_env.boot_type {
+ proxmox_installer_common::setup::BootType::Bios => BootType::Bios,
+ proxmox_installer_common::setup::BootType::Efi => BootType::Efi,
+ },
secureboot: run_env.secure_boot,
},
- cpu_info: Self::gather_cpu_info(&run_env)?,
- dmi: SystemDMI::get()?,
- filesystem: answer.disks.fs_type,
+ cpu_info: gather_cpu_info(&run_env)?,
+ dmi: proxmox_installer_common::dmi::get()?,
+ filesystem: answer.disks.filesystem_details()?.to_type(),
fqdn,
machine_id: read_file("/etc/machine-id")?,
- disks: Self::gather_disks(&config, &run_env, &udev)?,
- network_interfaces: Self::gather_nic(&config, &run_env, &udev)?,
+ disks: gather_disks(&config, &run_env, &udev)?,
+ network_interfaces: gather_nic(&config, &run_env, &udev)?,
ssh_public_host_keys: SshPublicHostKeys {
ecdsa: read_file("/etc/ssh/ssh_host_ecdsa_key.pub")?,
ed25519: read_file("/etc/ssh/ssh_host_ed25519_key.pub")?,
@@ -335,10 +180,10 @@ impl PostHookInfo {
.target_hd
.as_ref()
.map(|hd| *hd == disk.path)
- .unwrap_or_default();
+ .unwrap_or(false);

anyhow::Ok(DiskInfo {
- size: (config.hdsize * (SIZE_GIB as f64)) as usize,
+ size: (config.hdsize * (SIZE_GIB as f64)) as u64,
is_bootdisk,
udev_properties: get_udev_properties(disk)?,
})
@@ -346,7 +191,7 @@ impl PostHookInfo {
.collect()
} else {
// If the filesystem is not LVM-based (thus Btrfs or ZFS), `config.disk_selection`
- // contains a list of indices identifiying the boot disks, as given by udev.
+ // contains a list of indices identifying the boot disks, as given by udev.
let selected_disks_indices: Vec<&String> = config.disk_selection.values().collect();

run_env
@@ -356,7 +201,7 @@ impl PostHookInfo {
let is_bootdisk = selected_disks_indices.contains(&&disk.index);

anyhow::Ok(DiskInfo {
- size: (config.hdsize * (SIZE_GIB as f64)) as usize,
+ size: (config.hdsize * (SIZE_GIB as f64)) as u64,
is_bootdisk,
udev_properties: get_udev_properties(disk)?,
})
@@ -443,7 +288,12 @@ impl PostHookInfo {

Ok(ProductInfo {
fullname: setup_info.config.fullname.clone(),
- short: setup_info.config.product,
+ short: match setup_info.config.product {
+ ProxmoxProduct::PVE => proxmox_installer_types::ProxmoxProduct::Pve,
+ ProxmoxProduct::PBS => proxmox_installer_types::ProxmoxProduct::Pbs,
+ ProxmoxProduct::PMG => proxmox_installer_types::ProxmoxProduct::Pmg,
+ ProxmoxProduct::PDM => proxmox_installer_types::ProxmoxProduct::Pdm,
+ },
version,
})
}
@@ -465,7 +315,7 @@ impl PostHookInfo {
run_cmd: &dyn Fn(&[&str]) -> Result<String>,
open_file: &dyn Fn(&str) -> Result<File>,
) -> Result<KernelVersionInformation> {
- let file = open_file(&Self::find_kernel_image_path(run_cmd)?)?;
+ let file = open_file(&find_kernel_image_path(run_cmd)?)?;

// Read the 2-byte `kernel_version` field at offset 0x20e [0] from the file ..
// https://www.kernel.org/doc/html/latest/arch/x86/boot.html#the-real-mode-kernel-header
@@ -525,7 +375,7 @@ impl PostHookInfo {
run_cmd: &dyn Fn(&[&str]) -> Result<String>,
_open_file: &dyn Fn(&str) -> Result<File>,
) -> Result<KernelVersionInformation> {
- let image_path = Self::find_kernel_image_path(run_cmd)?;
+ let image_path = find_kernel_image_path(run_cmd)?;

let release = image_path
.strip_prefix("/boot/vmlinuz-")
@@ -556,7 +406,7 @@ impl PostHookInfo {
///
/// * `run_cmd` - Callback to run a command inside the target chroot.
fn find_kernel_image_path(run_cmd: &dyn Fn(&[&str]) -> Result<String>) -> Result<String> {
- let pkg_name = Self::find_kernel_package_name(run_cmd)?;
+ let pkg_name = find_kernel_package_name(run_cmd)?;

let all_files = run_cmd(&["dpkg-query", "--listfiles", &pkg_name])?;
for file in all_files.lines() {
@@ -663,6 +513,200 @@ impl PostHookInfo {

Ok(result)
}
+
+ #[cfg(test)]
+ mod tests {
+ use super::{find_kernel_image_path, find_kernel_package_name};
+
+ #[test]
+ fn finds_correct_kernel_package_name() {
+ let mocked_run_cmd = |cmd: &[&str]| {
+ if cmd[0] == "dpkg" {
+ assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+ Ok("amd64\n".to_owned())
+ } else {
+ assert_eq!(
+ cmd,
+ &[
+ "dpkg-query",
+ "--showformat",
+ "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+ "--show",
+ "proxmox-kernel-[0-9]*",
+ ]
+ );
+ Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
+ "#
+ .to_owned())
+ }
+ };
+
+ assert_eq!(
+ find_kernel_package_name(&mocked_run_cmd).unwrap(),
+ "proxmox-kernel-6.8.8-2-pve-signed"
+ );
+ }
+
+ #[test]
+ fn finds_correct_kernel_package_name_arm64() {
+ let mocked_run_cmd = |cmd: &[&str]| {
+ if cmd[0] == "dpkg" {
+ assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+ Ok("arm64\n".to_owned())
+ } else {
+ assert_eq!(
+ cmd,
+ &[
+ "dpkg-query",
+ "--showformat",
+ "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+ "--show",
+ "proxmox-kernel-[0-9]*",
+ ]
+ );
+ Ok(r#"ii |all|proxmox-kernel-6.17
+un ||proxmox-kernel-6.17.2-1-pve
+ii |arm64|proxmox-kernel-6.17.2-1-pve-signed
+ "#
+ .to_owned())
+ }
+ };
+
+ assert_eq!(
+ find_kernel_package_name(&mocked_run_cmd).unwrap(),
+ "proxmox-kernel-6.17.2-1-pve-signed"
+ );
+ }
+
+ #[test]
+ fn find_kernel_package_name_fails_on_wrong_architecture() {
+ let mocked_run_cmd = |cmd: &[&str]| {
+ if cmd[0] == "dpkg" {
+ assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+ Ok("arm64\n".to_owned())
+ } else {
+ assert_eq!(
+ cmd,
+ &[
+ "dpkg-query",
+ "--showformat",
+ "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+ "--show",
+ "proxmox-kernel-[0-9]*",
+ ]
+ );
+ Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
+ "#
+ .to_owned())
+ }
+ };
+
+ assert_eq!(
+ find_kernel_package_name(&mocked_run_cmd)
+ .unwrap_err()
+ .to_string(),
+ "failed to find installed kernel package"
+ );
+ }
+
+ #[test]
+ fn find_kernel_package_name_fails_on_missing_package() {
+ let mocked_run_cmd = |cmd: &[&str]| {
+ if cmd[0] == "dpkg" {
+ assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+ Ok("amd64\n".to_owned())
+ } else {
+ assert_eq!(
+ cmd,
+ &[
+ "dpkg-query",
+ "--showformat",
+ "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+ "--show",
+ "proxmox-kernel-[0-9]*",
+ ]
+ );
+ Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ "#
+ .to_owned())
+ }
+ };
+
+ assert_eq!(
+ find_kernel_package_name(&mocked_run_cmd)
+ .unwrap_err()
+ .to_string(),
+ "failed to find installed kernel package"
+ );
+ }
+
+ #[test]
+ fn finds_correct_absolute_kernel_image_path() {
+ let mocked_run_cmd = |cmd: &[&str]| {
+ if cmd[0] == "dpkg" {
+ assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+ Ok("amd64\n".to_owned())
+ } else if cmd[0..=1] == ["dpkg-query", "--showformat"] {
+ assert_eq!(
+ cmd,
+ &[
+ "dpkg-query",
+ "--showformat",
+ "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+ "--show",
+ "proxmox-kernel-[0-9]*",
+ ]
+ );
+ Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
+ "#
+ .to_owned())
+ } else {
+ assert_eq!(
+ cmd,
+ [
+ "dpkg-query",
+ "--listfiles",
+ "proxmox-kernel-6.8.8-2-pve-signed"
+ ]
+ );
+ Ok(r#"
+/.
+/boot
+/boot/System.map-6.8.8-2-pve
+/boot/config-6.8.8-2-pve
+/boot/vmlinuz-6.8.8-2-pve
+/lib
+/lib/modules
+/lib/modules/6.8.8-2-pve
+/lib/modules/6.8.8-2-pve/kernel
+/lib/modules/6.8.8-2-pve/kernel/arch
+/lib/modules/6.8.8-2-pve/kernel/arch/x86
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aegis128-aesni.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aesni-intel.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx2-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-gfni-avx512-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/blowfish-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko
+ "#
+ .to_owned())
+ }
+ };
+
+ assert_eq!(
+ find_kernel_image_path(&mocked_run_cmd).unwrap(),
+ "/boot/vmlinuz-6.8.8-2-pve"
+ );
+ }
+ }
}

/// Runs the specified callback with the mounted chroot, passing along the
@@ -700,7 +744,9 @@ fn with_chroot<R, F: FnOnce(&str) -> Result<R>>(callback: F) -> Result<R> {
/// optional certificate fingerprint for HTTPS). If configured, retrieves all relevant information
/// about the installed system and sends them to the given endpoint.
fn do_main() -> Result<()> {
- let answer = Answer::try_from_reader(std::io::stdin().lock())?;
+ let mut raw_toml = String::new();
+ std::io::stdin().read_to_string(&mut raw_toml)?;
+ let answer: AutoInstallerConfig = toml::from_str(&raw_toml)?;

if let Some(PostNotificationHookInfo {
url,
@@ -709,7 +755,7 @@ fn do_main() -> Result<()> {
{
println!("Found post-installation-webhook; sending POST request to '{url}'.");

- let info = with_chroot(|target_path| PostHookInfo::gather(target_path, &answer))?;
+ let info = with_chroot(|target_path| detail::gather(target_path, &answer))?;

if let Err(err) = fs::write(
"/run/proxmox-installer/post-hook-data.json",
@@ -743,197 +789,3 @@ fn main() -> ExitCode {
}
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::PostHookInfo;
-
- #[test]
- fn finds_correct_kernel_package_name() {
- let mocked_run_cmd = |cmd: &[&str]| {
- if cmd[0] == "dpkg" {
- assert_eq!(cmd, &["dpkg", "--print-architecture"]);
- Ok("amd64\n".to_owned())
- } else {
- assert_eq!(
- cmd,
- &[
- "dpkg-query",
- "--showformat",
- "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
- "--show",
- "proxmox-kernel-[0-9]*",
- ]
- );
- Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
- "#
- .to_owned())
- }
- };
-
- assert_eq!(
- PostHookInfo::find_kernel_package_name(&mocked_run_cmd).unwrap(),
- "proxmox-kernel-6.8.8-2-pve-signed"
- );
- }
-
- #[test]
- fn finds_correct_kernel_package_name_arm64() {
- let mocked_run_cmd = |cmd: &[&str]| {
- if cmd[0] == "dpkg" {
- assert_eq!(cmd, &["dpkg", "--print-architecture"]);
- Ok("arm64\n".to_owned())
- } else {
- assert_eq!(
- cmd,
- &[
- "dpkg-query",
- "--showformat",
- "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
- "--show",
- "proxmox-kernel-[0-9]*",
- ]
- );
- Ok(r#"ii |all|proxmox-kernel-6.17
-un ||proxmox-kernel-6.17.2-1-pve
-ii |arm64|proxmox-kernel-6.17.2-1-pve-signed
- "#
- .to_owned())
- }
- };
-
- assert_eq!(
- PostHookInfo::find_kernel_package_name(&mocked_run_cmd).unwrap(),
- "proxmox-kernel-6.17.2-1-pve-signed"
- );
- }
-
- #[test]
- fn find_kernel_package_name_fails_on_wrong_architecture() {
- let mocked_run_cmd = |cmd: &[&str]| {
- if cmd[0] == "dpkg" {
- assert_eq!(cmd, &["dpkg", "--print-architecture"]);
- Ok("arm64\n".to_owned())
- } else {
- assert_eq!(
- cmd,
- &[
- "dpkg-query",
- "--showformat",
- "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
- "--show",
- "proxmox-kernel-[0-9]*",
- ]
- );
- Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
- "#
- .to_owned())
- }
- };
-
- assert_eq!(
- PostHookInfo::find_kernel_package_name(&mocked_run_cmd)
- .unwrap_err()
- .to_string(),
- "failed to find installed kernel package"
- );
- }
-
- #[test]
- fn find_kernel_package_name_fails_on_missing_package() {
- let mocked_run_cmd = |cmd: &[&str]| {
- if cmd[0] == "dpkg" {
- assert_eq!(cmd, &["dpkg", "--print-architecture"]);
- Ok("amd64\n".to_owned())
- } else {
- assert_eq!(
- cmd,
- &[
- "dpkg-query",
- "--showformat",
- "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
- "--show",
- "proxmox-kernel-[0-9]*",
- ]
- );
- Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
- "#
- .to_owned())
- }
- };
-
- assert_eq!(
- PostHookInfo::find_kernel_package_name(&mocked_run_cmd)
- .unwrap_err()
- .to_string(),
- "failed to find installed kernel package"
- );
- }
-
- #[test]
- fn finds_correct_absolute_kernel_image_path() {
- let mocked_run_cmd = |cmd: &[&str]| {
- if cmd[0] == "dpkg" {
- assert_eq!(cmd, &["dpkg", "--print-architecture"]);
- Ok("amd64\n".to_owned())
- } else if cmd[0..=1] == ["dpkg-query", "--showformat"] {
- assert_eq!(
- cmd,
- &[
- "dpkg-query",
- "--showformat",
- "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
- "--show",
- "proxmox-kernel-[0-9]*",
- ]
- );
- Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
- "#
- .to_owned())
- } else {
- assert_eq!(
- cmd,
- [
- "dpkg-query",
- "--listfiles",
- "proxmox-kernel-6.8.8-2-pve-signed"
- ]
- );
- Ok(r#"
-/.
-/boot
-/boot/System.map-6.8.8-2-pve
-/boot/config-6.8.8-2-pve
-/boot/vmlinuz-6.8.8-2-pve
-/lib
-/lib/modules
-/lib/modules/6.8.8-2-pve
-/lib/modules/6.8.8-2-pve/kernel
-/lib/modules/6.8.8-2-pve/kernel/arch
-/lib/modules/6.8.8-2-pve/kernel/arch/x86
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aegis128-aesni.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aesni-intel.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx2-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-gfni-avx512-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/blowfish-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko
- "#
- .to_owned())
- }
- };
-
- assert_eq!(
- PostHookInfo::find_kernel_image_path(&mocked_run_cmd).unwrap(),
- "/boot/vmlinuz-6.8.8-2-pve"
- );
- }
-}
--
2.53.0