public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH installer v2 1/3] tui: refactor `NetworkOptions` to have a `defaults_from()` function
Date: Wed, 25 Oct 2023 10:56:22 +0200	[thread overview]
Message-ID: <20231025085634.171618-2-c.heiss@proxmox.com> (raw)
In-Reply-To: <20231025085634.171618-1-c.heiss@proxmox.com>

This aligns it with the other constructors for options struct by
introducing a `::defaults_from()` function.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
 proxmox-tui-installer/src/main.rs    | 15 +++---
 proxmox-tui-installer/src/options.rs | 75 +++++++++++++---------------
 proxmox-tui-installer/src/setup.rs   | 10 ++--
 3 files changed, 45 insertions(+), 55 deletions(-)

diff --git a/proxmox-tui-installer/src/main.rs b/proxmox-tui-installer/src/main.rs
index 0a61e39..a342a08 100644
--- a/proxmox-tui-installer/src/main.rs
+++ b/proxmox-tui-installer/src/main.rs
@@ -166,7 +166,6 @@ enum InstallerStep {
 #[derive(Clone)]
 struct InstallerState {
     options: InstallerOptions,
-    /// FIXME: Remove:
     setup_info: SetupInfo,
     runtime_info: RuntimeInfo,
     locales: LocaleInfo,
@@ -184,7 +183,7 @@ fn main() {
         _ => cfg!(debug_assertions),
     };
 
-    let (locales, runtime_info) = match installer_setup(in_test_mode) {
+    let (setup_info, locales, runtime_info) = match installer_setup(in_test_mode) {
         Ok(result) => result,
         Err(err) => initial_setup_error(&mut siv, &err),
     };
@@ -197,10 +196,10 @@ fn main() {
             bootdisk: BootdiskOptions::defaults_from(&runtime_info.disks[0]),
             timezone: TimezoneOptions::defaults_from(&runtime_info, &locales),
             password: Default::default(),
-            network: NetworkOptions::from(&runtime_info.network),
+            network: NetworkOptions::defaults_from(&setup_info, &runtime_info.network),
             autoreboot: false,
         },
-        setup_info: setup_info().clone(), // FIXME: REMOVE
+        setup_info,
         runtime_info,
         locales,
         steps: HashMap::new(),
@@ -211,20 +210,20 @@ fn main() {
     siv.run();
 }
 
-fn installer_setup(in_test_mode: bool) -> Result<(LocaleInfo, RuntimeInfo), String> {
+fn installer_setup(in_test_mode: bool) -> Result<(SetupInfo, LocaleInfo, RuntimeInfo), String> {
     let base_path = if in_test_mode { "./testdir" } else { "/" };
     let mut path = PathBuf::from(base_path);
 
     path.push("run");
     path.push("proxmox-installer");
 
-    let installer_info = {
+    let installer_info: SetupInfo = {
         let mut path = path.clone();
         path.push("iso-info.json");
 
         setup::read_json(&path).map_err(|err| format!("Failed to retrieve setup info: {err}"))?
     };
-    init_setup_info(installer_info);
+    init_setup_info(installer_info.clone());
 
     let locale_info = {
         let mut path = path.clone();
@@ -245,7 +244,7 @@ fn installer_setup(in_test_mode: bool) -> Result<(LocaleInfo, RuntimeInfo), Stri
     if runtime_info.disks.is_empty() {
         Err("The installer could not find any supported hard disks.".to_owned())
     } else {
-        Ok((locale_info, runtime_info))
+        Ok((installer_info, locale_info, runtime_info))
     }
 }
 
diff --git a/proxmox-tui-installer/src/options.rs b/proxmox-tui-installer/src/options.rs
index abc8c7b..85b39b8 100644
--- a/proxmox-tui-installer/src/options.rs
+++ b/proxmox-tui-installer/src/options.rs
@@ -1,7 +1,7 @@
 use std::net::{IpAddr, Ipv4Addr};
 use std::{cmp, fmt};
 
-use crate::setup::{LocaleInfo, NetworkInfo, RuntimeInfo};
+use crate::setup::{LocaleInfo, NetworkInfo, RuntimeInfo, SetupInfo};
 use crate::utils::{CidrAddress, Fqdn};
 use crate::SummaryOption;
 
@@ -339,49 +339,25 @@ pub struct NetworkOptions {
 
 impl NetworkOptions {
     const DEFAULT_DOMAIN: &str = "example.invalid";
-}
 
-impl Default for NetworkOptions {
-    fn default() -> Self {
-        let fqdn = format!(
-            "{}.{}",
-            crate::current_product().default_hostname(),
-            Self::DEFAULT_DOMAIN
-        );
-        // TODO: Retrieve automatically
-        Self {
+    pub fn defaults_from(setup: &SetupInfo, network: &NetworkInfo) -> Self {
+        let mut this = Self {
             ifname: String::new(),
-            fqdn: fqdn.parse().unwrap(),
+            fqdn: Self::construct_fqdn(network, setup.config.product.default_hostname()),
             // Safety: The provided mask will always be valid.
             address: CidrAddress::new(Ipv4Addr::UNSPECIFIED, 0).unwrap(),
             gateway: Ipv4Addr::UNSPECIFIED.into(),
             dns_server: Ipv4Addr::UNSPECIFIED.into(),
-        }
-    }
-}
-
-impl From<&NetworkInfo> for NetworkOptions {
-    fn from(info: &NetworkInfo) -> Self {
-        let mut this = Self::default();
+        };
 
-        if let Some(ip) = info.dns.dns.first() {
+        if let Some(ip) = network.dns.dns.first() {
             this.dns_server = *ip;
         }
 
-        let hostname = info
-            .hostname
-            .as_deref()
-            .unwrap_or_else(|| crate::current_product().default_hostname());
-        let domain = info.dns.domain.as_deref().unwrap_or(Self::DEFAULT_DOMAIN);
-
-        if let Ok(fqdn) = Fqdn::from(&format!("{hostname}.{domain}")) {
-            this.fqdn = fqdn;
-        }
-
-        if let Some(routes) = &info.routes {
+        if let Some(routes) = &network.routes {
             let mut filled = false;
             if let Some(gw) = &routes.gateway4 {
-                if let Some(iface) = info.interfaces.get(&gw.dev) {
+                if let Some(iface) = network.interfaces.get(&gw.dev) {
                     this.ifname = iface.name.clone();
                     if let Some(addresses) = &iface.addresses {
                         if let Some(addr) = addresses.iter().find(|addr| addr.is_ipv4()) {
@@ -394,7 +370,7 @@ impl From<&NetworkInfo> for NetworkOptions {
             }
             if !filled {
                 if let Some(gw) = &routes.gateway6 {
-                    if let Some(iface) = info.interfaces.get(&gw.dev) {
+                    if let Some(iface) = network.interfaces.get(&gw.dev) {
                         if let Some(addresses) = &iface.addresses {
                             if let Some(addr) = addresses.iter().find(|addr| addr.is_ipv6()) {
                                 this.ifname = iface.name.clone();
@@ -409,6 +385,23 @@ impl From<&NetworkInfo> for NetworkOptions {
 
         this
     }
+
+    fn construct_fqdn(network: &NetworkInfo, default_hostname: &str) -> Fqdn {
+        let hostname = network.hostname.as_deref().unwrap_or(default_hostname);
+
+        let domain = network
+            .dns
+            .domain
+            .as_deref()
+            .unwrap_or(Self::DEFAULT_DOMAIN);
+
+        Fqdn::from(&format!("{hostname}.{domain}")).unwrap_or_else(|_| {
+            // Safety: This will always result in a valid FQDN, as we control & know
+            // the values of default_hostname (one of "pve", "pmg" or "pbs") and
+            // constant-defined DEFAULT_DOMAIN.
+            Fqdn::from(&format!("{}.{}", default_hostname, Self::DEFAULT_DOMAIN)).unwrap()
+        })
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -460,8 +453,8 @@ mod tests {
     };
     use std::{collections::HashMap, path::PathBuf};
 
-    fn fill_setup_info() {
-        crate::init_setup_info(SetupInfo {
+    fn dummy_setup_info() -> SetupInfo {
+        SetupInfo {
             config: ProductConfig {
                 fullname: "Proxmox VE".to_owned(),
                 product: ProxmoxProduct::PVE,
@@ -474,12 +467,12 @@ mod tests {
             locations: IsoLocations {
                 iso: PathBuf::new(),
             },
-        });
+        }
     }
 
     #[test]
     fn network_options_from_setup_network_info() {
-        fill_setup_info();
+        let setup = dummy_setup_info();
 
         let mut interfaces = HashMap::new();
         interfaces.insert(
@@ -512,7 +505,7 @@ mod tests {
         };
 
         assert_eq!(
-            NetworkOptions::from(&info),
+            NetworkOptions::defaults_from(&setup, &info),
             NetworkOptions {
                 ifname: "eth0".to_owned(),
                 fqdn: Fqdn::from("foo.bar.com").unwrap(),
@@ -524,7 +517,7 @@ mod tests {
 
         info.hostname = None;
         assert_eq!(
-            NetworkOptions::from(&info),
+            NetworkOptions::defaults_from(&setup, &info),
             NetworkOptions {
                 ifname: "eth0".to_owned(),
                 fqdn: Fqdn::from("pve.bar.com").unwrap(),
@@ -536,7 +529,7 @@ mod tests {
 
         info.dns.domain = None;
         assert_eq!(
-            NetworkOptions::from(&info),
+            NetworkOptions::defaults_from(&setup, &info),
             NetworkOptions {
                 ifname: "eth0".to_owned(),
                 fqdn: Fqdn::from("pve.example.invalid").unwrap(),
@@ -548,7 +541,7 @@ mod tests {
 
         info.hostname = Some("foo".to_owned());
         assert_eq!(
-            NetworkOptions::from(&info),
+            NetworkOptions::defaults_from(&setup, &info),
             NetworkOptions {
                 ifname: "eth0".to_owned(),
                 fqdn: Fqdn::from("foo.example.invalid").unwrap(),
diff --git a/proxmox-tui-installer/src/setup.rs b/proxmox-tui-installer/src/setup.rs
index 7238131..5575759 100644
--- a/proxmox-tui-installer/src/setup.rs
+++ b/proxmox-tui-installer/src/setup.rs
@@ -196,12 +196,10 @@ impl From<InstallerOptions> for InstallConfig {
 
             mngmt_nic: options.network.ifname,
 
-            hostname: options
-                .network
-                .fqdn
-                .host()
-                .unwrap_or_else(|| crate::current_product().default_hostname())
-                .to_owned(),
+            // Safety: At this point, it is know that we have a valid FQDN, as
+            // this is set by the TUI network panel, which only lets the user
+            // continue if a valid FQDN is provided.
+            hostname: options.network.fqdn.host().expect("valid FQDN").to_owned(),
             domain: options.network.fqdn.domain(),
             cidr: options.network.address,
             gateway: options.network.gateway,
-- 
2.42.0





  reply	other threads:[~2023-10-25  8:57 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-25  8:56 [pve-devel] [PATCH installer v2 0/3] tui: remove global, unsafe setup info Christoph Heiss
2023-10-25  8:56 ` Christoph Heiss [this message]
2023-10-25  8:56 ` [pve-devel] [PATCH installer v2 2/3] tui: bootdisk: pass down product info to advanced dialog Christoph Heiss
2023-10-25  8:56 ` [pve-devel] [PATCH installer v2 3/3] tui: remove obsolete, global `SetupInfo` state Christoph Heiss
2023-10-25 17:04 ` [pve-devel] applied-series: [PATCH installer v2 0/3] tui: remove global, unsafe setup info Thomas Lamprecht

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=20231025085634.171618-2-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pve-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