Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 103 additions & 97 deletions crates/libs/node/runtime/core/src/runtime_inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ struct ArtifactComponentInstallation {

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResolvedExecutionEnvironment {
/// Inventory-owned binding selected with these runtime files and components.
/// Inventory-owned source binding for authenticating these runtime files.
/// An unbound restore can use its verified runtime without asserting a
/// kernel identity that its historical snapshot never recorded.
pub environment: Option<EnvironmentReference>,
pub execution: ExecutionMetadata,
pub runtime_files: RuntimeInstallationFiles,
Expand Down Expand Up @@ -155,8 +157,8 @@ impl ResolvedExecutionEnvironment {
/// Artifacts verified for one explicit binding, separate from legacy discovery.
#[derive(Clone, Debug)]
struct InstalledEnvironment {
runtime: RuntimeInstallation,
components: Vec<ArtifactComponentInstallation>,
runtime: Result<RuntimeInstallation, std::sync::Arc<RuntimeInventoryError>>,
components: Result<Vec<ArtifactComponentInstallation>, std::sync::Arc<RuntimeInventoryError>>,
}

#[derive(Clone, Debug)]
Expand All @@ -167,10 +169,7 @@ pub struct RuntimeInventory {
/// layout's default for the bounded legacy execution path.
defaults: Vec<RuntimeIdentity>,
fingerprints: Vec<(PathBuf, PathFingerprint)>,
environments: BTreeMap<
EnvironmentReference,
Result<InstalledEnvironment, std::sync::Arc<RuntimeInventoryError>>,
>,
environments: BTreeMap<EnvironmentReference, InstalledEnvironment>,
#[cfg(feature = "artifact-store")]
unavailable: Vec<UnavailableArtifact>,
retry_after: Option<tokio::time::Instant>,
Expand Down Expand Up @@ -853,19 +852,19 @@ impl RuntimeInventory {
.iter()
.flat_map(|entry| entry.files.files.values().map(PathBuf::as_path))
.chain(self.components.iter().map(|entry| entry.path.as_path()))
.chain(
self.environments
.values()
.filter_map(|entry| entry.as_ref().ok())
.flat_map(|environment| {
environment.runtime.files.paths().chain(
environment
.components
.iter()
.map(|entry| entry.path.as_path()),
)
}),
)
.chain(self.environments.values().flat_map(|environment| {
environment
.runtime
.iter()
.flat_map(|runtime| runtime.files.paths())
.chain(
environment
.components
.iter()
.flatten()
.map(|entry| entry.path.as_path()),
)
}))
}

/// Blocking validation of root-controlled file metadata and, for external
Expand Down Expand Up @@ -1104,19 +1103,32 @@ impl RuntimeInventory {
) -> Result<ResolvedExecutionEnvironment, RuntimeInventoryError> {
let binding = requested
.and_then(|metadata| metadata.environment())
.and_then(|reference| self.environments.get(&reference))
.map(|entry| {
entry
.and_then(|reference| self.environments.get(&reference));
let runtimes = match binding {
Some(environment) => std::slice::from_ref(
environment
.runtime
.as_ref()
.map_err(|error| RuntimeInventoryError::UnavailableBinding(error.clone()))
})
.transpose()?;
let runtimes = binding
.map(|environment| std::slice::from_ref(&environment.runtime))
.unwrap_or(&self.runtimes);
let components = binding
.map(|environment| environment.components.as_slice())
.unwrap_or(&self.components);
.map_err(|error| RuntimeInventoryError::UnavailableBinding(error.clone()))?,
),
None => &self.runtimes,
};
// A snapshot already contains the running guest kernel. Its recorded
// identity remains provenance, not a dependency on a boot image file.
let restoring = mode == ExecutionSelectionMode::PersistedStateRestore;
let components = match binding {
Some(environment) if !restoring => environment
.components
.as_ref()
.map_err(|error| RuntimeInventoryError::UnavailableBinding(error.clone()))?
.as_slice(),
Some(environment) => environment
.components
.as_ref()
.map(Vec::as_slice)
.unwrap_or(&[]),
None => &self.components,
};
let find_runtime = |identity: &RuntimeIdentity| {
runtimes
.iter()
Expand Down Expand Up @@ -1179,77 +1191,58 @@ impl RuntimeInventory {
let mut selected_components = Vec::new();
if let Some(requested) = requested {
for component in requested.components() {
match components
if restoring && component.kind == ArtifactComponentKind::Kernel {
resolved_components.push(component.clone());
continue;
}
let installed = components
.iter()
.find(|installed| component_matches(&installed.metadata, component))
{
Some(installed) => resolved_components.push(installed.metadata.clone()),
None if mode == ExecutionSelectionMode::PersistedStateRestore
&& component.kind == ArtifactComponentKind::Kernel =>
{
tracing::warn!(
?component,
"requested restore kernel component is not installed; snapshot restore does not require the boot kernel"
);
resolved_components.push(component.clone());
}
None => {
return Err(RuntimeInventoryError::NoCompatibleComponent(
component.clone(),
));
}
}
.ok_or_else(|| {
RuntimeInventoryError::NoCompatibleComponent(component.clone())
})?;
resolved_components.push(installed.metadata.clone());
}
}
let boot_role = boot_component_role();
let requested_kernel = requested.and_then(|metadata| {
metadata.component(ArtifactComponentKind::Kernel, Some(&boot_role))
});
let kernel = if let Some(requested_kernel) = requested_kernel
&& mode != ExecutionSelectionMode::PersistedStateRestore
{
components
.iter()
.find(|installed| component_matches(&installed.metadata, requested_kernel))
} else if let Some(requested_kernel) = requested_kernel {
components
.iter()
.find(|installed| component_matches(&installed.metadata, requested_kernel))
.or_else(|| {
components.iter().find(|installed| {
installed.metadata.kind == ArtifactComponentKind::Kernel
&& installed.legacy_default
})
if !restoring {
let boot_role = boot_component_role();
let requested_kernel = requested.and_then(|metadata| {
metadata.component(ArtifactComponentKind::Kernel, Some(&boot_role))
});
let kernel = if let Some(requested_kernel) = requested_kernel {
components
.iter()
.find(|installed| component_matches(&installed.metadata, requested_kernel))
} else {
components.iter().find(|installed| {
installed.metadata.kind == ArtifactComponentKind::Kernel
&& if mode == ExecutionSelectionMode::NewColdBoot {
installed.default_for_new
} else {
installed.legacy_default
}
})
} else if mode != ExecutionSelectionMode::NewColdBoot {
components.iter().find(|installed| {
installed.metadata.kind == ArtifactComponentKind::Kernel && installed.legacy_default
})
} else {
components.iter().find(|installed| {
installed.metadata.kind == ArtifactComponentKind::Kernel
&& installed.default_for_new
})
}
.ok_or_else(|| match requested_kernel {
Some(component) => RuntimeInventoryError::NoCompatibleComponent(component.clone()),
None => RuntimeInventoryError::NoDefaultComponent {
kind: ArtifactComponentKind::Kernel,
selection: if mode != ExecutionSelectionMode::NewColdBoot {
ComponentDefaultSelection::LegacyArtifact
} else {
ComponentDefaultSelection::NewArtifact
}
.ok_or_else(|| match requested_kernel {
Some(component) => RuntimeInventoryError::NoCompatibleComponent(component.clone()),
None => RuntimeInventoryError::NoDefaultComponent {
kind: ArtifactComponentKind::Kernel,
selection: if mode == ExecutionSelectionMode::NewColdBoot {
ComponentDefaultSelection::NewArtifact
} else {
ComponentDefaultSelection::LegacyArtifact
},
},
},
})?;
if requested_kernel.is_none() {
resolved_components.push(kernel.metadata.clone());
})?;
if requested_kernel.is_none() {
resolved_components.push(kernel.metadata.clone());
}
selected_components.push(ResolvedArtifactComponent {
metadata: kernel.metadata.clone(),
path: kernel.path.clone(),
integrity: kernel.integrity,
});
}
selected_components.push(ResolvedArtifactComponent {
metadata: kernel.metadata.clone(),
path: kernel.path.clone(),
integrity: kernel.integrity,
});
let execution = ExecutionMetadata::new(
selected_runtime.runtime.clone(),
selected_runtime.guest,
Expand All @@ -1261,7 +1254,20 @@ impl RuntimeInventory {
} else {
self.initial_environment
.as_ref()
.filter(|definition| definition.check_execution(&execution).is_ok())
.filter(|definition| {
if definition.check_execution(&execution).is_ok() {
return true;
}
// Restore does not consume boot inputs. Authenticate the
// initial runtime independently without inventing kernel
// provenance in the returned ExecutionMetadata.
let spec = definition.descriptor().spec();
let pplx_space_common_core::types::RuntimeBinding::ManagedRelease(release) =
&spec.runtime;
restoring
&& execution.runtime() == &release.runtime
&& execution.guest() == spec.guest
})
.map(|definition| definition.reference())
};
Ok(ResolvedExecutionEnvironment {
Expand Down
14 changes: 5 additions & 9 deletions crates/libs/node/runtime/core/src/runtime_inventory/managed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ impl RuntimeInventory {
Ok(inventory)
}

/// Include a complete pinned environment; never change new/legacy defaults.
/// Include pinned artifacts independently; never change new/legacy defaults.
/// Missing or invalid environment bytes withdraw that binding, not E0.
pub fn include_environment(
&mut self,
Expand Down Expand Up @@ -254,8 +254,7 @@ impl RuntimeInventory {
// External installations share version keys but lack digest evidence.
self.environments
.values()
.filter_map(|entry| entry.as_ref().ok())
.map(|entry| &entry.runtime)
.filter_map(|entry| entry.runtime.as_ref().ok())
.find(|entry| {
entry.runtime == release.release
&& entry.files.paths().all(|path| {
Expand Down Expand Up @@ -336,12 +335,9 @@ impl RuntimeInventory {
};
// A failed managed binding cannot remove or borrow a legacy installation.
// Keep a failed entry too, so explicit requests cannot fall back to it.
let installed = match (runtime, component) {
(Ok(runtime), Ok(component)) => Ok(InstalledEnvironment {
runtime,
components: vec![component],
}),
(Err(error), _) | (_, Err(error)) => Err(error),
let installed = InstalledEnvironment {
runtime,
components: component.map(|component| vec![component]),
};
self.environments
.insert(environment.definition().reference(), installed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,22 @@ fn managed_e0_survives_cache_loss_without_selecting_other_installs() {
binding
.resolve(Some(&legacy), RuntimeKind::Firecracker, mode)
.is_ok(),
allowed,
"legacy marker {target} must not silently change kernels"
allowed || mode == ExecutionSelectionMode::PersistedStateRestore,
"only a cold boot depends on the legacy marker {target}"
);
if mode == ExecutionSelectionMode::PersistedStateRestore {
let resolved = binding
.resolve(Some(&legacy), RuntimeKind::Firecracker, mode)
.unwrap();
assert!(resolved.components.is_empty());
assert!(resolved.execution.components().is_empty());
assert_eq!(
resolved.environment,
(legacy.guest() == initial.definition().unwrap().descriptor().spec().guest)
.then(|| initial.definition().unwrap().reference())
);
assert_eq!(resolved.execution.environment(), None);
}
}
}
let mut component = selected.components[0].metadata.clone();
Expand Down Expand Up @@ -520,7 +533,12 @@ fn legacy_asset_alias_retains_e0_boot_and_restore() {
let selected = managed
.resolve(requested, RuntimeKind::Firecracker, mode)
.unwrap_or_else(|error| panic!("{mode:?}: {error}"));
assert_eq!(selected.components[0].path, image.canonicalize().unwrap());
if mode == ExecutionSelectionMode::PersistedStateRestore {
assert!(selected.components.is_empty());
assert_eq!(selected.execution.components(), legacy.components());
} else {
assert_eq!(selected.components[0].path, image.canonicalize().unwrap());
}
}
assert!(managed.check_files_unchanged());
let other = assets.join("v1.16.0/kernel/vmlinux.bin");
Expand Down Expand Up @@ -660,7 +678,12 @@ fn nondefault_inventory_preserves_e0_and_recovers_only_the_exact_pair() {
.path(RuntimeFileRole::FirecrackerBinary),
Some(legacy_bin.join("firecracker").as_path())
);
assert_eq!(legacy.components[0].path, legacy_kernel);
if mode == ExecutionSelectionMode::PersistedStateRestore {
assert!(legacy.components.is_empty());
assert_eq!(legacy.execution.components(), legacy_request.components());
} else {
assert_eq!(legacy.components[0].path, legacy_kernel);
}
}
let report = inventory.to_proto();
assert!(report.runtimes.iter().any(|entry| {
Expand Down
Loading
Loading