fix: added K8sName type for strict naming of Kubernetes resources

This commit is contained in:
Willem 2025-11-17 15:20:51 -05:00
parent 759a9287d3
commit 90b89224d8
2 changed files with 97 additions and 0 deletions

View File

@ -0,0 +1,96 @@
use std::str::FromStr;
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct K8sName(pub String);
impl K8sName {
#[cfg(test)]
pub fn dummy() -> Self {
K8sName("example".to_string())
}
fn is_valid(name: &str) -> bool {
if name.is_empty() || name.len() > 63 {
return false;
}
let b = name.as_bytes();
if !b[0].is_ascii_alphanumeric() || !b[b.len() - 1].is_ascii_alphanumeric() {
return false;
}
b.iter()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || *c == b'-')
}
}
impl FromStr for K8sName {
type Err = K8sNameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !Self::is_valid(s) {
return Err(K8sNameError::InvalidFormat(format!(
"Invalid Kubernetes resource name '{s}': \
must match DNS-1123 (lowercase alphanumeric, hyphens, <=63 chars)"
)));
};
Ok(K8sName(s.to_string()))
}
}
#[derive(Debug)]
pub enum K8sNameError {
InvalidFormat(String),
}
impl From<&K8sName> for String {
fn from(value: &K8sName) -> Self {
value.0.clone()
}
}
impl std::fmt::Display for K8sName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_name() {
assert!(K8sName::from_str("k8s-name-test").is_ok());
assert!(K8sName::from_str("n").is_ok());
assert!(K8sName::from_str("node1").is_ok());
assert!(K8sName::from_str("my-app-v2").is_ok());
assert!(K8sName::from_str("service123").is_ok());
assert!(K8sName::from_str("abcdefghijklmnopqrstuvwxyz-1234567890").is_ok());
}
#[test]
fn test_invalid_name() {
assert!(K8sName::from_str("").is_err());
assert!(K8sName::from_str(".config").is_err());
assert!(K8sName::from_str("_hidden").is_err());
assert!(K8sName::from_str("UPPER-CASE").is_err());
assert!(K8sName::from_str("123-$$$").is_err());
assert!(K8sName::from_str("app!name").is_err());
assert!(K8sName::from_str("my..app").is_err());
assert!(K8sName::from_str("backend-").is_err());
assert!(K8sName::from_str("-frontend").is_err());
assert!(K8sName::from_str("InvalidName").is_err());
assert!(
K8sName::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
.is_err()
);
assert!(K8sName::from_str("k8s name").is_err());
assert!(K8sName::from_str("k8s_name").is_err());
assert!(K8sName::from_str("k8s@name").is_err());
}
}

View File

@ -1,3 +1,4 @@
pub mod id;
pub mod k8s_name;
pub mod net;
pub mod switch;