public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH installer v3 32/38] post-hook: switch to types in proxmox-installer-types
Date: Fri,  3 Apr 2026 18:54:04 +0200	[thread overview]
Message-ID: <20260403165437.2166551-33-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260403165437.2166551-1-c.heiss@proxmox.com>

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





  parent reply	other threads:[~2026-04-03 16:57 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-03 16:53 [PATCH proxmox/yew-pwt/datacenter-manager/installer v3 00/38] add auto-installer integration Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 01/38] api-macro: allow $ in identifier name Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 02/38] schema: oneOf: allow single string variant Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 03/38] schema: implement UpdaterType for HashMap and BTreeMap Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 04/38] network-types: move `Fqdn` type from proxmox-installer-common Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 05/38] network-types: implement api type for Fqdn Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 06/38] network-types: add api wrapper type for std::net::IpAddr Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 07/38] network-types: cidr: implement generic `IpAddr::new` constructor Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 08/38] network-types: fqdn: implement standard library Error for Fqdn Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 09/38] node-status: make KernelVersionInformation Clone + PartialEq Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 10/38] installer-types: add common types used by the installer Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 11/38] installer-types: add types used by the auto-installer Christoph Heiss
2026-04-03 16:53 ` [PATCH proxmox v3 12/38] installer-types: implement api type for all externally-used types Christoph Heiss
2026-04-03 16:53 ` [PATCH yew-widget-toolkit v3 13/38] widget: kvlist: add widget for user-modifiable data tables Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 14/38] api-types, cli: use ReturnType::new() instead of constructing it manually Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 15/38] api-types: add api types for auto-installer integration Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 16/38] config: add auto-installer configuration module Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 17/38] acl: wire up new /system/auto-installation acl path Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 18/38] server: api: add auto-installer integration module Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 19/38] server: api: auto-installer: add access token management endpoints Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 20/38] client: add bindings for auto-installer endpoints Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 21/38] ui: auto-installer: add installations overview panel Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 22/38] ui: auto-installer: add prepared answer configuration panel Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 23/38] ui: auto-installer: add access token " Christoph Heiss
2026-04-03 16:53 ` [PATCH datacenter-manager v3 24/38] docs: add documentation for auto-installer integration Christoph Heiss
2026-04-03 16:53 ` [PATCH installer v3 25/38] install: iso env: use JSON boolean literals for product config Christoph Heiss
2026-04-03 16:53 ` [PATCH installer v3 26/38] common: http: allow passing custom headers to post() Christoph Heiss
2026-04-03 16:53 ` [PATCH installer v3 27/38] common: options: move regex construction out of loop Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 28/38] assistant: support adding an authorization token for HTTP-based answers Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 29/38] tree-wide: used moved `Fqdn` type to proxmox-network-types Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 30/38] tree-wide: use `Cidr` type from proxmox-network-types Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 31/38] tree-wide: switch to filesystem types from proxmox-installer-types Christoph Heiss
2026-04-03 16:54 ` Christoph Heiss [this message]
2026-04-03 16:54 ` [PATCH installer v3 33/38] auto: sysinfo: switch to " Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 34/38] fetch-answer: " Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 35/38] fetch-answer: http: prefer json over toml for answer format Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 36/38] fetch-answer: send auto-installer HTTP authorization token if set Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 37/38] tree-wide: switch out `Answer` -> `AutoInstallerConfig` types Christoph Heiss
2026-04-03 16:54 ` [PATCH installer v3 38/38] auto: drop now-dead answer file definitions Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260403165437.2166551-33-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal