Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/wadm-types/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl From<ComponentProperties> for wadm::types::ComponentProperties {
id: properties.id,
config: properties.config.into_iter().map(|c| c.into()).collect(),
secrets: properties.secrets.into_iter().map(|c| c.into()).collect(),
limits: properties.limits.into_iter().map(|c| c.into()).collect(),
}
}
}
Expand Down Expand Up @@ -431,6 +432,7 @@ impl From<wadm::types::ComponentProperties> for ComponentProperties {
id: properties.id,
config: properties.config.into_iter().map(|c| c.into()).collect(),
secrets: properties.secrets.into_iter().map(|c| c.into()).collect(),
limits: properties.limits.map(Into::into),
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions crates/wadm-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ pub struct ComponentProperties {
/// these values at runtime using `wasmcloud:secrets/store`.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub secrets: Vec<SecretProperty>,
/// This Config holds the component's metadata properties like memory limits and execution time limits
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limits: Option<LimitsConfig>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default, ToSchema, JsonSchema)]
Expand Down Expand Up @@ -333,6 +336,20 @@ pub struct SecretSourceProperty {
pub version: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, ToSchema, JsonSchema)]
pub struct LimitProperties {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_linear_memory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_execution_time: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, ToSchema, JsonSchema)]
pub struct LimitsConfig {
pub name: String,
pub properties: LimitProperties,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, ToSchema, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct CapabilityProperties {
Expand Down Expand Up @@ -845,6 +862,7 @@ mod test {
id: None,
config: vec![],
secrets: vec![],
limits: None,
},
},
traits: Some(trait_vec),
Expand Down
11 changes: 11 additions & 0 deletions crates/wadm-types/wit/deps/wadm/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ interface types {
id: option<string>,
config: list<config-property>,
secrets: list<secret-property>,
limits: list<limits-config>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should bump the version of the package and add a since annotation on these. @brooksmtownsend any preference on whether we do a minor bump or a patch bump? I think with wit, if you add something to a record it is a breaking change, right?

}

record limits-config {
name: string,
properties: limit-properties,
}

record limit-properties {
max-linear-memory: option<string>,
max-execution-time: option<string>,
}

// Properties for a capability
Expand Down
2 changes: 2 additions & 0 deletions crates/wadm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ uuid = { workspace = true }
wadm-types = { workspace = true }
wasmcloud-control-interface = { workspace = true }
wasmcloud-secrets-types = { workspace = true }
humantime = "2.2.0"
parse-size = "1.1.0"

[dev-dependencies]
serial_test = "3"
4 changes: 4 additions & 0 deletions crates/wadm/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl Command {
component_id,
host_id,
count,
limits,
reference,
annotations,
model_name,
Expand All @@ -94,6 +95,7 @@ impl Command {
annotations: annotations.to_owned(),
// We don't know this field from the command
claims: None,
limits: limits.clone(),
}),
Some(Event::ComponentScaleFailed(ComponentScaleFailed {
component_id: component_id.to_owned(),
Expand Down Expand Up @@ -122,6 +124,8 @@ pub struct ScaleComponent {
pub host_id: String,
/// The number of components to scale to
pub count: u32,
/// The limits for the component, if any
pub limits: Option<HashMap<String, String>>,
/// The OCI or bindle reference to scale
pub reference: String,
/// The name of the model/manifest that generated this command
Expand Down
1 change: 1 addition & 0 deletions crates/wadm/src/events/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ pub struct ComponentScaled {
pub claims: Option<ComponentClaims>,
pub image_ref: String,
pub max_instances: usize,
pub limits: Option<HashMap<String, String>>,
pub component_id: String,
#[serde(default)]
pub host_id: String,
Expand Down
3 changes: 2 additions & 1 deletion crates/wadm/src/scaler/configscaler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ mod test {
image_ref: "foo".to_string(),
max_instances: 0,
component_id: "fooo".to_string(),
host_id: "hostid".to_string()
host_id: "hostid".to_string(),
limits: None,
}))
.await
.expect("handle_event should succeed"),
Expand Down
84 changes: 83 additions & 1 deletion crates/wadm/src/scaler/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use std::{collections::HashMap, time::Duration};

use anyhow::Result;
use parse_size::parse_size;
use tracing::{error, warn};
use wadm_types::{
api::StatusInfo, CapabilityProperties, Component, ComponentProperties, ConfigProperty,
Expand Down Expand Up @@ -195,7 +196,14 @@ fn component_scalers<S, P, L>(
&properties.secrets,
policies,
);

// write a function call here that converts the limits from componentProperties and unmarshalls them into the correct format
// It first checks if the limts are set in their correct foormat, ie humantime for max_execution_time and human readable memory
// for max_memory_limits like "4gb" or so
// if successful, they are set in a hashmap with the keys "max_execution_time" and "max_memory_limits" and then passed to the scalers.
let limits = properties
.limits
.as_ref()
.and_then(|_| compute_limits(properties));
config_names.append(&mut secret_names.clone());
// TODO(#451): Consider a way to report on status of a shared component
match (trt.trait_type.as_str(), &trt.properties, &properties.image) {
Expand Down Expand Up @@ -226,6 +234,7 @@ fn component_scalers<S, P, L>(
p.to_owned(),
component_name,
config_names,
limits,
),
notifier.clone(),
config_scalers,
Expand All @@ -246,6 +255,7 @@ fn component_scalers<S, P, L>(
p.to_owned(),
component_name,
config_names,
limits,
),
notifier.clone(),
config_scalers,
Expand Down Expand Up @@ -741,6 +751,78 @@ fn resolve_manifest_component<'a>(
}
}

// add new function to compute the limits from componentProperties and unmarshall them into the correct format
// It first checks if the limits are set in their correct format, ie humantime for max_execution_time and human readable memory
/// Parse and validate memory size from human-readable format to bytes
fn parse_and_validate_memory_size(input: &str) -> Result<usize, String> {
let bytes = parse_size(input).map_err(|e| format!("Parse error: {}", e))?;

if bytes > usize::MAX as u64 {
Err(format!(
"Size too large: max allowed is {} bytes (got {} bytes)",
usize::MAX,
bytes
))
} else {
Ok(bytes as usize)
}
}

/// Parse execution time from humantime format to seconds
fn parse_and_validate_execution_time(input: &str) -> Result<u64, String> {
humantime::parse_duration(input)
.map(|duration| duration.as_secs())
.map_err(|e| format!("Invalid time format: {}", e))
}

/// Computes limits for components from ComponentProperties
/// Converts human-readable formats to numeric values for the control interface
pub(crate) fn compute_limits(properties: &ComponentProperties) -> Option<HashMap<String, String>> {
let mut limits = HashMap::new();

if let Some(max_execution_time) = properties
.limits
.as_ref()
.and_then(|l| l.properties.max_execution_time.as_ref())
{
match parse_and_validate_execution_time(max_execution_time) {
Ok(seconds) => {
limits.insert("max_execution_time".to_string(), seconds.to_string());
}
Err(e) => {
warn!(
"Invalid max_execution_time format '{}': {}",
max_execution_time, e
);
}
}
}

if let Some(max_memory_limits) = &properties
.limits
.as_ref()
.and_then(|l| l.properties.max_linear_memory.as_ref())
{
match parse_and_validate_memory_size(max_memory_limits) {
Ok(bytes) => {
limits.insert("max_memory_limit".to_string(), bytes.to_string());
}
Err(e) => {
warn!(
"Invalid max_memory_limits format '{}': {}",
max_memory_limits, e
);
}
}
}

if limits.is_empty() {
None
} else {
Some(limits)
}
}

#[cfg(test)]
mod test {
use super::compute_component_id;
Expand Down
Loading
Loading