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
7 changes: 7 additions & 0 deletions crates/openshell-core/src/driver_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

use crate::proto::compute::v1::DriverSandbox;

/// Path to the sandbox supervisor binary inside the container image.
///
/// All compute drivers must launch this binary as the container entrypoint to
/// start the sandboxed environment. The value must be kept in sync with the
/// path used when building the `openshell-sandbox` image layer.
pub const SUPERVISOR_IMAGE_BINARY_PATH: &str = "/openshell-sandbox";

/// Return the effective log level for a sandbox.
///
/// Uses the level from the sandbox spec when non-empty, falling back to
Expand Down
9 changes: 9 additions & 0 deletions crates/openshell-core/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
/// Override at runtime with the `OPENSHELL_COMMUNITY_REGISTRY` env var.
pub const DEFAULT_COMMUNITY_REGISTRY: &str = "ghcr.io/nvidia/openshell-community/sandboxes";

/// Return the default sandbox image reference (`{registry}/base:latest`).
///
/// Used by all compute drivers as the fallback image when none is specified in
/// the sandbox spec.
#[must_use]
pub fn default_sandbox_image() -> String {
format!("{DEFAULT_COMMUNITY_REGISTRY}/base:latest")
}

/// Resolve a user-supplied image string into a fully-qualified reference.
///
/// Resolution rules (applied in order):
Expand Down
14 changes: 2 additions & 12 deletions crates/openshell-driver-docker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use bollard::query_parameters::{
use bytes::Bytes;
use futures::{Stream, StreamExt};
use openshell_core::config::{DEFAULT_DOCKER_NETWORK_NAME, DEFAULT_STOP_TIMEOUT_SECS};
use openshell_core::driver_utils::SUPERVISOR_IMAGE_BINARY_PATH;
use openshell_core::gpu::cdi_gpu_device_ids;
use openshell_core::proto::compute::v1::{
CreateSandboxRequest, CreateSandboxResponse, DeleteSandboxRequest, DeleteSandboxResponse,
Expand Down Expand Up @@ -68,10 +69,6 @@ const DOCKER_NETWORK_DRIVER: &str = "bridge";
/// explicit `supervisor_bin` override or local build is available.
const DEFAULT_DOCKER_SUPERVISOR_IMAGE_REPO: &str = "ghcr.io/nvidia/openshell/supervisor";

/// Path to the supervisor binary inside the `openshell/supervisor` image
/// (a `FROM scratch` image containing only the binary).
const SUPERVISOR_IMAGE_BINARY_PATH: &str = "/openshell-sandbox";

/// Return the default `ghcr.io/nvidia/openshell/supervisor:<tag>` reference
/// used when no supervisor binary override is provided.
pub fn default_docker_supervisor_image() -> String {
Expand Down Expand Up @@ -173,7 +170,7 @@ pub struct DockerComputeConfig {
impl Default for DockerComputeConfig {
fn default() -> Self {
Self {
default_image: default_sandbox_image(),
default_image: openshell_core::image::default_sandbox_image(),
image_pull_policy: String::new(),
sandbox_namespace: "default".to_string(),
grpc_endpoint: String::new(),
Expand All @@ -189,13 +186,6 @@ impl Default for DockerComputeConfig {
}
}

fn default_sandbox_image() -> String {
format!(
"{}/base:latest",
openshell_core::image::DEFAULT_COMMUNITY_REGISTRY
)
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct DockerGuestTlsPaths {
pub(crate) ca: PathBuf,
Expand Down
9 changes: 1 addition & 8 deletions crates/openshell-driver-kubernetes/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Default for KubernetesComputeConfig {
fn default() -> Self {
Self {
namespace: DEFAULT_K8S_NAMESPACE.to_string(),
default_image: default_sandbox_image(),
default_image: openshell_core::image::default_sandbox_image(),
Comment thread
johntmyers marked this conversation as resolved.
// Default empty so the gateway omits `imagePullPolicy` from pod
// specs and Kubernetes applies its own default (Always for `latest`,
// IfNotPresent otherwise). `DEFAULT_IMAGE_PULL_POLICY` ("missing")
Expand All @@ -93,13 +93,6 @@ impl Default for KubernetesComputeConfig {
}
}

fn default_sandbox_image() -> String {
format!(
"{}/base:latest",
openshell_core::image::DEFAULT_COMMUNITY_REGISTRY
)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 1 addition & 7 deletions crates/openshell-driver-kubernetes/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use kube::core::gvk::GroupVersionKind;
use kube::core::{DynamicObject, ObjectMeta};
use kube::runtime::watcher::{self, Event};
use kube::{Client, Error as KubeError};
use openshell_core::driver_utils::SUPERVISOR_IMAGE_BINARY_PATH;
use openshell_core::progress::{
PROGRESS_STEP_PULLING_IMAGE, PROGRESS_STEP_REQUESTING_SANDBOX, PROGRESS_STEP_STARTING_SANDBOX,
mark_progress_active, mark_progress_complete, mark_progress_detail,
Expand Down Expand Up @@ -767,13 +768,6 @@ fn supervisor_volume_mount() -> serde_json::Value {
})
}

/// Path of the supervisor binary inside the supervisor image.
///
/// The supervisor image places the binary at the filesystem root. We invoke
/// it directly so the init path does not depend on shell utilities or PATH
/// resolution inside the image.
const SUPERVISOR_IMAGE_BINARY_PATH: &str = "/openshell-sandbox";

/// Build an image volume that mounts the supervisor OCI image directly.
///
/// Requires Kubernetes >= v1.33 (`ImageVolume` beta) or >= v1.36 (GA).
Expand Down
9 changes: 1 addition & 8 deletions crates/openshell-driver-podman/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl Default for PodmanComputeConfig {
fn default() -> Self {
Self {
socket_path: Self::default_socket_path(),
default_image: default_sandbox_image(),
default_image: openshell_core::image::default_sandbox_image(),
image_pull_policy: ImagePullPolicy::default(),
grpc_endpoint: String::new(),
gateway_port: openshell_core::config::DEFAULT_SERVER_PORT,
Expand All @@ -195,13 +195,6 @@ impl Default for PodmanComputeConfig {
}
}

fn default_sandbox_image() -> String {
format!(
"{}/base:latest",
openshell_core::image::DEFAULT_COMMUNITY_REGISTRY
)
}

impl std::fmt::Debug for PodmanComputeConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PodmanComputeConfig")
Expand Down
35 changes: 27 additions & 8 deletions crates/openshell-providers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ pub trait ProviderPlugin: Send + Sync {
}
}

/// Blanket implementation of [`ProviderPlugin`] for [`ProviderDiscoverySpec`].
///
/// Providers that only need standard env-var discovery can register their
/// `SPEC` constant directly, instead of defining a dedicated struct and
/// repeating the same three-method delegation.
impl ProviderPlugin for ProviderDiscoverySpec {
fn id(&self) -> &'static str {
self.id
}

fn discover_existing(&self) -> Result<Option<DiscoveredProvider>, ProviderError> {
discover_with_spec(self, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
self.credential_env_vars
}
}

#[derive(Default)]
pub struct ProviderRegistry {
plugins: HashMap<&'static str, Box<dyn ProviderPlugin>>,
Expand All @@ -82,16 +101,16 @@ impl ProviderRegistry {
#[must_use]
pub fn new() -> Self {
let mut registry = Self::default();
registry.register(providers::claude::ClaudeProvider);
registry.register(providers::codex::CodexProvider);
registry.register(providers::copilot::CopilotProvider);
registry.register(providers::claude::SPEC);
registry.register(providers::codex::SPEC);
registry.register(providers::copilot::SPEC);
registry.register(providers::opencode::OpencodeProvider);
registry.register(providers::generic::GenericProvider);
registry.register(providers::openai::OpenaiProvider);
registry.register(providers::anthropic::AnthropicProvider);
registry.register(providers::nvidia::NvidiaProvider);
registry.register(providers::gitlab::GitlabProvider);
registry.register(providers::github::GithubProvider);
registry.register(providers::openai::SPEC);
registry.register(providers::anthropic::SPEC);
registry.register(providers::nvidia::SPEC);
registry.register(providers::gitlab::SPEC);
registry.register(providers::github::SPEC);
registry.register(providers::outlook::OutlookProvider);
registry
}
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-providers/src/providers/anthropic.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct AnthropicProvider;
use crate::ProviderDiscoverySpec;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "anthropic",
credential_env_vars: &["ANTHROPIC_API_KEY"],
};

impl ProviderPlugin for AnthropicProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

#[cfg(test)]
mod tests {
use super::SPEC;
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-providers/src/providers/claude.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct ClaudeProvider;
use crate::ProviderDiscoverySpec;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "claude-code",
credential_env_vars: &["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"],
};

impl ProviderPlugin for ClaudeProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

#[cfg(test)]
mod tests {
use super::SPEC;
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-providers/src/providers/codex.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct CodexProvider;
use crate::ProviderDiscoverySpec;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "codex",
Comment thread
johntmyers marked this conversation as resolved.
credential_env_vars: &["OPENAI_API_KEY"],
};

impl ProviderPlugin for CodexProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

#[cfg(test)]
mod tests {
use super::SPEC;
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-providers/src/providers/copilot.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct CopilotProvider;
use crate::ProviderDiscoverySpec;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "copilot",
credential_env_vars: &["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"],
};

impl ProviderPlugin for CopilotProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

#[cfg(test)]
mod tests {
use super::SPEC;
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-providers/src/providers/github.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct GithubProvider;
use crate::ProviderDiscoverySpec;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "github",
credential_env_vars: &["GITHUB_TOKEN", "GH_TOKEN"],
};

impl ProviderPlugin for GithubProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

#[cfg(test)]
mod tests {
use super::SPEC;
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-providers/src/providers/gitlab.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct GitlabProvider;
use crate::ProviderDiscoverySpec;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "gitlab",
credential_env_vars: &["GITLAB_TOKEN", "GLAB_TOKEN", "CI_JOB_TOKEN"],
};

impl ProviderPlugin for GitlabProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

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