Skip to content

Commit 1f4dfcd

Browse files
Feature/data plane v0 placement launch plan (#38)
* feat: implement data-plane-v0-placement-launch-plan API * feat: Data Plane V0 Placement-to-Launch Plan * fix: address codex review findings for launch plan * docs, examples: fix quickstart numbering and plan-launch.sh style * chore: ignore .codex metadata directory * feat: make launch planning node-scoped Signed-off-by: InderdeepBajwa <InderdeepBajwa@users.noreply.github.com> * feat: add node-side launch preparation * fix: satisfy clippy for node preparation --------- Signed-off-by: InderdeepBajwa <InderdeepBajwa@users.noreply.github.com>
1 parent 9c4a1ef commit 1f4dfcd

19 files changed

Lines changed: 1673 additions & 15 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ schedune-control-plane/schedune-control-plane
55
bin/
66
var/
77
.schedune.pid
8+
.codex

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help build build-agent build-control-plane test test-agent test-control-plane fmt lint doctor dev-preflight dev-up dev-down demo dev-db-reset example-intake example-schedule example-launch-validate example-launch-dry-run example-launch-execute example-launch-list example-launch-observe example-readiness example-orphans
1+
.PHONY: help build build-agent build-control-plane test test-agent test-control-plane fmt lint doctor dev-preflight dev-up dev-down demo dev-db-reset example-intake example-schedule example-launch-plan example-launch-validate example-launch-dry-run example-launch-execute example-launch-list example-launch-observe example-readiness example-orphans
22

33
# Configuration
44
BIN_DIR=bin
@@ -91,6 +91,9 @@ example-intake: ## Ingest the local node capability payload
9191
example-schedule: ## Run a workload eligibility evaluation explanation
9292
@bash examples/curls/schedule-explain.sh
9393

94+
example-launch-plan: ## Plan a launch (placement, validation, and dry-run) without executing
95+
@bash examples/curls/plan-launch.sh
96+
9497
example-launch-validate: ## Validate a launch without executing it
9598
@bash examples/curls/launch-validate.sh
9699

docs/api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Schedune provides a clear, RESTful interface for scheduling, validating, executi
1313
### Orchestration
1414
* `POST /api/v1alpha1/schedule/explain`: Given a `WorkloadIntent`, evaluates all nodes and returns `RankedNodes` and `RejectedNodes` with explicit `HardRejectionCodes`.
1515
* `POST /api/v1alpha1/schedule/select`: Simulates placement and returns the optimal node ID for the given intent.
16+
* `POST /api/v1alpha1/plan/launch`: Bridges scheduling and execution. Returns a hydrated `LaunchSpec` and explains rejections. In `dry_run` mode, validates selected node readiness and returns a pending `PreparationResult` indicating that node-agent scoped preparation is required. The node agent can then be invoked locally via `schedune-agent prepare --spec <path>`.
17+
18+
### Node Agent
19+
* `schedune-agent prepare --spec <path>`: Local CLI command to prepare the selected runtime backend for a workload. To be called by the node agent after receiving a `PrepareOnNode` action from `/plan/launch`. Reads a `LaunchSpec` JSON from file or stdin (`-`) and outputs a `LaunchPreparationResult` JSON.
1620

1721
### Execution
1822
* `POST /api/v1alpha1/launch/validate`: Given a `LaunchSpec`, assesses if the specific host has the prerequisites to run the chosen backend without launching the process.

docs/quickstart.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,24 @@ make example-schedule
8686
```
8787
*(This evaluates an x86 VM intent and explains the eligibility outcome.)*
8888

89-
#### 6. Validate Launch
89+
#### 6. Plan Launch
90+
91+
Combine your workload intent and a launch template to find an eligible node, validate the launch on it, and dry-run prepare the environment—all without executing anything:
92+
93+
```bash
94+
make example-launch-plan
95+
```
96+
*(This bridges scheduling and execution into a single, safe, read-only explainable response. If `next_actions` includes `PrepareOnNode`, you can invoke the local node agent using `schedune-agent prepare --spec <path>` to locally provision the environment without executing the VM.)*
97+
98+
#### 7. Validate Launch
9099

91100
Validate a launch payload to ensure the backend is available and capabilities match, without starting the VM:
92101

93102
```bash
94103
make example-launch-validate
95104
```
96105

97-
#### 7. Execute Launch
106+
#### 8. Execute Launch
98107

99108
If your host has the required binary (`cloud-hypervisor`), execute the launch:
100109

@@ -103,7 +112,7 @@ make example-launch-execute
103112
```
104113
*Note the returned `execution_id`.*
105114

106-
#### 8. Inspect Lifecycle
115+
#### 9. Inspect Lifecycle
107116

108117
Use the `execution_id` to inspect the VM's state, readiness, trace, and events:
109118

@@ -124,15 +133,15 @@ make example-readiness EXECUTION_ID=<EXECUTION_ID>
124133
curl http://127.0.0.1:9090/api/v1alpha1/launch/<EXECUTION_ID>/events
125134
```
126135

127-
#### 9. Inspect Orphans
136+
#### 10. Inspect Orphans
128137

129138
Schedune automatically sweeps for orphan processes. You can list them via:
130139

131140
```bash
132141
make example-orphans
133142
```
134143

135-
#### 10. Terminate
144+
#### 11. Terminate
136145

137146
When you are done, terminate the execution and stop the control plane:
138147

examples/curls/plan-launch.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
API_BASE="${API_BASE:-http://127.0.0.1:9090}"
5+
6+
echo "=== Planning a VM Launch (Placement -> Validation -> DryRun) ==="
7+
8+
if command -v python3 >/dev/null 2>&1; then
9+
curl -s -X POST -H "Content-Type: application/json" -d @examples/plans/vm-plan.json "${API_BASE}/api/v1alpha1/plan/launch" | python3 -m json.tool || true
10+
else
11+
curl -s -X POST -H "Content-Type: application/json" -d @examples/plans/vm-plan.json "${API_BASE}/api/v1alpha1/plan/launch"
12+
echo ""
13+
fi

examples/plans/vm-plan.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"schema_version": "v1alpha1",
3+
"mode": "dry_run",
4+
"workload_intent": {
5+
"schema_version": "v1alpha1",
6+
"workload_id": "wl-demo-plan",
7+
"tenant_id": "demo-tenant",
8+
"runtime_class": "VirtualMachine",
9+
"required_architecture": "x86_64",
10+
"max_telemetry_age_sec": 300,
11+
"requires_kvm": true,
12+
"required_compatibility_classes": [
13+
"linux_x86_kvm"
14+
],
15+
"preferred_features": [
16+
"qemu_binary_present"
17+
],
18+
"allow_backend_fallback": true
19+
},
20+
"launch_template": {
21+
"schema_version": "v1alpha1",
22+
"workload_id": "wl-demo-plan",
23+
"tenant_id": "demo-tenant",
24+
"runtime_class": "VirtualMachine",
25+
"architecture": "x86_64",
26+
"vcpu": 2,
27+
"memory_mb": 1024,
28+
"storage": [
29+
{
30+
"host_path": "/var/run/demo.qcow2",
31+
"format": "qcow2",
32+
"read_only": false
33+
}
34+
]
35+
}
36+
}

schedune-agent/src/main.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod collectors;
33
mod daemon;
44
mod health;
55
mod migration;
6+
mod prepare;
67
mod scheduler_contract;
78

89
use anyhow::Result;
@@ -36,6 +37,12 @@ enum Commands {
3637
#[arg(short, long, default_value_t = 8080)]
3738
port: u16,
3839
},
40+
/// Prepare the launch environment for a workload
41+
Prepare {
42+
/// Path to the LaunchSpec JSON file (or '-' for stdin)
43+
#[arg(long)]
44+
spec: String,
45+
},
3946
}
4047

4148
#[tokio::main]
@@ -80,6 +87,19 @@ async fn main() -> Result<()> {
8087
Commands::Serve { port } => {
8188
daemon::run(*port).await;
8289
}
90+
Commands::Prepare { spec } => {
91+
let json_str = if spec == "-" {
92+
std::io::read_to_string(std::io::stdin())?
93+
} else {
94+
std::fs::read_to_string(spec)?
95+
};
96+
97+
let launch_spec: crate::prepare::LaunchSpec = serde_json::from_str(&json_str)?;
98+
let result = crate::prepare::prepare_launch(launch_spec);
99+
100+
let json = serde_json::to_string_pretty(&result)?;
101+
println!("{}", json);
102+
}
83103
}
84104

85105
Ok(())

0 commit comments

Comments
 (0)