use super::{
native_image::{decode_image, image_route, ImageFacts},
native_info::decode_info,
native_status::{decode_inspect, inspect_route, InspectOutcome, State},
observation::INFO_ROUTE,
ownership::ExpectedContainer,
protected_context::ProtectedContext,
};
pub struct CapturedImage {
facts: ImageFacts,
image_bytes: Vec<u8>,
config_bytes: Vec<u8>,
epoch: String,
}
impl std::fmt::Debug for CapturedImage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CapturedImage").finish_non_exhaustive()
}
}
impl CapturedImage {
pub fn facts(&self) -> &ImageFacts {
&self.facts
}
pub fn image_bytes(&self) -> &[u8] {
&self.image_bytes
}
pub fn config_bytes(&self) -> &[u8] {
&self.config_bytes
}
pub fn operator_epoch(&self) -> &str {
&self.epoch
}
pub fn match_context(&self, context: &ProtectedContext) -> Result<(), &'static str> {
let (expected, bytes) = context.snapshot()?;
if bytes != self.config_bytes || expected.operator_epoch != self.epoch {
return Err("captured image context mismatch");
}
Ok(())
}
}
impl ProtectedContext {
pub fn capture_image_for_plan<F>(&self, id: &str, get: F) -> Result<CapturedImage, &'static str>
where
F: FnMut(&str) -> Result<(u16, Vec<u8>), &'static str>,
{
let (expected, bytes) = self.snapshot()?;
let config_bytes = bytes.to_vec();
let epoch = expected.operator_epoch.clone();
let (facts, image_bytes) =
self.inspect_checked_with(&image_route(id)?, get, |route, status, bytes| {
Ok((
decode_image(id, "GET", route, status, bytes)?,
bytes.to_vec(),
))
})?;
let captured = CapturedImage {
facts,
image_bytes,
config_bytes,
epoch,
};
captured.match_context(self)?;
Ok(captured)
}
pub fn inspect_with<F>(&self, id: &str, get: F) -> Result<InspectOutcome, &'static str>
where
F: FnMut(&str) -> Result<(u16, Vec<u8>), &'static str>,
{
self.inspect_checked_with(&inspect_route(id)?, get, |route, status, bytes| {
decode_inspect(id, "GET", route, status, bytes)
})
}
pub fn inspect_owned_with<F>(
&self,
owner: &ExpectedContainer,
get: F,
) -> Result<State, &'static str>
where
F: FnMut(&str) -> Result<(u16, Vec<u8>), &'static str>,
{
owner.validate()?;
self.inspect_checked_with(&inspect_route(&owner.id)?, get, |route, status, bytes| {
owner.match_inspect("GET", route, status, bytes)
})
}
pub fn inspect_image_with<F>(&self, id: &str, get: F) -> Result<ImageFacts, &'static str>
where
F: FnMut(&str) -> Result<(u16, Vec<u8>), &'static str>,
{
self.inspect_checked_with(&image_route(id)?, get, |route, status, bytes| {
decode_image(id, "GET", route, status, bytes)
})
}
fn inspect_checked_with<F, T, D>(
&self,
route: &str,
mut get: F,
decode: D,
) -> Result<T, &'static str>
where
F: FnMut(&str) -> Result<(u16, Vec<u8>), &'static str>,
D: FnOnce(&str, u16, &[u8]) -> Result<T, &'static str>,
{
let (expected, _) = self.snapshot()?;
expected.validate_local_socket_policy()?;
let exchange = |get: &mut F, path: &str| {
self.revalidate()?;
let response = get(path)?;
self.revalidate()?;
Ok::<_, &'static str>(response)
};
let (status, bytes) = exchange(&mut get, INFO_ROUTE)?;
if status != 200 {
return Err("initial native info unavailable");
}
let before = decode_info(&bytes, expected.observation.socket.clone())?;
expected.match_current_operation(expected, &before, &before)?;
let (status, bytes) = exchange(&mut get, route)?;
let result = decode(route, status, &bytes)?;
let (status, bytes) = exchange(&mut get, INFO_ROUTE)?;
if status != 200 {
return Err("final native info unavailable");
}
let after = decode_info(&bytes, expected.observation.socket.clone())?;
expected.match_current_operation(expected, &before, &after)?;
self.revalidate()?;
Ok(result)
}
}