Skip to content

Commit 84bb437

Browse files
committed
refactor(server): extract kubernetes compute driver
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent dafb799 commit 84bb437

22 files changed

Lines changed: 1427 additions & 873 deletions

File tree

‎Cargo.lock‎

Lines changed: 23 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎architecture/gateway.md‎

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,21 @@ graph TD
7070
| Persistence | `crates/openshell-server/src/persistence/mod.rs` | `Store` enum (SQLite/Postgres), generic object CRUD, protobuf codec |
7171
| Persistence: SQLite | `crates/openshell-server/src/persistence/sqlite.rs` | `SqliteStore` with sqlx |
7272
| Persistence: Postgres | `crates/openshell-server/src/persistence/postgres.rs` | `PostgresStore` with sqlx |
73-
| Sandbox K8s | `crates/openshell-server/src/sandbox/mod.rs` | `SandboxClient`, CRD creation/deletion, Kubernetes watcher, phase derivation |
73+
| Compute runtime | `crates/openshell-server/src/compute/mod.rs` | `ComputeRuntime`, gateway-owned sandbox lifecycle orchestration over a compute backend |
74+
| Compute driver: Kubernetes | `crates/openshell-driver-kubernetes/src/driver.rs` | Kubernetes CRD create/delete, endpoint resolution, watch stream, pod template translation |
7475
| Sandbox index | `crates/openshell-server/src/sandbox_index.rs` | `SandboxIndex` -- in-memory name/pod-to-id correlation |
75-
| Watch bus | `crates/openshell-server/src/sandbox_watch.rs` | `SandboxWatchBus`, `PlatformEventBus`, Kubernetes event tailer |
76+
| Watch bus | `crates/openshell-server/src/sandbox_watch.rs` | `SandboxWatchBus` -- in-memory broadcast for persisted sandbox updates |
7677
| Tracing bus | `crates/openshell-server/src/tracing_bus.rs` | `TracingLogBus` -- captures tracing events keyed by `sandbox_id` |
7778

7879
Proto definitions consumed by the gateway:
7980

8081
| Proto file | Package | Defines |
8182
|------------|---------|---------|
8283
| `proto/openshell.proto` | `openshell.v1` | `OpenShell` service, sandbox/provider/SSH/watch messages |
84+
| `proto/compute_driver.proto` | `openshell.compute.v1` | Internal `ComputeDriver` service, endpoint resolution, compute watch stream envelopes |
8385
| `proto/inference.proto` | `openshell.inference.v1` | `Inference` service: `SetClusterInference`, `GetClusterInference`, `GetInferenceBundle` |
84-
| `proto/datamodel.proto` | `openshell.datamodel.v1` | `Sandbox`, `SandboxSpec`, `SandboxStatus`, `Provider`, `SandboxPhase` |
85-
| `proto/sandbox.proto` | `openshell.sandbox.v1` | `SandboxPolicy`, `NetworkPolicyRule`, `SettingValue`, `EffectiveSetting`, `SettingScope`, `PolicySource`, `GetSandboxSettingsRequest/Response`, `GetGatewaySettingsRequest/Response` |
86+
| `proto/datamodel.proto` | `openshell.datamodel.v1` | `Provider` |
87+
| `proto/sandbox.proto` | `openshell.sandbox.v1` | Shared sandbox lifecycle types (`Sandbox`, `SandboxSpec`, `SandboxStatus`, `SandboxPhase`, `PlatformEvent`) plus policy/settings messages |
8688

8789
## Startup Sequence
8890

@@ -94,11 +96,10 @@ The gateway boots in `main()` (`crates/openshell-server/src/main.rs`) and procee
9496
4. **Build `Config`** -- Assembles a `openshell_core::Config` from the parsed arguments.
9597
5. **Call `run_server()`** (`crates/openshell-server/src/lib.rs`):
9698
1. Connect to the persistence store (`Store::connect`), which auto-detects SQLite vs Postgres from the URL prefix and runs migrations.
97-
2. Create `SandboxClient` (initializes a `kube::Client` from in-cluster or kubeconfig).
99+
2. Create `ComputeRuntime` with the in-process Kubernetes compute backend (`KubernetesComputeDriver`).
98100
3. Build `ServerState` (shared via `Arc<ServerState>` across all handlers).
99101
4. **Spawn background tasks**:
100-
- `spawn_sandbox_watcher` -- watches Kubernetes Sandbox CRDs and syncs state to the store.
101-
- `spawn_kube_event_tailer` -- watches Kubernetes Events in the sandbox namespace and publishes them to the `PlatformEventBus`.
102+
- `ComputeRuntime::spawn_watchers` -- consumes the compute-driver watch stream, updates persisted sandbox records, and republishes platform events.
102103
5. Create `MultiplexService`.
103104
6. Bind `TcpListener` on `config.bind_address`.
104105
7. Optionally create `TlsAcceptor` from cert/key files.
@@ -137,7 +138,7 @@ All handlers share an `Arc<ServerState>` (`crates/openshell-server/src/lib.rs`):
137138
pub struct ServerState {
138139
pub config: Config,
139140
pub store: Arc<Store>,
140-
pub sandbox_client: SandboxClient,
141+
pub compute: ComputeRuntime,
141142
pub sandbox_index: SandboxIndex,
142143
pub sandbox_watch_bus: SandboxWatchBus,
143144
pub tracing_log_bus: TracingLogBus,
@@ -148,10 +149,10 @@ pub struct ServerState {
148149
```
149150

150151
- **`store`** -- persistence backend (SQLite or Postgres) for all object types.
151-
- **`sandbox_client`** -- Kubernetes client scoped to the sandbox namespace; creates/deletes CRDs and resolves pod IPs.
152-
- **`sandbox_index`** -- in-memory bidirectional index mapping sandbox names and agent pod names to sandbox IDs. Used by the event tailer to correlate Kubernetes events.
152+
- **`compute`** -- gateway-owned compute orchestration. Persists sandbox lifecycle transitions, validates create requests through the compute backend, resolves exec/SSH endpoints, and consumes the backend watch stream.
153+
- **`sandbox_index`** -- in-memory bidirectional index mapping sandbox names and agent pod names to sandbox IDs. Updated from compute-driver sandbox snapshots.
153154
- **`sandbox_watch_bus`** -- `broadcast`-based notification bus keyed by sandbox ID. Producers call `notify(&id)` when the persisted sandbox record changes; consumers in `WatchSandbox` streams receive `()` signals and re-read the record.
154-
- **`tracing_log_bus`** -- captures `tracing` events that include a `sandbox_id` field and republishes them as `SandboxLogLine` messages. Maintains a per-sandbox tail buffer (default 200 entries). Also contains a nested `PlatformEventBus` for Kubernetes events.
155+
- **`tracing_log_bus`** -- captures `tracing` events that include a `sandbox_id` field and republishes them as `SandboxLogLine` messages. Maintains a per-sandbox tail buffer (default 200 entries). Also contains a nested `PlatformEventBus` for compute-driver platform events.
155156
- **`settings_mutex`** -- serializes settings mutations (global and sandbox) to prevent read-modify-write races. Held for the duration of any setting set/delete or global policy set/delete operation. See [Gateway Settings Channel](gateway-settings.md#global-policy-lifecycle).
156157

157158
## Protocol Multiplexing
@@ -499,15 +500,15 @@ The Helm chart template is at `deploy/helm/openshell/templates/statefulset.yaml`
499500

500501
### Sandbox CRD Management
501502

502-
`SandboxClient` (`crates/openshell-server/src/sandbox/mod.rs`) manages `agents.x-k8s.io/v1alpha1/Sandbox` CRDs.
503+
`KubernetesComputeDriver` (`crates/openshell-driver-kubernetes/src/driver.rs`) manages `agents.x-k8s.io/v1alpha1/Sandbox` CRDs behind the gateway's compute interface.
503504

504-
- **Create**: Translates a `Sandbox` proto into a Kubernetes `DynamicObject` with labels (`openshell.ai/sandbox-id`, `openshell.ai/managed-by: openshell`) and a spec that includes the pod template, environment variables, and gateway-required env vars (`OPENSHELL_SANDBOX_ID`, `OPENSHELL_ENDPOINT`, `OPENSHELL_SSH_LISTEN_ADDR`, etc.). When callers do not provide custom `volumeClaimTemplates`, the server injects a default `workspace` PVC and mounts it at `/sandbox` so the default sandbox home/workdir survives pod rescheduling.
505+
- **Create**: Translates a shared `openshell.sandbox.v1.Sandbox` message into a Kubernetes `DynamicObject` with labels (`openshell.ai/sandbox-id`, `openshell.ai/managed-by: openshell`) and a spec that includes the pod template, environment variables, and gateway-required env vars (`OPENSHELL_SANDBOX_ID`, `OPENSHELL_ENDPOINT`, `OPENSHELL_SSH_LISTEN_ADDR`, etc.). When callers do not provide custom `volumeClaimTemplates`, the driver injects a default `workspace` PVC and mounts it at `/sandbox` so the default sandbox home/workdir survives pod rescheduling.
505506
- **Delete**: Calls the Kubernetes API to delete the CRD by name. Returns `false` if already gone (404).
506507
- **Pod IP resolution**: `agent_pod_ip()` fetches the agent pod and reads `status.podIP`.
507508

508509
### Sandbox Watcher
509510

510-
`spawn_sandbox_watcher()` (`crates/openshell-server/src/sandbox/mod.rs`) runs a Kubernetes watcher on `Sandbox` CRDs and processes three event types:
511+
The Kubernetes driver emits `WatchSandboxes` events through `proto/compute_driver.proto`. `ComputeRuntime` consumes that stream and applies the resulting snapshots to the store.
511512

512513
- **Applied**: Extracts the sandbox ID from labels (or falls back to name prefix stripping), reads the CRD status, derives the phase, and upserts the sandbox record in the store. Notifies the watch bus.
513514
- **Deleted**: Removes the sandbox record from the store and the index. Notifies the watch bus.
@@ -530,7 +531,7 @@ All other `Ready=False` reasons are treated as terminal failures (`Error` phase)
530531

531532
### Kubernetes Event Tailer
532533

533-
`spawn_kube_event_tailer()` (`crates/openshell-server/src/sandbox_watch.rs`) watches all Kubernetes `Event` objects in the sandbox namespace and correlates them to sandbox IDs using `SandboxIndex`:
534+
The Kubernetes driver also watches namespace-scoped Kubernetes `Event` objects and correlates them to sandbox IDs before emitting them as compute-driver platform events:
534535

535536
- Events involving `kind: Sandbox` are correlated by sandbox name.
536537
- Events involving `kind: Pod` are correlated by agent pod name.

‎crates/openshell-core/build.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3232
"../../proto/openshell.proto",
3333
"../../proto/datamodel.proto",
3434
"../../proto/sandbox.proto",
35+
"../../proto/compute_driver.proto",
3536
"../../proto/inference.proto",
3637
"../../proto/test.proto",
3738
];

‎crates/openshell-core/src/proto/mod.rs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ pub mod sandbox {
4242
}
4343
}
4444

45+
#[allow(
46+
clippy::all,
47+
clippy::pedantic,
48+
clippy::nursery,
49+
unused_qualifications,
50+
rust_2018_idioms
51+
)]
52+
pub mod compute {
53+
pub mod v1 {
54+
include!(concat!(env!("OUT_DIR"), "/openshell.compute.v1.rs"));
55+
}
56+
}
57+
4558
#[allow(
4659
clippy::all,
4760
clippy::pedantic,
@@ -66,6 +79,7 @@ pub mod inference {
6679
}
6780
}
6881

82+
pub use compute::v1::*;
6983
pub use datamodel::v1::*;
7084
pub use inference::v1::*;
7185
pub use openshell::*;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
[package]
5+
name = "openshell-driver-kubernetes"
6+
description = "Kubernetes compute driver for OpenShell"
7+
version.workspace = true
8+
edition.workspace = true
9+
rust-version.workspace = true
10+
license.workspace = true
11+
repository.workspace = true
12+
13+
[[bin]]
14+
name = "openshell-driver-kubernetes"
15+
path = "src/main.rs"
16+
17+
[dependencies]
18+
openshell-core = { path = "../openshell-core" }
19+
20+
tokio = { workspace = true }
21+
tonic = { workspace = true, features = ["transport"] }
22+
prost = { workspace = true }
23+
prost-types = { workspace = true }
24+
futures = { workspace = true }
25+
tokio-stream = { workspace = true }
26+
kube = { workspace = true }
27+
kube-runtime = { workspace = true }
28+
k8s-openapi = { workspace = true }
29+
serde_json = { workspace = true }
30+
clap = { workspace = true }
31+
tracing = { workspace = true }
32+
tracing-subscriber = { workspace = true }
33+
thiserror = { workspace = true }
34+
miette = { workspace = true }
35+
36+
[lints]
37+
workspace = true
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#[derive(Debug, Clone)]
5+
pub struct KubernetesComputeConfig {
6+
pub namespace: String,
7+
pub default_image: String,
8+
pub image_pull_policy: String,
9+
pub grpc_endpoint: String,
10+
pub ssh_listen_addr: String,
11+
pub ssh_port: u16,
12+
pub ssh_handshake_secret: String,
13+
pub ssh_handshake_skew_secs: u64,
14+
pub client_tls_secret_name: String,
15+
pub host_gateway_ip: String,
16+
}

0 commit comments

Comments
 (0)