Publish Clusterflux release candidate 6e83783
This commit is contained in:
parent
4d75257c6e
commit
f3640643bd
16 changed files with 4667 additions and 106 deletions
|
|
@ -1,18 +1,73 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const MAX_EXTERNAL_ID_BYTES: usize = 255;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct IdParseError {
|
||||
id_type: &'static str,
|
||||
reason: &'static str,
|
||||
}
|
||||
|
||||
impl IdParseError {
|
||||
fn new(id_type: &'static str, reason: &'static str) -> Self {
|
||||
Self { id_type, reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IdParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} is invalid: {}", self.id_type, self.reason)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for IdParseError {}
|
||||
|
||||
fn validate_id(value: &str, id_type: &'static str) -> Result<(), IdParseError> {
|
||||
if value.is_empty() || value.trim().is_empty() {
|
||||
return Err(IdParseError::new(
|
||||
id_type,
|
||||
"value must not be empty or whitespace-only",
|
||||
));
|
||||
}
|
||||
if value.len() > MAX_EXTERNAL_ID_BYTES {
|
||||
return Err(IdParseError::new(
|
||||
id_type,
|
||||
"value exceeds the 255-byte limit",
|
||||
));
|
||||
}
|
||||
if value.chars().any(char::is_control) {
|
||||
return Err(IdParseError::new(
|
||||
id_type,
|
||||
"control characters are forbidden",
|
||||
));
|
||||
}
|
||||
if !value.bytes().all(|byte| {
|
||||
byte.is_ascii_alphanumeric()
|
||||
|| matches!(byte, b'-' | b'_' | b'.' | b':' | b'/' | b'@' | b'+')
|
||||
}) {
|
||||
return Err(IdParseError::new(
|
||||
id_type,
|
||||
"only ASCII letters, digits, '-', '_', '.', ':', '/', '@', and '+' are allowed",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! id_type {
|
||||
($name:ident) => {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct $name(String);
|
||||
|
||||
impl $name {
|
||||
pub fn new(value: impl Into<String>) -> Self {
|
||||
pub fn try_new(value: impl Into<String>) -> Result<Self, IdParseError> {
|
||||
let value = value.into();
|
||||
assert!(
|
||||
!value.trim().is_empty(),
|
||||
concat!(stringify!($name), " cannot be empty")
|
||||
);
|
||||
Self(value)
|
||||
validate_id(&value, stringify!($name))?;
|
||||
Ok(Self(value))
|
||||
}
|
||||
|
||||
/// Constructs an identifier from a trusted, internally generated value.
|
||||
pub fn new(value: impl Into<String>) -> Self {
|
||||
Self::try_new(value).unwrap_or_else(|error| panic!("{error}"))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
|
|
@ -44,3 +99,39 @@ id_type!(TaskDefinitionId);
|
|||
id_type!(TaskInstanceId);
|
||||
id_type!(TenantId);
|
||||
id_type!(UserId);
|
||||
|
||||
pub type DebugSessionId = ProcessId;
|
||||
pub type RequestId = LaunchAttemptId;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_hostile_values<T>(parse: impl Fn(String) -> Result<T, IdParseError>) {
|
||||
for value in [
|
||||
String::new(),
|
||||
" ".to_owned(),
|
||||
"bad\0id".to_owned(),
|
||||
"bad id".to_owned(),
|
||||
"x".repeat(MAX_EXTERNAL_ID_BYTES + 1),
|
||||
] {
|
||||
assert!(parse(value).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_external_identifier_type_rejects_hostile_values() {
|
||||
assert_hostile_values(AgentId::try_new);
|
||||
assert_hostile_values(ArtifactId::try_new);
|
||||
assert_hostile_values(DebugSessionId::try_new);
|
||||
assert_hostile_values(NodeId::try_new);
|
||||
assert_hostile_values(ProcessId::try_new);
|
||||
assert_hostile_values(LaunchAttemptId::try_new);
|
||||
assert_hostile_values(ProjectId::try_new);
|
||||
assert_hostile_values(RequestId::try_new);
|
||||
assert_hostile_values(TaskDefinitionId::try_new);
|
||||
assert_hostile_values(TaskInstanceId::try_new);
|
||||
assert_hostile_values(TenantId::try_new);
|
||||
assert_hostile_values(UserId::try_new);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,8 +66,9 @@ pub use execution::{
|
|||
WasmTaskOutcome, WasmTaskResult, MAX_WASM_TASK_ENVELOPE_BYTES, WASM_TASK_ABI_VERSION,
|
||||
};
|
||||
pub use ids::{
|
||||
AgentId, ArtifactId, LaunchAttemptId, NodeId, ProcessId, ProjectId, TaskDefinitionId,
|
||||
TaskInstanceId, TenantId, UserId,
|
||||
AgentId, ArtifactId, DebugSessionId, IdParseError, LaunchAttemptId, NodeId, ProcessId,
|
||||
ProjectId, RequestId, TaskDefinitionId, TaskInstanceId, TenantId, UserId,
|
||||
MAX_EXTERNAL_ID_BYTES,
|
||||
};
|
||||
pub use limits::{
|
||||
LargeArgumentPolicy, LimitError, LimitKind, LogBuffer, LogRecord, ResourceLimits,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue