runtime: remove runtime-owned id
This commit is contained in:
parent
6fedf81785
commit
49f085916e
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
title: 'Remove Runtime-owned runtime_id'
|
title: 'Remove Runtime-owned runtime_id'
|
||||||
state: 'inprogress'
|
state: 'done'
|
||||||
created_at: '2026-07-13T07:42:49Z'
|
created_at: '2026-07-13T07:42:49Z'
|
||||||
updated_at: '2026-07-13T07:43:36Z'
|
updated_at: '2026-07-13T08:37:44Z'
|
||||||
assignee: null
|
assignee: null
|
||||||
queued_by: 'yoi ticket'
|
queued_by: 'yoi ticket'
|
||||||
queued_at: '2026-07-13T07:43:36Z'
|
queued_at: '2026-07-13T07:43:36Z'
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,42 @@ Ticket を `yoi ticket` が queued にしました。
|
||||||
State changed to `inprogress`.
|
State changed to `inprogress`.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: implementation_report author: hare at: 2026-07-13T08:37:44Z -->
|
||||||
|
|
||||||
|
## Implementation report
|
||||||
|
|
||||||
|
Implemented removal of Runtime-owned runtime_id from worker-runtime.
|
||||||
|
|
||||||
|
- Removed `RuntimeId` and made worker-runtime Worker authority Runtime-local (`WorkerRef { worker_id }`).
|
||||||
|
- Removed `runtime_id` from Runtime options, summaries, event cursors/subscriptions, Worker summaries/details, Runtime errors, persisted Runtime state, and Worker snapshots.
|
||||||
|
- Removed REST server `--runtime-id`; the process exposes a single Runtime identified externally by URL/backend alias only.
|
||||||
|
- Changed REST server default store selection to durable fs-store with a default user data root, keeping `--store memory` as the explicit throwaway mode.
|
||||||
|
- Changed fs-store layout so the supplied root is the Runtime store, not `root/runtimes/<runtime-id>`.
|
||||||
|
- Added legacy single-runtime layout migration from the old `runtimes/<encoded-runtime-id>/` layout into the root store when exactly one legacy Runtime directory exists.
|
||||||
|
- Changed deterministic Worker runtime names from `runtime-<runtime_id>-<worker_id>` to Runtime-local `worker-runtime-<worker_id>`.
|
||||||
|
- Kept workspace-server runtime ids as backend/endpoint aliases and adapted embedded/remote projections to attach those aliases at the workspace boundary instead of passing them into worker-runtime.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- `cargo fmt --check`
|
||||||
|
- `git diff --check`
|
||||||
|
- `cargo check -q`
|
||||||
|
- `cargo test -q -p worker-runtime --features fs-store,ws-server`
|
||||||
|
- `cargo test -q -p yoi-workspace-server`
|
||||||
|
- `cargo run -q -p worker-runtime --features ws-server,fs-store --bin worker-runtime-rest-server -- --help`
|
||||||
|
- `cargo run -q -p worker-runtime --features ws-server,fs-store --bin worker-runtime-rest-server -- --runtime-id arc` rejects the removed option
|
||||||
|
- started `worker-runtime-rest-server` with `--fs-root /tmp/yoi-runtime-idless-test` and verified `/v1/runtime` reports `backend: fs_store` without runtime_id
|
||||||
|
- `nix build .#yoi --no-link`
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: state_changed author: "yoi ticket" at: 2026-07-13T08:37:44Z from: inprogress to: done reason: cli_state field: state -->
|
||||||
|
|
||||||
|
## State changed
|
||||||
|
|
||||||
|
State changed to `done`.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::execution::WorkerExecutionStatus;
|
use crate::execution::WorkerExecutionStatus;
|
||||||
use crate::identity::{RuntimeId, WorkerId, WorkerRef};
|
use crate::identity::{WorkerId, WorkerRef};
|
||||||
use crate::interaction::WorkerInput;
|
use crate::interaction::WorkerInput;
|
||||||
use crate::profile_archive::{ProfileSourceArchive, ProfileSourceArchiveRef};
|
use crate::profile_archive::{ProfileSourceArchive, ProfileSourceArchiveRef};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -209,7 +209,6 @@ impl WorkerStatus {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct WorkerSummary {
|
pub struct WorkerSummary {
|
||||||
pub worker_ref: WorkerRef,
|
pub worker_ref: WorkerRef,
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub worker_id: WorkerId,
|
pub worker_id: WorkerId,
|
||||||
pub status: WorkerStatus,
|
pub status: WorkerStatus,
|
||||||
pub execution: WorkerExecutionStatus,
|
pub execution: WorkerExecutionStatus,
|
||||||
|
|
@ -224,7 +223,6 @@ pub struct WorkerSummary {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct WorkerDetail {
|
pub struct WorkerDetail {
|
||||||
pub worker_ref: WorkerRef,
|
pub worker_ref: WorkerRef,
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub worker_id: WorkerId,
|
pub worker_id: WorkerId,
|
||||||
pub status: WorkerStatus,
|
pub status: WorkerStatus,
|
||||||
pub execution: WorkerExecutionStatus,
|
pub execution: WorkerExecutionStatus,
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,18 @@
|
||||||
use crate::execution::WorkerExecutionResult;
|
use crate::execution::WorkerExecutionResult;
|
||||||
use crate::identity::{RuntimeId, WorkerId};
|
use crate::identity::WorkerId;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Errors returned by the embedded Runtime API.
|
/// Errors returned by the embedded Runtime API.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
#[error("runtime {runtime_id} is stopped")]
|
#[error("runtime is stopped")]
|
||||||
RuntimeStopped { runtime_id: RuntimeId },
|
RuntimeStopped,
|
||||||
|
|
||||||
#[error(
|
|
||||||
"worker {worker_id} belongs to runtime {actual_runtime_id}, not runtime {expected_runtime_id}"
|
|
||||||
)]
|
|
||||||
WrongRuntime {
|
|
||||||
expected_runtime_id: RuntimeId,
|
|
||||||
actual_runtime_id: RuntimeId,
|
|
||||||
worker_id: WorkerId,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("cursor belongs to runtime {actual_runtime_id}, not runtime {expected_runtime_id}")]
|
|
||||||
WrongRuntimeCursor {
|
|
||||||
expected_runtime_id: RuntimeId,
|
|
||||||
actual_runtime_id: RuntimeId,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("initial worker input must be user input, got {kind}")]
|
#[error("initial worker input must be user input, got {kind}")]
|
||||||
InvalidInitialInputKind { kind: String },
|
InvalidInitialInputKind { kind: String },
|
||||||
|
|
||||||
#[error("worker {worker_id} was not found in runtime {runtime_id}")]
|
#[error("worker {worker_id} was not found")]
|
||||||
WorkerNotFound {
|
WorkerNotFound { worker_id: WorkerId },
|
||||||
runtime_id: RuntimeId,
|
|
||||||
worker_id: WorkerId,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("worker {worker_id} has no execution backend: {message}")]
|
#[error("worker {worker_id} has no execution backend: {message}")]
|
||||||
WorkerExecutionUnavailable {
|
WorkerExecutionUnavailable {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::config_bundle::ConfigBundle;
|
||||||
use crate::diagnostics::{DiagnosticSeverity, RuntimeDiagnostic};
|
use crate::diagnostics::{DiagnosticSeverity, RuntimeDiagnostic};
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
use crate::execution::WorkerExecutionStatus;
|
use crate::execution::WorkerExecutionStatus;
|
||||||
use crate::identity::{RuntimeId, WorkerId, WorkerRef};
|
use crate::identity::{WorkerId, WorkerRef};
|
||||||
use crate::management::{RuntimeBackendKind, RuntimeLimits, RuntimeStatus};
|
use crate::management::{RuntimeBackendKind, RuntimeLimits, RuntimeStatus};
|
||||||
#[cfg(feature = "ws-server")]
|
#[cfg(feature = "ws-server")]
|
||||||
use crate::observation::WorkerObservationEvent;
|
use crate::observation::WorkerObservationEvent;
|
||||||
|
|
@ -16,10 +16,10 @@ use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
const SCHEMA_VERSION: u32 = 1;
|
const SCHEMA_VERSION: u32 = 1;
|
||||||
const RUNTIMES_DIR: &str = "runtimes";
|
|
||||||
const RUNTIME_FILE: &str = "runtime.json";
|
const RUNTIME_FILE: &str = "runtime.json";
|
||||||
const EVENTS_FILE: &str = "events.jsonl";
|
const EVENTS_FILE: &str = "events.jsonl";
|
||||||
const WORKERS_DIR: &str = "workers";
|
const WORKERS_DIR: &str = "workers";
|
||||||
|
const LEGACY_RUNTIMES_DIR: &str = "runtimes";
|
||||||
const WORKER_FILE: &str = "worker.json";
|
const WORKER_FILE: &str = "worker.json";
|
||||||
#[cfg(feature = "ws-server")]
|
#[cfg(feature = "ws-server")]
|
||||||
const OBSERVATIONS_FILE: &str = "observations.jsonl";
|
const OBSERVATIONS_FILE: &str = "observations.jsonl";
|
||||||
|
|
@ -29,9 +29,8 @@ static NEXT_TMP_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
||||||
/// Options for constructing a filesystem-backed Runtime store.
|
/// Options for constructing a filesystem-backed Runtime store.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct FsRuntimeStoreOptions {
|
pub struct FsRuntimeStoreOptions {
|
||||||
/// Root directory containing all Runtime-scoped store data.
|
/// Root directory containing this Runtime's store data.
|
||||||
pub root: PathBuf,
|
pub root: PathBuf,
|
||||||
pub runtime_id: Option<RuntimeId>,
|
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub limits: RuntimeLimits,
|
pub limits: RuntimeLimits,
|
||||||
}
|
}
|
||||||
|
|
@ -40,23 +39,19 @@ impl FsRuntimeStoreOptions {
|
||||||
pub fn new(root: impl Into<PathBuf>) -> Self {
|
pub fn new(root: impl Into<PathBuf>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
root: root.into(),
|
root: root.into(),
|
||||||
runtime_id: None,
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filesystem persistence boundary for Worker Runtime state.
|
/// Filesystem persistence boundary for one Worker Runtime state.
|
||||||
///
|
///
|
||||||
/// Authority is the typed `runtime_id + worker_id` pair. Those ids are encoded
|
/// Authority is Runtime-local typed Worker identity. Legacy pod paths, socket
|
||||||
/// into path components only after validation; legacy pod paths, socket paths,
|
/// paths, and session paths are deliberately not part of the layout or lookup API.
|
||||||
/// and session paths are deliberately not part of the layout or lookup API.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct FsRuntimeStore {
|
pub struct FsRuntimeStore {
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
runtime_id: RuntimeId,
|
|
||||||
runtime_dir: PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FsRuntimeStore {
|
impl FsRuntimeStore {
|
||||||
|
|
@ -64,12 +59,8 @@ impl FsRuntimeStore {
|
||||||
&self.root
|
&self.root
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runtime_id(&self) -> &RuntimeId {
|
|
||||||
&self.runtime_id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runtime_dir(&self) -> &Path {
|
pub fn runtime_dir(&self) -> &Path {
|
||||||
&self.runtime_dir
|
&self.root
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read persisted Runtime events directly from the event log with the same
|
/// Read persisted Runtime events directly from the event log with the same
|
||||||
|
|
@ -80,12 +71,6 @@ impl FsRuntimeStore {
|
||||||
limit: usize,
|
limit: usize,
|
||||||
max_limit: usize,
|
max_limit: usize,
|
||||||
) -> Result<RuntimeEventBatch, RuntimeError> {
|
) -> Result<RuntimeEventBatch, RuntimeError> {
|
||||||
if cursor.runtime_id != self.runtime_id {
|
|
||||||
return Err(RuntimeError::WrongRuntimeCursor {
|
|
||||||
expected_runtime_id: self.runtime_id.clone(),
|
|
||||||
actual_runtime_id: cursor.runtime_id.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if limit > max_limit {
|
if limit > max_limit {
|
||||||
return Err(RuntimeError::LimitTooLarge {
|
return Err(RuntimeError::LimitTooLarge {
|
||||||
requested: limit,
|
requested: limit,
|
||||||
|
|
@ -109,49 +94,33 @@ impl FsRuntimeStore {
|
||||||
let has_more = events.iter().any(|event| event.id >= next_event_id);
|
let has_more = events.iter().any(|event| event.id >= next_event_id);
|
||||||
|
|
||||||
Ok(RuntimeEventBatch {
|
Ok(RuntimeEventBatch {
|
||||||
runtime_id: self.runtime_id.clone(),
|
cursor: EventCursor { next_event_id },
|
||||||
cursor: EventCursor {
|
|
||||||
runtime_id: self.runtime_id.clone(),
|
|
||||||
next_event_id,
|
|
||||||
},
|
|
||||||
events: selected,
|
events: selected,
|
||||||
has_more,
|
has_more,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open_or_create(
|
pub(crate) fn open_or_create(root: PathBuf) -> Result<OpenedFsRuntimeStore, RuntimeError> {
|
||||||
root: PathBuf,
|
let existed = root.exists();
|
||||||
runtime_id: RuntimeId,
|
if existed && !root.is_dir() {
|
||||||
) -> Result<OpenedFsRuntimeStore, RuntimeError> {
|
|
||||||
fs::create_dir_all(root.join(RUNTIMES_DIR)).map_err(|source| RuntimeError::StoreIo {
|
|
||||||
operation: "create store root",
|
|
||||||
path: root.join(RUNTIMES_DIR),
|
|
||||||
source,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let runtime_dir = runtime_dir(&root, &runtime_id);
|
|
||||||
let existed = runtime_dir.exists();
|
|
||||||
if existed && !runtime_dir.is_dir() {
|
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
operation: "open runtime store",
|
operation: "open runtime store",
|
||||||
path: runtime_dir,
|
path: root,
|
||||||
message: "runtime path exists but is not a directory".to_string(),
|
message: "runtime path exists but is not a directory".to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::create_dir_all(runtime_dir.join(WORKERS_DIR)).map_err(|source| {
|
if existed {
|
||||||
RuntimeError::StoreIo {
|
migrate_legacy_single_runtime_layout(&root)?;
|
||||||
operation: "create runtime store",
|
|
||||||
path: runtime_dir.join(WORKERS_DIR),
|
|
||||||
source,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::create_dir_all(root.join(WORKERS_DIR)).map_err(|source| RuntimeError::StoreIo {
|
||||||
|
operation: "create runtime store",
|
||||||
|
path: root.join(WORKERS_DIR),
|
||||||
|
source,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let store = Self {
|
let store = Self { root };
|
||||||
root,
|
|
||||||
runtime_id,
|
|
||||||
runtime_dir,
|
|
||||||
};
|
|
||||||
let state = if existed {
|
let state = if existed {
|
||||||
Some(store.load_runtime_state()?)
|
Some(store.load_runtime_state()?)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -227,10 +196,10 @@ impl FsRuntimeStore {
|
||||||
pub(crate) fn load_runtime_state(&self) -> Result<PersistedRuntimeState, RuntimeError> {
|
pub(crate) fn load_runtime_state(&self) -> Result<PersistedRuntimeState, RuntimeError> {
|
||||||
let runtime_path = self.runtime_path();
|
let runtime_path = self.runtime_path();
|
||||||
let mut snapshot: RuntimeSnapshot = read_json(&runtime_path, "read runtime snapshot")?;
|
let mut snapshot: RuntimeSnapshot = read_json(&runtime_path, "read runtime snapshot")?;
|
||||||
snapshot.validate(&self.runtime_id, &runtime_path)?;
|
snapshot.validate(&runtime_path)?;
|
||||||
|
|
||||||
let events = read_json_lines::<RuntimeEvent>(&self.events_path(), "read events")?;
|
let events = read_json_lines::<RuntimeEvent>(&self.events_path(), "read events")?;
|
||||||
let workers_dir = self.runtime_dir.join(WORKERS_DIR);
|
let workers_dir = self.root.join(WORKERS_DIR);
|
||||||
if !workers_dir.exists() {
|
if !workers_dir.exists() {
|
||||||
return Err(RuntimeError::StoreMissing {
|
return Err(RuntimeError::StoreMissing {
|
||||||
operation: "read workers",
|
operation: "read workers",
|
||||||
|
|
@ -286,10 +255,7 @@ impl FsRuntimeStore {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if worker_snapshot
|
if worker_snapshot.validate(&worker_snapshot_path).is_err() {
|
||||||
.validate(&self.runtime_id, &worker_snapshot_path)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
record_worker_load_diagnostic(
|
record_worker_load_diagnostic(
|
||||||
&mut snapshot,
|
&mut snapshot,
|
||||||
Some(worker_snapshot.worker_ref.clone()),
|
Some(worker_snapshot.worker_ref.clone()),
|
||||||
|
|
@ -355,30 +321,20 @@ impl FsRuntimeStore {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_worker_ref(&self, worker_ref: &WorkerRef) -> Result<(), RuntimeError> {
|
fn ensure_worker_ref(&self, _worker_ref: &WorkerRef) -> Result<(), RuntimeError> {
|
||||||
if worker_ref.runtime_id == self.runtime_id {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(RuntimeError::WrongRuntime {
|
|
||||||
expected_runtime_id: self.runtime_id.clone(),
|
|
||||||
actual_runtime_id: worker_ref.runtime_id.clone(),
|
|
||||||
worker_id: worker_ref.worker_id.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_path(&self) -> PathBuf {
|
fn runtime_path(&self) -> PathBuf {
|
||||||
self.runtime_dir.join(RUNTIME_FILE)
|
self.root.join(RUNTIME_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn events_path(&self) -> PathBuf {
|
fn events_path(&self) -> PathBuf {
|
||||||
self.runtime_dir.join(EVENTS_FILE)
|
self.root.join(EVENTS_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_dir(&self, worker_id: &WorkerId) -> PathBuf {
|
fn worker_dir(&self, worker_id: &WorkerId) -> PathBuf {
|
||||||
self.runtime_dir
|
self.root.join(WORKERS_DIR).join(worker_id.to_string())
|
||||||
.join(WORKERS_DIR)
|
|
||||||
.join(encoded_component(&worker_id.to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ws-server")]
|
#[cfg(feature = "ws-server")]
|
||||||
|
|
@ -395,7 +351,6 @@ pub(crate) struct OpenedFsRuntimeStore {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct PersistedRuntimeState {
|
pub(crate) struct PersistedRuntimeState {
|
||||||
pub(crate) runtime_id: RuntimeId,
|
|
||||||
pub(crate) display_name: Option<String>,
|
pub(crate) display_name: Option<String>,
|
||||||
pub(crate) status: RuntimeStatus,
|
pub(crate) status: RuntimeStatus,
|
||||||
pub(crate) limits: RuntimeLimits,
|
pub(crate) limits: RuntimeLimits,
|
||||||
|
|
@ -423,7 +378,6 @@ pub(crate) struct PersistedWorkerRecord {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
struct RuntimeSnapshot {
|
struct RuntimeSnapshot {
|
||||||
schema_version: u32,
|
schema_version: u32,
|
||||||
runtime_id: RuntimeId,
|
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
backend: RuntimeBackendKind,
|
backend: RuntimeBackendKind,
|
||||||
status: RuntimeStatus,
|
status: RuntimeStatus,
|
||||||
|
|
@ -456,7 +410,6 @@ impl RuntimeSnapshot {
|
||||||
fn from_persisted(state: &PersistedRuntimeState) -> Self {
|
fn from_persisted(state: &PersistedRuntimeState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
schema_version: SCHEMA_VERSION,
|
schema_version: SCHEMA_VERSION,
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
display_name: state.display_name.clone(),
|
display_name: state.display_name.clone(),
|
||||||
backend: RuntimeBackendKind::FsStore,
|
backend: RuntimeBackendKind::FsStore,
|
||||||
status: state.status,
|
status: state.status,
|
||||||
|
|
@ -469,7 +422,7 @@ impl RuntimeSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, expected_runtime_id: &RuntimeId, path: &Path) -> Result<(), RuntimeError> {
|
fn validate(&self, path: &Path) -> Result<(), RuntimeError> {
|
||||||
if self.schema_version != SCHEMA_VERSION {
|
if self.schema_version != SCHEMA_VERSION {
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
operation: "read runtime snapshot",
|
operation: "read runtime snapshot",
|
||||||
|
|
@ -480,16 +433,6 @@ impl RuntimeSnapshot {
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if &self.runtime_id != expected_runtime_id {
|
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
|
||||||
operation: "read runtime snapshot",
|
|
||||||
path: path.to_path_buf(),
|
|
||||||
message: format!(
|
|
||||||
"runtime snapshot id {} does not match requested runtime {}",
|
|
||||||
self.runtime_id, expected_runtime_id
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if self.backend != RuntimeBackendKind::FsStore {
|
if self.backend != RuntimeBackendKind::FsStore {
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
operation: "read runtime snapshot",
|
operation: "read runtime snapshot",
|
||||||
|
|
@ -507,7 +450,6 @@ impl RuntimeSnapshot {
|
||||||
#[cfg(feature = "ws-server")] observation_events: Vec<WorkerObservationEvent>,
|
#[cfg(feature = "ws-server")] observation_events: Vec<WorkerObservationEvent>,
|
||||||
) -> PersistedRuntimeState {
|
) -> PersistedRuntimeState {
|
||||||
PersistedRuntimeState {
|
PersistedRuntimeState {
|
||||||
runtime_id: self.runtime_id,
|
|
||||||
display_name: self.display_name,
|
display_name: self.display_name,
|
||||||
status: self.status,
|
status: self.status,
|
||||||
limits: self.limits,
|
limits: self.limits,
|
||||||
|
|
@ -549,7 +491,7 @@ impl WorkerSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, expected_runtime_id: &RuntimeId, path: &Path) -> Result<(), RuntimeError> {
|
fn validate(&self, path: &Path) -> Result<(), RuntimeError> {
|
||||||
if self.schema_version != SCHEMA_VERSION {
|
if self.schema_version != SCHEMA_VERSION {
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
operation: "read worker snapshot",
|
operation: "read worker snapshot",
|
||||||
|
|
@ -560,16 +502,6 @@ impl WorkerSnapshot {
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if self.worker_ref.runtime_id != *expected_runtime_id {
|
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
|
||||||
operation: "read worker snapshot",
|
|
||||||
path: path.to_path_buf(),
|
|
||||||
message: format!(
|
|
||||||
"worker belongs to runtime {}, expected {}",
|
|
||||||
self.worker_ref.runtime_id, expected_runtime_id
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if self.worker_ref.worker_id != self.worker_id {
|
if self.worker_ref.worker_id != self.worker_id {
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
operation: "read worker snapshot",
|
operation: "read worker snapshot",
|
||||||
|
|
@ -595,27 +527,81 @@ impl WorkerSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_dir(root: &Path, runtime_id: &RuntimeId) -> PathBuf {
|
fn migrate_legacy_single_runtime_layout(root: &Path) -> Result<(), RuntimeError> {
|
||||||
root.join(RUNTIMES_DIR)
|
if root.join(RUNTIME_FILE).exists() {
|
||||||
.join(encoded_component(runtime_id.as_str()))
|
return Ok(());
|
||||||
|
}
|
||||||
|
let legacy_root = root.join(LEGACY_RUNTIMES_DIR);
|
||||||
|
if !legacy_root.is_dir() {
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encoded_component(value: &str) -> String {
|
let mut candidates = Vec::new();
|
||||||
let mut encoded = String::with_capacity(3 + value.len() * 2);
|
for entry in fs::read_dir(&legacy_root).map_err(|source| RuntimeError::StoreIo {
|
||||||
encoded.push_str("id-");
|
operation: "read legacy runtime store root",
|
||||||
for byte in value.as_bytes() {
|
path: legacy_root.clone(),
|
||||||
encoded.push(hex_digit(byte >> 4));
|
source,
|
||||||
encoded.push(hex_digit(byte & 0x0f));
|
})? {
|
||||||
|
let entry = entry.map_err(|source| RuntimeError::StoreIo {
|
||||||
|
operation: "read legacy runtime store entry",
|
||||||
|
path: legacy_root.clone(),
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
let path = entry.path();
|
||||||
|
if path.join(RUNTIME_FILE).is_file() {
|
||||||
|
candidates.push(path);
|
||||||
}
|
}
|
||||||
encoded
|
}
|
||||||
|
if candidates.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if candidates.len() > 1 {
|
||||||
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
|
operation: "migrate legacy runtime store",
|
||||||
|
path: legacy_root,
|
||||||
|
message: "multiple legacy runtime directories exist; choose a concrete fs root"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hex_digit(value: u8) -> char {
|
let legacy_dir = candidates.remove(0);
|
||||||
match value {
|
rename_if_exists(
|
||||||
0..=9 => (b'0' + value) as char,
|
&legacy_dir.join(RUNTIME_FILE),
|
||||||
10..=15 => (b'a' + (value - 10)) as char,
|
&root.join(RUNTIME_FILE),
|
||||||
_ => unreachable!("hex digit nybble is always <= 15"),
|
"migrate legacy runtime snapshot",
|
||||||
|
)?;
|
||||||
|
rename_if_exists(
|
||||||
|
&legacy_dir.join(EVENTS_FILE),
|
||||||
|
&root.join(EVENTS_FILE),
|
||||||
|
"migrate legacy runtime events",
|
||||||
|
)?;
|
||||||
|
rename_if_exists(
|
||||||
|
&legacy_dir.join(WORKERS_DIR),
|
||||||
|
&root.join(WORKERS_DIR),
|
||||||
|
"migrate legacy runtime workers",
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rename_if_exists(src: &Path, dst: &Path, operation: &'static str) -> Result<(), RuntimeError> {
|
||||||
|
if !src.exists() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if dst.exists() {
|
||||||
|
return Err(RuntimeError::StoreCorrupt {
|
||||||
|
operation,
|
||||||
|
path: dst.to_path_buf(),
|
||||||
|
message: format!(
|
||||||
|
"cannot migrate {} because destination already exists",
|
||||||
|
src.display()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fs::rename(src, dst).map_err(|source| RuntimeError::StoreIo {
|
||||||
|
operation,
|
||||||
|
path: dst.to_path_buf(),
|
||||||
|
source,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_json<T>(path: &Path, operation: &'static str) -> Result<T, RuntimeError>
|
fn read_json<T>(path: &Path, operation: &'static str) -> Result<T, RuntimeError>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::catalog::{
|
||||||
};
|
};
|
||||||
use crate::config_bundle::{ConfigBundle, ConfigBundleAvailability, ConfigBundleSummary};
|
use crate::config_bundle::{ConfigBundle, ConfigBundleAvailability, ConfigBundleSummary};
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
use crate::identity::{RuntimeId, WorkerId, WorkerRef};
|
use crate::identity::{WorkerId, WorkerRef};
|
||||||
use crate::interaction::{WorkerInput, WorkerInteractionAck};
|
use crate::interaction::{WorkerInput, WorkerInteractionAck};
|
||||||
use crate::management::{RuntimeLimits, RuntimeSummary, WorkerDeleteResult};
|
use crate::management::{RuntimeLimits, RuntimeSummary, WorkerDeleteResult};
|
||||||
#[cfg(feature = "ws-server")]
|
#[cfg(feature = "ws-server")]
|
||||||
|
|
@ -50,9 +50,6 @@ pub struct RuntimeHttpServerConfig {
|
||||||
/// Address for the Runtime process to bind. Use a loopback address unless a
|
/// Address for the Runtime process to bind. Use a loopback address unless a
|
||||||
/// trusted backend proxy explicitly owns network exposure.
|
/// trusted backend proxy explicitly owns network exposure.
|
||||||
pub bind_addr: SocketAddr,
|
pub bind_addr: SocketAddr,
|
||||||
/// Optional explicit Runtime authority id. If omitted, the Runtime library
|
|
||||||
/// generates one.
|
|
||||||
pub runtime_id: Option<RuntimeId>,
|
|
||||||
/// Optional display label surfaced by `GET /v1/runtime`.
|
/// Optional display label surfaced by `GET /v1/runtime`.
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
/// Bounded Runtime API limits.
|
/// Bounded Runtime API limits.
|
||||||
|
|
@ -68,7 +65,6 @@ impl Default for RuntimeHttpServerConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
bind_addr: default_runtime_http_bind_addr(),
|
bind_addr: default_runtime_http_bind_addr(),
|
||||||
runtime_id: None,
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
store: RuntimeHttpStoreSelection::Memory,
|
store: RuntimeHttpStoreSelection::Memory,
|
||||||
|
|
@ -81,7 +77,6 @@ impl fmt::Debug for RuntimeHttpServerConfig {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("RuntimeHttpServerConfig")
|
f.debug_struct("RuntimeHttpServerConfig")
|
||||||
.field("bind_addr", &self.bind_addr)
|
.field("bind_addr", &self.bind_addr)
|
||||||
.field("runtime_id", &self.runtime_id)
|
|
||||||
.field("display_name", &self.display_name)
|
.field("display_name", &self.display_name)
|
||||||
.field("limits", &self.limits)
|
.field("limits", &self.limits)
|
||||||
.field("store", &self.store)
|
.field("store", &self.store)
|
||||||
|
|
@ -118,10 +113,9 @@ pub async fn serve_runtime_http(
|
||||||
|
|
||||||
/// Build the REST router for an existing Runtime.
|
/// Build the REST router for an existing Runtime.
|
||||||
///
|
///
|
||||||
/// Handlers delegate to [`Runtime`] methods and keep Worker authority as
|
/// Handlers delegate to [`Runtime`] methods and keep Worker authority Runtime-local.
|
||||||
/// `(runtime_id, worker_id)`. The path contains only a Runtime-local
|
/// The path contains only a Runtime-local `worker_id`; backend aliases are not
|
||||||
/// `worker_id`; the server supplies its own Runtime id instead of accepting a
|
/// accepted or forwarded as Runtime authority.
|
||||||
/// legacy pod/socket/session path as authority.
|
|
||||||
pub fn runtime_http_router(runtime: Runtime, local_token: Option<String>) -> Router {
|
pub fn runtime_http_router(runtime: Runtime, local_token: Option<String>) -> Router {
|
||||||
let state = RuntimeHttpState {
|
let state = RuntimeHttpState {
|
||||||
runtime,
|
runtime,
|
||||||
|
|
@ -712,7 +706,10 @@ async fn cancel_worker(
|
||||||
Ok(Json(RuntimeHttpWorkerLifecycleResponse { ack }))
|
Ok(Json(RuntimeHttpWorkerLifecycleResponse { ack }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_ref_for(runtime: &Runtime, worker_id: String) -> Result<WorkerRef, RuntimeHttpRestError> {
|
fn worker_ref_for(
|
||||||
|
_runtime: &Runtime,
|
||||||
|
worker_id: String,
|
||||||
|
) -> Result<WorkerRef, RuntimeHttpRestError> {
|
||||||
let worker_id = WorkerId::parse(&worker_id).ok_or_else(|| {
|
let worker_id = WorkerId::parse(&worker_id).ok_or_else(|| {
|
||||||
RuntimeHttpRestError::new(
|
RuntimeHttpRestError::new(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
|
|
@ -720,10 +717,7 @@ fn worker_ref_for(runtime: &Runtime, worker_id: String) -> Result<WorkerRef, Run
|
||||||
"worker_id must be an unsigned integer",
|
"worker_id must be an unsigned integer",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let runtime_id = runtime
|
Ok(WorkerRef::new(worker_id))
|
||||||
.runtime_id()
|
|
||||||
.map_err(RuntimeHttpRestError::runtime)?;
|
|
||||||
Ok(WorkerRef::new(runtime_id, worker_id))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_optional_lifecycle_request(
|
fn parse_optional_lifecycle_request(
|
||||||
|
|
@ -820,7 +814,7 @@ fn status_for_runtime_error(error: &RuntimeError) -> StatusCode {
|
||||||
RuntimeError::WorkerNotFound { .. } | RuntimeError::ConfigBundleMissing { .. } => {
|
RuntimeError::WorkerNotFound { .. } | RuntimeError::ConfigBundleMissing { .. } => {
|
||||||
StatusCode::NOT_FOUND
|
StatusCode::NOT_FOUND
|
||||||
}
|
}
|
||||||
RuntimeError::RuntimeStopped { .. }
|
RuntimeError::RuntimeStopped
|
||||||
| RuntimeError::WorkerExecutionUnavailable { .. }
|
| RuntimeError::WorkerExecutionUnavailable { .. }
|
||||||
| RuntimeError::ExecutionBackendUnavailable { .. }
|
| RuntimeError::ExecutionBackendUnavailable { .. }
|
||||||
| RuntimeError::WorkerExecutionRejected { .. } => StatusCode::CONFLICT,
|
| RuntimeError::WorkerExecutionRejected { .. } => StatusCode::CONFLICT,
|
||||||
|
|
@ -829,9 +823,7 @@ fn status_for_runtime_error(error: &RuntimeError) -> StatusCode {
|
||||||
| RuntimeError::InvalidInitialInputKind { .. }
|
| RuntimeError::InvalidInitialInputKind { .. }
|
||||||
| RuntimeError::ConfigBundleDigestMismatch { .. }
|
| RuntimeError::ConfigBundleDigestMismatch { .. }
|
||||||
| RuntimeError::InvalidProfileSelector { .. }
|
| RuntimeError::InvalidProfileSelector { .. }
|
||||||
| RuntimeError::UnsupportedConfigDeclaration { .. }
|
| RuntimeError::UnsupportedConfigDeclaration { .. } => StatusCode::BAD_REQUEST,
|
||||||
| RuntimeError::WrongRuntime { .. }
|
|
||||||
| RuntimeError::WrongRuntimeCursor { .. } => StatusCode::BAD_REQUEST,
|
|
||||||
RuntimeError::StoreIo { .. }
|
RuntimeError::StoreIo { .. }
|
||||||
| RuntimeError::StoreMissing { .. }
|
| RuntimeError::StoreMissing { .. }
|
||||||
| RuntimeError::StoreCorrupt { .. }
|
| RuntimeError::StoreCorrupt { .. }
|
||||||
|
|
@ -841,9 +833,7 @@ fn status_for_runtime_error(error: &RuntimeError) -> StatusCode {
|
||||||
|
|
||||||
fn code_for_runtime_error(error: &RuntimeError) -> &'static str {
|
fn code_for_runtime_error(error: &RuntimeError) -> &'static str {
|
||||||
match error {
|
match error {
|
||||||
RuntimeError::RuntimeStopped { .. } => "runtime_stopped",
|
RuntimeError::RuntimeStopped => "runtime_stopped",
|
||||||
RuntimeError::WrongRuntime { .. } => "wrong_runtime",
|
|
||||||
RuntimeError::WrongRuntimeCursor { .. } => "wrong_runtime_cursor",
|
|
||||||
RuntimeError::WorkerNotFound { .. } => "worker_not_found",
|
RuntimeError::WorkerNotFound { .. } => "worker_not_found",
|
||||||
RuntimeError::WorkerExecutionUnavailable { .. } => "worker_execution_unavailable",
|
RuntimeError::WorkerExecutionUnavailable { .. } => "worker_execution_unavailable",
|
||||||
RuntimeError::ExecutionBackendUnavailable { .. } => "execution_backend_unavailable",
|
RuntimeError::ExecutionBackendUnavailable { .. } => "execution_backend_unavailable",
|
||||||
|
|
@ -1038,8 +1028,8 @@ mod tests {
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
let created: RuntimeHttpWorkerResponse = read_json(response).await;
|
let created: RuntimeHttpWorkerResponse = read_json(response).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
created.worker.worker_ref.runtime_id,
|
created.worker.worker_ref.worker_id,
|
||||||
runtime.runtime_id().unwrap()
|
created.worker.worker_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let input = WorkerInput::user("hello from backend");
|
let input = WorkerInput::user("hello from backend");
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Public Runtime identity.
|
|
||||||
///
|
|
||||||
/// This is the first half of Worker authority. Runtime APIs that operate on a
|
|
||||||
/// Worker require this id alongside a [`WorkerId`]; socket paths, session paths,
|
|
||||||
/// and display names are deliberately not authority.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
||||||
#[serde(transparent)]
|
|
||||||
pub struct RuntimeId(String);
|
|
||||||
|
|
||||||
impl RuntimeId {
|
|
||||||
pub fn new(value: impl Into<String>) -> Option<Self> {
|
|
||||||
let value = value.into();
|
|
||||||
if value.trim().is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Self(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn generated(sequence: u64) -> Self {
|
|
||||||
Self(format!("runtime-mem-{sequence:016x}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for RuntimeId {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runtime-local Worker identity.
|
/// Runtime-local Worker identity.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
|
|
@ -64,18 +30,14 @@ impl fmt::Display for WorkerId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete public authority reference for Worker operations.
|
/// Runtime-local authority reference for Worker operations.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct WorkerRef {
|
pub struct WorkerRef {
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub worker_id: WorkerId,
|
pub worker_id: WorkerId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerRef {
|
impl WorkerRef {
|
||||||
pub fn new(runtime_id: RuntimeId, worker_id: WorkerId) -> Self {
|
pub fn new(worker_id: WorkerId) -> Self {
|
||||||
Self {
|
Self { worker_id }
|
||||||
runtime_id,
|
|
||||||
worker_id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ use worker_runtime::fs_store::FsRuntimeStoreOptions;
|
||||||
use worker_runtime::http_server::{
|
use worker_runtime::http_server::{
|
||||||
RuntimeHttpServerConfig, RuntimeHttpServerError, RuntimeHttpStoreSelection,
|
RuntimeHttpServerConfig, RuntimeHttpServerError, RuntimeHttpStoreSelection,
|
||||||
};
|
};
|
||||||
use worker_runtime::identity::RuntimeId;
|
|
||||||
use worker_runtime::worker_backend::{ProfileRuntimeWorkerFactory, WorkerRuntimeExecutionBackend};
|
use worker_runtime::worker_backend::{ProfileRuntimeWorkerFactory, WorkerRuntimeExecutionBackend};
|
||||||
use worker_runtime::working_directory::LocalGitWorktreeMaterializer;
|
use worker_runtime::working_directory::LocalGitWorktreeMaterializer;
|
||||||
use worker_runtime::{Runtime, RuntimeOptions};
|
use worker_runtime::{Runtime, RuntimeOptions};
|
||||||
|
|
@ -105,7 +104,6 @@ fn build_runtime(config: &ProcessConfig) -> Result<Runtime, ProcessError> {
|
||||||
}
|
}
|
||||||
RuntimeHttpStoreSelection::Fs { root } => {
|
RuntimeHttpStoreSelection::Fs { root } => {
|
||||||
let mut options = FsRuntimeStoreOptions::new(root.clone());
|
let mut options = FsRuntimeStoreOptions::new(root.clone());
|
||||||
options.runtime_id = config.http.runtime_id.clone();
|
|
||||||
options.display_name = config.http.display_name.clone();
|
options.display_name = config.http.display_name.clone();
|
||||||
options.limits = config.http.limits.clone();
|
options.limits = config.http.limits.clone();
|
||||||
Runtime::with_fs_store_and_execution_backend(options, backend)
|
Runtime::with_fs_store_and_execution_backend(options, backend)
|
||||||
|
|
@ -135,19 +133,26 @@ fn default_working_directory_runtime_root() -> PathBuf {
|
||||||
|
|
||||||
fn runtime_options_from_http(config: &RuntimeHttpServerConfig) -> RuntimeOptions {
|
fn runtime_options_from_http(config: &RuntimeHttpServerConfig) -> RuntimeOptions {
|
||||||
RuntimeOptions {
|
RuntimeOptions {
|
||||||
runtime_id: config.runtime_id.clone(),
|
|
||||||
display_name: config.display_name.clone(),
|
display_name: config.display_name.clone(),
|
||||||
limits: config.limits.clone(),
|
limits: config.limits.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_fs_root() -> Option<PathBuf> {
|
||||||
|
manifest::paths::data_dir()
|
||||||
|
.map(|data_dir| data_dir.join("worker-runtime-rest"))
|
||||||
|
.or_else(|| Some(env::temp_dir().join("yoi-worker-runtime-rest")))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_args<I, S>(args: I) -> Result<Option<ProcessConfig>, ProcessError>
|
fn parse_args<I, S>(args: I) -> Result<Option<ProcessConfig>, ProcessError>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
let mut config = ProcessConfig::default()?;
|
let mut config = ProcessConfig::default()?;
|
||||||
let mut store = StoreArg::Memory;
|
let mut store = StoreArg::Fs {
|
||||||
|
root: default_fs_root(),
|
||||||
|
};
|
||||||
let mut args = args.into_iter().map(Into::into).collect::<VecDeque<_>>();
|
let mut args = args.into_iter().map(Into::into).collect::<VecDeque<_>>();
|
||||||
|
|
||||||
while let Some(arg) = args.pop_front() {
|
while let Some(arg) = args.pop_front() {
|
||||||
|
|
@ -160,12 +165,6 @@ where
|
||||||
ProcessError::usage(format!("invalid --bind socket address `{value}`: {error}"))
|
ProcessError::usage(format!("invalid --bind socket address `{value}`: {error}"))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
"--runtime-id" => {
|
|
||||||
let value = take_value(&flag, inline_value, &mut args)?;
|
|
||||||
config.http.runtime_id = Some(RuntimeId::new(value).ok_or_else(|| {
|
|
||||||
ProcessError::usage("--runtime-id must not be empty".to_string())
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
"--display-name" => {
|
"--display-name" => {
|
||||||
config.http.display_name = Some(take_value(&flag, inline_value, &mut args)?);
|
config.http.display_name = Some(take_value(&flag, inline_value, &mut args)?);
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +200,9 @@ where
|
||||||
let value = take_value(&flag, inline_value, &mut args)?;
|
let value = take_value(&flag, inline_value, &mut args)?;
|
||||||
store = match value.as_str() {
|
store = match value.as_str() {
|
||||||
"memory" => StoreArg::Memory,
|
"memory" => StoreArg::Memory,
|
||||||
"fs" | "fs-store" => StoreArg::Fs { root: None },
|
"fs" | "fs-store" => StoreArg::Fs {
|
||||||
|
root: default_fs_root(),
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ProcessError::usage(format!(
|
return Err(ProcessError::usage(format!(
|
||||||
"unsupported --store `{value}`; expected `memory` or `fs`"
|
"unsupported --store `{value}`; expected `memory` or `fs`"
|
||||||
|
|
@ -317,9 +318,7 @@ fn apply_store_selection(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
StoreArg::Fs { root } => {
|
StoreArg::Fs { root } => {
|
||||||
let root = root.ok_or_else(|| {
|
let root = root.unwrap_or_else(|| env::temp_dir().join("yoi-worker-runtime-rest"));
|
||||||
ProcessError::usage("--store fs requires --fs-root <PATH>".to_string())
|
|
||||||
})?;
|
|
||||||
config.store = RuntimeHttpStoreSelection::Fs { root };
|
config.store = RuntimeHttpStoreSelection::Fs { root };
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -418,7 +417,6 @@ Starts a worker-backed Runtime REST command API for a trusted backend/proxy.\n\
|
||||||
Browsers must not connect to this Runtime process directly.\n\n\
|
Browsers must not connect to this Runtime process directly.\n\n\
|
||||||
Options:\n\
|
Options:\n\
|
||||||
--bind <ADDR> Bind socket address (default: 127.0.0.1:38800)\n\
|
--bind <ADDR> Bind socket address (default: 127.0.0.1:38800)\n\
|
||||||
--runtime-id <ID> Runtime authority id (default: generated)\n\
|
|
||||||
--display-name <NAME> Runtime display name\n\
|
--display-name <NAME> Runtime display name\n\
|
||||||
--workspace <PATH> Workspace root used for spawned Workers (default: cwd)\n\
|
--workspace <PATH> Workspace root used for spawned Workers (default: cwd)\n\
|
||||||
--cwd <PATH> Process cwd used for spawned Workers (default: workspace)\n\
|
--cwd <PATH> Process cwd used for spawned Workers (default: workspace)\n\
|
||||||
|
|
@ -428,8 +426,8 @@ Options:\n\
|
||||||
--worker-runtime-base-dir <PATH> Worker controller runtime directory\n\
|
--worker-runtime-base-dir <PATH> Worker controller runtime directory\n\
|
||||||
--backend-resource-endpoint <URL> Internal Backend resource fetch endpoint for resource handles\n\
|
--backend-resource-endpoint <URL> Internal Backend resource fetch endpoint for resource handles\n\
|
||||||
--backend-resource-token <TOKEN> Optional bearer token for the Backend resource fetch endpoint\n\
|
--backend-resource-token <TOKEN> Optional bearer token for the Backend resource fetch endpoint\n\
|
||||||
--store <memory|fs> Runtime catalog store selection (default: memory)\n\
|
--store <memory|fs> Runtime catalog store selection (default: fs)\n\
|
||||||
--fs-root <PATH> Runtime catalog filesystem store root\n\
|
--fs-root <PATH> Runtime catalog filesystem store root (default: user data dir)\n\
|
||||||
--local-token <TOKEN> Minimal local bearer token placeholder\n\
|
--local-token <TOKEN> Minimal local bearer token placeholder\n\
|
||||||
--local-token-env <ENV> Read local bearer token placeholder from env\n\
|
--local-token-env <ENV> Read local bearer token placeholder from env\n\
|
||||||
--max-event-batch-items <N> Override event batch limit\n\
|
--max-event-batch-items <N> Override event batch limit\n\
|
||||||
|
|
@ -471,7 +469,6 @@ mod tests {
|
||||||
let config = parse_args([
|
let config = parse_args([
|
||||||
"--bind",
|
"--bind",
|
||||||
"127.0.0.1:0",
|
"127.0.0.1:0",
|
||||||
"--runtime-id=runtime-alpha",
|
|
||||||
"--display-name",
|
"--display-name",
|
||||||
"Runtime Alpha",
|
"Runtime Alpha",
|
||||||
"--workspace",
|
"--workspace",
|
||||||
|
|
@ -491,7 +488,6 @@ mod tests {
|
||||||
let config = parse_args([
|
let config = parse_args([
|
||||||
"--bind",
|
"--bind",
|
||||||
"127.0.0.1:0",
|
"127.0.0.1:0",
|
||||||
"--runtime-id=runtime-alpha",
|
|
||||||
"--display-name",
|
"--display-name",
|
||||||
"Runtime Alpha",
|
"Runtime Alpha",
|
||||||
"--workspace",
|
"--workspace",
|
||||||
|
|
@ -510,10 +506,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(config.http.bind_addr, "127.0.0.1:0".parse().unwrap());
|
assert_eq!(config.http.bind_addr, "127.0.0.1:0".parse().unwrap());
|
||||||
assert_eq!(
|
|
||||||
config.http.runtime_id.as_ref().map(ToString::to_string),
|
|
||||||
Some("runtime-alpha".to_string())
|
|
||||||
);
|
|
||||||
assert_eq!(config.http.display_name.as_deref(), Some("Runtime Alpha"));
|
assert_eq!(config.http.display_name.as_deref(), Some("Runtime Alpha"));
|
||||||
assert_eq!(config.workspace_root, PathBuf::from("/tmp/workspace"));
|
assert_eq!(config.workspace_root, PathBuf::from("/tmp/workspace"));
|
||||||
assert_eq!(config.cwd, PathBuf::from("/tmp/workspace/subdir"));
|
assert_eq!(config.cwd, PathBuf::from("/tmp/workspace/subdir"));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::identity::{RuntimeId, WorkerId};
|
use crate::identity::WorkerId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Runtime backend kind.
|
/// Runtime backend kind.
|
||||||
|
|
@ -35,7 +35,6 @@ impl Default for RuntimeLimits {
|
||||||
/// Options used to construct an embedded memory Runtime.
|
/// Options used to construct an embedded memory Runtime.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct RuntimeOptions {
|
pub struct RuntimeOptions {
|
||||||
pub runtime_id: Option<RuntimeId>,
|
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub limits: RuntimeLimits,
|
pub limits: RuntimeLimits,
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +42,6 @@ pub struct RuntimeOptions {
|
||||||
impl Default for RuntimeOptions {
|
impl Default for RuntimeOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
runtime_id: None,
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +54,6 @@ fn unknown_platform_component() -> String {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct WorkerDeleteResult {
|
pub struct WorkerDeleteResult {
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub worker_id: WorkerId,
|
pub worker_id: WorkerId,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +61,6 @@ pub struct WorkerDeleteResult {
|
||||||
/// Management-plane summary for a Runtime.
|
/// Management-plane summary for a Runtime.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct RuntimeSummary {
|
pub struct RuntimeSummary {
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub backend: RuntimeBackendKind,
|
pub backend: RuntimeBackendKind,
|
||||||
pub status: RuntimeStatus,
|
pub status: RuntimeStatus,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use crate::identity::{RuntimeId, WorkerRef};
|
use crate::identity::WorkerRef;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Event cursor. `next_event_id` is the first event id that should be returned
|
/// Event cursor. `next_event_id` is the first event id that should be returned
|
||||||
/// by the next poll.
|
/// by the next poll.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct EventCursor {
|
pub struct EventCursor {
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub next_event_id: u64,
|
pub next_event_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,7 +12,6 @@ pub struct EventCursor {
|
||||||
/// poll-only so HTTP/WS/SSE dependencies are not pulled into this crate.
|
/// poll-only so HTTP/WS/SSE dependencies are not pulled into this crate.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct EventSubscription {
|
pub struct EventSubscription {
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub cursor: EventCursor,
|
pub cursor: EventCursor,
|
||||||
pub mode: EventSubscriptionMode,
|
pub mode: EventSubscriptionMode,
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +46,6 @@ pub enum RuntimeEventKind {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct RuntimeEventBatch {
|
pub struct RuntimeEventBatch {
|
||||||
pub runtime_id: RuntimeId,
|
|
||||||
pub cursor: EventCursor,
|
pub cursor: EventCursor,
|
||||||
pub events: Vec<RuntimeEvent>,
|
pub events: Vec<RuntimeEvent>,
|
||||||
pub has_more: bool,
|
pub has_more: bool,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::identity::{RuntimeId, WorkerId};
|
use crate::identity::WorkerId;
|
||||||
use crate::profile_archive::{ProfileSourceArchive, ProfileSourceArchiveRef, sha256_hex};
|
use crate::profile_archive::{ProfileSourceArchive, ProfileSourceArchiveRef, sha256_hex};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -179,13 +179,13 @@ impl ProfileSourceArchiveCache {
|
||||||
|
|
||||||
pub fn build_profile_source_archive_fetch_request(
|
pub fn build_profile_source_archive_fetch_request(
|
||||||
handle: BackendResourceHandle,
|
handle: BackendResourceHandle,
|
||||||
runtime_id: &RuntimeId,
|
runtime_id: &str,
|
||||||
worker_id: Option<&WorkerId>,
|
worker_id: Option<&WorkerId>,
|
||||||
) -> BackendResourceFetchRequest {
|
) -> BackendResourceFetchRequest {
|
||||||
let audit_correlation_id = handle.audit_correlation_id.clone();
|
let audit_correlation_id = handle.audit_correlation_id.clone();
|
||||||
BackendResourceFetchRequest {
|
BackendResourceFetchRequest {
|
||||||
handle,
|
handle,
|
||||||
runtime_id: runtime_id.as_str().to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: worker_id.map(|id| id.to_string()),
|
worker_id: worker_id.map(|id| id.to_string()),
|
||||||
audit_correlation_id,
|
audit_correlation_id,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use crate::execution::{
|
||||||
use crate::fs_store::{
|
use crate::fs_store::{
|
||||||
FsRuntimeStore, FsRuntimeStoreOptions, PersistedRuntimeState, PersistedWorkerRecord,
|
FsRuntimeStore, FsRuntimeStoreOptions, PersistedRuntimeState, PersistedWorkerRecord,
|
||||||
};
|
};
|
||||||
use crate::identity::{RuntimeId, WorkerId, WorkerRef};
|
use crate::identity::{WorkerId, WorkerRef};
|
||||||
use crate::interaction::{WorkerInput, WorkerInputKind, WorkerInteractionAck};
|
use crate::interaction::{WorkerInput, WorkerInputKind, WorkerInteractionAck};
|
||||||
use crate::management::{
|
use crate::management::{
|
||||||
RuntimeBackendKind, RuntimeLimits, RuntimeOptions, RuntimeStatus, RuntimeSummary,
|
RuntimeBackendKind, RuntimeLimits, RuntimeOptions, RuntimeStatus, RuntimeSummary,
|
||||||
|
|
@ -38,13 +38,10 @@ use crate::observation::{WorkerObservationCursor, WorkerObservationEvent};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
#[cfg(feature = "ws-server")]
|
#[cfg(feature = "ws-server")]
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
#[cfg(feature = "ws-server")]
|
#[cfg(feature = "ws-server")]
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
static NEXT_RUNTIME_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
|
||||||
|
|
||||||
/// Concrete embedded Runtime domain entity.
|
/// Concrete embedded Runtime domain entity.
|
||||||
///
|
///
|
||||||
/// The default implementation is memory-backed and tools/provider-less by
|
/// The default implementation is memory-backed and tools/provider-less by
|
||||||
|
|
@ -65,10 +62,7 @@ impl Runtime {
|
||||||
|
|
||||||
/// Create a memory-backed Runtime with explicit options.
|
/// Create a memory-backed Runtime with explicit options.
|
||||||
pub fn with_options(options: RuntimeOptions) -> Self {
|
pub fn with_options(options: RuntimeOptions) -> Self {
|
||||||
let runtime_id = options.runtime_id.unwrap_or_else(|| {
|
let mut state = RuntimeState::new(options.display_name, options.limits);
|
||||||
RuntimeId::generated(NEXT_RUNTIME_SEQUENCE.fetch_add(1, Ordering::Relaxed))
|
|
||||||
});
|
|
||||||
let mut state = RuntimeState::new(runtime_id, options.display_name, options.limits);
|
|
||||||
state.push_event(None, RuntimeEventKind::RuntimeStarted, "runtime started");
|
state.push_event(None, RuntimeEventKind::RuntimeStarted, "runtime started");
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Mutex::new(state)),
|
inner: Arc::new(Mutex::new(state)),
|
||||||
|
|
@ -87,10 +81,9 @@ impl Runtime {
|
||||||
|
|
||||||
/// Create or restore a filesystem-backed Runtime.
|
/// Create or restore a filesystem-backed Runtime.
|
||||||
///
|
///
|
||||||
/// The store is scoped by typed Runtime identity under `options.root`; if the
|
/// The store is scoped by `options.root`; if the directory already exists,
|
||||||
/// Runtime directory already exists, persisted state is loaded and validated.
|
/// persisted state is loaded and validated. If it does not exist, a fresh
|
||||||
/// If it does not exist, a fresh Runtime is initialized and durable files are
|
/// Runtime is initialized and durable files are created before return.
|
||||||
/// created before the Runtime is returned.
|
|
||||||
#[cfg(feature = "fs-store")]
|
#[cfg(feature = "fs-store")]
|
||||||
pub fn with_fs_store(options: FsRuntimeStoreOptions) -> Result<Self, RuntimeError> {
|
pub fn with_fs_store(options: FsRuntimeStoreOptions) -> Result<Self, RuntimeError> {
|
||||||
Self::with_fs_store_inner(options, None)
|
Self::with_fs_store_inner(options, None)
|
||||||
|
|
@ -110,19 +103,12 @@ impl Runtime {
|
||||||
options: FsRuntimeStoreOptions,
|
options: FsRuntimeStoreOptions,
|
||||||
execution_backend: Option<WorkerExecutionBackendRef>,
|
execution_backend: Option<WorkerExecutionBackendRef>,
|
||||||
) -> Result<Self, RuntimeError> {
|
) -> Result<Self, RuntimeError> {
|
||||||
let runtime_id = options.runtime_id.unwrap_or_else(|| {
|
let opened = FsRuntimeStore::open_or_create(options.root)?;
|
||||||
RuntimeId::generated(NEXT_RUNTIME_SEQUENCE.fetch_add(1, Ordering::Relaxed))
|
|
||||||
});
|
|
||||||
let opened = FsRuntimeStore::open_or_create(options.root, runtime_id.clone())?;
|
|
||||||
let mut state = if let Some(persisted) = opened.state {
|
let mut state = if let Some(persisted) = opened.state {
|
||||||
RuntimeState::from_persisted(persisted, opened.store)?
|
RuntimeState::from_persisted(persisted, opened.store)?
|
||||||
} else {
|
} else {
|
||||||
let mut state = RuntimeState::new_fs_backed(
|
let mut state =
|
||||||
runtime_id,
|
RuntimeState::new_fs_backed(options.display_name, options.limits, opened.store);
|
||||||
options.display_name,
|
|
||||||
options.limits,
|
|
||||||
opened.store,
|
|
||||||
);
|
|
||||||
let event_id =
|
let event_id =
|
||||||
state.push_event(None, RuntimeEventKind::RuntimeStarted, "runtime started");
|
state.push_event(None, RuntimeEventKind::RuntimeStarted, "runtime started");
|
||||||
state.persist_runtime_snapshot()?;
|
state.persist_runtime_snapshot()?;
|
||||||
|
|
@ -137,11 +123,6 @@ impl Runtime {
|
||||||
Ok(runtime)
|
Ok(runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runtime id half of public Worker authority.
|
|
||||||
pub fn runtime_id(&self) -> Result<RuntimeId, RuntimeError> {
|
|
||||||
Ok(self.lock()?.runtime_id.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Management-plane summary.
|
/// Management-plane summary.
|
||||||
pub fn summary(&self) -> Result<RuntimeSummary, RuntimeError> {
|
pub fn summary(&self) -> Result<RuntimeSummary, RuntimeError> {
|
||||||
let state = self.lock()?;
|
let state = self.lock()?;
|
||||||
|
|
@ -157,7 +138,6 @@ impl Runtime {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RuntimeSummary {
|
Ok(RuntimeSummary {
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
display_name: state.display_name.clone(),
|
display_name: state.display_name.clone(),
|
||||||
backend: state.backend,
|
backend: state.backend,
|
||||||
status: state.status,
|
status: state.status,
|
||||||
|
|
@ -225,17 +205,12 @@ impl Runtime {
|
||||||
return Ok(state.last_event_id());
|
return Ok(state.last_event_id());
|
||||||
}
|
}
|
||||||
state.status = RuntimeStatus::Stopped;
|
state.status = RuntimeStatus::Stopped;
|
||||||
let runtime_id = state.runtime_id.clone();
|
|
||||||
for worker in state.workers.values_mut() {
|
for worker in state.workers.values_mut() {
|
||||||
if worker.status.is_active() {
|
if worker.status.is_active() {
|
||||||
worker.status = WorkerStatus::Stopped;
|
worker.status = WorkerStatus::Stopped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let event_id = state.push_event(
|
let event_id = state.push_event(None, RuntimeEventKind::RuntimeStopped, "runtime stopped");
|
||||||
None,
|
|
||||||
RuntimeEventKind::RuntimeStopped,
|
|
||||||
format!("runtime {runtime_id} stopped"),
|
|
||||||
);
|
|
||||||
state.persist_runtime_snapshot()?;
|
state.persist_runtime_snapshot()?;
|
||||||
state.persist_workers()?;
|
state.persist_workers()?;
|
||||||
state.persist_event_by_id(event_id)?;
|
state.persist_event_by_id(event_id)?;
|
||||||
|
|
@ -369,7 +344,7 @@ impl Runtime {
|
||||||
|
|
||||||
let worker_id = WorkerId::generated(state.next_worker_sequence);
|
let worker_id = WorkerId::generated(state.next_worker_sequence);
|
||||||
state.next_worker_sequence += 1;
|
state.next_worker_sequence += 1;
|
||||||
let worker_ref = WorkerRef::new(state.runtime_id.clone(), worker_id.clone());
|
let worker_ref = WorkerRef::new(worker_id.clone());
|
||||||
let event_id = state.push_event(
|
let event_id = state.push_event(
|
||||||
Some(worker_ref.clone()),
|
Some(worker_ref.clone()),
|
||||||
RuntimeEventKind::WorkerCreated,
|
RuntimeEventKind::WorkerCreated,
|
||||||
|
|
@ -461,7 +436,7 @@ impl Runtime {
|
||||||
Ok(state
|
Ok(state
|
||||||
.workers
|
.workers
|
||||||
.values()
|
.values()
|
||||||
.map(|worker| worker.summary(&state.runtime_id))
|
.map(|worker| worker.summary())
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,7 +444,7 @@ impl Runtime {
|
||||||
pub fn worker_detail(&self, worker_ref: &WorkerRef) -> Result<WorkerDetail, RuntimeError> {
|
pub fn worker_detail(&self, worker_ref: &WorkerRef) -> Result<WorkerDetail, RuntimeError> {
|
||||||
let state = self.lock()?;
|
let state = self.lock()?;
|
||||||
let worker = state.worker(worker_ref)?;
|
let worker = state.worker(worker_ref)?;
|
||||||
Ok(worker.detail(&state.runtime_id))
|
Ok(worker.detail())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accept input into a Worker.
|
/// Accept input into a Worker.
|
||||||
|
|
@ -571,7 +546,6 @@ impl Runtime {
|
||||||
result: WorkerExecutionResult,
|
result: WorkerExecutionResult,
|
||||||
) -> Result<WorkerDetail, RuntimeError> {
|
) -> Result<WorkerDetail, RuntimeError> {
|
||||||
let mut state = self.lock()?;
|
let mut state = self.lock()?;
|
||||||
let runtime_id = state.runtime_id.clone();
|
|
||||||
let detail = {
|
let detail = {
|
||||||
let binding = WorkerExecutionBindingIdentity::from_handle(&handle);
|
let binding = WorkerExecutionBindingIdentity::from_handle(&handle);
|
||||||
let worker = state.worker_mut(worker_ref)?;
|
let worker = state.worker_mut(worker_ref)?;
|
||||||
|
|
@ -581,7 +555,7 @@ impl Runtime {
|
||||||
execution = execution.with_working_directory(status);
|
execution = execution.with_working_directory(status);
|
||||||
}
|
}
|
||||||
worker.execution = execution.with_result(result);
|
worker.execution = execution.with_result(result);
|
||||||
worker.detail(&runtime_id)
|
worker.detail()
|
||||||
};
|
};
|
||||||
state.persist_runtime_snapshot()?;
|
state.persist_runtime_snapshot()?;
|
||||||
state.persist_worker(&worker_ref.worker_id)?;
|
state.persist_worker(&worker_ref.worker_id)?;
|
||||||
|
|
@ -708,7 +682,6 @@ impl Runtime {
|
||||||
}
|
}
|
||||||
let removed = state.workers.remove(&worker_ref.worker_id).ok_or_else(|| {
|
let removed = state.workers.remove(&worker_ref.worker_id).ok_or_else(|| {
|
||||||
RuntimeError::WorkerNotFound {
|
RuntimeError::WorkerNotFound {
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
worker_id: worker_ref.worker_id,
|
worker_id: worker_ref.worker_id,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -725,7 +698,6 @@ impl Runtime {
|
||||||
state.delete_worker_snapshot(&worker_ref.worker_id)?;
|
state.delete_worker_snapshot(&worker_ref.worker_id)?;
|
||||||
state.persist_event_by_id(event_id)?;
|
state.persist_event_by_id(event_id)?;
|
||||||
Ok(WorkerDeleteResult {
|
Ok(WorkerDeleteResult {
|
||||||
runtime_id: removed.worker_ref.runtime_id,
|
|
||||||
worker_id: removed.worker_id,
|
worker_id: removed.worker_id,
|
||||||
deleted: true,
|
deleted: true,
|
||||||
})
|
})
|
||||||
|
|
@ -733,18 +705,13 @@ impl Runtime {
|
||||||
|
|
||||||
/// Cursor pointing to the beginning of Runtime events.
|
/// Cursor pointing to the beginning of Runtime events.
|
||||||
pub fn event_cursor_from_start(&self) -> Result<EventCursor, RuntimeError> {
|
pub fn event_cursor_from_start(&self) -> Result<EventCursor, RuntimeError> {
|
||||||
let state = self.lock()?;
|
Ok(EventCursor { next_event_id: 1 })
|
||||||
Ok(EventCursor {
|
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
next_event_id: 1,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cursor pointing after the current last event.
|
/// Cursor pointing after the current last event.
|
||||||
pub fn event_cursor_now(&self) -> Result<EventCursor, RuntimeError> {
|
pub fn event_cursor_now(&self) -> Result<EventCursor, RuntimeError> {
|
||||||
let state = self.lock()?;
|
let state = self.lock()?;
|
||||||
Ok(EventCursor {
|
Ok(EventCursor {
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
next_event_id: state.last_event_id() + 1,
|
next_event_id: state.last_event_id() + 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -756,12 +723,6 @@ impl Runtime {
|
||||||
limit: usize,
|
limit: usize,
|
||||||
) -> Result<RuntimeEventBatch, RuntimeError> {
|
) -> Result<RuntimeEventBatch, RuntimeError> {
|
||||||
let state = self.lock()?;
|
let state = self.lock()?;
|
||||||
if cursor.runtime_id != state.runtime_id {
|
|
||||||
return Err(RuntimeError::WrongRuntimeCursor {
|
|
||||||
expected_runtime_id: state.runtime_id.clone(),
|
|
||||||
actual_runtime_id: cursor.runtime_id.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if limit > state.limits.max_event_batch_items {
|
if limit > state.limits.max_event_batch_items {
|
||||||
return Err(RuntimeError::LimitTooLarge {
|
return Err(RuntimeError::LimitTooLarge {
|
||||||
requested: limit,
|
requested: limit,
|
||||||
|
|
@ -784,11 +745,7 @@ impl Runtime {
|
||||||
.unwrap_or(cursor.next_event_id);
|
.unwrap_or(cursor.next_event_id);
|
||||||
let has_more = state.events.iter().any(|event| event.id >= next_event_id);
|
let has_more = state.events.iter().any(|event| event.id >= next_event_id);
|
||||||
Ok(RuntimeEventBatch {
|
Ok(RuntimeEventBatch {
|
||||||
runtime_id: state.runtime_id.clone(),
|
cursor: EventCursor { next_event_id },
|
||||||
cursor: EventCursor {
|
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
next_event_id,
|
|
||||||
},
|
|
||||||
events,
|
events,
|
||||||
has_more,
|
has_more,
|
||||||
})
|
})
|
||||||
|
|
@ -796,15 +753,7 @@ impl Runtime {
|
||||||
|
|
||||||
/// Create a poll-only placeholder subscription boundary for future streaming.
|
/// Create a poll-only placeholder subscription boundary for future streaming.
|
||||||
pub fn subscribe_events(&self, cursor: EventCursor) -> Result<EventSubscription, RuntimeError> {
|
pub fn subscribe_events(&self, cursor: EventCursor) -> Result<EventSubscription, RuntimeError> {
|
||||||
let state = self.lock()?;
|
|
||||||
if cursor.runtime_id != state.runtime_id {
|
|
||||||
return Err(RuntimeError::WrongRuntimeCursor {
|
|
||||||
expected_runtime_id: state.runtime_id.clone(),
|
|
||||||
actual_runtime_id: cursor.runtime_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(EventSubscription {
|
Ok(EventSubscription {
|
||||||
runtime_id: state.runtime_id.clone(),
|
|
||||||
cursor,
|
cursor,
|
||||||
mode: EventSubscriptionMode::PollOnly,
|
mode: EventSubscriptionMode::PollOnly,
|
||||||
})
|
})
|
||||||
|
|
@ -1132,7 +1081,6 @@ enum RuntimePersistence {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RuntimeState {
|
struct RuntimeState {
|
||||||
runtime_id: RuntimeId,
|
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
backend: RuntimeBackendKind,
|
backend: RuntimeBackendKind,
|
||||||
#[cfg_attr(not(feature = "fs-store"), allow(dead_code))]
|
#[cfg_attr(not(feature = "fs-store"), allow(dead_code))]
|
||||||
|
|
@ -1157,9 +1105,8 @@ struct RuntimeState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeState {
|
impl RuntimeState {
|
||||||
fn new(runtime_id: RuntimeId, display_name: Option<String>, limits: RuntimeLimits) -> Self {
|
fn new(display_name: Option<String>, limits: RuntimeLimits) -> Self {
|
||||||
Self {
|
Self {
|
||||||
runtime_id,
|
|
||||||
display_name,
|
display_name,
|
||||||
backend: RuntimeBackendKind::Memory,
|
backend: RuntimeBackendKind::Memory,
|
||||||
persistence: RuntimePersistence::Memory,
|
persistence: RuntimePersistence::Memory,
|
||||||
|
|
@ -1185,13 +1132,11 @@ impl RuntimeState {
|
||||||
|
|
||||||
#[cfg(feature = "fs-store")]
|
#[cfg(feature = "fs-store")]
|
||||||
fn new_fs_backed(
|
fn new_fs_backed(
|
||||||
runtime_id: RuntimeId,
|
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
limits: RuntimeLimits,
|
limits: RuntimeLimits,
|
||||||
store: FsRuntimeStore,
|
store: FsRuntimeStore,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
runtime_id,
|
|
||||||
display_name,
|
display_name,
|
||||||
backend: RuntimeBackendKind::FsStore,
|
backend: RuntimeBackendKind::FsStore,
|
||||||
persistence: RuntimePersistence::Fs(store),
|
persistence: RuntimePersistence::Fs(store),
|
||||||
|
|
@ -1220,18 +1165,6 @@ impl RuntimeState {
|
||||||
persisted: PersistedRuntimeState,
|
persisted: PersistedRuntimeState,
|
||||||
store: FsRuntimeStore,
|
store: FsRuntimeStore,
|
||||||
) -> Result<Self, RuntimeError> {
|
) -> Result<Self, RuntimeError> {
|
||||||
if persisted.runtime_id != *store.runtime_id() {
|
|
||||||
return Err(RuntimeError::StoreCorrupt {
|
|
||||||
operation: "restore runtime state",
|
|
||||||
path: store.runtime_dir().to_path_buf(),
|
|
||||||
message: format!(
|
|
||||||
"persisted runtime id {} does not match store runtime {}",
|
|
||||||
persisted.runtime_id,
|
|
||||||
store.runtime_id()
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut workers = BTreeMap::new();
|
let mut workers = BTreeMap::new();
|
||||||
let mut diagnostics = persisted.diagnostics;
|
let mut diagnostics = persisted.diagnostics;
|
||||||
let mut next_diagnostic_id = persisted.next_diagnostic_id;
|
let mut next_diagnostic_id = persisted.next_diagnostic_id;
|
||||||
|
|
@ -1281,7 +1214,6 @@ impl RuntimeState {
|
||||||
let observation_events = persisted.observation_events.into_iter().collect();
|
let observation_events = persisted.observation_events.into_iter().collect();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
runtime_id: persisted.runtime_id,
|
|
||||||
display_name: persisted.display_name,
|
display_name: persisted.display_name,
|
||||||
backend: RuntimeBackendKind::FsStore,
|
backend: RuntimeBackendKind::FsStore,
|
||||||
persistence: RuntimePersistence::Fs(store),
|
persistence: RuntimePersistence::Fs(store),
|
||||||
|
|
@ -1307,7 +1239,6 @@ impl RuntimeState {
|
||||||
#[cfg(feature = "fs-store")]
|
#[cfg(feature = "fs-store")]
|
||||||
fn persisted_state(&self) -> PersistedRuntimeState {
|
fn persisted_state(&self) -> PersistedRuntimeState {
|
||||||
PersistedRuntimeState {
|
PersistedRuntimeState {
|
||||||
runtime_id: self.runtime_id.clone(),
|
|
||||||
display_name: self.display_name.clone(),
|
display_name: self.display_name.clone(),
|
||||||
status: self.status,
|
status: self.status,
|
||||||
limits: self.limits.clone(),
|
limits: self.limits.clone(),
|
||||||
|
|
@ -1350,8 +1281,7 @@ impl RuntimeState {
|
||||||
self.workers
|
self.workers
|
||||||
.get(worker_id)
|
.get(worker_id)
|
||||||
.ok_or_else(|| RuntimeError::WorkerNotFound {
|
.ok_or_else(|| RuntimeError::WorkerNotFound {
|
||||||
runtime_id: self.runtime_id.clone(),
|
worker_id: *worker_id,
|
||||||
worker_id: worker_id.clone(),
|
|
||||||
})?;
|
})?;
|
||||||
store.write_worker_snapshot(&worker.persisted_record())?;
|
store.write_worker_snapshot(&worker.persisted_record())?;
|
||||||
}
|
}
|
||||||
|
|
@ -1439,9 +1369,7 @@ impl RuntimeState {
|
||||||
|
|
||||||
fn ensure_running(&self) -> Result<(), RuntimeError> {
|
fn ensure_running(&self) -> Result<(), RuntimeError> {
|
||||||
if self.status == RuntimeStatus::Stopped {
|
if self.status == RuntimeStatus::Stopped {
|
||||||
Err(RuntimeError::RuntimeStopped {
|
Err(RuntimeError::RuntimeStopped)
|
||||||
runtime_id: self.runtime_id.clone(),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -1478,17 +1406,9 @@ impl RuntimeState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_worker_ref(&self, worker_ref: &WorkerRef) -> Result<(), RuntimeError> {
|
fn ensure_worker_ref(&self, worker_ref: &WorkerRef) -> Result<(), RuntimeError> {
|
||||||
if worker_ref.runtime_id != self.runtime_id {
|
|
||||||
return Err(RuntimeError::WrongRuntime {
|
|
||||||
expected_runtime_id: self.runtime_id.clone(),
|
|
||||||
actual_runtime_id: worker_ref.runtime_id.clone(),
|
|
||||||
worker_id: worker_ref.worker_id.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if !self.workers.contains_key(&worker_ref.worker_id) {
|
if !self.workers.contains_key(&worker_ref.worker_id) {
|
||||||
return Err(RuntimeError::WorkerNotFound {
|
return Err(RuntimeError::WorkerNotFound {
|
||||||
runtime_id: self.runtime_id.clone(),
|
worker_id: worker_ref.worker_id,
|
||||||
worker_id: worker_ref.worker_id.clone(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -1499,8 +1419,7 @@ impl RuntimeState {
|
||||||
self.workers
|
self.workers
|
||||||
.get(&worker_ref.worker_id)
|
.get(&worker_ref.worker_id)
|
||||||
.ok_or_else(|| RuntimeError::WorkerNotFound {
|
.ok_or_else(|| RuntimeError::WorkerNotFound {
|
||||||
runtime_id: self.runtime_id.clone(),
|
worker_id: worker_ref.worker_id,
|
||||||
worker_id: worker_ref.worker_id.clone(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1509,8 +1428,7 @@ impl RuntimeState {
|
||||||
self.workers
|
self.workers
|
||||||
.get_mut(&worker_ref.worker_id)
|
.get_mut(&worker_ref.worker_id)
|
||||||
.ok_or_else(|| RuntimeError::WorkerNotFound {
|
.ok_or_else(|| RuntimeError::WorkerNotFound {
|
||||||
runtime_id: self.runtime_id.clone(),
|
worker_id: worker_ref.worker_id,
|
||||||
worker_id: worker_ref.worker_id.clone(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1713,11 +1631,10 @@ struct WorkerRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerRecord {
|
impl WorkerRecord {
|
||||||
fn summary(&self, runtime_id: &RuntimeId) -> WorkerSummary {
|
fn summary(&self) -> WorkerSummary {
|
||||||
WorkerSummary {
|
WorkerSummary {
|
||||||
worker_ref: self.worker_ref.clone(),
|
worker_ref: self.worker_ref.clone(),
|
||||||
runtime_id: runtime_id.clone(),
|
worker_id: self.worker_id,
|
||||||
worker_id: self.worker_id.clone(),
|
|
||||||
status: self.status,
|
status: self.status,
|
||||||
execution: self.execution.clone(),
|
execution: self.execution.clone(),
|
||||||
profile: self.request.profile.clone(),
|
profile: self.request.profile.clone(),
|
||||||
|
|
@ -1727,11 +1644,10 @@ impl WorkerRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detail(&self, runtime_id: &RuntimeId) -> WorkerDetail {
|
fn detail(&self) -> WorkerDetail {
|
||||||
WorkerDetail {
|
WorkerDetail {
|
||||||
worker_ref: self.worker_ref.clone(),
|
worker_ref: self.worker_ref.clone(),
|
||||||
runtime_id: runtime_id.clone(),
|
worker_id: self.worker_id,
|
||||||
worker_id: self.worker_id.clone(),
|
|
||||||
status: self.status,
|
status: self.status,
|
||||||
execution: self.execution.clone(),
|
execution: self.execution.clone(),
|
||||||
profile: self.request.profile.clone(),
|
profile: self.request.profile.clone(),
|
||||||
|
|
@ -1841,6 +1757,7 @@ mod tests {
|
||||||
WorkerExecutionRestoreRequest, WorkerExecutionRunState,
|
WorkerExecutionRestoreRequest, WorkerExecutionRunState,
|
||||||
};
|
};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
fn task_request(_objective: &str) -> CreateWorkerRequest {
|
fn task_request(_objective: &str) -> CreateWorkerRequest {
|
||||||
|
|
@ -2034,11 +1951,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_list_and_detail_preserve_runtime_worker_authority() {
|
fn create_list_and_detail_preserve_runtime_local_worker_authority() {
|
||||||
let runtime = runtime_with_backend();
|
let runtime = runtime_with_backend();
|
||||||
let detail = runtime.create_worker(task_request("implement v0")).unwrap();
|
let detail = runtime.create_worker(task_request("implement v0")).unwrap();
|
||||||
|
|
||||||
assert_eq!(detail.worker_ref.runtime_id, runtime.runtime_id().unwrap());
|
|
||||||
assert_eq!(detail.status, WorkerStatus::Running);
|
assert_eq!(detail.status, WorkerStatus::Running);
|
||||||
assert_eq!(detail.config_bundle.as_ref().unwrap().id, "bundle-1");
|
assert_eq!(detail.config_bundle.as_ref().unwrap().id, "bundle-1");
|
||||||
|
|
||||||
|
|
@ -2106,16 +2022,6 @@ mod tests {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_worker_refs_from_another_runtime() {
|
|
||||||
let runtime_a = runtime_with_backend();
|
|
||||||
let runtime_b = runtime_with_backend();
|
|
||||||
let detail = runtime_a.create_worker(task_request("runtime a")).unwrap();
|
|
||||||
|
|
||||||
let err = runtime_b.worker_detail(&detail.worker_ref).unwrap_err();
|
|
||||||
assert!(matches!(err, RuntimeError::WrongRuntime { .. }));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_worker_rejects_system_initial_input_without_persisting_worker() {
|
fn create_worker_rejects_system_initial_input_without_persisting_worker() {
|
||||||
let runtime = runtime_with_backend();
|
let runtime = runtime_with_backend();
|
||||||
|
|
@ -2493,11 +2399,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn fs_store_restores_workers_events_and_protocol_observations() {
|
fn fs_store_restores_workers_events_and_protocol_observations() {
|
||||||
let root = fs_store_root("restore");
|
let root = fs_store_root("restore");
|
||||||
let runtime_id = RuntimeId::new("runtime-fs-authority").unwrap();
|
|
||||||
let runtime = Runtime::with_fs_store_and_execution_backend(
|
let runtime = Runtime::with_fs_store_and_execution_backend(
|
||||||
crate::fs_store::FsRuntimeStoreOptions {
|
crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
runtime_id: Some(runtime_id.clone()),
|
|
||||||
display_name: Some("filesystem runtime".to_string()),
|
display_name: Some("filesystem runtime".to_string()),
|
||||||
limits: RuntimeLimits {
|
limits: RuntimeLimits {
|
||||||
max_event_batch_items: 2,
|
max_event_batch_items: 2,
|
||||||
|
|
@ -2527,7 +2431,6 @@ mod tests {
|
||||||
|
|
||||||
let restored = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
let restored = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
runtime_id: Some(runtime_id.clone()),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
})
|
})
|
||||||
|
|
@ -2606,11 +2509,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn fs_store_restores_active_worker_execution_handles() {
|
fn fs_store_restores_active_worker_execution_handles() {
|
||||||
let root = fs_store_root("execution-restore");
|
let root = fs_store_root("execution-restore");
|
||||||
let runtime_id = RuntimeId::new("runtime-execution-restore").unwrap();
|
|
||||||
let runtime = Runtime::with_fs_store_and_execution_backend(
|
let runtime = Runtime::with_fs_store_and_execution_backend(
|
||||||
crate::fs_store::FsRuntimeStoreOptions {
|
crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
runtime_id: Some(runtime_id.clone()),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
},
|
},
|
||||||
|
|
@ -2627,7 +2528,6 @@ mod tests {
|
||||||
let restored = Runtime::with_fs_store_and_execution_backend(
|
let restored = Runtime::with_fs_store_and_execution_backend(
|
||||||
crate::fs_store::FsRuntimeStoreOptions {
|
crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
runtime_id: Some(runtime_id),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
},
|
},
|
||||||
|
|
@ -2666,11 +2566,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn fs_store_keeps_worker_stale_when_execution_restore_fails() {
|
fn fs_store_keeps_worker_stale_when_execution_restore_fails() {
|
||||||
let root = fs_store_root("execution-restore-failed");
|
let root = fs_store_root("execution-restore-failed");
|
||||||
let runtime_id = RuntimeId::new("runtime-execution-restore-failed").unwrap();
|
|
||||||
let runtime = Runtime::with_fs_store_and_execution_backend(
|
let runtime = Runtime::with_fs_store_and_execution_backend(
|
||||||
crate::fs_store::FsRuntimeStoreOptions {
|
crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
runtime_id: Some(runtime_id.clone()),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
},
|
},
|
||||||
|
|
@ -2691,7 +2589,6 @@ mod tests {
|
||||||
let restored = Runtime::with_fs_store_and_execution_backend(
|
let restored = Runtime::with_fs_store_and_execution_backend(
|
||||||
crate::fs_store::FsRuntimeStoreOptions {
|
crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
runtime_id: Some(runtime_id),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
},
|
},
|
||||||
|
|
@ -2734,10 +2631,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn fs_store_reports_corrupt_and_missing_data() {
|
fn fs_store_reports_corrupt_and_missing_data() {
|
||||||
let corrupt_root = fs_store_root("corrupt");
|
let corrupt_root = fs_store_root("corrupt");
|
||||||
let corrupt_runtime_id = RuntimeId::new("runtime-corrupt").unwrap();
|
|
||||||
let corrupt_runtime = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
let corrupt_runtime = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: corrupt_root.clone(),
|
root: corrupt_root.clone(),
|
||||||
runtime_id: Some(corrupt_runtime_id.clone()),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
})
|
})
|
||||||
|
|
@ -2751,7 +2646,6 @@ mod tests {
|
||||||
drop(corrupt_runtime);
|
drop(corrupt_runtime);
|
||||||
let err = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
let err = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: corrupt_root.clone(),
|
root: corrupt_root.clone(),
|
||||||
runtime_id: Some(corrupt_runtime_id),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
})
|
})
|
||||||
|
|
@ -2760,11 +2654,9 @@ mod tests {
|
||||||
let _ = std::fs::remove_dir_all(corrupt_root);
|
let _ = std::fs::remove_dir_all(corrupt_root);
|
||||||
|
|
||||||
let missing_root = fs_store_root("missing");
|
let missing_root = fs_store_root("missing");
|
||||||
let missing_runtime_id = RuntimeId::new("runtime-missing").unwrap();
|
|
||||||
let missing_runtime = Runtime::with_fs_store_and_execution_backend(
|
let missing_runtime = Runtime::with_fs_store_and_execution_backend(
|
||||||
crate::fs_store::FsRuntimeStoreOptions {
|
crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: missing_root.clone(),
|
root: missing_root.clone(),
|
||||||
runtime_id: Some(missing_runtime_id.clone()),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
},
|
},
|
||||||
|
|
@ -2785,7 +2677,6 @@ mod tests {
|
||||||
drop(missing_runtime);
|
drop(missing_runtime);
|
||||||
let loaded = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
let loaded = Runtime::with_fs_store(crate::fs_store::FsRuntimeStoreOptions {
|
||||||
root: missing_root.clone(),
|
root: missing_root.clone(),
|
||||||
runtime_id: Some(missing_runtime_id),
|
|
||||||
display_name: None,
|
display_name: None,
|
||||||
limits: RuntimeLimits::default(),
|
limits: RuntimeLimits::default(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -147,11 +147,7 @@ impl ProfileRuntimeWorkerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_worker_name_for_ref(worker_ref: &crate::identity::WorkerRef) -> String {
|
fn runtime_worker_name_for_ref(worker_ref: &crate::identity::WorkerRef) -> String {
|
||||||
format!(
|
format!("worker-runtime-{}", worker_ref.worker_id)
|
||||||
"runtime-{}-{}",
|
|
||||||
sanitize_worker_name_component(worker_ref.runtime_id.as_str()),
|
|
||||||
worker_ref.worker_id
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_worker_name(request: &WorkerExecutionSpawnRequest) -> String {
|
fn runtime_worker_name(request: &WorkerExecutionSpawnRequest) -> String {
|
||||||
|
|
@ -238,19 +234,6 @@ impl ProfileRuntimeWorkerFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sanitize_worker_name_component(value: &str) -> String {
|
|
||||||
value
|
|
||||||
.chars()
|
|
||||||
.map(|ch| {
|
|
||||||
if ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_') {
|
|
||||||
ch
|
|
||||||
} else {
|
|
||||||
'-'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum RuntimeWorkspaceBackendRef {
|
enum RuntimeWorkspaceBackendRef {
|
||||||
None,
|
None,
|
||||||
|
|
@ -1110,7 +1093,6 @@ mod tests {
|
||||||
RepositorySelector, WorkingDirectoryRepository, WorkingDirectoryRequest,
|
RepositorySelector, WorkingDirectoryRepository, WorkingDirectoryRequest,
|
||||||
};
|
};
|
||||||
use crate::execution::WorkerExecutionContext;
|
use crate::execution::WorkerExecutionContext;
|
||||||
use crate::identity::RuntimeId;
|
|
||||||
use crate::management::RuntimeOptions;
|
use crate::management::RuntimeOptions;
|
||||||
use crate::observation::WorkerObservationCursor;
|
use crate::observation::WorkerObservationCursor;
|
||||||
use crate::working_directory::LocalGitWorktreeMaterializer;
|
use crate::working_directory::LocalGitWorktreeMaterializer;
|
||||||
|
|
@ -1372,10 +1354,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn runtime_worker_name_is_namespaced_by_runtime_id() {
|
fn runtime_worker_name_is_runtime_local() {
|
||||||
let runtime_id = RuntimeId::new("arc:remote".to_string()).unwrap();
|
let worker_ref = crate::identity::WorkerRef::new(crate::identity::WorkerId::new(1));
|
||||||
let worker_ref =
|
|
||||||
crate::identity::WorkerRef::new(runtime_id, crate::identity::WorkerId::new(1));
|
|
||||||
let request = WorkerExecutionSpawnRequest {
|
let request = WorkerExecutionSpawnRequest {
|
||||||
worker_ref: worker_ref.clone(),
|
worker_ref: worker_ref.clone(),
|
||||||
request: create_request("1"),
|
request: create_request("1"),
|
||||||
|
|
@ -1386,7 +1366,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ProfileRuntimeWorkerFactory::runtime_worker_name(&request),
|
ProfileRuntimeWorkerFactory::runtime_worker_name(&request),
|
||||||
"runtime-arc-remote-1"
|
"worker-runtime-1"
|
||||||
);
|
);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
ProfileRuntimeWorkerFactory::runtime_worker_name(&request),
|
ProfileRuntimeWorkerFactory::runtime_worker_name(&request),
|
||||||
|
|
@ -1441,13 +1421,8 @@ mod tests {
|
||||||
observed_cwds: observed_cwds.clone(),
|
observed_cwds: observed_cwds.clone(),
|
||||||
};
|
};
|
||||||
let backend = WorkerRuntimeExecutionBackend::new(factory).unwrap();
|
let backend = WorkerRuntimeExecutionBackend::new(factory).unwrap();
|
||||||
let runtime = EmbeddedRuntime::with_execution_backend(
|
let runtime =
|
||||||
RuntimeOptions {
|
EmbeddedRuntime::with_execution_backend(RuntimeOptions::default(), Arc::new(backend))
|
||||||
runtime_id: RuntimeId::new("embedded"),
|
|
||||||
..RuntimeOptions::default()
|
|
||||||
},
|
|
||||||
Arc::new(backend),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
runtime.store_config_bundle(test_bundle()).unwrap();
|
runtime.store_config_bundle(test_bundle()).unwrap();
|
||||||
let detail = runtime.create_worker(create_request("chat")).unwrap();
|
let detail = runtime.create_worker(create_request("chat")).unwrap();
|
||||||
|
|
@ -1515,13 +1490,8 @@ mod tests {
|
||||||
.with_working_directory_materializer(LocalGitWorktreeMaterializer::new(
|
.with_working_directory_materializer(LocalGitWorktreeMaterializer::new(
|
||||||
runtime_base.path(),
|
runtime_base.path(),
|
||||||
));
|
));
|
||||||
let runtime = EmbeddedRuntime::with_execution_backend(
|
let runtime =
|
||||||
RuntimeOptions {
|
EmbeddedRuntime::with_execution_backend(RuntimeOptions::default(), Arc::new(backend))
|
||||||
runtime_id: RuntimeId::new("embedded-materialized"),
|
|
||||||
..RuntimeOptions::default()
|
|
||||||
},
|
|
||||||
Arc::new(backend),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
runtime.store_config_bundle(test_bundle()).unwrap();
|
runtime.store_config_bundle(test_bundle()).unwrap();
|
||||||
let mut request = create_request("chat");
|
let mut request = create_request("chat");
|
||||||
|
|
|
||||||
|
|
@ -745,7 +745,7 @@ fn validate_relative_cwd(
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::catalog::{RepositorySelector, WorkingDirectoryRepository};
|
use crate::catalog::{RepositorySelector, WorkingDirectoryRepository};
|
||||||
use crate::identity::{RuntimeId, WorkerId};
|
use crate::identity::{WorkerId, WorkerRef};
|
||||||
|
|
||||||
fn git(path: &Path, args: &[&str]) {
|
fn git(path: &Path, args: &[&str]) {
|
||||||
let status = Command::new("git")
|
let status = Command::new("git")
|
||||||
|
|
@ -786,10 +786,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_ref(sequence: u64) -> WorkerRef {
|
fn worker_ref(sequence: u64) -> WorkerRef {
|
||||||
WorkerRef::new(
|
WorkerRef::new(WorkerId::generated(sequence))
|
||||||
RuntimeId::new("runtime-test").unwrap(),
|
|
||||||
WorkerId::generated(sequence),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,7 @@ use worker_runtime::http_server::{
|
||||||
RuntimeHttpWorkerLifecycleResponse, RuntimeHttpWorkerResponse, RuntimeHttpWorkersResponse,
|
RuntimeHttpWorkerLifecycleResponse, RuntimeHttpWorkerResponse, RuntimeHttpWorkersResponse,
|
||||||
RuntimeHttpWorkingDirectoriesResponse, RuntimeHttpWorkingDirectoryResponse,
|
RuntimeHttpWorkingDirectoriesResponse, RuntimeHttpWorkingDirectoryResponse,
|
||||||
};
|
};
|
||||||
use worker_runtime::identity::{
|
use worker_runtime::identity::{WorkerId as EmbeddedWorkerId, WorkerRef as EmbeddedWorkerRef};
|
||||||
RuntimeId as EmbeddedRuntimeId, WorkerId as EmbeddedWorkerId, WorkerRef as EmbeddedWorkerRef,
|
|
||||||
};
|
|
||||||
use worker_runtime::interaction::{
|
use worker_runtime::interaction::{
|
||||||
WorkerInput as EmbeddedWorkerInput, WorkerInputKind as EmbeddedWorkerInputKind,
|
WorkerInput as EmbeddedWorkerInput, WorkerInputKind as EmbeddedWorkerInputKind,
|
||||||
};
|
};
|
||||||
|
|
@ -1128,10 +1126,7 @@ pub struct EmbeddedWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn embedded_runtime_options() -> EmbeddedRuntimeOptions {
|
fn embedded_runtime_options() -> EmbeddedRuntimeOptions {
|
||||||
let runtime_id = EmbeddedRuntimeId::new(EMBEDDED_RUNTIME_ID)
|
|
||||||
.expect("embedded runtime id is a non-empty literal");
|
|
||||||
EmbeddedRuntimeOptions {
|
EmbeddedRuntimeOptions {
|
||||||
runtime_id: Some(runtime_id),
|
|
||||||
display_name: Some("embedded".to_string()),
|
display_name: Some("embedded".to_string()),
|
||||||
..EmbeddedRuntimeOptions::default()
|
..EmbeddedRuntimeOptions::default()
|
||||||
}
|
}
|
||||||
|
|
@ -1159,12 +1154,9 @@ impl EmbeddedWorkerRuntime {
|
||||||
store_root: impl Into<PathBuf>,
|
store_root: impl Into<PathBuf>,
|
||||||
backend: std::sync::Arc<dyn worker_runtime::execution::WorkerExecutionBackend>,
|
backend: std::sync::Arc<dyn worker_runtime::execution::WorkerExecutionBackend>,
|
||||||
) -> Result<Self, worker_runtime::error::RuntimeError> {
|
) -> Result<Self, worker_runtime::error::RuntimeError> {
|
||||||
let runtime_id = EmbeddedRuntimeId::new(EMBEDDED_RUNTIME_ID)
|
|
||||||
.expect("embedded runtime id is a non-empty literal");
|
|
||||||
let runtime = worker_runtime::Runtime::with_fs_store_and_execution_backend(
|
let runtime = worker_runtime::Runtime::with_fs_store_and_execution_backend(
|
||||||
FsRuntimeStoreOptions {
|
FsRuntimeStoreOptions {
|
||||||
root: store_root.into(),
|
root: store_root.into(),
|
||||||
runtime_id: Some(runtime_id),
|
|
||||||
display_name: Some("embedded".to_string()),
|
display_name: Some("embedded".to_string()),
|
||||||
limits: EmbeddedRuntimeOptions::default().limits,
|
limits: EmbeddedRuntimeOptions::default().limits,
|
||||||
},
|
},
|
||||||
|
|
@ -1181,13 +1173,8 @@ impl EmbeddedWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_runtime(workspace_id: impl AsRef<str>, runtime: worker_runtime::Runtime) -> Self {
|
pub fn from_runtime(workspace_id: impl AsRef<str>, runtime: worker_runtime::Runtime) -> Self {
|
||||||
let runtime_id = runtime
|
|
||||||
.runtime_id()
|
|
||||||
.ok()
|
|
||||||
.map(|id| id.as_str().to_string())
|
|
||||||
.unwrap_or_else(|| EMBEDDED_RUNTIME_ID.to_string());
|
|
||||||
Self {
|
Self {
|
||||||
runtime_id,
|
runtime_id: EMBEDDED_RUNTIME_ID.to_string(),
|
||||||
host_id: host_id_for_embedded_workspace(workspace_id.as_ref()),
|
host_id: host_id_for_embedded_workspace(workspace_id.as_ref()),
|
||||||
runtime,
|
runtime,
|
||||||
execution_enabled: false,
|
execution_enabled: false,
|
||||||
|
|
@ -1196,10 +1183,7 @@ impl EmbeddedWorkerRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_ref(&self, worker_id: &str) -> Option<EmbeddedWorkerRef> {
|
fn worker_ref(&self, worker_id: &str) -> Option<EmbeddedWorkerRef> {
|
||||||
Some(EmbeddedWorkerRef::new(
|
Some(EmbeddedWorkerRef::new(EmbeddedWorkerId::parse(worker_id)?))
|
||||||
EmbeddedRuntimeId::new(self.runtime_id.clone())?,
|
|
||||||
EmbeddedWorkerId::parse(worker_id)?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_stop_embedded_worker(
|
fn can_stop_embedded_worker(
|
||||||
|
|
@ -1704,7 +1688,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime {
|
||||||
match self.runtime.delete_worker(&worker_ref) {
|
match self.runtime.delete_worker(&worker_ref) {
|
||||||
Ok(result) => WorkerDeleteResult {
|
Ok(result) => WorkerDeleteResult {
|
||||||
state: WorkerOperationState::Accepted,
|
state: WorkerOperationState::Accepted,
|
||||||
runtime_id: result.runtime_id.to_string(),
|
runtime_id: self.runtime_id.clone(),
|
||||||
worker_id: result.worker_id.to_string(),
|
worker_id: result.worker_id.to_string(),
|
||||||
deleted: result.deleted,
|
deleted: result.deleted,
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
|
|
@ -2256,11 +2240,10 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime {
|
||||||
.profile
|
.profile
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| embedded_profile_selector(&request.intent));
|
.unwrap_or_else(|| embedded_profile_selector(&request.intent));
|
||||||
let runtime_id = EmbeddedRuntimeId::new(self.runtime_id.clone());
|
|
||||||
let profile_source = match default_profile_source_archive_http_source(
|
let profile_source = match default_profile_source_archive_http_source(
|
||||||
&profile,
|
&profile,
|
||||||
&self.workspace_id,
|
&self.workspace_id,
|
||||||
runtime_id.as_ref(),
|
Some(self.runtime_id.as_str()),
|
||||||
&self.resource_broker,
|
&self.resource_broker,
|
||||||
&self.backend_base_url,
|
&self.backend_base_url,
|
||||||
) {
|
) {
|
||||||
|
|
@ -2380,7 +2363,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime {
|
||||||
{
|
{
|
||||||
Ok(response) => WorkerDeleteResult {
|
Ok(response) => WorkerDeleteResult {
|
||||||
state: WorkerOperationState::Accepted,
|
state: WorkerOperationState::Accepted,
|
||||||
runtime_id: response.worker.runtime_id.to_string(),
|
runtime_id: self.runtime_id.clone(),
|
||||||
worker_id: response.worker.worker_id.to_string(),
|
worker_id: response.worker.worker_id.to_string(),
|
||||||
deleted: response.worker.deleted,
|
deleted: response.worker.deleted,
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
|
|
@ -2598,7 +2581,7 @@ fn default_profile_source_archive_source(
|
||||||
fn default_profile_source_archive_http_source(
|
fn default_profile_source_archive_http_source(
|
||||||
profile: &ProfileSelector,
|
profile: &ProfileSelector,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
runtime_id: Option<&EmbeddedRuntimeId>,
|
runtime_id: Option<&str>,
|
||||||
resource_broker: &BackendResourceBroker,
|
resource_broker: &BackendResourceBroker,
|
||||||
backend_base_url: &str,
|
backend_base_url: &str,
|
||||||
) -> Result<ProfileSourceArchiveSource, String> {
|
) -> Result<ProfileSourceArchiveSource, String> {
|
||||||
|
|
@ -2636,7 +2619,7 @@ enum ProfileSourceArchiveTransport {
|
||||||
fn default_embedded_config_bundle(
|
fn default_embedded_config_bundle(
|
||||||
profile: &ProfileSelector,
|
profile: &ProfileSelector,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
runtime_id: Option<&EmbeddedRuntimeId>,
|
runtime_id: Option<&str>,
|
||||||
resource_broker: &BackendResourceBroker,
|
resource_broker: &BackendResourceBroker,
|
||||||
archive_transport: ProfileSourceArchiveTransport,
|
archive_transport: ProfileSourceArchiveTransport,
|
||||||
) -> Result<ConfigBundle, String> {
|
) -> Result<ConfigBundle, String> {
|
||||||
|
|
@ -2844,17 +2827,11 @@ fn remote_lifecycle_rejected(
|
||||||
|
|
||||||
fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnostic {
|
fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnostic {
|
||||||
match error {
|
match error {
|
||||||
EmbeddedRuntimeError::RuntimeStopped { .. } => diagnostic(
|
EmbeddedRuntimeError::RuntimeStopped => diagnostic(
|
||||||
"embedded_runtime_stopped",
|
"embedded_runtime_stopped",
|
||||||
DiagnosticSeverity::Warning,
|
DiagnosticSeverity::Warning,
|
||||||
"Embedded Runtime is stopped".to_string(),
|
"Embedded Runtime is stopped".to_string(),
|
||||||
),
|
),
|
||||||
EmbeddedRuntimeError::WrongRuntime { .. }
|
|
||||||
| EmbeddedRuntimeError::WrongRuntimeCursor { .. } => diagnostic(
|
|
||||||
"embedded_runtime_wrong_identity",
|
|
||||||
DiagnosticSeverity::Warning,
|
|
||||||
"Embedded Runtime rejected a worker/runtime identity mismatch".to_string(),
|
|
||||||
),
|
|
||||||
EmbeddedRuntimeError::WorkerNotFound { .. } => diagnostic(
|
EmbeddedRuntimeError::WorkerNotFound { .. } => diagnostic(
|
||||||
"embedded_worker_not_found",
|
"embedded_worker_not_found",
|
||||||
DiagnosticSeverity::Warning,
|
DiagnosticSeverity::Warning,
|
||||||
|
|
@ -3218,7 +3195,7 @@ mod tests {
|
||||||
fn embedded_builtin_decodal_profiles_resolve_through_archive() {
|
fn embedded_builtin_decodal_profiles_resolve_through_archive() {
|
||||||
let root = tempfile::tempdir().unwrap();
|
let root = tempfile::tempdir().unwrap();
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = EmbeddedRuntimeId::new("runtime-test".to_string()).unwrap();
|
let runtime_id = "runtime-test";
|
||||||
for selector in [
|
for selector in [
|
||||||
ProfileSelector::RuntimeDefault,
|
ProfileSelector::RuntimeDefault,
|
||||||
ProfileSelector::Builtin("builtin:companion".to_string()),
|
ProfileSelector::Builtin("builtin:companion".to_string()),
|
||||||
|
|
@ -3230,7 +3207,7 @@ mod tests {
|
||||||
let bundle = default_embedded_config_bundle(
|
let bundle = default_embedded_config_bundle(
|
||||||
&selector,
|
&selector,
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
&broker,
|
&broker,
|
||||||
ProfileSourceArchiveTransport::BackendResourceHandle,
|
ProfileSourceArchiveTransport::BackendResourceHandle,
|
||||||
)
|
)
|
||||||
|
|
@ -3241,7 +3218,7 @@ mod tests {
|
||||||
.fetch_profile_source_archive(
|
.fetch_profile_source_archive(
|
||||||
worker_runtime::resource::BackendResourceFetchRequest {
|
worker_runtime::resource::BackendResourceFetchRequest {
|
||||||
handle: handle.clone(),
|
handle: handle.clone(),
|
||||||
runtime_id: runtime_id.as_str().to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: None,
|
worker_id: None,
|
||||||
audit_correlation_id: handle.audit_correlation_id.clone(),
|
audit_correlation_id: handle.audit_correlation_id.clone(),
|
||||||
},
|
},
|
||||||
|
|
@ -3269,11 +3246,11 @@ mod tests {
|
||||||
fn remote_default_bundle_inlines_profile_archive_for_standalone_runtime() {
|
fn remote_default_bundle_inlines_profile_archive_for_standalone_runtime() {
|
||||||
let root = tempfile::tempdir().unwrap();
|
let root = tempfile::tempdir().unwrap();
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = EmbeddedRuntimeId::new("remote:test".to_string()).unwrap();
|
let runtime_id = "remote:test";
|
||||||
let bundle = default_embedded_config_bundle(
|
let bundle = default_embedded_config_bundle(
|
||||||
&ProfileSelector::Builtin("builtin:coder".to_string()),
|
&ProfileSelector::Builtin("builtin:coder".to_string()),
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
&broker,
|
&broker,
|
||||||
ProfileSourceArchiveTransport::Inline,
|
ProfileSourceArchiveTransport::Inline,
|
||||||
)
|
)
|
||||||
|
|
@ -3294,11 +3271,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn remote_profile_source_archive_url_uses_workspace_id_not_host_id() {
|
fn remote_profile_source_archive_url_uses_workspace_id_not_host_id() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = EmbeddedRuntimeId::new("remote:test".to_string()).unwrap();
|
let runtime_id = "remote:test";
|
||||||
let source = default_profile_source_archive_http_source(
|
let source = default_profile_source_archive_http_source(
|
||||||
&ProfileSelector::Builtin("builtin:coder".to_string()),
|
&ProfileSelector::Builtin("builtin:coder".to_string()),
|
||||||
"workspace-actual",
|
"workspace-actual",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
&broker,
|
&broker,
|
||||||
"http://127.0.0.1:8787/",
|
"http://127.0.0.1:8787/",
|
||||||
)
|
)
|
||||||
|
|
@ -3319,12 +3296,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn embedded_archive_rejects_unknown_selectors() {
|
fn embedded_archive_rejects_unknown_selectors() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = EmbeddedRuntimeId::new("runtime-test".to_string()).unwrap();
|
let runtime_id = "runtime-test";
|
||||||
assert!(
|
assert!(
|
||||||
default_embedded_config_bundle(
|
default_embedded_config_bundle(
|
||||||
&ProfileSelector::Builtin("builtin:missing".to_string()),
|
&ProfileSelector::Builtin("builtin:missing".to_string()),
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
&broker,
|
&broker,
|
||||||
ProfileSourceArchiveTransport::BackendResourceHandle,
|
ProfileSourceArchiveTransport::BackendResourceHandle,
|
||||||
)
|
)
|
||||||
|
|
@ -3334,7 +3311,7 @@ mod tests {
|
||||||
default_embedded_config_bundle(
|
default_embedded_config_bundle(
|
||||||
&ProfileSelector::Named("custom".to_string()),
|
&ProfileSelector::Named("custom".to_string()),
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
&broker,
|
&broker,
|
||||||
ProfileSourceArchiveTransport::BackendResourceHandle,
|
ProfileSourceArchiveTransport::BackendResourceHandle,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use chrono::{Duration, Utc};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use worker_runtime::identity::{RuntimeId, WorkerId};
|
use worker_runtime::identity::WorkerId;
|
||||||
use worker_runtime::profile_archive::ProfileSourceArchive;
|
use worker_runtime::profile_archive::ProfileSourceArchive;
|
||||||
use worker_runtime::resource::{
|
use worker_runtime::resource::{
|
||||||
BackendResourceClient, BackendResourceError, BackendResourceFetchRequest,
|
BackendResourceClient, BackendResourceError, BackendResourceFetchRequest,
|
||||||
|
|
@ -29,7 +29,7 @@ impl BackendResourceBroker {
|
||||||
pub fn issue_profile_source_archive_handle(
|
pub fn issue_profile_source_archive_handle(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: impl Into<String>,
|
workspace_id: impl Into<String>,
|
||||||
runtime_id: Option<&RuntimeId>,
|
runtime_id: Option<&str>,
|
||||||
worker_id: Option<&WorkerId>,
|
worker_id: Option<&WorkerId>,
|
||||||
archive: ProfileSourceArchive,
|
archive: ProfileSourceArchive,
|
||||||
) -> BackendResourceHandle {
|
) -> BackendResourceHandle {
|
||||||
|
|
@ -41,7 +41,7 @@ impl BackendResourceBroker {
|
||||||
kind: BackendResourceKind::ProfileSourceArchive,
|
kind: BackendResourceKind::ProfileSourceArchive,
|
||||||
workspace_id: workspace_id.clone(),
|
workspace_id: workspace_id.clone(),
|
||||||
scope_id: Some("workspace-profile-source".to_string()),
|
scope_id: Some("workspace-profile-source".to_string()),
|
||||||
runtime_id: runtime_id.map(|id| id.as_str().to_string()),
|
runtime_id: runtime_id.map(|id| id.to_string()),
|
||||||
worker_id: worker_id.map(|id| id.to_string()),
|
worker_id: worker_id.map(|id| id.to_string()),
|
||||||
resource_id: archive.reference.id.clone(),
|
resource_id: archive.reference.id.clone(),
|
||||||
digest: archive.reference.digest.clone(),
|
digest: archive.reference.digest.clone(),
|
||||||
|
|
@ -57,7 +57,7 @@ impl BackendResourceBroker {
|
||||||
profile_source_graph: Some(archive.reference.source_graph.clone()),
|
profile_source_graph: Some(archive.reference.source_graph.clone()),
|
||||||
};
|
};
|
||||||
let stored = StoredResource {
|
let stored = StoredResource {
|
||||||
runtime_id: runtime_id.map(|id| id.as_str().to_string()),
|
runtime_id: runtime_id.map(|id| id.to_string()),
|
||||||
worker_id: worker_id.map(|id| id.to_string()),
|
worker_id: worker_id.map(|id| id.to_string()),
|
||||||
handle: handle.clone(),
|
handle: handle.clone(),
|
||||||
archive,
|
archive,
|
||||||
|
|
@ -167,7 +167,7 @@ fn verify_handle_shape(handle: &BackendResourceHandle) -> Result<(), BackendReso
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use worker_runtime::identity::{RuntimeId, WorkerId};
|
use worker_runtime::identity::WorkerId;
|
||||||
use worker_runtime::profile_archive::{
|
use worker_runtime::profile_archive::{
|
||||||
ProfileSourceArchive, ProfileSourceArchiveRef, ProfileSourceGraphSummary, sha256_hex,
|
ProfileSourceArchive, ProfileSourceArchiveRef, ProfileSourceGraphSummary, sha256_hex,
|
||||||
};
|
};
|
||||||
|
|
@ -214,13 +214,13 @@ mod tests {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
handle: BackendResourceHandle,
|
handle: BackendResourceHandle,
|
||||||
runtime_id: &RuntimeId,
|
runtime_id: &str,
|
||||||
worker_id: Option<&WorkerId>,
|
worker_id: Option<&WorkerId>,
|
||||||
) -> BackendResourceFetchRequest {
|
) -> BackendResourceFetchRequest {
|
||||||
BackendResourceFetchRequest {
|
BackendResourceFetchRequest {
|
||||||
audit_correlation_id: handle.audit_correlation_id.clone(),
|
audit_correlation_id: handle.audit_correlation_id.clone(),
|
||||||
handle,
|
handle,
|
||||||
runtime_id: runtime_id.as_str().to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: worker_id.map(|id| id.to_string()),
|
worker_id: worker_id.map(|id| id.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,17 +228,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn broker_issues_and_verifies_profile_source_archive_handles() {
|
fn broker_issues_and_verifies_profile_source_archive_handles() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = RuntimeId::new("runtime-test").unwrap();
|
let runtime_id = "runtime-test";
|
||||||
let handle = broker.issue_profile_source_archive_handle(
|
let handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
None,
|
None,
|
||||||
archive(),
|
archive(),
|
||||||
);
|
);
|
||||||
let response = broker
|
let response = broker
|
||||||
.fetch_profile_source_archive(BackendResourceFetchRequest {
|
.fetch_profile_source_archive(BackendResourceFetchRequest {
|
||||||
handle: handle.clone(),
|
handle: handle.clone(),
|
||||||
runtime_id: runtime_id.as_str().to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: None,
|
worker_id: None,
|
||||||
audit_correlation_id: handle.audit_correlation_id.clone(),
|
audit_correlation_id: handle.audit_correlation_id.clone(),
|
||||||
})
|
})
|
||||||
|
|
@ -250,19 +250,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn broker_rejects_runtime_mismatch() {
|
fn broker_rejects_runtime_mismatch() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_a = RuntimeId::new("runtime-a").unwrap();
|
let runtime_a = "runtime-a";
|
||||||
let handle = broker.issue_profile_source_archive_handle(
|
let handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_a),
|
Some(runtime_a),
|
||||||
None,
|
None,
|
||||||
archive(),
|
archive(),
|
||||||
);
|
);
|
||||||
let err = broker
|
let err = broker
|
||||||
.fetch_profile_source_archive(request(
|
.fetch_profile_source_archive(request(handle, "runtime-b", None))
|
||||||
handle,
|
|
||||||
&RuntimeId::new("runtime-b").unwrap(),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert!(matches!(err, BackendResourceError::Unauthorized { .. }));
|
assert!(matches!(err, BackendResourceError::Unauthorized { .. }));
|
||||||
}
|
}
|
||||||
|
|
@ -270,12 +266,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn broker_rejects_worker_mismatch() {
|
fn broker_rejects_worker_mismatch() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = RuntimeId::new("runtime-test").unwrap();
|
let runtime_id = "runtime-test";
|
||||||
let worker_a = WorkerId::new(1);
|
let worker_a = WorkerId::new(1);
|
||||||
let worker_b = WorkerId::new(2);
|
let worker_b = WorkerId::new(2);
|
||||||
let handle = broker.issue_profile_source_archive_handle(
|
let handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
Some(&worker_a),
|
Some(&worker_a),
|
||||||
archive(),
|
archive(),
|
||||||
);
|
);
|
||||||
|
|
@ -288,10 +284,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn broker_rejects_expiry_extension_from_request_handle() {
|
fn broker_rejects_expiry_extension_from_request_handle() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = RuntimeId::new("runtime-test").unwrap();
|
let runtime_id = "runtime-test";
|
||||||
let handle = broker.issue_profile_source_archive_handle(
|
let handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
None,
|
None,
|
||||||
archive(),
|
archive(),
|
||||||
);
|
);
|
||||||
|
|
@ -314,10 +310,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn broker_rejects_policy_tampered_request_handle() {
|
fn broker_rejects_policy_tampered_request_handle() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = RuntimeId::new("runtime-test").unwrap();
|
let runtime_id = "runtime-test";
|
||||||
let mut handle = broker.issue_profile_source_archive_handle(
|
let mut handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
None,
|
None,
|
||||||
archive(),
|
archive(),
|
||||||
);
|
);
|
||||||
|
|
@ -331,11 +327,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn broker_uses_stored_max_bytes_when_request_handle_is_tampered() {
|
fn broker_uses_stored_max_bytes_when_request_handle_is_tampered() {
|
||||||
let broker = BackendResourceBroker::default();
|
let broker = BackendResourceBroker::default();
|
||||||
let runtime_id = RuntimeId::new("runtime-test").unwrap();
|
let runtime_id = "runtime-test";
|
||||||
let archive = archive_with_len((DEFAULT_PROFILE_SOURCE_ARCHIVE_MAX_BYTES + 1) as usize);
|
let archive = archive_with_len((DEFAULT_PROFILE_SOURCE_ARCHIVE_MAX_BYTES + 1) as usize);
|
||||||
let mut handle = broker.issue_profile_source_archive_handle(
|
let mut handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
None,
|
None,
|
||||||
archive,
|
archive,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6003,10 +6003,10 @@ mod tests {
|
||||||
let api = test_api(workspace.path()).await;
|
let api = test_api(workspace.path()).await;
|
||||||
let broker = api.resource_broker.clone();
|
let broker = api.resource_broker.clone();
|
||||||
let archive = test_profile_archive();
|
let archive = test_profile_archive();
|
||||||
let runtime_id = worker_runtime::identity::RuntimeId::new("runtime-test").unwrap();
|
let runtime_id = "runtime-test";
|
||||||
let handle = broker.issue_profile_source_archive_handle(
|
let handle = broker.issue_profile_source_archive_handle(
|
||||||
"workspace-test",
|
"workspace-test",
|
||||||
Some(&runtime_id),
|
Some(runtime_id),
|
||||||
None,
|
None,
|
||||||
archive,
|
archive,
|
||||||
);
|
);
|
||||||
|
|
@ -6022,7 +6022,7 @@ mod tests {
|
||||||
let response = client
|
let response = client
|
||||||
.fetch_resource(worker_runtime::resource::BackendResourceFetchRequest {
|
.fetch_resource(worker_runtime::resource::BackendResourceFetchRequest {
|
||||||
audit_correlation_id: handle.audit_correlation_id.clone(),
|
audit_correlation_id: handle.audit_correlation_id.clone(),
|
||||||
runtime_id: runtime_id.as_str().to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: None,
|
worker_id: None,
|
||||||
handle: handle.clone(),
|
handle: handle.clone(),
|
||||||
})
|
})
|
||||||
|
|
@ -6035,7 +6035,7 @@ mod tests {
|
||||||
let error = client
|
let error = client
|
||||||
.fetch_resource(worker_runtime::resource::BackendResourceFetchRequest {
|
.fetch_resource(worker_runtime::resource::BackendResourceFetchRequest {
|
||||||
audit_correlation_id: tampered.audit_correlation_id.clone(),
|
audit_correlation_id: tampered.audit_correlation_id.clone(),
|
||||||
runtime_id: runtime_id.as_str().to_string(),
|
runtime_id: runtime_id.to_string(),
|
||||||
worker_id: None,
|
worker_id: None,
|
||||||
handle: tampered,
|
handle: tampered,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user