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
14 changes: 13 additions & 1 deletion CubeAPI/src/cubemaster/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,10 @@ impl CubeMasterError {
/// as a potential source of routing ambiguity;
/// * `.` and `..` are reserved for relative path resolution and easily slip
/// through naive equality checks.
fn validate_path_segment(name: &'static str, value: &str) -> Result<(), CubeMasterError> {
pub(crate) fn validate_path_segment(
name: &'static str,
value: &str,
) -> Result<(), CubeMasterError> {
let is_valid = !value.is_empty()
&& value
.bytes()
Expand Down Expand Up @@ -637,6 +640,15 @@ pub struct CreateSandboxRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub labels: Option<HashMap<String, String>>,

#[serde(
rename = "create_time_env_vars",
skip_serializing_if = "Option::is_none"
)]
/// Sandbox-level env vars requested at create time. CubeMaster forwards
/// them to cubelet via an internal annotation, and cubelet uses them to
/// initialize envd after sandbox startup.
pub create_time_env_vars: Option<HashMap<String, String>>,

#[serde(rename = "distribution_scope", skip_serializing_if = "Option::is_none")]
pub distribution_scope: Option<Vec<String>>,

Expand Down
29 changes: 27 additions & 2 deletions CubeAPI/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ pub struct SandboxVolumeMount {
/// Rule: ID abbreviations → uppercase (templateID, sandboxID, envVars);
/// allow_internet_access is a known SDK snake_case quirk;
/// lifecycle is a nested object — see SandboxLifecycleConfig.
/// `envVars` is the canonical field name; `envs` is accepted as a compatibility
/// alias for E2B SDK callers.
#[derive(Debug, Deserialize, Validate, ToSchema)]
#[allow(dead_code)]
pub struct NewSandbox {
Expand Down Expand Up @@ -199,7 +201,11 @@ pub struct NewSandbox {
)]
pub distribution_scope: Option<Vec<String>>,

#[serde(rename = "envVars", skip_serializing_if = "Option::is_none")]
#[serde(
alias = "envs",
rename = "envVars",
skip_serializing_if = "Option::is_none"
)]
pub env_vars: Option<EnvVars>,

#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -510,7 +516,7 @@ fn default_page_limit() -> i32 {

#[cfg(test)]
mod tests {
use super::SandboxNetworkConfig;
use super::{NewSandbox, SandboxNetworkConfig};

#[test]
fn sandbox_network_config_accepts_snake_case_policy_fields() {
Expand All @@ -526,6 +532,25 @@ mod tests {
);
assert_eq!(cfg.deny_out, Some(vec!["0.0.0.0/0".to_string()]));
}

#[test]
fn new_sandbox_accepts_e2b_envs_alias() {
let req: NewSandbox = serde_json::from_value(serde_json::json!({
"templateID": "tpl-1",
"envs": {
"CUBE_TEST_ENV": "value"
}
}))
.expect("new sandbox request should deserialize");

assert_eq!(
req.env_vars
.as_ref()
.and_then(|envs| envs.get("CUBE_TEST_ENV"))
.map(String::as_str),
Some("value")
);
}
}

// ─── Templates ─────────────────────────────────────────────────────────────
Expand Down
Loading
Loading