From: Christoph Heiss <c.heiss@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH installer v4 31/40] tree-wide: used moved `Fqdn` type to proxmox-network-types
Date: Thu, 30 Apr 2026 14:47:00 +0200 [thread overview]
Message-ID: <20260430124712.1614305-32-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260430124712.1614305-1-c.heiss@proxmox.com>
Now that the `Fqdn` has been moved to the proxmox-network-types crate,
use it from there.
No functional changes.
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
Changes v3 -> v4:
* no changes
Changes v2 -> v3:
* new patch
Cargo.toml | 6 +
proxmox-auto-installer/Cargo.toml | 1 +
proxmox-auto-installer/src/answer.rs | 4 +-
proxmox-auto-installer/src/utils.rs | 6 +-
proxmox-installer-common/Cargo.toml | 1 +
proxmox-installer-common/src/options.rs | 3 +-
proxmox-installer-common/src/utils.rs | 241 ---------------------
proxmox-tui-installer/Cargo.toml | 1 +
proxmox-tui-installer/src/setup.rs | 5 +-
proxmox-tui-installer/src/views/network.rs | 6 +-
10 files changed, 20 insertions(+), 254 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 3075bcc..379ee6b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,3 +27,9 @@ serde_plain = "1.0"
toml = "0.8"
proxmox-auto-installer.path = "./proxmox-auto-installer"
proxmox-installer-common.path = "./proxmox-installer-common"
+proxmox-network-types = "1.0"
+
+# Local path overrides
+# NOTE: You must run `cargo update` after changing this for it to take effect!
+[patch.crates-io]
+# proxmox-network-types.path = "../proxmox/proxmox-network-types"
diff --git a/proxmox-auto-installer/Cargo.toml b/proxmox-auto-installer/Cargo.toml
index 8a5283e..0086e5d 100644
--- a/proxmox-auto-installer/Cargo.toml
+++ b/proxmox-auto-installer/Cargo.toml
@@ -14,6 +14,7 @@ homepage = "https://www.proxmox.com"
anyhow.workspace = true
log.workspace = true
proxmox-installer-common = { workspace = true, features = ["http"] }
+proxmox-network-types.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_plain.workspace = true
diff --git a/proxmox-auto-installer/src/answer.rs b/proxmox-auto-installer/src/answer.rs
index d12e088..40e6557 100644
--- a/proxmox-auto-installer/src/answer.rs
+++ b/proxmox-auto-installer/src/answer.rs
@@ -4,8 +4,10 @@ use proxmox_installer_common::{
BtrfsCompressOption, BtrfsRaidLevel, FsType, NetworkInterfacePinningOptions,
ZfsChecksumOption, ZfsCompressOption, ZfsRaidLevel,
},
- utils::{CidrAddress, Fqdn},
+ utils::CidrAddress,
};
+use proxmox_network_types::fqdn::Fqdn;
+
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, HashMap},
diff --git a/proxmox-auto-installer/src/utils.rs b/proxmox-auto-installer/src/utils.rs
index 09b3408..9998491 100644
--- a/proxmox-auto-installer/src/utils.rs
+++ b/proxmox-auto-installer/src/utils.rs
@@ -535,11 +535,7 @@ pub fn parse_answer(
.map(|o| o.mapping)
.unwrap_or_default(),
- hostname: network_settings
- .fqdn
- .host()
- .unwrap_or(setup_info.config.product.default_hostname())
- .to_string(),
+ hostname: network_settings.fqdn.host().to_owned(),
domain: network_settings.fqdn.domain(),
cidr: network_settings.address,
gateway: network_settings.gateway,
diff --git a/proxmox-installer-common/Cargo.toml b/proxmox-installer-common/Cargo.toml
index b3ce3d7..7469627 100644
--- a/proxmox-installer-common/Cargo.toml
+++ b/proxmox-installer-common/Cargo.toml
@@ -13,6 +13,7 @@ regex.workspace = true
serde = { workspace = true, features = [ "derive" ] }
serde_json.workspace = true
serde_plain.workspace = true
+proxmox-network-types.workspace = true
# `http` feature
hex = { version = "0.4", optional = true }
diff --git a/proxmox-installer-common/src/options.rs b/proxmox-installer-common/src/options.rs
index dcf4fe7..feb0dc4 100644
--- a/proxmox-installer-common/src/options.rs
+++ b/proxmox-installer-common/src/options.rs
@@ -10,7 +10,8 @@ use std::{cmp, fmt};
use crate::disk_checks::check_raid_min_disks;
use crate::net::{MAX_IFNAME_LEN, MIN_IFNAME_LEN};
use crate::setup::{LocaleInfo, NetworkInfo, RuntimeInfo, SetupInfo};
-use crate::utils::{CidrAddress, Fqdn};
+use crate::utils::CidrAddress;
+use proxmox_network_types::fqdn::Fqdn;
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all(deserialize = "lowercase", serialize = "UPPERCASE"))]
diff --git a/proxmox-installer-common/src/utils.rs b/proxmox-installer-common/src/utils.rs
index ffc862e..e86abdf 100644
--- a/proxmox-installer-common/src/utils.rs
+++ b/proxmox-installer-common/src/utils.rs
@@ -139,244 +139,3 @@ fn check_mask_limit(addr: &IpAddr, mask: usize) -> Result<(), CidrAddressParseEr
Ok(())
}
}
-
-/// Possible errors that might occur when parsing FQDNs.
-#[derive(Debug, Eq, PartialEq)]
-pub enum FqdnParseError {
- MissingHostname,
- NumericHostname,
- InvalidPart(String),
- TooLong(usize),
-}
-
-impl fmt::Display for FqdnParseError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use FqdnParseError::*;
- match self {
- MissingHostname => write!(f, "missing hostname part"),
- NumericHostname => write!(f, "hostname cannot be purely numeric"),
- InvalidPart(part) => write!(
- f,
- "FQDN must only consist of alphanumeric characters and dashes. Invalid part: '{part}'",
- ),
- TooLong(len) => write!(f, "FQDN too long: {len} > {}", Fqdn::MAX_LENGTH),
- }
- }
-}
-
-/// A type for safely representing fully-qualified domain names (FQDNs).
-///
-/// It considers following RFCs:
-/// - [RFC952] (sec. "ASSUMPTIONS", 1.)
-/// - [RFC1035] (sec. 2.3. "Conventions")
-/// - [RFC1123] (sec. 2.1. "Host Names and Numbers")
-/// - [RFC3492]
-/// - [RFC4343]
-///
-/// .. and applies some restriction given by Debian, e.g. 253 instead of 255
-/// maximum total length and maximum 63 characters per label, per the
-/// [hostname(7)].
-///
-/// Additionally:
-/// - It enforces the restriction as per Bugzilla #1054, in that
-/// purely numeric hostnames are not allowed - against RFC1123 sec. 2.1.
-///
-/// Some terminology:
-/// - "label" - a single part of a FQDN, e.g. {label}.{label}.{tld}
-///
-/// [RFC952]: <https://www.ietf.org/rfc/rfc952.txt>
-/// [RFC1035]: <https://www.ietf.org/rfc/rfc1035.txt>
-/// [RFC1123]: <https://www.ietf.org/rfc/rfc1123.txt>
-/// [RFC3492]: <https://www.ietf.org/rfc/rfc3492.txt>
-/// [RFC4343]: <https://www.ietf.org/rfc/rfc4343.txt>
-/// [hostname(7)]: <https://manpages.debian.org/stable/manpages/hostname.7.en.html>
-#[derive(Clone, Debug, Eq)]
-pub struct Fqdn {
- parts: Vec<String>,
-}
-
-impl Fqdn {
- /// Maximum length of a single label of the FQDN
- const MAX_LABEL_LENGTH: usize = 63;
- /// Maximum total length of the FQDN
- const MAX_LENGTH: usize = 253;
-
- pub fn from(fqdn: &str) -> Result<Self, FqdnParseError> {
- if fqdn.len() > Self::MAX_LENGTH {
- return Err(FqdnParseError::TooLong(fqdn.len()));
- }
-
- let parts = fqdn
- .split('.')
- .map(ToOwned::to_owned)
- .collect::<Vec<String>>();
-
- for part in &parts {
- if !Self::validate_single(part) {
- return Err(FqdnParseError::InvalidPart(part.clone()));
- }
- }
-
- if parts.len() < 2 {
- Err(FqdnParseError::MissingHostname)
- } else if parts[0].chars().all(|c| c.is_ascii_digit()) {
- // Do not allow a purely numeric hostname, see:
- // https://bugzilla.proxmox.com/show_bug.cgi?id=1054
- Err(FqdnParseError::NumericHostname)
- } else {
- Ok(Self { parts })
- }
- }
-
- pub fn host(&self) -> Option<&str> {
- self.has_host().then_some(&self.parts[0])
- }
-
- pub fn domain(&self) -> String {
- let parts = if self.has_host() {
- &self.parts[1..]
- } else {
- &self.parts
- };
-
- parts.join(".")
- }
-
- /// Checks whether the FQDN has a hostname associated with it, i.e. is has more than 1 part.
- fn has_host(&self) -> bool {
- self.parts.len() > 1
- }
-
- fn validate_single(s: &str) -> bool {
- !s.is_empty()
- && s.len() <= Self::MAX_LABEL_LENGTH
- // First character must be alphanumeric
- && s.chars()
- .next()
- .map(|c| c.is_ascii_alphanumeric())
- .unwrap_or_default()
- // .. last character as well,
- && s.chars()
- .last()
- .map(|c| c.is_ascii_alphanumeric())
- .unwrap_or_default()
- // and anything between must be alphanumeric or -
- && s.chars()
- .skip(1)
- .take(s.len().saturating_sub(2))
- .all(|c| c.is_ascii_alphanumeric() || c == '-')
- }
-}
-
-impl FromStr for Fqdn {
- type Err = FqdnParseError;
-
- fn from_str(value: &str) -> Result<Self, Self::Err> {
- Self::from(value)
- }
-}
-
-impl fmt::Display for Fqdn {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.parts.join("."))
- }
-}
-
-impl<'de> Deserialize<'de> for Fqdn {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- let s: String = Deserialize::deserialize(deserializer)?;
- s.parse()
- .map_err(|_| serde::de::Error::custom("invalid FQDN"))
- }
-}
-
-impl PartialEq for Fqdn {
- // Case-insensitive comparison, as per RFC 952 "ASSUMPTIONS", RFC 1035 sec. 2.3.3. "Character
- // Case" and RFC 4343 as a whole
- fn eq(&self, other: &Self) -> bool {
- if self.parts.len() != other.parts.len() {
- return false;
- }
-
- self.parts
- .iter()
- .zip(other.parts.iter())
- .all(|(a, b)| a.to_lowercase() == b.to_lowercase())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn fqdn_construct() {
- use FqdnParseError::*;
- assert!(Fqdn::from("foo.example.com").is_ok());
- assert!(Fqdn::from("foo-bar.com").is_ok());
- assert!(Fqdn::from("a-b.com").is_ok());
-
- assert_eq!(Fqdn::from("foo"), Err(MissingHostname));
-
- assert_eq!(Fqdn::from("-foo.com"), Err(InvalidPart("-foo".to_owned())));
- assert_eq!(Fqdn::from("foo-.com"), Err(InvalidPart("foo-".to_owned())));
- assert_eq!(Fqdn::from("foo.com-"), Err(InvalidPart("com-".to_owned())));
- assert_eq!(Fqdn::from("-o-.com"), Err(InvalidPart("-o-".to_owned())));
-
- // https://bugzilla.proxmox.com/show_bug.cgi?id=1054
- assert_eq!(Fqdn::from("123.com"), Err(NumericHostname));
- assert!(Fqdn::from("foo123.com").is_ok());
- assert!(Fqdn::from("123foo.com").is_ok());
-
- assert!(Fqdn::from(&format!("{}.com", "a".repeat(63))).is_ok());
- assert_eq!(
- Fqdn::from(&format!("{}.com", "a".repeat(250))),
- Err(TooLong(254)),
- );
- assert_eq!(
- Fqdn::from(&format!("{}.com", "a".repeat(64))),
- Err(InvalidPart("a".repeat(64))),
- );
-
- // https://bugzilla.proxmox.com/show_bug.cgi?id=5230
- assert_eq!(
- Fqdn::from("123@foo.com"),
- Err(InvalidPart("123@foo".to_owned()))
- );
- }
-
- #[test]
- fn fqdn_parts() {
- let fqdn = Fqdn::from("pve.example.com").unwrap();
- assert_eq!(fqdn.host().unwrap(), "pve");
- assert_eq!(fqdn.domain(), "example.com");
- assert_eq!(
- fqdn.parts,
- &["pve".to_owned(), "example".to_owned(), "com".to_owned()]
- );
- }
-
- #[test]
- fn fqdn_display() {
- assert_eq!(
- Fqdn::from("foo.example.com").unwrap().to_string(),
- "foo.example.com"
- );
- }
-
- #[test]
- fn fqdn_compare() {
- assert_eq!(Fqdn::from("example.com"), Fqdn::from("example.com"));
- assert_eq!(Fqdn::from("example.com"), Fqdn::from("ExAmPle.Com"));
- assert_eq!(Fqdn::from("ExAmPle.Com"), Fqdn::from("example.com"));
- assert_ne!(
- Fqdn::from("subdomain.ExAmPle.Com"),
- Fqdn::from("example.com")
- );
- assert_ne!(Fqdn::from("foo.com"), Fqdn::from("bar.com"));
- assert_ne!(Fqdn::from("example.com"), Fqdn::from("example.net"));
- }
-}
diff --git a/proxmox-tui-installer/Cargo.toml b/proxmox-tui-installer/Cargo.toml
index cc2baeb..1ca91cb 100644
--- a/proxmox-tui-installer/Cargo.toml
+++ b/proxmox-tui-installer/Cargo.toml
@@ -9,6 +9,7 @@ homepage = "https://www.proxmox.com"
[dependencies]
proxmox-installer-common.workspace = true
+proxmox-network-types.workspace = true
anyhow.workspace = true
serde_json.workspace = true
diff --git a/proxmox-tui-installer/src/setup.rs b/proxmox-tui-installer/src/setup.rs
index 3ab1869..98dbcac 100644
--- a/proxmox-tui-installer/src/setup.rs
+++ b/proxmox-tui-installer/src/setup.rs
@@ -36,10 +36,7 @@ impl From<InstallerOptions> for InstallConfig {
mngmt_nic: options.network.ifname,
network_interface_pin_map: pinning_opts.map(|o| o.mapping.clone()).unwrap_or_default(),
- // 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(),
+ hostname: options.network.fqdn.host().to_owned(),
domain: options.network.fqdn.domain(),
cidr: options.network.address,
gateway: options.network.gateway,
diff --git a/proxmox-tui-installer/src/views/network.rs b/proxmox-tui-installer/src/views/network.rs
index 970c353..53e0d65 100644
--- a/proxmox-tui-installer/src/views/network.rs
+++ b/proxmox-tui-installer/src/views/network.rs
@@ -12,13 +12,15 @@ use std::{
sync::{Arc, Mutex},
};
-use super::{CidrAddressEditView, FormView};
use proxmox_installer_common::{
net::MAX_IFNAME_LEN,
options::{NetworkInterfacePinningOptions, NetworkOptions},
setup::{Interface, NetworkInfo},
- utils::{CidrAddress, Fqdn},
+ utils::CidrAddress,
};
+use proxmox_network_types::fqdn::Fqdn;
+
+use super::{CidrAddressEditView, FormView};
struct NetworkViewOptions {
selected_mac: String,
--
2.53.0
next prev parent reply other threads:[~2026-04-30 12:50 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-30 12:46 [PATCH datacenter-manager/installer/proxmox/yew-comp v4 00/40] add auto-installer integration Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 01/40] api-macro: allow $ in identifier name Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 02/40] schema: oneOf: allow single string variant Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 03/40] schema: implement UpdaterType for HashMap and BTreeMap Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 04/40] network-types: move `Fqdn` type from proxmox-installer-common Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 05/40] network-types: implement api type for Fqdn Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 06/40] network-types: add api wrapper type for std::net::IpAddr Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 07/40] network-types: cidr: implement generic `IpAddr::new` constructor Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 08/40] network-types: fqdn: implement standard library Error for Fqdn Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 09/40] node-status: make KernelVersionInformation Clone + PartialEq Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 10/40] installer-types: add common types used by the installer Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 11/40] installer-types: add types used by the auto-installer Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 12/40] installer-types: implement api type for all externally-used types Christoph Heiss
2026-04-30 12:46 ` [PATCH yew-comp v4 13/40] widget: kvlist: add widget for user-modifiable data tables Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 14/40] api-types, cli: use ReturnType::new() instead of constructing it manually Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 15/40] api-types: add api types for auto-installer integration Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 16/40] config: add auto-installer configuration module Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 17/40] acl: wire up new /system/auto-installation acl path Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 18/40] server: api: add auto-installer integration module Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 19/40] server: api: auto-installer: add access token management endpoints Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 20/40] client: add bindings for auto-installer endpoints Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 21/40] ui: auto-installer: add installations overview panel Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 22/40] ui: auto-installer: add prepared answer configuration panel Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 23/40] ui: auto-installer: add access token " Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 24/40] docs: add documentation for auto-installer integration Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 25/40] install: iso env: use JSON boolean literals for product config Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 26/40] common: http: allow passing custom headers to post() Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 27/40] common: http: retrieve error message from body on post() Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 28/40] common: options: move regex construction out of loop Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 29/40] assistant: support adding an authorization token for HTTP-based answers Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 30/40] post-hook: run cargo fmt Christoph Heiss
2026-04-30 12:47 ` Christoph Heiss [this message]
2026-04-30 12:47 ` [PATCH installer v4 32/40] tree-wide: use `Cidr` type from proxmox-network-types Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 33/40] tree-wide: switch to filesystem types from proxmox-installer-types Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 34/40] auto: sysinfo: switch to " Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 35/40] fetch-answer: " Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 36/40] fetch-answer: http: prefer json over toml for answer format Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 37/40] fetch-answer: send auto-installer HTTP authorization token if set Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 38/40] fetch-answer: print full error messages when fetching failed Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 39/40] tree-wide: switch out `Answer` -> `AutoInstallerConfig` types Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 40/40] 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=20260430124712.1614305-32-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.