Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions environment-providers/openshell/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ openshell-policy = { git = "https://github.com/NVIDIA/OpenShell", rev = "6648bd0
openshell-sdk = { git = "https://github.com/NVIDIA/OpenShell", rev = "6648bd0c290efbc41ba131ee9831ee45cd431f94" }
serde = { workspace = true }
serde_json = { workspace = true }
sha2 = "0.10"
thiserror = { workspace = true }
tokio = { version = "1.43", features = ["macros", "rt-multi-thread"] }
tonic = "0.14"
Expand Down
2 changes: 1 addition & 1 deletion environment-providers/openshell/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Use a registered OpenShell gateway and match the CLI, gateway, and runtime compo

Environment configuration uses `provider="openshell"` and `control_location="in_env_control"`. The gateway connection selects a named workspace or explicitly uses OpenShell's default workspace. Workspace and artifact roots are absolute POSIX paths inside the sandbox; `runtime.artifacts` remains the host collection destination. Select an immutable image digest or local Docker image ID. Task images contain the SDK, `fabric-agent-ctl`, the selected adapter, and the agent/harness.

When `settings.sandbox_name` is omitted, the OpenShell provider derives a stable name from the Fabric environment ID so it can reconcile a create request whose response times out.
When `settings.sandbox_name` is omitted, the OpenShell provider derives a stable 19-character name from the Fabric environment ID, within OpenShell's sandbox-name limit. This lets it reconcile a create request whose response times out.

With an existing Fabric-ready configuration, use the normal SDK lifecycle:

Expand Down
35 changes: 34 additions & 1 deletion environment-providers/openshell/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use openshell_sdk::{
};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value, json};
use sha2::{Digest, Sha256};
use thiserror::Error;

const PROTOCOL_VERSION: &str = "fabric.environment-provider.v1alpha1";
Expand All @@ -32,6 +33,15 @@ const DEFAULT_EXEC_TIMEOUT_SECONDS: u64 = 30;
const GATEWAY_CALL_TIMEOUT_SECONDS: u64 = 30;
const MAX_OPERATION_TIMEOUT_SECONDS: u64 = 600;

// OpenShell 0.1.2 limits DNS-routable sandbox names to 19 bytes. Keep the
// environment ID in the label for exact identity checks during reconciliation.
fn generated_sandbox_name(environment_id: &str) -> String {
let digest = Sha256::digest(environment_id.as_bytes());
let mut prefix = [0_u8; 8];
prefix.copy_from_slice(&digest[..8]);
format!("nf-{:016x}", u64::from_be_bytes(prefix))
}

/// Provider process failure.
#[derive(Debug, Error)]
pub enum ProviderError {
Expand Down Expand Up @@ -602,7 +612,7 @@ where
let sandbox_name = settings
.sandbox_name
.clone()
.unwrap_or_else(|| format!("nemo-fabric-{environment_id}"));
.unwrap_or_else(|| generated_sandbox_name(environment_id));
Comment thread
AnuradhaKaruppiah marked this conversation as resolved.
let sandbox = match bounded_gateway_call(
"create",
Duration::from_secs(GATEWAY_CALL_TIMEOUT_SECONDS),
Expand Down Expand Up @@ -2054,6 +2064,20 @@ mod tests {
use super::*;
use std::sync::{Arc, Mutex};

#[test]
fn generated_sandbox_names_fit_the_openshell_gateway_contract() {
let id = "nemo-fabric-environment-1760559084-12345-678";
let name = generated_sandbox_name(id);
assert_eq!(name.len(), 19);
assert!(name.starts_with("nf-"));
assert!(
name.bytes()
.all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')
);
assert_eq!(name, generated_sandbox_name(id));
assert_ne!(name, generated_sandbox_name("another-environment-id"));
}

#[tokio::test]
async fn bounded_gateway_call_reports_timeouts_without_sdk_details() {
let error = bounded_gateway_call(
Expand Down Expand Up @@ -2993,6 +3017,15 @@ mod tests {
workspace: Option<&str>,
request: SandboxCreate,
) -> Result<SandboxSnapshot, ProviderError> {
let name = request.name.as_deref().expect("sandbox name");
assert!(
name.len() <= 19,
"OpenShell rejects sandbox names longer than 19 bytes: {name}"
);
assert!(
name.bytes()
.all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')
);
assert_eq!(request.environment["FABRIC_VISIBLE"], "yes");
assert_eq!(request.command, ["fabric-agent-ctl", "serve"]);
self.calls.lock().expect("calls").push(format!(
Expand Down
Loading