mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-04 08:28:19 +08:00
refactor(server): normalize compute driver config acquisition (#1974)
* refactor(server): remove unused compute runtime constructor parameter Signed-off-by: Evan Lezar <elezar@nvidia.com> * refactor(server): normalize compute driver type imports Signed-off-by: Evan Lezar <elezar@nvidia.com> * refactor(server): key driver config tables by name Signed-off-by: Evan Lezar <elezar@nvidia.com> * refactor(server): normalize compute driver config acquisition Signed-off-by: Evan Lezar <elezar@nvidia.com> --------- Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
@@ -14,10 +14,10 @@ use tracing::{info, warn};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::certgen;
|
||||
use crate::compute::{DockerComputeConfig, VmComputeConfig};
|
||||
use crate::compute::driver_config::GuestTlsPaths;
|
||||
use crate::config_file::{self, ConfigFile, GatewayFileSection};
|
||||
use crate::defaults::{self, LocalTlsPaths};
|
||||
use crate::{run_server, tracing_bus::TracingLogBus};
|
||||
use crate::{ServerStartupConfig, run_server, tracing_bus::TracingLogBus};
|
||||
|
||||
/// `OpenShell` gateway process - gRPC and HTTP server with protocol multiplexing.
|
||||
///
|
||||
@@ -232,34 +232,30 @@ pub async fn run_cli() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
fn prepare_server_config(args: &mut RunArgs, matches: &ArgMatches) -> Result<ServerStartupConfig> {
|
||||
// Load TOML when explicitly requested, or from the default XDG location
|
||||
// when that file exists. Missing default config is not an error: runtime
|
||||
// defaults and OPENSHELL_* env vars are enough for package-managed starts.
|
||||
let config_path = resolve_config_path(&args)?;
|
||||
let config_path = resolve_config_path(args)?;
|
||||
let file: Option<ConfigFile> = if let Some(path) = config_path {
|
||||
Some(config_file::load(&path).map_err(|e| miette::miette!("{e}"))?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(file) = file.as_ref() {
|
||||
merge_file_into_args(&mut args, &file.openshell.gateway, &matches);
|
||||
merge_file_into_args(args, &file.openshell.gateway, matches);
|
||||
}
|
||||
normalize_compute_driver_socket_args(&mut args, &matches)?;
|
||||
normalize_compute_driver_socket_args(args, matches)?;
|
||||
|
||||
let local_tls = apply_runtime_defaults(&mut args)?;
|
||||
let local_tls = apply_runtime_defaults(args)?;
|
||||
let guest_tls = local_tls.as_ref().map(GuestTlsPaths::from);
|
||||
let local_jwt = defaults::complete_local_jwt_config()?;
|
||||
|
||||
let tracing_log_bus = TracingLogBus::new();
|
||||
tracing_log_bus.install_subscriber(
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level)),
|
||||
);
|
||||
|
||||
let bind = SocketAddr::new(args.bind_address, args.port);
|
||||
|
||||
let has_client_ca = args.tls_client_ca.is_some();
|
||||
let has_oidc = args.oidc_issuer.is_some();
|
||||
let mtls_auth_enabled = resolve_mtls_auth_enabled(&args, &matches, file.as_ref());
|
||||
let mtls_auth_enabled = resolve_mtls_auth_enabled(args, matches, file.as_ref());
|
||||
|
||||
if args.disable_tls && has_client_ca {
|
||||
return Err(miette::miette!(
|
||||
@@ -278,7 +274,7 @@ async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
}
|
||||
if mtls_auth_enabled
|
||||
&& matches!(
|
||||
effective_single_driver(&args),
|
||||
effective_single_driver(args),
|
||||
Some(ComputeDriverKind::Kubernetes)
|
||||
)
|
||||
{
|
||||
@@ -329,14 +325,14 @@ async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
let health_bind = resolve_aux_listener(
|
||||
args.bind_address,
|
||||
args.health_port,
|
||||
&matches,
|
||||
matches,
|
||||
"health_port",
|
||||
|| file_gateway.and_then(|g| g.health_bind_address),
|
||||
);
|
||||
let metrics_bind = resolve_aux_listener(
|
||||
args.bind_address,
|
||||
args.metrics_port,
|
||||
&matches,
|
||||
matches,
|
||||
"metrics_port",
|
||||
|| file_gateway.and_then(|g| g.metrics_bind_address),
|
||||
);
|
||||
@@ -422,15 +418,31 @@ async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
config.gateway_jwt = Some(jwt);
|
||||
}
|
||||
|
||||
let vm_config = build_vm_config(
|
||||
file.as_ref(),
|
||||
local_tls.as_ref(),
|
||||
args.disable_tls,
|
||||
args.port,
|
||||
)?;
|
||||
let docker_config = build_docker_config(file.as_ref(), local_tls.as_ref())?;
|
||||
Ok(ServerStartupConfig {
|
||||
config,
|
||||
config_file: file,
|
||||
guest_tls,
|
||||
})
|
||||
}
|
||||
|
||||
if args.disable_tls {
|
||||
async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
let prepared = prepare_server_config(&mut args, &matches)?;
|
||||
|
||||
let tracing_log_bus = TracingLogBus::new();
|
||||
tracing_log_bus.install_subscriber(
|
||||
EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::new(&prepared.config.log_level)),
|
||||
);
|
||||
|
||||
let has_client_ca = prepared
|
||||
.config
|
||||
.tls
|
||||
.as_ref()
|
||||
.and_then(|tls| tls.client_ca_path.as_ref())
|
||||
.is_some();
|
||||
let has_oidc = prepared.config.oidc.is_some();
|
||||
|
||||
if prepared.config.tls.is_none() {
|
||||
warn!("TLS disabled — listening on plaintext HTTP");
|
||||
} else {
|
||||
info!("TLS enabled — listening on encrypted HTTPS");
|
||||
@@ -439,22 +451,22 @@ async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
if has_client_ca {
|
||||
info!("TLS client certificate verification enabled");
|
||||
}
|
||||
if config.mtls_auth.enabled {
|
||||
if prepared.config.mtls_auth.enabled {
|
||||
info!("mTLS user authentication enabled");
|
||||
}
|
||||
if has_oidc {
|
||||
info!("OIDC authentication enabled");
|
||||
}
|
||||
if config.auth.allow_unauthenticated_users {
|
||||
if prepared.config.auth.allow_unauthenticated_users {
|
||||
warn!(
|
||||
"Unauthenticated user access enabled — only use this for trusted local development or a fully trusted fronting proxy"
|
||||
);
|
||||
}
|
||||
|
||||
if !config.auth.allow_unauthenticated_users
|
||||
&& !config.mtls_auth.enabled
|
||||
if !prepared.config.auth.allow_unauthenticated_users
|
||||
&& !prepared.config.mtls_auth.enabled
|
||||
&& !has_oidc
|
||||
&& config.gateway_jwt.is_none()
|
||||
&& prepared.config.gateway_jwt.is_none()
|
||||
{
|
||||
warn!(
|
||||
"Neither mTLS user auth nor OIDC nor sandbox JWT auth is configured — \
|
||||
@@ -462,17 +474,11 @@ async fn run_from_args(mut args: RunArgs, matches: ArgMatches) -> Result<()> {
|
||||
);
|
||||
}
|
||||
|
||||
info!(bind = %config.bind_address, "Starting OpenShell server");
|
||||
info!(bind = %prepared.config.bind_address, "Starting OpenShell server");
|
||||
|
||||
Box::pin(run_server(
|
||||
config,
|
||||
vm_config,
|
||||
docker_config,
|
||||
file,
|
||||
tracing_log_bus,
|
||||
))
|
||||
.await
|
||||
.into_diagnostic()
|
||||
Box::pin(run_server(prepared, tracing_log_bus))
|
||||
.await
|
||||
.into_diagnostic()
|
||||
}
|
||||
|
||||
fn parse_compute_driver(value: &str) -> std::result::Result<String, String> {
|
||||
@@ -751,87 +757,6 @@ fn resolve_mtls_auth_enabled(
|
||||
is_singleplayer_driver(args)
|
||||
}
|
||||
|
||||
/// Build [`VmComputeConfig`] from the `[openshell.drivers.vm]` table
|
||||
/// inherited from `[openshell.gateway]`.
|
||||
fn build_vm_config(
|
||||
file: Option<&ConfigFile>,
|
||||
local_tls: Option<&LocalTlsPaths>,
|
||||
disable_tls: bool,
|
||||
gateway_port: u16,
|
||||
) -> Result<VmComputeConfig> {
|
||||
let mut cfg = if let Some(file) = file {
|
||||
let merged = config_file::driver_table(
|
||||
ComputeDriverKind::Vm,
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get("vm"),
|
||||
);
|
||||
merged
|
||||
.try_into::<VmComputeConfig>()
|
||||
.map_err(|e| miette::miette!("invalid [openshell.drivers.vm] table: {e}"))?
|
||||
} else {
|
||||
VmComputeConfig::default()
|
||||
};
|
||||
|
||||
if cfg.state_dir.as_os_str().is_empty() {
|
||||
cfg.state_dir = VmComputeConfig::default_state_dir();
|
||||
}
|
||||
if cfg.grpc_endpoint.trim().is_empty() && (disable_tls || local_tls.is_some()) {
|
||||
let scheme = if disable_tls { "http" } else { "https" };
|
||||
cfg.grpc_endpoint = format!("{scheme}://127.0.0.1:{gateway_port}");
|
||||
}
|
||||
apply_guest_tls_defaults(
|
||||
&mut cfg.guest_tls_ca,
|
||||
&mut cfg.guest_tls_cert,
|
||||
&mut cfg.guest_tls_key,
|
||||
local_tls,
|
||||
);
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Build [`DockerComputeConfig`] using the same inheritance pattern as
|
||||
/// [`build_vm_config`].
|
||||
fn build_docker_config(
|
||||
file: Option<&ConfigFile>,
|
||||
local_tls: Option<&LocalTlsPaths>,
|
||||
) -> Result<DockerComputeConfig> {
|
||||
let mut cfg = if let Some(file) = file {
|
||||
let merged = config_file::driver_table(
|
||||
ComputeDriverKind::Docker,
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get("docker"),
|
||||
);
|
||||
merged
|
||||
.try_into::<DockerComputeConfig>()
|
||||
.map_err(|e| miette::miette!("invalid [openshell.drivers.docker] table: {e}"))?
|
||||
} else {
|
||||
DockerComputeConfig::default()
|
||||
};
|
||||
apply_guest_tls_defaults(
|
||||
&mut cfg.guest_tls_ca,
|
||||
&mut cfg.guest_tls_cert,
|
||||
&mut cfg.guest_tls_key,
|
||||
local_tls,
|
||||
);
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
fn apply_guest_tls_defaults(
|
||||
ca: &mut Option<PathBuf>,
|
||||
cert: &mut Option<PathBuf>,
|
||||
key: &mut Option<PathBuf>,
|
||||
local_tls: Option<&LocalTlsPaths>,
|
||||
) {
|
||||
if ca.is_none()
|
||||
&& cert.is_none()
|
||||
&& key.is_none()
|
||||
&& let Some(paths) = local_tls
|
||||
{
|
||||
*ca = Some(paths.ca.clone());
|
||||
*cert = Some(paths.client_cert.clone());
|
||||
*key = Some(paths.client_key.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Cli, command};
|
||||
@@ -1793,6 +1718,51 @@ enable_loopback_service_http = false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_config_preparation_ignores_unselected_driver_tables() {
|
||||
let _lock = ENV_LOCK
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let state = tempfile::tempdir().unwrap();
|
||||
let local_tls = tempfile::tempdir().unwrap();
|
||||
let _g1 = EnvVarGuard::set("XDG_STATE_HOME", state.path().to_str().unwrap());
|
||||
let _g2 = EnvVarGuard::set(
|
||||
"OPENSHELL_LOCAL_TLS_DIR",
|
||||
local_tls.path().to_str().unwrap(),
|
||||
);
|
||||
let config_path = state.path().join("gateway.toml");
|
||||
std::fs::write(
|
||||
&config_path,
|
||||
r#"
|
||||
[openshell.drivers.docker]
|
||||
unknown_docker_key = true
|
||||
|
||||
[openshell.drivers.vm]
|
||||
mem_mib = "not-a-number"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (mut args, matches) = parse_with_args(&[
|
||||
"openshell-gateway",
|
||||
"--config",
|
||||
config_path.to_str().unwrap(),
|
||||
"--db-url",
|
||||
"sqlite::memory:",
|
||||
"--drivers",
|
||||
"podman",
|
||||
"--disable-tls",
|
||||
]);
|
||||
|
||||
let prepared =
|
||||
super::prepare_server_config(&mut args, &matches).expect("server config is prepared");
|
||||
|
||||
assert_eq!(prepared.config.compute_drivers, vec!["podman".to_string()]);
|
||||
let file = prepared.config_file.expect("config file is preserved");
|
||||
assert!(file.openshell.drivers.contains_key("docker"));
|
||||
assert!(file.openshell.drivers.contains_key("vm"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn driver_inherits_shared_image_from_gateway_section() {
|
||||
// [openshell.gateway].default_image inherits into the K8s driver
|
||||
@@ -1807,7 +1777,7 @@ namespace = "agents"
|
||||
"#,
|
||||
);
|
||||
let merged = crate::config_file::driver_table(
|
||||
super::ComputeDriverKind::Kubernetes,
|
||||
super::ComputeDriverKind::Kubernetes.as_str(),
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get("kubernetes"),
|
||||
);
|
||||
@@ -1830,7 +1800,7 @@ default_image = "k8s-specific:1.0"
|
||||
"#,
|
||||
);
|
||||
let merged = crate::config_file::driver_table(
|
||||
super::ComputeDriverKind::Kubernetes,
|
||||
super::ComputeDriverKind::Kubernetes.as_str(),
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get("kubernetes"),
|
||||
);
|
||||
@@ -1839,18 +1809,4 @@ default_image = "k8s-specific:1.0"
|
||||
.expect("deserializes");
|
||||
assert_eq!(parsed.default_image, "k8s-specific:1.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn docker_config_reads_bind_mount_opt_in_from_driver_table() {
|
||||
let file = config_file_from_toml(
|
||||
r"
|
||||
[openshell.drivers.docker]
|
||||
enable_bind_mounts = true
|
||||
",
|
||||
);
|
||||
|
||||
let cfg = super::build_docker_config(Some(&file), None).expect("docker config");
|
||||
|
||||
assert!(cfg.enable_bind_mounts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Selected compute-driver config construction.
|
||||
//!
|
||||
//! This module owns loading the selected driver config from TOML, applying
|
||||
//! driver-specific environment overrides, and applying gateway startup defaults.
|
||||
//! It does not acquire, connect to, or start compute drivers.
|
||||
|
||||
use crate::config_file;
|
||||
use crate::defaults::LocalTlsPaths;
|
||||
use openshell_core::{ComputeDriverKind, Error, Result};
|
||||
use openshell_driver_docker::DockerComputeConfig;
|
||||
use openshell_driver_kubernetes::KubernetesComputeConfig;
|
||||
use openshell_driver_podman::PodmanComputeConfig;
|
||||
use serde::Deserialize;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::VmComputeConfig;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GuestTlsPaths {
|
||||
ca: PathBuf,
|
||||
cert: PathBuf,
|
||||
key: PathBuf,
|
||||
}
|
||||
|
||||
impl From<&LocalTlsPaths> for GuestTlsPaths {
|
||||
fn from(paths: &LocalTlsPaths) -> Self {
|
||||
Self {
|
||||
ca: paths.ca.clone(),
|
||||
cert: paths.client_cert.clone(),
|
||||
key: paths.client_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DriverStartupContext<'a> {
|
||||
pub file: Option<&'a config_file::ConfigFile>,
|
||||
pub guest_tls: Option<&'a GuestTlsPaths>,
|
||||
pub gateway_port: u16,
|
||||
pub gateway_tls_enabled: bool,
|
||||
pub endpoint_overrides: &'a BTreeMap<String, PathBuf>,
|
||||
}
|
||||
|
||||
/// Build the selected Kubernetes config from TOML plus runtime defaults.
|
||||
pub fn kubernetes_config_from_context(
|
||||
context: DriverStartupContext<'_>,
|
||||
) -> Result<KubernetesComputeConfig> {
|
||||
let mut cfg = driver_config_from_context(context, ComputeDriverKind::Kubernetes.as_str())?;
|
||||
apply_kubernetes_runtime_defaults(&mut cfg);
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
pub fn kubernetes_config_for_k8s_sa_bootstrap(
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
) -> Result<KubernetesComputeConfig> {
|
||||
let Some(file) = file else {
|
||||
return Err(Error::config(
|
||||
"K8s ServiceAccount bootstrap requires [openshell.drivers.kubernetes] when sandbox JWT issuing is enabled in-cluster",
|
||||
));
|
||||
};
|
||||
if !file.openshell.drivers.contains_key("kubernetes") {
|
||||
return Err(Error::config(
|
||||
"K8s ServiceAccount bootstrap requires [openshell.drivers.kubernetes] when sandbox JWT issuing is enabled in-cluster",
|
||||
));
|
||||
}
|
||||
driver_config_from_file(Some(file), ComputeDriverKind::Kubernetes.as_str())
|
||||
}
|
||||
|
||||
/// Build the selected Podman config from TOML plus runtime defaults.
|
||||
pub fn podman_config_from_context(
|
||||
context: DriverStartupContext<'_>,
|
||||
) -> Result<PodmanComputeConfig> {
|
||||
let mut podman = driver_config_from_context(context, ComputeDriverKind::Podman.as_str())?;
|
||||
apply_podman_runtime_defaults(&mut podman, context);
|
||||
Ok(podman)
|
||||
}
|
||||
|
||||
/// Build the selected Docker config from TOML plus runtime defaults.
|
||||
pub fn docker_config_from_context(
|
||||
context: DriverStartupContext<'_>,
|
||||
) -> Result<DockerComputeConfig> {
|
||||
let mut cfg = driver_config_from_context(context, ComputeDriverKind::Docker.as_str())?;
|
||||
apply_docker_runtime_defaults(&mut cfg, context);
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Build the selected VM config from TOML plus runtime defaults.
|
||||
pub fn vm_config_from_context(context: DriverStartupContext<'_>) -> Result<VmComputeConfig> {
|
||||
let mut cfg = driver_config_from_context(context, ComputeDriverKind::Vm.as_str())?;
|
||||
apply_vm_runtime_defaults(&mut cfg, context);
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
pub fn remote_driver_config_from_context(
|
||||
context: DriverStartupContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<RemoteDriverConfig> {
|
||||
let mut cfg = driver_config_from_context(context, name)?;
|
||||
apply_remote_driver_overrides(&mut cfg, context, name);
|
||||
validate_remote_driver_config(&cfg, name)?;
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RemoteDriverConfig {
|
||||
#[serde(default)]
|
||||
pub socket_path: PathBuf,
|
||||
}
|
||||
|
||||
fn driver_config_from_context<T>(context: DriverStartupContext<'_>, driver_name: &str) -> Result<T>
|
||||
where
|
||||
T: Default + serde::de::DeserializeOwned,
|
||||
{
|
||||
driver_config_from_file(context.file, driver_name)
|
||||
}
|
||||
|
||||
fn driver_config_from_file<T>(
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
driver_name: &str,
|
||||
) -> Result<T>
|
||||
where
|
||||
T: Default + serde::de::DeserializeOwned,
|
||||
{
|
||||
let Some(file) = file else {
|
||||
return Ok(T::default());
|
||||
};
|
||||
let merged = config_file::driver_table(
|
||||
driver_name,
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get(driver_name),
|
||||
);
|
||||
merged.try_into().map_err(|e| {
|
||||
Error::config(format!(
|
||||
"invalid [openshell.drivers.{driver_name}] table: {e}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_kubernetes_runtime_defaults(k8s: &mut KubernetesComputeConfig) {
|
||||
if let Ok(size) = std::env::var("OPENSHELL_K8S_WORKSPACE_DEFAULT_STORAGE_SIZE") {
|
||||
k8s.workspace_default_storage_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_podman_runtime_defaults(
|
||||
podman: &mut PodmanComputeConfig,
|
||||
context: DriverStartupContext<'_>,
|
||||
) {
|
||||
podman.gateway_port = context.gateway_port;
|
||||
apply_podman_env_overrides(podman);
|
||||
apply_guest_tls_defaults_to_split_fields(
|
||||
&mut podman.guest_tls_ca,
|
||||
&mut podman.guest_tls_cert,
|
||||
&mut podman.guest_tls_key,
|
||||
context.guest_tls,
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_docker_runtime_defaults(cfg: &mut DockerComputeConfig, context: DriverStartupContext<'_>) {
|
||||
apply_guest_tls_defaults_to_split_fields(
|
||||
&mut cfg.guest_tls_ca,
|
||||
&mut cfg.guest_tls_cert,
|
||||
&mut cfg.guest_tls_key,
|
||||
context.guest_tls,
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_vm_runtime_defaults(cfg: &mut VmComputeConfig, context: DriverStartupContext<'_>) {
|
||||
if cfg.state_dir.as_os_str().is_empty() {
|
||||
cfg.state_dir = VmComputeConfig::default_state_dir();
|
||||
}
|
||||
if cfg.grpc_endpoint.trim().is_empty()
|
||||
&& (!context.gateway_tls_enabled || context.guest_tls.is_some())
|
||||
{
|
||||
let scheme = if context.gateway_tls_enabled {
|
||||
"https"
|
||||
} else {
|
||||
"http"
|
||||
};
|
||||
cfg.grpc_endpoint = format!("{scheme}://127.0.0.1:{}", context.gateway_port);
|
||||
}
|
||||
|
||||
apply_guest_tls_defaults_to_split_fields(
|
||||
&mut cfg.guest_tls_ca,
|
||||
&mut cfg.guest_tls_cert,
|
||||
&mut cfg.guest_tls_key,
|
||||
context.guest_tls,
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_podman_env_overrides(podman: &mut PodmanComputeConfig) {
|
||||
if let Ok(p) = std::env::var("OPENSHELL_PODMAN_SOCKET") {
|
||||
podman.socket_path = PathBuf::from(p);
|
||||
}
|
||||
if let Ok(ip) = std::env::var("OPENSHELL_PODMAN_HOST_GATEWAY_IP") {
|
||||
podman.host_gateway_ip = ip;
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_remote_driver_overrides(
|
||||
cfg: &mut RemoteDriverConfig,
|
||||
context: DriverStartupContext<'_>,
|
||||
name: &str,
|
||||
) {
|
||||
if let Some(socket_path) = context.endpoint_overrides.get(name) {
|
||||
cfg.socket_path.clone_from(socket_path);
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_remote_driver_config(cfg: &RemoteDriverConfig, name: &str) -> Result<()> {
|
||||
if !cfg.socket_path.as_os_str().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
Err(Error::config(format!(
|
||||
"remote compute driver '{name}' requires socket_path"
|
||||
)))
|
||||
}
|
||||
|
||||
fn apply_guest_tls_defaults_to_split_fields(
|
||||
ca: &mut Option<PathBuf>,
|
||||
cert: &mut Option<PathBuf>,
|
||||
key: &mut Option<PathBuf>,
|
||||
defaults: Option<&GuestTlsPaths>,
|
||||
) {
|
||||
if ca.is_none()
|
||||
&& cert.is_none()
|
||||
&& key.is_none()
|
||||
&& let Some(paths) = defaults
|
||||
{
|
||||
*ca = Some(paths.ca.clone());
|
||||
*cert = Some(paths.cert.clone());
|
||||
*key = Some(paths.key.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn test_context(file: Option<&config_file::ConfigFile>) -> DriverStartupContext<'_> {
|
||||
static EMPTY_ENDPOINT_OVERRIDES: std::sync::LazyLock<BTreeMap<String, PathBuf>> =
|
||||
std::sync::LazyLock::new(BTreeMap::new);
|
||||
test_context_with_endpoint_overrides(file, &EMPTY_ENDPOINT_OVERRIDES)
|
||||
}
|
||||
|
||||
fn test_context_with_endpoint_overrides<'a>(
|
||||
file: Option<&'a config_file::ConfigFile>,
|
||||
endpoint_overrides: &'a BTreeMap<String, PathBuf>,
|
||||
) -> DriverStartupContext<'a> {
|
||||
DriverStartupContext {
|
||||
file,
|
||||
guest_tls: None,
|
||||
gateway_port: openshell_core::config::DEFAULT_SERVER_PORT,
|
||||
gateway_tls_enabled: false,
|
||||
endpoint_overrides,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn k8s_sa_bootstrap_rejects_missing_kubernetes_driver_config() {
|
||||
let err = kubernetes_config_for_k8s_sa_bootstrap(None).unwrap_err();
|
||||
assert!(err.to_string().contains("[openshell.drivers.kubernetes]"));
|
||||
|
||||
let file: config_file::ConfigFile =
|
||||
toml::from_str("[openshell.gateway]\n").expect("valid config");
|
||||
let err = kubernetes_config_for_k8s_sa_bootstrap(Some(&file)).unwrap_err();
|
||||
assert!(err.to_string().contains("[openshell.drivers.kubernetes]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn k8s_sa_bootstrap_uses_configured_namespace_and_service_account() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r#"
|
||||
[openshell.gateway]
|
||||
|
||||
[openshell.drivers.kubernetes]
|
||||
namespace = "sandboxes"
|
||||
service_account_name = "sandbox-sa"
|
||||
"#,
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let cfg = kubernetes_config_for_k8s_sa_bootstrap(Some(&file)).unwrap();
|
||||
assert_eq!(cfg.namespace, "sandboxes");
|
||||
assert_eq!(cfg.service_account_name, "sandbox-sa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn podman_config_reads_bind_mount_opt_in_from_driver_table() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r"
|
||||
[openshell.drivers.podman]
|
||||
enable_bind_mounts = true
|
||||
",
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let cfg = podman_config_from_context(test_context(Some(&file))).expect("podman config");
|
||||
|
||||
assert!(cfg.enable_bind_mounts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn docker_config_reads_bind_mount_opt_in_from_driver_table() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r"
|
||||
[openshell.drivers.docker]
|
||||
enable_bind_mounts = true
|
||||
",
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let cfg = docker_config_from_context(test_context(Some(&file))).expect("docker config");
|
||||
|
||||
assert!(cfg.enable_bind_mounts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_driver_config_reads_socket_path_from_named_table() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r#"
|
||||
[openshell.drivers.kyma]
|
||||
socket_path = "/run/openshell/kyma.sock"
|
||||
"#,
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let cfg = remote_driver_config_from_context(test_context(Some(&file)), "kyma")
|
||||
.expect("remote config");
|
||||
|
||||
assert_eq!(cfg.socket_path, PathBuf::from("/run/openshell/kyma.sock"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_driver_config_uses_endpoint_override_without_file() {
|
||||
let endpoint_overrides =
|
||||
BTreeMap::from([("kyma".to_string(), PathBuf::from("/tmp/kyma.sock"))]);
|
||||
|
||||
let cfg = remote_driver_config_from_context(
|
||||
test_context_with_endpoint_overrides(None, &endpoint_overrides),
|
||||
"kyma",
|
||||
)
|
||||
.expect("remote config");
|
||||
|
||||
assert_eq!(cfg.socket_path, PathBuf::from("/tmp/kyma.sock"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_driver_config_endpoint_override_wins_over_file() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r#"
|
||||
[openshell.drivers.kyma]
|
||||
socket_path = "/run/openshell/kyma.sock"
|
||||
"#,
|
||||
)
|
||||
.expect("valid config");
|
||||
let endpoint_overrides =
|
||||
BTreeMap::from([("kyma".to_string(), PathBuf::from("/tmp/kyma.sock"))]);
|
||||
|
||||
let cfg = remote_driver_config_from_context(
|
||||
test_context_with_endpoint_overrides(Some(&file), &endpoint_overrides),
|
||||
"kyma",
|
||||
)
|
||||
.expect("remote config");
|
||||
|
||||
assert_eq!(cfg.socket_path, PathBuf::from("/tmp/kyma.sock"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_driver_config_rejects_missing_socket_path() {
|
||||
let err = remote_driver_config_from_context(test_context(None), "kyma").unwrap_err();
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("remote compute driver 'kyma' requires socket_path")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn docker_config_reports_selected_invalid_driver_table() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r"
|
||||
[openshell.drivers.docker]
|
||||
unknown_docker_key = true
|
||||
",
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let err = docker_config_from_context(test_context(Some(&file))).unwrap_err();
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("invalid [openshell.drivers.docker] table")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vm_config_reports_selected_invalid_driver_table() {
|
||||
let file: config_file::ConfigFile = toml::from_str(
|
||||
r#"
|
||||
[openshell.drivers.vm]
|
||||
mem_mib = "not-a-number"
|
||||
"#,
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let err = vm_config_from_context(test_context(Some(&file))).unwrap_err();
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("invalid [openshell.drivers.vm] table")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,13 @@
|
||||
|
||||
//! Gateway-owned compute orchestration over a pluggable compute backend.
|
||||
|
||||
pub mod driver_config;
|
||||
pub mod lease;
|
||||
pub mod vm;
|
||||
|
||||
pub use openshell_driver_docker::DockerComputeConfig;
|
||||
pub use openshell_driver_kubernetes::KubernetesComputeConfig;
|
||||
pub use openshell_driver_podman::PodmanComputeConfig;
|
||||
pub use vm::VmComputeConfig;
|
||||
|
||||
use crate::grpc::policy::SANDBOX_SETTINGS_OBJECT_TYPE;
|
||||
@@ -33,11 +36,9 @@ use openshell_core::proto::{
|
||||
};
|
||||
use openshell_driver_docker::DockerComputeDriver;
|
||||
use openshell_driver_kubernetes::{
|
||||
ComputeDriverService, KubernetesComputeConfig, KubernetesComputeDriver,
|
||||
};
|
||||
use openshell_driver_podman::{
|
||||
ComputeDriverService as PodmanDriverService, PodmanComputeConfig, PodmanComputeDriver,
|
||||
ComputeDriverService as KubernetesDriverService, KubernetesComputeDriver,
|
||||
};
|
||||
use openshell_driver_podman::{ComputeDriverService as PodmanDriverService, PodmanComputeDriver};
|
||||
use prost::Message;
|
||||
use std::fmt;
|
||||
use std::net::SocketAddr;
|
||||
@@ -292,7 +293,6 @@ impl ComputeRuntime {
|
||||
sandbox_watch_bus: SandboxWatchBus,
|
||||
tracing_log_bus: TracingLogBus,
|
||||
supervisor_sessions: Arc<SupervisorSessionRegistry>,
|
||||
_allows_loopback_endpoints: bool,
|
||||
gateway_bind_addresses: Vec<SocketAddr>,
|
||||
) -> Result<Self, ComputeError> {
|
||||
let capabilities = driver
|
||||
@@ -365,7 +365,6 @@ impl ComputeRuntime {
|
||||
sandbox_watch_bus,
|
||||
tracing_log_bus,
|
||||
supervisor_sessions,
|
||||
true,
|
||||
gateway_bind_addresses,
|
||||
)
|
||||
.await
|
||||
@@ -382,7 +381,7 @@ impl ComputeRuntime {
|
||||
let driver = KubernetesComputeDriver::new(config)
|
||||
.await
|
||||
.map_err(|err| ComputeError::Message(err.to_string()))?;
|
||||
let driver: SharedComputeDriver = Arc::new(ComputeDriverService::new(driver));
|
||||
let driver: SharedComputeDriver = Arc::new(KubernetesDriverService::new(driver));
|
||||
Self::from_driver(
|
||||
ComputeDriverKind::Kubernetes.as_str().to_string(),
|
||||
driver,
|
||||
@@ -394,7 +393,6 @@ impl ComputeRuntime {
|
||||
sandbox_watch_bus,
|
||||
tracing_log_bus,
|
||||
supervisor_sessions,
|
||||
false,
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
@@ -420,7 +418,6 @@ impl ComputeRuntime {
|
||||
sandbox_watch_bus,
|
||||
tracing_log_bus,
|
||||
supervisor_sessions,
|
||||
true,
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
@@ -449,7 +446,6 @@ impl ComputeRuntime {
|
||||
sandbox_watch_bus,
|
||||
tracing_log_bus,
|
||||
supervisor_sessions,
|
||||
true,
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -231,7 +231,7 @@ pub fn load(path: &Path) -> Result<ConfigFile, ConfigFileError> {
|
||||
/// the gateway section, which keeps each driver's `deny_unknown_fields`
|
||||
/// invariant intact.
|
||||
pub fn driver_table(
|
||||
driver: ComputeDriverKind,
|
||||
driver_name: &str,
|
||||
gateway: &GatewayFileSection,
|
||||
raw: Option<&toml::Value>,
|
||||
) -> toml::Value {
|
||||
@@ -240,7 +240,7 @@ pub fn driver_table(
|
||||
_ => toml::Table::new(),
|
||||
};
|
||||
|
||||
for key in inheritable_keys(driver) {
|
||||
for key in inheritable_keys(driver_name) {
|
||||
if merged.contains_key(*key) {
|
||||
continue;
|
||||
}
|
||||
@@ -255,9 +255,9 @@ pub fn driver_table(
|
||||
/// Inheritance allowlist (the Q4 "high-overlap set"). Each driver opts in
|
||||
/// to a specific subset so a gateway-wide default does not accidentally land
|
||||
/// in a driver table that does not understand the field.
|
||||
fn inheritable_keys(driver: ComputeDriverKind) -> &'static [&'static str] {
|
||||
match driver {
|
||||
ComputeDriverKind::Kubernetes => &[
|
||||
fn inheritable_keys(driver_name: &str) -> &'static [&'static str] {
|
||||
match driver_name.parse::<ComputeDriverKind>().ok() {
|
||||
Some(ComputeDriverKind::Kubernetes) => &[
|
||||
"namespace",
|
||||
"default_image",
|
||||
"supervisor_image",
|
||||
@@ -267,7 +267,7 @@ fn inheritable_keys(driver: ComputeDriverKind) -> &'static [&'static str] {
|
||||
"enable_user_namespaces",
|
||||
"sa_token_ttl_secs",
|
||||
],
|
||||
ComputeDriverKind::Docker => &[
|
||||
Some(ComputeDriverKind::Docker) => &[
|
||||
"sandbox_namespace",
|
||||
"default_image",
|
||||
"supervisor_image",
|
||||
@@ -276,7 +276,7 @@ fn inheritable_keys(driver: ComputeDriverKind) -> &'static [&'static str] {
|
||||
"guest_tls_cert",
|
||||
"guest_tls_key",
|
||||
],
|
||||
ComputeDriverKind::Podman => &[
|
||||
Some(ComputeDriverKind::Podman) => &[
|
||||
"default_image",
|
||||
"supervisor_image",
|
||||
"host_gateway_ip",
|
||||
@@ -284,12 +284,13 @@ fn inheritable_keys(driver: ComputeDriverKind) -> &'static [&'static str] {
|
||||
"guest_tls_cert",
|
||||
"guest_tls_key",
|
||||
],
|
||||
ComputeDriverKind::Vm => &[
|
||||
Some(ComputeDriverKind::Vm) => &[
|
||||
"default_image",
|
||||
"guest_tls_ca",
|
||||
"guest_tls_cert",
|
||||
"guest_tls_key",
|
||||
],
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +485,7 @@ version = 2
|
||||
namespace = "agents"
|
||||
};
|
||||
let merged = driver_table(
|
||||
ComputeDriverKind::Kubernetes,
|
||||
ComputeDriverKind::Kubernetes.as_str(),
|
||||
&gateway,
|
||||
Some(&toml::Value::Table(raw)),
|
||||
);
|
||||
@@ -511,7 +512,7 @@ version = 2
|
||||
host_gateway_ip: Some("10.0.0.1".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let merged = driver_table(ComputeDriverKind::Docker, &gateway, None);
|
||||
let merged = driver_table(ComputeDriverKind::Docker.as_str(), &gateway, None);
|
||||
let table = merged.as_table().expect("table");
|
||||
assert_eq!(
|
||||
table.get("sandbox_namespace").and_then(|v| v.as_str()),
|
||||
@@ -534,7 +535,7 @@ version = 2
|
||||
host_gateway_ip: Some("192.168.127.254".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let merged = driver_table(ComputeDriverKind::Podman, &gateway, None);
|
||||
let merged = driver_table(ComputeDriverKind::Podman.as_str(), &gateway, None);
|
||||
let table = merged.as_table().expect("table");
|
||||
assert_eq!(
|
||||
table.get("default_image").and_then(|v| v.as_str()),
|
||||
@@ -556,7 +557,7 @@ version = 2
|
||||
default_image = "driver-specific"
|
||||
};
|
||||
let merged = driver_table(
|
||||
ComputeDriverKind::Podman,
|
||||
ComputeDriverKind::Podman.as_str(),
|
||||
&gateway,
|
||||
Some(&toml::Value::Table(raw)),
|
||||
);
|
||||
@@ -578,7 +579,7 @@ version = 2
|
||||
client_tls_secret_name: Some("openshell-sandbox-tls".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let merged = driver_table(ComputeDriverKind::Docker, &gateway, None);
|
||||
let merged = driver_table(ComputeDriverKind::Docker.as_str(), &gateway, None);
|
||||
assert!(
|
||||
!merged
|
||||
.as_table()
|
||||
@@ -587,6 +588,28 @@ version = 2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_driver_table_does_not_inherit_gateway_defaults() {
|
||||
let gateway = GatewayFileSection {
|
||||
default_image: Some("gateway-default:1.0".to_string()),
|
||||
host_gateway_ip: Some("10.0.0.1".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let raw = toml::toml! {
|
||||
socket_path = "/run/openshell/kyma.sock"
|
||||
};
|
||||
|
||||
let merged = driver_table("kyma", &gateway, Some(&toml::Value::Table(raw)));
|
||||
let table = merged.as_table().expect("table");
|
||||
|
||||
assert_eq!(
|
||||
table.get("socket_path").and_then(|v| v.as_str()),
|
||||
Some("/run/openshell/kyma.sock")
|
||||
);
|
||||
assert!(!table.contains_key("default_image"));
|
||||
assert!(!table.contains_key("host_gateway_ip"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_path_is_io_error() {
|
||||
let err = load(Path::new("/nonexistent/openshell-gateway.toml"))
|
||||
|
||||
+110
-286
@@ -53,11 +53,9 @@ mod ws_tunnel;
|
||||
|
||||
use metrics_exporter_prometheus::PrometheusBuilder;
|
||||
use openshell_core::{ComputeDriverKind, Config, Error, Result};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(test)]
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -69,17 +67,22 @@ use tracing::{debug, error, info, warn};
|
||||
#[cfg(test)]
|
||||
pub(crate) static TEST_ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
use compute::{ComputeRuntime, DockerComputeConfig, VmComputeConfig};
|
||||
use compute::ComputeRuntime;
|
||||
pub use grpc::OpenShellService;
|
||||
pub use http::{health_router, http_router, metrics_router, service_http_router};
|
||||
pub use multiplex::{MultiplexService, MultiplexedService};
|
||||
use openshell_driver_kubernetes::KubernetesComputeConfig;
|
||||
pub use persistence::Store;
|
||||
use sandbox_index::SandboxIndex;
|
||||
use sandbox_watch::SandboxWatchBus;
|
||||
pub use tls::TlsAcceptor;
|
||||
use tracing_bus::TracingLogBus;
|
||||
|
||||
pub(crate) struct ServerStartupConfig {
|
||||
pub config: Config,
|
||||
pub config_file: Option<config_file::ConfigFile>,
|
||||
pub guest_tls: Option<compute::driver_config::GuestTlsPaths>,
|
||||
}
|
||||
|
||||
/// Server state shared across handlers.
|
||||
#[derive(Debug)]
|
||||
pub struct ServerState {
|
||||
@@ -205,13 +208,16 @@ impl ServerState {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the server fails to start or encounters a fatal error.
|
||||
pub async fn run_server(
|
||||
config: Config,
|
||||
vm_config: VmComputeConfig,
|
||||
docker_config: DockerComputeConfig,
|
||||
config_file: Option<config_file::ConfigFile>,
|
||||
pub(crate) async fn run_server(
|
||||
startup: ServerStartupConfig,
|
||||
tracing_log_bus: TracingLogBus,
|
||||
) -> Result<()> {
|
||||
let ServerStartupConfig {
|
||||
config,
|
||||
config_file,
|
||||
guest_tls,
|
||||
} = startup;
|
||||
|
||||
let database_url = config.database_url.trim();
|
||||
if database_url.is_empty() {
|
||||
return Err(Error::config("database_url is required"));
|
||||
@@ -240,11 +246,16 @@ pub async fn run_server(
|
||||
let sandbox_index = SandboxIndex::new();
|
||||
let sandbox_watch_bus = SandboxWatchBus::new();
|
||||
let supervisor_sessions = Arc::new(supervisor_session::SupervisorSessionRegistry::new());
|
||||
let driver_startup = compute::driver_config::DriverStartupContext {
|
||||
file: config_file.as_ref(),
|
||||
guest_tls: guest_tls.as_ref(),
|
||||
gateway_port: config.bind_address.port(),
|
||||
gateway_tls_enabled: config.tls.is_some(),
|
||||
endpoint_overrides: &config.compute_driver_endpoints,
|
||||
};
|
||||
let compute = build_compute_runtime(
|
||||
&config,
|
||||
&vm_config,
|
||||
&docker_config,
|
||||
config_file.as_ref(),
|
||||
driver_startup,
|
||||
store.clone(),
|
||||
sandbox_index.clone(),
|
||||
sandbox_watch_bus.clone(),
|
||||
@@ -322,7 +333,8 @@ pub async fn run_server(
|
||||
if state.sandbox_jwt_issuer.is_some() && std::env::var_os("KUBERNETES_SERVICE_HOST").is_some() {
|
||||
// Pod lookups and TokenReview identity checks must match the sandbox
|
||||
// namespace and service account used by the Kubernetes driver.
|
||||
let kubernetes_config = kubernetes_config_for_k8s_sa_bootstrap(config_file.as_ref())?;
|
||||
let kubernetes_config =
|
||||
compute::driver_config::kubernetes_config_for_k8s_sa_bootstrap(config_file.as_ref())?;
|
||||
let sandbox_namespace = kubernetes_config.namespace;
|
||||
let sandbox_service_account = kubernetes_config.service_account_name;
|
||||
match kube::Client::try_default().await {
|
||||
@@ -719,29 +731,23 @@ async fn terminate_signal() {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn build_compute_runtime(
|
||||
config: &Config,
|
||||
vm_config: &VmComputeConfig,
|
||||
docker_config: &DockerComputeConfig,
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
driver_startup: compute::driver_config::DriverStartupContext<'_>,
|
||||
store: Arc<Store>,
|
||||
sandbox_index: SandboxIndex,
|
||||
sandbox_watch_bus: SandboxWatchBus,
|
||||
tracing_log_bus: TracingLogBus,
|
||||
supervisor_sessions: Arc<supervisor_session::SupervisorSessionRegistry>,
|
||||
) -> Result<ComputeRuntime> {
|
||||
let driver = configured_compute_driver(config, file)?;
|
||||
let driver = configured_compute_driver(config, driver_startup)?;
|
||||
info!(driver = %driver.name(), "Using compute driver");
|
||||
if let ConfiguredComputeDriver::Builtin(kind) = &driver {
|
||||
warn_if_kubernetes_sandbox_jwt_expiry_disabled(config, *kind);
|
||||
}
|
||||
|
||||
match driver {
|
||||
let runtime = match driver {
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Kubernetes) => {
|
||||
let mut k8s = kubernetes_config_from_file(file)?;
|
||||
if let Ok(size) = std::env::var("OPENSHELL_K8S_WORKSPACE_DEFAULT_STORAGE_SIZE") {
|
||||
k8s.workspace_default_storage_size = size;
|
||||
}
|
||||
warn_if_kubernetes_sandbox_jwt_expiry_disabled(config);
|
||||
let k8s_config =
|
||||
compute::driver_config::kubernetes_config_from_context(driver_startup)?;
|
||||
ComputeRuntime::new_kubernetes(
|
||||
k8s,
|
||||
k8s_config,
|
||||
store,
|
||||
sandbox_index,
|
||||
sandbox_watch_bus,
|
||||
@@ -749,32 +755,24 @@ async fn build_compute_runtime(
|
||||
supervisor_sessions.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}")))
|
||||
}
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Docker) => ComputeRuntime::new_docker(
|
||||
config.clone(),
|
||||
docker_config.clone(),
|
||||
store,
|
||||
sandbox_index,
|
||||
sandbox_watch_bus,
|
||||
tracing_log_bus,
|
||||
supervisor_sessions,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}"))),
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Docker) => {
|
||||
let docker_config = compute::driver_config::docker_config_from_context(driver_startup)?;
|
||||
ComputeRuntime::new_docker(
|
||||
config.clone(),
|
||||
docker_config,
|
||||
store,
|
||||
sandbox_index,
|
||||
sandbox_watch_bus,
|
||||
tracing_log_bus,
|
||||
supervisor_sessions,
|
||||
)
|
||||
.await
|
||||
}
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Podman) => {
|
||||
let mut podman = podman_config_from_file(file)?;
|
||||
podman.gateway_port = config.bind_address.port();
|
||||
if let Ok(p) = std::env::var("OPENSHELL_PODMAN_SOCKET") {
|
||||
podman.socket_path = PathBuf::from(p);
|
||||
}
|
||||
if let Ok(ip) = std::env::var("OPENSHELL_PODMAN_HOST_GATEWAY_IP") {
|
||||
podman.host_gateway_ip = ip;
|
||||
}
|
||||
apply_podman_local_tls_defaults(config, &mut podman)?;
|
||||
|
||||
let podman_config = compute::driver_config::podman_config_from_context(driver_startup)?;
|
||||
ComputeRuntime::new_podman(
|
||||
podman,
|
||||
podman_config,
|
||||
store,
|
||||
sandbox_index,
|
||||
sandbox_watch_bus,
|
||||
@@ -782,10 +780,10 @@ async fn build_compute_runtime(
|
||||
supervisor_sessions,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}")))
|
||||
}
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Vm) => {
|
||||
let endpoint = compute::vm::spawn(config, vm_config).await?;
|
||||
let vm_config = compute::driver_config::vm_config_from_context(driver_startup)?;
|
||||
let endpoint = compute::vm::spawn(config, &vm_config).await?;
|
||||
ComputeRuntime::new_remote_driver(
|
||||
endpoint,
|
||||
store,
|
||||
@@ -795,16 +793,16 @@ async fn build_compute_runtime(
|
||||
supervisor_sessions,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}")))
|
||||
}
|
||||
ConfiguredComputeDriver::Remote(remote) => {
|
||||
let RemoteComputeDriverSelection { name, socket_path } = remote;
|
||||
ConfiguredComputeDriver::Remote { name } => {
|
||||
let remote_config =
|
||||
compute::driver_config::remote_driver_config_from_context(driver_startup, &name)?;
|
||||
info!(
|
||||
driver = %name,
|
||||
socket = %socket_path.display(),
|
||||
socket = %remote_config.socket_path.display(),
|
||||
"Using remote compute driver endpoint"
|
||||
);
|
||||
let endpoint = compute::connect_remote_compute_driver(name, &socket_path)
|
||||
let endpoint = compute::connect_remote_compute_driver(name, &remote_config.socket_path)
|
||||
.await
|
||||
.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}")))?;
|
||||
ComputeRuntime::new_remote_driver(
|
||||
@@ -816,117 +814,30 @@ async fn build_compute_runtime(
|
||||
supervisor_sessions,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a [`KubernetesComputeConfig`] from the file's
|
||||
/// `[openshell.drivers.kubernetes]` table merged with inheritable
|
||||
/// `[openshell.gateway]` defaults. Falls back to the driver's `Default`
|
||||
/// when no file is present.
|
||||
fn kubernetes_config_from_file(
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
) -> Result<KubernetesComputeConfig> {
|
||||
let Some(file) = file else {
|
||||
return Ok(KubernetesComputeConfig::default());
|
||||
};
|
||||
let merged = config_file::driver_table(
|
||||
ComputeDriverKind::Kubernetes,
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get("kubernetes"),
|
||||
);
|
||||
merged
|
||||
.try_into()
|
||||
.map_err(|e| Error::config(format!("invalid [openshell.drivers.kubernetes] table: {e}")))
|
||||
}
|
||||
|
||||
fn kubernetes_config_for_k8s_sa_bootstrap(
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
) -> Result<KubernetesComputeConfig> {
|
||||
let Some(file) = file else {
|
||||
return Err(Error::config(
|
||||
"K8s ServiceAccount bootstrap requires [openshell.drivers.kubernetes] when sandbox JWT issuing is enabled in-cluster",
|
||||
));
|
||||
};
|
||||
if !file.openshell.drivers.contains_key("kubernetes") {
|
||||
return Err(Error::config(
|
||||
"K8s ServiceAccount bootstrap requires [openshell.drivers.kubernetes] when sandbox JWT issuing is enabled in-cluster",
|
||||
));
|
||||
}
|
||||
kubernetes_config_from_file(Some(file))
|
||||
}
|
||||
|
||||
/// Same pattern as [`kubernetes_config_from_file`] but for Podman.
|
||||
fn podman_config_from_file(
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
) -> Result<openshell_driver_podman::PodmanComputeConfig> {
|
||||
let Some(file) = file else {
|
||||
return Ok(openshell_driver_podman::PodmanComputeConfig::default());
|
||||
};
|
||||
let merged = config_file::driver_table(
|
||||
ComputeDriverKind::Podman,
|
||||
&file.openshell.gateway,
|
||||
file.openshell.drivers.get("podman"),
|
||||
);
|
||||
merged
|
||||
.try_into()
|
||||
.map_err(|e| Error::config(format!("invalid [openshell.drivers.podman] table: {e}")))
|
||||
}
|
||||
|
||||
fn apply_podman_local_tls_defaults(
|
||||
config: &Config,
|
||||
podman: &mut openshell_driver_podman::PodmanComputeConfig,
|
||||
) -> Result<()> {
|
||||
if config.tls.is_none()
|
||||
|| podman.guest_tls_ca.is_some()
|
||||
|| podman.guest_tls_cert.is_some()
|
||||
|| podman.guest_tls_key.is_some()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(paths) = defaults::complete_local_tls_paths()
|
||||
.map_err(|e| Error::config(format!("failed to resolve local TLS defaults: {e}")))?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
podman.guest_tls_ca = Some(paths.ca);
|
||||
podman.guest_tls_cert = Some(paths.client_cert);
|
||||
podman.guest_tls_key = Some(paths.client_key);
|
||||
Ok(())
|
||||
runtime.map_err(|e| Error::execution(format!("failed to create compute runtime: {e}")))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum ConfiguredComputeDriver {
|
||||
Builtin(ComputeDriverKind),
|
||||
Remote(RemoteComputeDriverSelection),
|
||||
Remote { name: String },
|
||||
}
|
||||
|
||||
impl ConfiguredComputeDriver {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Builtin(kind) => kind.as_str(),
|
||||
Self::Remote(remote) => &remote.name,
|
||||
Self::Remote { name } => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct RemoteComputeDriverSelection {
|
||||
name: String,
|
||||
socket_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct RemoteComputeDriverConfig {
|
||||
socket_path: PathBuf,
|
||||
}
|
||||
|
||||
fn configured_compute_driver(
|
||||
config: &Config,
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
driver_startup: compute::driver_config::DriverStartupContext<'_>,
|
||||
) -> Result<ConfiguredComputeDriver> {
|
||||
match config.compute_drivers.as_slice() {
|
||||
[] => match openshell_core::config::detect_driver() {
|
||||
@@ -939,7 +850,7 @@ fn configured_compute_driver(
|
||||
set --drivers or OPENSHELL_DRIVERS to kubernetes, podman, docker, or vm",
|
||||
)),
|
||||
},
|
||||
[driver] => resolve_configured_compute_driver(driver, config, file),
|
||||
[driver] => resolve_configured_compute_driver(driver, driver_startup),
|
||||
drivers => Err(Error::config(format!(
|
||||
"multiple compute drivers are not supported yet; configured drivers: {}",
|
||||
drivers.join(",")
|
||||
@@ -949,75 +860,37 @@ fn configured_compute_driver(
|
||||
|
||||
fn resolve_configured_compute_driver(
|
||||
driver_name: &str,
|
||||
config: &Config,
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
driver_startup: compute::driver_config::DriverStartupContext<'_>,
|
||||
) -> Result<ConfiguredComputeDriver> {
|
||||
let name = openshell_core::config::normalize_compute_driver_name(driver_name)
|
||||
.map_err(Error::config)?;
|
||||
let driver_kind = builtin_compute_driver(&name);
|
||||
if let Some(socket_path) = config.compute_driver_endpoints.get(&name) {
|
||||
if driver_kind.is_some() {
|
||||
return Err(Error::config(format!(
|
||||
"compute driver '{name}' is a reserved built-in driver and cannot be selected with a socket endpoint"
|
||||
)));
|
||||
}
|
||||
return Ok(ConfiguredComputeDriver::Remote(
|
||||
RemoteComputeDriverSelection {
|
||||
name,
|
||||
socket_path: socket_path.clone(),
|
||||
},
|
||||
));
|
||||
if driver_kind.is_some() && driver_startup.endpoint_overrides.contains_key(&name) {
|
||||
return Err(Error::config(format!(
|
||||
"compute driver '{name}' is a reserved built-in driver and cannot be selected with a socket endpoint"
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(kind) = driver_kind {
|
||||
return Ok(ConfiguredComputeDriver::Builtin(kind));
|
||||
}
|
||||
|
||||
let socket_path = remote_driver_socket_from_file(&name, file)?;
|
||||
Ok(ConfiguredComputeDriver::Remote(
|
||||
RemoteComputeDriverSelection { name, socket_path },
|
||||
))
|
||||
Ok(ConfiguredComputeDriver::Remote { name })
|
||||
}
|
||||
|
||||
fn builtin_compute_driver(name: &str) -> Option<ComputeDriverKind> {
|
||||
name.parse().ok()
|
||||
}
|
||||
|
||||
fn remote_driver_socket_from_file(
|
||||
name: &str,
|
||||
file: Option<&config_file::ConfigFile>,
|
||||
) -> Result<PathBuf> {
|
||||
let Some(file) = file else {
|
||||
return Err(Error::config(format!(
|
||||
"compute driver '{name}' is not a built-in driver; configure [openshell.drivers.{name}].socket_path or pass --drivers {name} with --compute-driver-socket"
|
||||
)));
|
||||
};
|
||||
let Some(raw) = file.openshell.drivers.get(name) else {
|
||||
return Err(Error::config(format!(
|
||||
"compute driver '{name}' is not a built-in driver; configure [openshell.drivers.{name}].socket_path"
|
||||
)));
|
||||
};
|
||||
let config = raw
|
||||
.clone()
|
||||
.try_into::<RemoteComputeDriverConfig>()
|
||||
.map_err(|err| {
|
||||
Error::config(format!(
|
||||
"invalid [openshell.drivers.{name}] table for remote compute driver: {err}"
|
||||
))
|
||||
})?;
|
||||
Ok(config.socket_path)
|
||||
fn kubernetes_sandbox_jwt_expiry_disabled(config: &Config) -> bool {
|
||||
config
|
||||
.gateway_jwt
|
||||
.as_ref()
|
||||
.is_some_and(|jwt| jwt.ttl_secs == 0)
|
||||
}
|
||||
|
||||
fn kubernetes_sandbox_jwt_expiry_disabled(config: &Config, driver: ComputeDriverKind) -> bool {
|
||||
matches!(driver, ComputeDriverKind::Kubernetes)
|
||||
&& config
|
||||
.gateway_jwt
|
||||
.as_ref()
|
||||
.is_some_and(|jwt| jwt.ttl_secs == 0)
|
||||
}
|
||||
|
||||
fn warn_if_kubernetes_sandbox_jwt_expiry_disabled(config: &Config, driver: ComputeDriverKind) {
|
||||
if kubernetes_sandbox_jwt_expiry_disabled(config, driver) {
|
||||
fn warn_if_kubernetes_sandbox_jwt_expiry_disabled(config: &Config) {
|
||||
if kubernetes_sandbox_jwt_expiry_disabled(config) {
|
||||
warn!(
|
||||
"Kubernetes gateway configured with non-expiring sandbox JWTs (gateway_jwt.ttl_secs = 0); set ttl_secs > 0 for shared Kubernetes deployments"
|
||||
);
|
||||
@@ -1030,8 +903,7 @@ mod tests {
|
||||
ConfiguredComputeDriver, ConnectionProtocol, MultiplexService, ServerState, TlsAcceptor,
|
||||
allow_plaintext_service_http, classify_initial_bytes, configured_compute_driver,
|
||||
gateway_listener_addresses, is_benign_tls_handshake_failure,
|
||||
kubernetes_config_for_k8s_sa_bootstrap, kubernetes_sandbox_jwt_expiry_disabled,
|
||||
serve_gateway_listener,
|
||||
kubernetes_sandbox_jwt_expiry_disabled, serve_gateway_listener,
|
||||
};
|
||||
use openshell_core::{
|
||||
ComputeDriverKind, Config,
|
||||
@@ -1039,7 +911,6 @@ mod tests {
|
||||
};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tempfile::{TempDir, tempdir};
|
||||
@@ -1049,6 +920,19 @@ mod tests {
|
||||
|
||||
use crate::tls_test_utils::{generate_test_certs_with_ca, install_rustls_provider};
|
||||
|
||||
fn test_driver_startup<'a>(
|
||||
config: &'a Config,
|
||||
file: Option<&'a super::config_file::ConfigFile>,
|
||||
) -> crate::compute::driver_config::DriverStartupContext<'a> {
|
||||
crate::compute::driver_config::DriverStartupContext {
|
||||
file,
|
||||
guest_tls: None,
|
||||
gateway_port: openshell_core::config::DEFAULT_SERVER_PORT,
|
||||
gateway_tls_enabled: false,
|
||||
endpoint_overrides: &config.compute_driver_endpoints,
|
||||
}
|
||||
}
|
||||
|
||||
fn test_tls_acceptor() -> (TempDir, TlsAcceptor) {
|
||||
install_rustls_provider();
|
||||
|
||||
@@ -1344,7 +1228,7 @@ mod tests {
|
||||
// Empty drivers triggers auto-detection, which may return Some or None
|
||||
// depending on the environment. This test verifies the auto-detection path
|
||||
// is taken rather than immediately returning an error.
|
||||
let result = configured_compute_driver(&config, None);
|
||||
let result = configured_compute_driver(&config, test_driver_startup(&config, None));
|
||||
// Either we get a detected driver or an error about none being detected.
|
||||
match result {
|
||||
Ok(ConfiguredComputeDriver::Builtin(driver)) => {
|
||||
@@ -1358,8 +1242,8 @@ mod tests {
|
||||
"auto-detected unexpected driver: {driver:?}"
|
||||
);
|
||||
}
|
||||
Ok(ConfiguredComputeDriver::Remote(remote)) => {
|
||||
panic!("auto-detection returned remote driver: {remote:?}");
|
||||
Ok(ConfiguredComputeDriver::Remote { name }) => {
|
||||
panic!("auto-detection returned remote driver: {name}");
|
||||
}
|
||||
Err(e) => {
|
||||
assert!(
|
||||
@@ -1375,7 +1259,8 @@ mod tests {
|
||||
fn configured_compute_driver_rejects_multiple_entries() {
|
||||
let config = Config::new(None)
|
||||
.with_compute_drivers([ComputeDriverKind::Kubernetes, ComputeDriverKind::Podman]);
|
||||
let err = configured_compute_driver(&config, None).unwrap_err();
|
||||
let err =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap_err();
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("multiple compute drivers are not supported yet")
|
||||
@@ -1386,7 +1271,8 @@ mod tests {
|
||||
#[test]
|
||||
fn configured_compute_driver_accepts_podman() {
|
||||
let config = Config::new(None).with_compute_drivers([ComputeDriverKind::Podman]);
|
||||
let driver = configured_compute_driver(&config, None).unwrap();
|
||||
let driver =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap();
|
||||
assert!(matches!(
|
||||
driver,
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Podman)
|
||||
@@ -1396,7 +1282,8 @@ mod tests {
|
||||
#[test]
|
||||
fn configured_compute_driver_accepts_vm() {
|
||||
let config = Config::new(None).with_compute_drivers([ComputeDriverKind::Vm]);
|
||||
let driver = configured_compute_driver(&config, None).unwrap();
|
||||
let driver =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap();
|
||||
assert!(matches!(
|
||||
driver,
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Vm)
|
||||
@@ -1406,7 +1293,8 @@ mod tests {
|
||||
#[test]
|
||||
fn configured_compute_driver_accepts_docker() {
|
||||
let config = Config::new(None).with_compute_drivers([ComputeDriverKind::Docker]);
|
||||
let driver = configured_compute_driver(&config, None).unwrap();
|
||||
let driver =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap();
|
||||
assert!(matches!(
|
||||
driver,
|
||||
ConfiguredComputeDriver::Builtin(ComputeDriverKind::Docker)
|
||||
@@ -1414,28 +1302,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_compute_driver_resolves_named_remote_from_file() {
|
||||
let file: super::config_file::ConfigFile = toml::from_str(
|
||||
r#"
|
||||
[openshell.gateway]
|
||||
compute_drivers = ["kyma"]
|
||||
|
||||
[openshell.drivers.kyma]
|
||||
socket_path = "/run/openshell/kyma.sock"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
fn configured_compute_driver_resolves_named_remote() {
|
||||
let config = Config::new(None).with_compute_drivers(["kyma"]);
|
||||
|
||||
let driver = configured_compute_driver(&config, Some(&file)).unwrap();
|
||||
let driver =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap();
|
||||
|
||||
match driver {
|
||||
ConfiguredComputeDriver::Remote(remote) => {
|
||||
assert_eq!(remote.name, "kyma");
|
||||
assert_eq!(
|
||||
remote.socket_path,
|
||||
PathBuf::from("/run/openshell/kyma.sock")
|
||||
);
|
||||
ConfiguredComputeDriver::Remote { name } => {
|
||||
assert_eq!(name, "kyma");
|
||||
}
|
||||
ConfiguredComputeDriver::Builtin(other) => {
|
||||
panic!("expected remote driver, got builtin driver {other:?}")
|
||||
@@ -1449,7 +1324,8 @@ socket_path = "/run/openshell/kyma.sock"
|
||||
.with_compute_drivers([ComputeDriverKind::Vm])
|
||||
.with_compute_driver_endpoint("vm", "/run/openshell/vm.sock");
|
||||
|
||||
let err = configured_compute_driver(&config, None).unwrap_err();
|
||||
let err =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap_err();
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
@@ -1464,7 +1340,8 @@ socket_path = "/run/openshell/kyma.sock"
|
||||
.with_compute_drivers([ComputeDriverKind::Docker])
|
||||
.with_compute_driver_endpoint("docker", "/run/openshell/docker.sock");
|
||||
|
||||
let err = configured_compute_driver(&config, None).unwrap_err();
|
||||
let err =
|
||||
configured_compute_driver(&config, test_driver_startup(&config, None)).unwrap_err();
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
@@ -1474,7 +1351,7 @@ socket_path = "/run/openshell/kyma.sock"
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kubernetes_sandbox_jwt_expiry_disabled_warns_only_for_kubernetes_zero_ttl() {
|
||||
fn kubernetes_sandbox_jwt_expiry_disabled_warns_for_zero_ttl() {
|
||||
fn config_with_jwt_ttl(ttl_secs: u64) -> Config {
|
||||
let mut config = Config::new(None);
|
||||
config.gateway_jwt = Some(openshell_core::GatewayJwtConfig {
|
||||
@@ -1488,65 +1365,12 @@ socket_path = "/run/openshell/kyma.sock"
|
||||
}
|
||||
|
||||
assert!(kubernetes_sandbox_jwt_expiry_disabled(
|
||||
&config_with_jwt_ttl(0),
|
||||
ComputeDriverKind::Kubernetes
|
||||
&config_with_jwt_ttl(0)
|
||||
));
|
||||
assert!(!kubernetes_sandbox_jwt_expiry_disabled(
|
||||
&config_with_jwt_ttl(3600),
|
||||
ComputeDriverKind::Kubernetes
|
||||
&config_with_jwt_ttl(3600)
|
||||
));
|
||||
assert!(!kubernetes_sandbox_jwt_expiry_disabled(
|
||||
&config_with_jwt_ttl(0),
|
||||
ComputeDriverKind::Docker
|
||||
));
|
||||
assert!(!kubernetes_sandbox_jwt_expiry_disabled(
|
||||
&Config::new(None),
|
||||
ComputeDriverKind::Kubernetes
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn k8s_sa_bootstrap_rejects_missing_kubernetes_driver_config() {
|
||||
let err = kubernetes_config_for_k8s_sa_bootstrap(None).unwrap_err();
|
||||
assert!(err.to_string().contains("[openshell.drivers.kubernetes]"));
|
||||
|
||||
let file: crate::config_file::ConfigFile =
|
||||
toml::from_str("[openshell.gateway]\n").expect("valid config");
|
||||
let err = kubernetes_config_for_k8s_sa_bootstrap(Some(&file)).unwrap_err();
|
||||
assert!(err.to_string().contains("[openshell.drivers.kubernetes]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn k8s_sa_bootstrap_uses_configured_namespace_and_service_account() {
|
||||
let file: crate::config_file::ConfigFile = toml::from_str(
|
||||
r#"
|
||||
[openshell.gateway]
|
||||
|
||||
[openshell.drivers.kubernetes]
|
||||
namespace = "sandboxes"
|
||||
service_account_name = "sandbox-sa"
|
||||
"#,
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let cfg = kubernetes_config_for_k8s_sa_bootstrap(Some(&file)).unwrap();
|
||||
assert_eq!(cfg.namespace, "sandboxes");
|
||||
assert_eq!(cfg.service_account_name, "sandbox-sa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn podman_config_reads_bind_mount_opt_in_from_driver_table() {
|
||||
let file: crate::config_file::ConfigFile = toml::from_str(
|
||||
r"
|
||||
[openshell.drivers.podman]
|
||||
enable_bind_mounts = true
|
||||
",
|
||||
)
|
||||
.expect("valid config");
|
||||
|
||||
let cfg = crate::podman_config_from_file(Some(&file)).expect("podman config");
|
||||
|
||||
assert!(cfg.enable_bind_mounts);
|
||||
assert!(!kubernetes_sandbox_jwt_expiry_disabled(&Config::new(None)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user