use super::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct RelocationWorkerRequest {
pub operation_id: String,
}
pub fn run_plan(
executable: &Path,
request: &RelocationRequest,
timeout: u64,
) -> Result<RelocationPlan, FileError> {
protocol::run_typed(executable, "files-relocation-plan-worker", request, timeout)
}
pub fn run(
executable: &Path,
request: &RelocationRequest,
timeout: u64,
) -> Result<RelocationReceipt, FileError> {
protocol::payload(request, timeout)?;
let prepared = STORE.create(|id, path| RelocationReceipt::new(id, path, request.clone()))?;
supervision::announce(&prepared.operation_id, "relocation receipt")?;
let result = supervision::run(
executable,
"files-relocation-worker",
&RelocationWorkerRequest {
operation_id: prepared.operation_id.clone(),
},
timeout,
)
.and_then(|receipt| {
protocol::verify_receipt(
&prepared,
receipt,
&read_receipt(&prepared.operation_id)?,
"relocation result differs from prepared/stored evidence",
)
});
match result {
Ok(receipt) => Ok(receipt),
Err(mut error) => {
error.message.push_str(
"; names may have changed; inspect receipt and reobserve before any retry",
);
let mut receipt = read_receipt(&prepared.operation_id).unwrap_or(prepared);
receipt.status = RelocationStatus::Uncertain;
receipt.completion_verified = false;
receipt.error = Some(error);
save(&receipt)?;
Ok(receipt)
}
}
}
pub fn read_receipt(id: &str) -> Result<RelocationReceipt, FileError> {
let receipt: RelocationReceipt = STORE.load(id)?;
if receipt.operation_id != id
|| STORE.directory(id)?.join("receipt.json").to_str() != Some(&receipt.receipt_path)
{
return Err(FileError::new(
FileErrorCode::Internal,
"relocation receipt identity/path mismatch",
));
}
Ok(receipt)
}
pub fn read_supervised_request() -> Result<RelocationWorkerRequest, FileError> {
supervision::read_request()
}
pub fn execute_worker(input: &RelocationWorkerRequest) -> Result<RelocationReceipt, FileError> {
STORE.claim(&input.operation_id)?;
let mut receipt = read_receipt(&input.operation_id)?;
if receipt.stage != RelocationStage::Preflight
|| receipt.mutation_attempted
|| receipt.error.is_some()
{
return Err(FileError::new(
FileErrorCode::InvalidOptions,
"relocation has progressed; replay rejected",
));
}
let result = policy::NoMaterialization::enter()
.and_then(|_policy| relocation::execute(&MacFiles, &mut receipt, &mut save));
if let Err(error) = result {
receipt.record_error(error);
}
save(&receipt)?;
Ok(receipt)
}
pub(super) fn save(receipt: &RelocationReceipt) -> Result<(), FileError> {
STORE.save(&receipt.operation_id, &receipt.receipt_path, receipt)
}
pub(in crate::files) fn execute_child(
platform: &impl RelocationPlatform,
request: &RelocationRequest,
checkpoint: &mut impl FnMut(&RelocationReceipt) -> Result<(), FileError>,
) -> Result<RelocationReceipt, FileError> {
let mut receipt = STORE.create(|id, path| RelocationReceipt::new(id, path, request.clone()))?;
let mut persist = |child: &RelocationReceipt| {
save(child)?;
checkpoint(child)
};
persist(&receipt)?;
STORE.claim(&receipt.operation_id)?;
if let Err(error) = relocation::execute(platform, &mut receipt, &mut persist) {
receipt.record_error(error);
}
persist(&receipt)?;
Ok(receipt)
}