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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ jobs:
public_url = "http://localhost:9090"
[server]
listen_addr = "127.0.0.1:9090"
backend = "bare_metal"
[bare_metal]
backend = "native_host"
[native_host]
agent_port = 6828
[database]
url = "postgresql://postgres@localhost/spur_cloud_test"
Expand Down
2 changes: 1 addition & 1 deletion crates/spur-cloud-api/src/auth/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ mod tests {
okta: None,
},
server: ServerConfig::default(),
bare_metal: None,
native_host: None,
update: Default::default(),
}),
}
Expand Down
10 changes: 5 additions & 5 deletions crates/spur-cloud-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::Deserialize;
pub enum Backend {
#[default]
K8s,
BareMetal,
NativeHost,
}

#[derive(Debug, Clone, Deserialize)]
Expand All @@ -21,9 +21,9 @@ pub struct Config {
#[serde(default)]
pub server: ServerConfig,

/// Bare-metal backend configuration (required when server.backend = "bare_metal")
/// Native-host backend configuration (required when server.backend = "native_host")
#[serde(default)]
pub bare_metal: Option<BareMetalConfig>,
pub native_host: Option<NativeHostConfig>,

/// Auto-update check configuration.
#[serde(default)]
Expand All @@ -36,7 +36,7 @@ pub struct ServerConfig {
pub listen_addr: String,
#[serde(default = "default_session_namespace")]
pub session_namespace: String,
/// Backend type: "k8s" (default) or "bare_metal"
/// Backend type: "k8s" (default) or "native_host"
#[serde(default)]
pub backend: Backend,
}
Expand All @@ -52,7 +52,7 @@ impl Default for ServerConfig {
}

#[derive(Debug, Clone, Deserialize)]
pub struct BareMetalConfig {
pub struct NativeHostConfig {
/// Port where spurd agents listen (default: 6818)
#[serde(default = "default_agent_port")]
pub agent_port: u16,
Expand Down
14 changes: 7 additions & 7 deletions crates/spur-cloud-api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ async fn main() -> anyhow::Result<()> {
info!("connected to kubernetes");
Some(client)
}
config::Backend::BareMetal => {
info!("bare-metal backend — skipping kubernetes client init");
config::Backend::NativeHost => {
info!("native-host backend — skipping kubernetes client init");
None
}
};
Expand Down Expand Up @@ -277,8 +277,8 @@ async fn transition_session_to_running(
}
}
}
config::Backend::BareMetal => {
let bm = state.config.bare_metal.as_ref();
config::Backend::NativeHost => {
let bm = state.config.native_host.as_ref();
let ssh_port = ssh::service_manager::ssh_port_for_session(
&session.id,
bm.map(|c| c.ssh_port_base).unwrap_or(10000),
Expand Down Expand Up @@ -417,11 +417,11 @@ async fn session_sync_loop(state: AppState) {

if new_state == SessionState::Running {
let node_name = job.nodelist.clone();
// K8s pod name format includes the sanitized node; for BareMetal the
// K8s pod name format includes the sanitized node; for NativeHost the
// value is unused for SSH but is still persisted to the DB.
let pod_name = match state.config.server.backend {
config::Backend::K8s => spur_client::pod_name_for(job_id, &node_name),
config::Backend::BareMetal => format!("spur-job-{}", job_id),
config::Backend::NativeHost => format!("spur-job-{}", job_id),
};

if let Err(e) =
Expand All @@ -441,7 +441,7 @@ async fn session_sync_loop(state: AppState) {
db::billing_repo::record_usage_end(&state.db, session.id, chrono::Utc::now())
.await;

// Clean up K8s SSH service (BareMetal sshd dies with the job)
// Clean up K8s SSH service (NativeHost sshd dies with the job)
if session.ssh_enabled {
if let config::Backend::K8s = state.config.server.backend {
let ns = &state.config.server.session_namespace;
Expand Down
18 changes: 9 additions & 9 deletions crates/spur-cloud-api/src/routes/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ pub async fn create_session(
String::new()
};

// Compute SSH port for bare-metal mode
let ssh_port = if req.ssh_enabled && state.config.server.backend == Backend::BareMetal {
let bm = state.config.bare_metal.as_ref();
// Compute SSH port for native-host mode
let ssh_port = if req.ssh_enabled && state.config.server.backend == Backend::NativeHost {
let bm = state.config.native_host.as_ref();
Some(ssh::service_manager::ssh_port_for_session(
&session.id,
bm.map(|c| c.ssh_port_base).unwrap_or(10000),
Expand All @@ -151,7 +151,7 @@ pub async fn create_session(
None
};

// Submit to Spur — different paths for K8s and bare-metal
// Submit to Spur — different paths for K8s and native-host
match state.config.server.backend {
Backend::K8s => {
// K8s mode: create a SpurJob CRD. The spur-k8s operator watches
Expand Down Expand Up @@ -188,8 +188,8 @@ pub async fn create_session(
}
}
}
Backend::BareMetal => {
// Bare-metal mode: submit directly to spurctld via gRPC
Backend::NativeHost => {
// Native-host mode: submit directly to spurctld via gRPC
let mut spur = state.spur.clone();
match spur_client::submit_session(
&mut spur,
Expand All @@ -203,7 +203,7 @@ pub async fn create_session(
&session.id.to_string(),
&ssh_keys_str,
ssh_port,
true, // bare_metal
true, // native_host
)
.await
{
Expand Down Expand Up @@ -299,8 +299,8 @@ pub async fn delete_session(
}
}
}
Backend::BareMetal => {
// Bare-metal: cancel via gRPC
Backend::NativeHost => {
// Native-host: cancel via gRPC
if let Some(job_id) = session.spur_job_id {
let mut spur = state.spur.clone();
if let Err(e) = spur_client::cancel_job(&mut spur, job_id as u32).await {
Expand Down
4 changes: 2 additions & 2 deletions crates/spur-cloud-api/src/routes/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub async fn terminal_upgrade(
})
.into_response()
}
Backend::BareMetal => {
Backend::NativeHost => {
let job_id = match session.spur_job_id {
Some(id) => id as u32,
None => {
Expand All @@ -84,7 +84,7 @@ pub async fn terminal_upgrade(
let spur = state.spur.clone();
let agent_port = state
.config
.bare_metal
.native_host
.as_ref()
.map(|c| c.agent_port)
.unwrap_or(6818);
Expand Down
10 changes: 5 additions & 5 deletions crates/spur-cloud-api/src/spur_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ pub async fn delete_spurjob_crd(

/// Submit a GPU session as a Spur job. Returns the assigned job ID.
///
/// `ssh_port`: If set, passed as GPUAAS_SSH_PORT (used in bare-metal mode for deterministic sshd port).
/// `bare_metal`: If true, clears container_image so Spur runs the job as a bare process.
/// `ssh_port`: If set, passed as GPUAAS_SSH_PORT (used in native-host mode for deterministic sshd port).
/// `native_host`: If true, clears container_image so Spur runs the job as a bare process.
pub async fn submit_session(
client: &mut SlurmControllerClient<Channel>,
name: &str,
Expand All @@ -221,7 +221,7 @@ pub async fn submit_session(
session_id: &str,
ssh_keys: &str,
ssh_port: Option<u16>,
bare_metal: bool,
native_host: bool,
) -> anyhow::Result<u32> {
let mut environment = HashMap::new();
environment.insert("GPUAAS_SESSION_ID".into(), session_id.to_string());
Expand Down Expand Up @@ -328,8 +328,8 @@ pub async fn submit_session(
nanos: 0,
}),
interactive: true,
// Bare-metal mode: skip container image, run as bare process
container_image: if bare_metal {
// Native-host mode: skip container image, run as bare process
container_image: if native_host {
String::new()
} else {
container_image.to_string()
Expand Down
2 changes: 1 addition & 1 deletion crates/spur-cloud-api/src/ssh/service_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use tracing::{debug, error, info};
use uuid::Uuid;

/// Compute a deterministic SSH port for a session based on its UUID.
/// Used in bare-metal mode where there is no K8s Service abstraction.
/// Used in native-host mode where there is no K8s Service abstraction.
pub fn ssh_port_for_session(session_id: &Uuid, base: u16, range: u16) -> u16 {
let hash = (session_id.as_u128() % range as u128) as u16;
base + hash
Expand Down
2 changes: 1 addition & 1 deletion crates/spur-cloud-api/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::config::Config;
pub struct AppState {
pub db: PgPool,
pub spur: SlurmControllerClient<Channel>,
/// K8s client — None when running in bare-metal mode.
/// K8s client — None when running in native-host mode.
pub kube: Option<kube::Client>,
pub config: Arc<Config>,
}
4 changes: 2 additions & 2 deletions crates/spur-cloud-api/src/terminal/ws_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const WS_PING_INTERVAL: std::time::Duration = std::time::Duration::from_secs(30)
/// Issue #39: Maximum time to wait for a WebSocket send before treating it as dead.
const WS_SEND_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);

/// Issue #39: Maximum retries for connecting to the agent (bare-metal mode).
/// Issue #39: Maximum retries for connecting to the agent (native-host mode).
const AGENT_CONNECT_RETRIES: u32 = 3;

/// Issue #39: Delay between agent connection retries.
Expand Down Expand Up @@ -163,7 +163,7 @@ pub async fn handle_terminal(
}

/// Bridge a WebSocket connection to a Spur agent's AttachJob gRPC stream.
/// Used in bare-metal mode — connects directly to the spurd agent on the compute node.
/// Used in native-host mode — connects directly to the spurd agent on the compute node.
///
/// Flow: xterm.js (browser) <-> WebSocket <-> AttachJob gRPC <-> nsenter bash (job)
///
Expand Down
8 changes: 4 additions & 4 deletions spur-cloud.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ public_url = "http://localhost:8080"
[server]
listen_addr = "0.0.0.0:8080"
session_namespace = "spur-sessions"
# Backend type: "k8s" (default) or "bare_metal"
# backend = "bare_metal"
# Backend type: "k8s" (default) or "native_host"
# backend = "native_host"

# Bare-metal backend configuration (only used when backend = "bare_metal")
# [bare_metal]
# Native-host backend configuration (only used when backend = "native_host")
# [native_host]
# agent_port = 6818 # Port where spurd agents listen
# ssh_port_base = 10000 # Base port for SSH port allocation
# ssh_port_range = 50000 # Range of SSH ports
Expand Down
Loading