use crate::local_fleet::{LocalFleetError, model::LocalResetRecord, ops};
use ic_host_fs::durable;
use std::{fs, path::Path};
fn read(directory: &Path) -> Result<Option<LocalResetRecord>, LocalFleetError> {
match ic_host_fs::read::read_file_no_follow(&directory.join("reset.json"), 4096)
.map_err(std::io::Error::from)
{
Ok(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(error) => Err(error.into()),
}
}
pub fn require_complete(directory: &Path) -> Result<(), LocalFleetError> {
if read(directory)?.is_some_and(|record| record.schema_version != 1 || !record.complete) {
return Err(LocalFleetError::Session);
}
Ok(())
}
pub fn begin(directory: &Path, expected: &str) -> Result<bool, LocalFleetError> {
let retained = ops::read_record(directory)?;
if let Some(record) = retained {
if record.session_id != expected
|| record.configuration.name
!= directory.file_name().unwrap_or_default().to_string_lossy()
{
return Err(LocalFleetError::Session);
}
} else {
let prior = read(directory)?.ok_or(LocalFleetError::Session)?;
if prior.schema_version != 1 || prior.session_id != expected {
return Err(LocalFleetError::Session);
}
if prior.complete {
return Ok(false);
}
}
let intent = LocalResetRecord {
schema_version: 1,
session_id: expected.into(),
complete: false,
};
durable::write_bytes(&directory.join("reset.json"), &serde_json::to_vec(&intent)?)
.map_err(crate::publication::ops::io_error)?;
Ok(true)
}
pub fn remove_instance(directory: &Path) -> Result<(), LocalFleetError> {
let reset = read(directory)?.ok_or(LocalFleetError::Session)?;
let instance = ops::instance_directory(directory, &reset.session_id)?;
match fs::symlink_metadata(&instance) {
Ok(metadata) if metadata.is_dir() => {}
Ok(_) => return Err(LocalFleetError::UnsafePath),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(error) => return Err(error.into()),
}
match fs::remove_dir_all(&instance) {
Ok(()) => Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(error) => Err(error.into()),
}
}
pub fn finish(directory: &Path, expected: &str) -> Result<(), LocalFleetError> {
for name in ["preparation.json", "desired.toml", "root-key.der"] {
match fs::remove_file(directory.join(name)) {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => return Err(error.into()),
}
}
match fs::remove_file(directory.join("environment.json")) {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => return Err(error.into()),
}
let record = LocalResetRecord {
schema_version: 1,
session_id: expected.into(),
complete: true,
};
durable::write_bytes(&directory.join("reset.json"), &serde_json::to_vec(&record)?)
.map_err(crate::publication::ops::io_error)?;
Ok(())
}