fix: added K8sName type for strict naming of Kubernetes resources
This commit is contained in:
96
harmony_types/src/k8s_name.rs
Normal file
96
harmony_types/src/k8s_name.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod id;
|
pub mod id;
|
||||||
|
pub mod k8s_name;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod switch;
|
pub mod switch;
|
||||||
|
|||||||
Reference in New Issue
Block a user