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
48 changes: 47 additions & 1 deletion crates/openshell-core/src/driver_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

//! Utility helpers shared across compute-driver crates.

use crate::proto::compute::v1::DriverSandbox;
use std::path::PathBuf;

use crate::proto::compute::v1::{DriverSandbox, GetCapabilitiesResponse};

// ---------------------------------------------------------------------------
// Sandbox container/pod label keys (openshell.ai/ namespace)
Expand Down Expand Up @@ -34,6 +36,50 @@ pub const LABEL_SANDBOX_NAMESPACE: &str = "openshell.ai/sandbox-namespace";
/// path used when building the `openshell-sandbox` image layer.
pub const SUPERVISOR_IMAGE_BINARY_PATH: &str = "/openshell-sandbox";

/// Return the XDG state path for a driver's sandbox JWT token file.
///
/// The resulting path is `$XDG_STATE_HOME/openshell/<driver_subdir>[/<namespace>]/<sandbox_id>/sandbox.jwt`.
///
/// `driver_subdir` is driver-specific, e.g. `"docker-sandbox-tokens"` or
/// `"podman-sandbox-tokens"`. When `namespace` is `Some`, it is appended as
/// an additional path component (with `/` and `\` replaced by `-`).
///
/// # Errors
/// Returns an error if the XDG state directory cannot be resolved.
pub fn sandbox_token_path(
driver_subdir: &str,
namespace: Option<&str>,
sandbox_id: &str,
) -> miette::Result<PathBuf> {
let mut path = crate::paths::xdg_state_dir()?
.join("openshell")
.join(driver_subdir);
if let Some(ns) = namespace {
path = path.join(ns.replace(['/', '\\'], "-"));
}
Ok(path.join(sandbox_id).join("sandbox.jwt"))
}

/// Build a [`GetCapabilitiesResponse`] from the common driver capability fields.
///
/// Every compute driver constructs this response with the same fields. Shared
/// here to avoid repeating the struct literal (and the always-zero `gpu_count`
/// default) in each driver crate.
pub fn build_capabilities_response(
driver_name: &str,
driver_version: impl Into<String>,
default_image: impl Into<String>,
supports_gpu: bool,
) -> GetCapabilitiesResponse {
GetCapabilitiesResponse {
driver_name: driver_name.to_string(),
driver_version: driver_version.into(),
default_image: default_image.into(),
supports_gpu,
gpu_count: 0,
}
}

/// Return the effective log level for a sandbox.
///
/// Uses the level from the sandbox spec when non-empty, falling back to
Expand Down
28 changes: 13 additions & 15 deletions crates/openshell-driver-docker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,12 @@ impl DockerComputeDriver {
}

fn capabilities(&self) -> GetCapabilitiesResponse {
GetCapabilitiesResponse {
driver_name: "docker".to_string(),
driver_version: self.config.daemon_version.clone(),
default_image: self.config.default_image.clone(),
supports_gpu: self.config.supports_gpu,
gpu_count: 0,
}
openshell_core::driver_utils::build_capabilities_response(
"docker",
&self.config.daemon_version,
&self.config.default_image,
self.config.supports_gpu,
)
}

fn validate_sandbox(
Expand Down Expand Up @@ -962,17 +961,16 @@ fn sandbox_token_host_path_by_id(
sandbox_id: &str,
config: &DockerDriverRuntimeConfig,
) -> Result<PathBuf, Status> {
let base = openshell_core::paths::xdg_state_dir().map_err(|err| {
openshell_core::driver_utils::sandbox_token_path(
"docker-sandbox-tokens",
Some(&config.sandbox_namespace),
sandbox_id,
)
.map_err(|err| {
Status::internal(format!(
"resolve sandbox token state directory failed: {err}"
))
})?;
Ok(base
.join("openshell")
.join("docker-sandbox-tokens")
.join(config.sandbox_namespace.replace(['/', '\\'], "-"))
.join(sandbox_id)
.join("sandbox.jwt"))
})
}

async fn write_sandbox_token_file(
Expand Down
13 changes: 6 additions & 7 deletions crates/openshell-driver-kubernetes/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,12 @@ impl KubernetesComputeDriver {
}

pub async fn capabilities(&self) -> Result<GetCapabilitiesResponse, String> {
Ok(GetCapabilitiesResponse {
driver_name: "kubernetes".to_string(),
driver_version: openshell_core::VERSION.to_string(),
default_image: self.config.default_image.clone(),
supports_gpu: self.has_gpu_capacity().await.unwrap_or(false),
gpu_count: 0,
})
Ok(openshell_core::driver_utils::build_capabilities_response(
"kubernetes",
openshell_core::VERSION,
&self.config.default_image,
self.has_gpu_capacity().await.unwrap_or(false),
))
}

pub fn default_image(&self) -> &str {
Expand Down
23 changes: 8 additions & 15 deletions crates/openshell-driver-podman/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,8 @@ fn validated_container_name(sandbox_name: &str) -> Result<String, ComputeDriverE
}

fn sandbox_token_host_path(sandbox_id: &str) -> Result<PathBuf, ComputeDriverError> {
let base = openshell_core::paths::xdg_state_dir()
.map_err(|err| ComputeDriverError::Message(format!("resolve state dir failed: {err}")))?;
Ok(base
.join("openshell")
.join("podman-sandbox-tokens")
.join(sandbox_id)
.join("sandbox.jwt"))
openshell_core::driver_utils::sandbox_token_path("podman-sandbox-tokens", None, sandbox_id)
.map_err(|err| ComputeDriverError::Message(format!("resolve state dir failed: {err}")))
}

async fn write_sandbox_token_file(
Expand Down Expand Up @@ -257,14 +252,12 @@ impl PodmanComputeDriver {

/// Report driver capabilities.
pub fn capabilities(&self) -> Result<GetCapabilitiesResponse, ComputeDriverError> {
let supports_gpu = Self::has_gpu_capacity();
Ok(GetCapabilitiesResponse {
driver_name: "podman".to_string(),
driver_version: openshell_core::VERSION.to_string(),
default_image: self.config.default_image.clone(),
supports_gpu,
gpu_count: 0,
})
Ok(openshell_core::driver_utils::build_capabilities_response(
"podman",
openshell_core::VERSION,
&self.config.default_image,
Self::has_gpu_capacity(),
))
}

#[must_use]
Expand Down
27 changes: 4 additions & 23 deletions crates/openshell-ocsf/src/builders/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,6 @@ impl<'a> BaseEventBuilder<'a> {
}
}

#[must_use]
pub fn severity(mut self, id: SeverityId) -> Self {
self.severity = id;
self
}
#[must_use]
pub fn status(mut self, id: StatusId) -> Self {
self.status = Some(id);
self
}
#[must_use]
pub fn message(mut self, msg: impl Into<String>) -> Self {
self.message = Some(msg.into());
self
}
#[must_use]
pub fn activity_name(mut self, name: impl Into<String>) -> Self {
self.activity_name = Some(name.into());
Expand All @@ -72,22 +57,18 @@ impl<'a> BaseEventBuilder<'a> {
self.severity,
self.ctx.metadata(&["container", "host"]),
);
if let Some(status) = self.status {
base.set_status(status);
}
if let Some(msg) = self.message {
base.set_message(msg);
}
base.set_device(self.ctx.device());
base.set_container(self.ctx.container());
if !self.unmapped.is_empty() {
base.unmapped = Some(serde_json::Value::Object(self.unmapped));
}
self.ctx
.apply_common_fields(&mut base, self.status, self.message);

OcsfEvent::Base(BaseEvent { base })
}
}

impl_builder_setters!(BaseEventBuilder);

#[cfg(test)]
mod tests {
use super::*;
Expand Down
28 changes: 4 additions & 24 deletions crates/openshell-ocsf/src/builders/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,6 @@ impl<'a> ConfigStateChangeBuilder<'a> {
}
}

#[must_use]
pub fn severity(mut self, id: SeverityId) -> Self {
self.severity = id;
self
}
#[must_use]
pub fn status(mut self, id: StatusId) -> Self {
self.status = Some(id);
self
}
#[must_use]
pub fn message(mut self, msg: impl Into<String>) -> Self {
self.message = Some(msg.into());
self
}

/// Set state with a custom label (OCSF `state_id` + display label).
#[must_use]
pub fn state(mut self, id: StateId, label: &str) -> Self {
Expand Down Expand Up @@ -92,17 +76,11 @@ impl<'a> ConfigStateChangeBuilder<'a> {
self.ctx
.metadata(&["security_control", "container", "host"]),
);
if let Some(status) = self.status {
base.set_status(status);
}
if let Some(msg) = self.message {
base.set_message(msg);
}
base.set_device(self.ctx.device());
base.set_container(self.ctx.container());
if !self.unmapped.is_empty() {
base.unmapped = Some(serde_json::Value::Object(self.unmapped));
}
self.ctx
.apply_common_fields(&mut base, self.status, self.message);

OcsfEvent::DeviceConfigStateChange(DeviceConfigStateChangeEvent {
base,
Expand All @@ -114,6 +92,8 @@ impl<'a> ConfigStateChangeBuilder<'a> {
}
}

impl_builder_setters!(ConfigStateChangeBuilder);

#[cfg(test)]
mod tests {
use super::*;
Expand Down
18 changes: 3 additions & 15 deletions crates/openshell-ocsf/src/builders/finding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,6 @@ impl<'a> DetectionFindingBuilder<'a> {
self
}
#[must_use]
pub fn severity(mut self, id: SeverityId) -> Self {
self.severity = id;
self
}
#[must_use]
pub fn action(mut self, id: ActionId) -> Self {
self.action = Some(id);
self
Expand Down Expand Up @@ -89,11 +84,6 @@ impl<'a> DetectionFindingBuilder<'a> {
self
}
#[must_use]
pub fn message(mut self, msg: impl Into<String>) -> Self {
self.message = Some(msg.into());
self
}
#[must_use]
pub fn log_source(mut self, source: impl Into<String>) -> Self {
self.log_source = Some(source.into());
self
Expand Down Expand Up @@ -147,11 +137,7 @@ impl<'a> DetectionFindingBuilder<'a> {
self.severity,
metadata,
);
if let Some(msg) = self.message {
base.set_message(msg);
}
base.set_device(self.ctx.device());
base.set_container(self.ctx.container());
self.ctx.apply_common_fields(&mut base, None, self.message);

OcsfEvent::DetectionFinding(DetectionFindingEvent {
base,
Expand All @@ -178,6 +164,8 @@ impl<'a> DetectionFindingBuilder<'a> {
}
}

impl_builder_setters!(DetectionFindingBuilder, no_status);

#[cfg(test)]
mod tests {
use super::*;
Expand Down
27 changes: 4 additions & 23 deletions crates/openshell-ocsf/src/builders/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,6 @@ impl<'a> HttpActivityBuilder<'a> {
self
}
#[must_use]
pub fn severity(mut self, id: SeverityId) -> Self {
self.severity = id;
self
}
#[must_use]
pub fn status(mut self, id: StatusId) -> Self {
self.status = Some(id);
self
}
#[must_use]
pub fn http_request(mut self, req: HttpRequest) -> Self {
self.http_request = Some(req);
self
Expand Down Expand Up @@ -104,11 +94,6 @@ impl<'a> HttpActivityBuilder<'a> {
self
}
#[must_use]
pub fn message(mut self, msg: impl Into<String>) -> Self {
self.message = Some(msg.into());
self
}
#[must_use]
pub fn status_detail(mut self, detail: impl Into<String>) -> Self {
self.status_detail = Some(detail.into());
self
Expand All @@ -128,17 +113,11 @@ impl<'a> HttpActivityBuilder<'a> {
self.ctx
.metadata(&["security_control", "network_proxy", "container", "host"]),
);
if let Some(status) = self.status {
base.set_status(status);
}
if let Some(msg) = self.message {
base.set_message(msg);
}
if let Some(detail) = self.status_detail {
base.set_status_detail(detail);
}
base.set_device(self.ctx.device());
base.set_container(self.ctx.container());
self.ctx
.apply_common_fields(&mut base, self.status, self.message);

OcsfEvent::HttpActivity(HttpActivityEvent {
base,
Expand All @@ -157,6 +136,8 @@ impl<'a> HttpActivityBuilder<'a> {
}
}

impl_builder_setters!(HttpActivityBuilder);

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