hypha: convert to using anyhow for error handling

This commit is contained in:
Alex Zenla 2024-01-30 02:15:03 -08:00
parent a1081ea79c
commit eec213c712
No known key found for this signature in database
GPG Key ID: 067B238899B51269
13 changed files with 42 additions and 190 deletions

View File

@ -13,6 +13,7 @@ resolver = "2"
version = "0.0.1" version = "0.0.1"
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0"
thiserror = "1.0" thiserror = "1.0"
log = "0.4.20" log = "0.4.20"
libc = "0.2" libc = "0.2"

View File

@ -5,6 +5,7 @@ edition = "2021"
resolver = "2" resolver = "2"
[dependencies] [dependencies]
anyhow = { workspace = true }
log = { workspace = true } log = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
zstd = { workspace = true } zstd = { workspace = true }

View File

@ -1,5 +1,5 @@
use anyhow::Result;
use hypha::container::init::ContainerInit; use hypha::container::init::ContainerInit;
use hypha::error::Result;
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();

View File

@ -1,6 +1,6 @@
use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use hypha::ctl::Controller; use hypha::ctl::Controller;
use hypha::error::{HyphaError, Result};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -48,8 +48,7 @@ fn main() -> Result<()> {
let args = ControllerArgs::parse(); let args = ControllerArgs::parse();
let store_path = if args.store == "auto" { let store_path = if args.store == "auto" {
default_store_path() default_store_path().ok_or_else(|| anyhow!("unable to determine default store path"))
.ok_or_else(|| HyphaError::new("unable to determine default store path"))
} else { } else {
Ok(PathBuf::from(args.store)) Ok(PathBuf::from(args.store))
}?; }?;
@ -57,7 +56,7 @@ fn main() -> Result<()> {
let store_path = store_path let store_path = store_path
.to_str() .to_str()
.map(|x| x.to_string()) .map(|x| x.to_string())
.ok_or_else(|| HyphaError::new("unable to convert store path to string"))?; .ok_or_else(|| anyhow!("unable to convert store path to string"))?;
let mut controller = Controller::new(store_path.clone())?; let mut controller = Controller::new(store_path.clone())?;

View File

@ -1,4 +1,4 @@
use crate::error::{HyphaError, Result}; use anyhow::{anyhow, Result};
use loopdev::{LoopControl, LoopDevice}; use loopdev::{LoopControl, LoopDevice};
use xenclient::BlockDeviceRef; use xenclient::BlockDeviceRef;
@ -16,11 +16,9 @@ impl AutoLoop {
device.with().read_only(true).attach(file)?; device.with().read_only(true).attach(file)?;
let path = device let path = device
.path() .path()
.ok_or(HyphaError::new("unable to get loop device path"))? .ok_or(anyhow!("unable to get loop device path"))?
.to_str() .to_str()
.ok_or(HyphaError::new( .ok_or(anyhow!("unable to convert loop device path to string",))?
"unable to convert loop device path to string",
))?
.to_string(); .to_string();
let major = device.major()?; let major = device.major()?;
let minor = device.minor()?; let minor = device.minor()?;

View File

@ -1,6 +1,5 @@
use crate::error::Result;
use crate::hypha_err;
use crate::shared::LaunchInfo; use crate::shared::LaunchInfo;
use anyhow::{anyhow, Result};
use log::trace; use log::trace;
use nix::libc::dup2; use nix::libc::dup2;
use nix::unistd::execve; use nix::unistd::execve;
@ -69,7 +68,9 @@ impl ContainerInit {
if let Some(cfg) = config.config() { if let Some(cfg) = config.config() {
self.run(cfg, &launch)?; self.run(cfg, &launch)?;
} else { } else {
return hypha_err!("unable to determine what to execute, image config doesn't tell us"); return Err(anyhow!(
"unable to determine what to execute, image config doesn't tell us"
));
} }
Ok(()) Ok(())
} }

View File

@ -1,6 +1,6 @@
use crate::error::Result;
use crate::image::ImageInfo; use crate::image::ImageInfo;
use crate::shared::LaunchInfo; use crate::shared::LaunchInfo;
use anyhow::Result;
use backhand::{FilesystemWriter, NodeHeader}; use backhand::{FilesystemWriter, NodeHeader};
use log::trace; use log::trace;
use std::fs; use std::fs;

View File

@ -2,11 +2,11 @@ pub mod cfgblk;
use crate::autoloop::AutoLoop; use crate::autoloop::AutoLoop;
use crate::ctl::cfgblk::ConfigBlock; use crate::ctl::cfgblk::ConfigBlock;
use crate::error::{HyphaError, Result};
use crate::image::cache::ImageCache; use crate::image::cache::ImageCache;
use crate::image::name::ImageName; use crate::image::name::ImageName;
use crate::image::{ImageCompiler, ImageInfo}; use crate::image::{ImageCompiler, ImageInfo};
use crate::shared::LaunchInfo; use crate::shared::LaunchInfo;
use anyhow::{anyhow, Result};
use loopdev::LoopControl; use loopdev::LoopControl;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
@ -82,16 +82,16 @@ impl Controller {
let image_squashfs_path = image_info let image_squashfs_path = image_info
.image_squashfs .image_squashfs
.to_str() .to_str()
.ok_or_else(|| HyphaError::new("failed to convert image squashfs path to string"))?; .ok_or_else(|| anyhow!("failed to convert image squashfs path to string"))?;
let cfgblk_dir_path = cfgblk let cfgblk_dir_path = cfgblk
.dir .dir
.to_str() .to_str()
.ok_or_else(|| HyphaError::new("failed to convert cfgblk directory path to string"))?; .ok_or_else(|| anyhow!("failed to convert cfgblk directory path to string"))?;
let cfgblk_squashfs_path = cfgblk let cfgblk_squashfs_path = cfgblk
.file .file
.to_str() .to_str()
.ok_or_else(|| HyphaError::new("failed to convert cfgblk squashfs path to string"))?; .ok_or_else(|| anyhow!("failed to convert cfgblk squashfs path to string"))?;
let image_squashfs_loop = self.autoloop.loopify(image_squashfs_path)?; let image_squashfs_loop = self.autoloop.loopify(image_squashfs_path)?;
let cfgblk_squashfs_loop = self.autoloop.loopify(cfgblk_squashfs_path)?; let cfgblk_squashfs_loop = self.autoloop.loopify(cfgblk_squashfs_path)?;
@ -149,17 +149,15 @@ impl Controller {
let dom_path = store.get_domain_path(domid)?; let dom_path = store.get_domain_path(domid)?;
let uuid = match store.read_string_optional(format!("{}/hypha/uuid", dom_path).as_str())? { let uuid = match store.read_string_optional(format!("{}/hypha/uuid", dom_path).as_str())? {
None => { None => {
return Err(HyphaError::new(&format!( return Err(anyhow!(
"domain {} was not found or not created by hypha", "domain {} was not found or not created by hypha",
domid domid
))) ))
} }
Some(value) => value, Some(value) => value,
}; };
if uuid.is_empty() { if uuid.is_empty() {
return Err(HyphaError::new( return Err(anyhow!("unable to find hypha uuid based on the domain",));
"unable to find hypha uuid based on the domain",
));
} }
let uuid = Uuid::parse_str(&uuid)?; let uuid = Uuid::parse_str(&uuid)?;
let loops = store.read_string(format!("{}/hypha/loops", dom_path).as_str())?; let loops = store.read_string(format!("{}/hypha/loops", dom_path).as_str())?;
@ -235,8 +233,8 @@ impl Controller {
None => continue, None => continue,
Some(value) => value, Some(value) => value,
}; };
let domid = u32::from_str(&domid_candidate) let domid =
.map_err(|_| HyphaError::new("failed to parse domid"))?; u32::from_str(&domid_candidate).map_err(|_| anyhow!("failed to parse domid"))?;
let uuid = Uuid::from_str(&uuid_string)?; let uuid = Uuid::from_str(&uuid_string)?;
let image = self let image = self
.client .client

View File

@ -1,141 +0,0 @@
use backhand::BackhandError;
use cli_tables::TableError;
use oci_spec::OciSpecError;
use std::error::Error;
use std::ffi::NulError;
use std::fmt::{Display, Formatter};
use std::num::ParseIntError;
use std::path::StripPrefixError;
use xenclient::XenClientError;
pub type Result<T> = std::result::Result<T, HyphaError>;
#[derive(Debug)]
pub struct HyphaError {
message: String,
}
impl HyphaError {
pub fn new(msg: &str) -> HyphaError {
HyphaError {
message: msg.to_string(),
}
}
}
impl Display for HyphaError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Error for HyphaError {
fn description(&self) -> &str {
&self.message
}
}
#[macro_export]
macro_rules! hypha_err {
($($arg:tt)*) => {{
use $crate::error::HyphaError;
let text = std::fmt::format(format_args!($($arg)*));
Err(HyphaError::new(text.as_str()))
}}
}
impl From<std::io::Error> for HyphaError {
fn from(value: std::io::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<XenClientError> for HyphaError {
fn from(value: XenClientError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<walkdir::Error> for HyphaError {
fn from(value: walkdir::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<StripPrefixError> for HyphaError {
fn from(value: StripPrefixError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<BackhandError> for HyphaError {
fn from(value: BackhandError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<serde_json::Error> for HyphaError {
fn from(value: serde_json::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<ureq::Error> for HyphaError {
fn from(value: ureq::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<ParseIntError> for HyphaError {
fn from(value: ParseIntError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<OciSpecError> for HyphaError {
fn from(value: OciSpecError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<url::ParseError> for HyphaError {
fn from(value: url::ParseError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<std::fmt::Error> for HyphaError {
fn from(value: std::fmt::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<uuid::Error> for HyphaError {
fn from(value: uuid::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<xenstore::error::Error> for HyphaError {
fn from(value: xenstore::error::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<TableError> for HyphaError {
fn from(value: TableError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<NulError> for HyphaError {
fn from(value: NulError) -> Self {
HyphaError::new(value.to_string().as_str())
}
}
impl From<nix::Error> for HyphaError {
fn from(value: nix::Error) -> Self {
HyphaError::new(value.to_string().as_str())
}
}

View File

@ -1,4 +1,4 @@
use crate::error::{HyphaError, Result}; use anyhow::{anyhow, Result};
use oci_spec::image::{Arch, Descriptor, ImageIndex, ImageManifest, MediaType, Os, ToDockerV2S2}; use oci_spec::image::{Arch, Descriptor, ImageIndex, ImageManifest, MediaType, Os, ToDockerV2S2};
use std::io::copy; use std::io::copy;
use std::io::{Read, Write}; use std::io::{Read, Write};
@ -63,21 +63,21 @@ impl RegistryClient {
MediaType::ImageIndex.to_docker_v2s2()?, MediaType::ImageIndex.to_docker_v2s2()?,
); );
let response = self.call(self.agent.get(url.as_str()).set("Accept", &accept))?; let response = self.call(self.agent.get(url.as_str()).set("Accept", &accept))?;
let content_type = response.header("Content-Type").ok_or_else(|| { let content_type = response
HyphaError::new("registry response did not have a Content-Type header") .header("Content-Type")
})?; .ok_or_else(|| anyhow!("registry response did not have a Content-Type header"))?;
if content_type == MediaType::ImageIndex.to_string() if content_type == MediaType::ImageIndex.to_string()
|| content_type == MediaType::ImageIndex.to_docker_v2s2()? || content_type == MediaType::ImageIndex.to_docker_v2s2()?
{ {
let index = ImageIndex::from_reader(response.into_reader())?; let index = ImageIndex::from_reader(response.into_reader())?;
let descriptor = self let descriptor = self
.pick_manifest(index) .pick_manifest(index)
.ok_or_else(|| HyphaError::new("unable to pick manifest from index"))?; .ok_or_else(|| anyhow!("unable to pick manifest from index"))?;
return self.get_manifest_with_digest(name, descriptor.digest()); return self.get_manifest_with_digest(name, descriptor.digest());
} }
let digest = response let digest = response
.header("Docker-Content-Digest") .header("Docker-Content-Digest")
.ok_or_else(|| HyphaError::new("fetching manifest did not yield a content digest"))? .ok_or_else(|| anyhow!("fetching manifest did not yield a content digest"))?
.to_string(); .to_string();
let manifest = ImageManifest::from_reader(response.into_reader())?; let manifest = ImageManifest::from_reader(response.into_reader())?;
Ok((manifest, digest)) Ok((manifest, digest))

View File

@ -2,10 +2,10 @@ pub mod cache;
pub mod fetch; pub mod fetch;
pub mod name; pub mod name;
use crate::error::{HyphaError, Result};
use crate::image::cache::ImageCache; use crate::image::cache::ImageCache;
use crate::image::fetch::RegistryClient; use crate::image::fetch::RegistryClient;
use crate::image::name::ImageName; use crate::image::name::ImageName;
use anyhow::{anyhow, Result};
use backhand::compression::Compressor; use backhand::compression::Compressor;
use backhand::{FilesystemCompressor, FilesystemWriter, NodeHeader}; use backhand::{FilesystemCompressor, FilesystemWriter, NodeHeader};
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
@ -160,10 +160,10 @@ impl ImageCompiler<'_> {
let mut entry = entry?; let mut entry = entry?;
let path = entry.path()?; let path = entry.path()?;
let Some(name) = path.file_name() else { let Some(name) = path.file_name() else {
return Err(HyphaError::new("unable to get file name")); return Err(anyhow!("unable to get file name"));
}; };
let Some(name) = name.to_str() else { let Some(name) = name.to_str() else {
return Err(HyphaError::new("unable to get file name as string")); return Err(anyhow!("unable to get file name as string"));
}; };
if name.starts_with(".wh.") { if name.starts_with(".wh.") {
@ -214,7 +214,7 @@ impl ImageCompiler<'_> {
} else if path.is_dir() { } else if path.is_dir() {
fs::remove_dir_all(&path)?; fs::remove_dir_all(&path)?;
} else { } else {
return Err(HyphaError::new("opaque whiteout entry did not exist")); return Err(anyhow!("opaque whiteout entry did not exist"));
} }
} }
} else { } else {
@ -280,7 +280,7 @@ impl ImageCompiler<'_> {
fn check_safe_path(&self, dst: &PathBuf, image_dir: &PathBuf) -> Result<()> { fn check_safe_path(&self, dst: &PathBuf, image_dir: &PathBuf) -> Result<()> {
let resolved = path_clean::clean(dst); let resolved = path_clean::clean(dst);
if !resolved.starts_with(image_dir) { if !resolved.starts_with(image_dir) {
return Err(HyphaError::new("layer attempts to work outside image dir")); return Err(anyhow!("layer attempts to work outside image dir"));
} }
Ok(()) Ok(())
} }
@ -306,7 +306,7 @@ impl ImageCompiler<'_> {
let mut file = File::create(&layer_path)?; let mut file = File::create(&layer_path)?;
let size = client.write_blob(&image.name, layer, &mut file)?; let size = client.write_blob(&image.name, layer, &mut file)?;
if layer.size() as u64 != size { if layer.size() as u64 != size {
return Err(HyphaError::new( return Err(anyhow!(
"downloaded layer size differs from size in manifest", "downloaded layer size differs from size in manifest",
)); ));
} }
@ -323,11 +323,7 @@ impl ImageCompiler<'_> {
MediaType::ImageLayer => LayerCompressionType::None, MediaType::ImageLayer => LayerCompressionType::None,
MediaType::ImageLayerGzip => LayerCompressionType::Gzip, MediaType::ImageLayerGzip => LayerCompressionType::Gzip,
MediaType::ImageLayerZstd => LayerCompressionType::Zstd, MediaType::ImageLayerZstd => LayerCompressionType::Zstd,
other => { other => return Err(anyhow!("found layer with unknown media type: {}", other)),
return Err(HyphaError::new(
format!("found layer with unknown media type: {}", other).as_str(),
))
}
}; };
Ok(LayerFile { Ok(LayerFile {
digest: layer.digest().clone(), digest: layer.digest().clone(),
@ -346,7 +342,7 @@ impl ImageCompiler<'_> {
.path() .path()
.strip_prefix(image_dir)? .strip_prefix(image_dir)?
.to_str() .to_str()
.ok_or_else(|| HyphaError::new("failed to strip prefix of tmpdir"))?; .ok_or_else(|| anyhow!("failed to strip prefix of tmpdir"))?;
let rel = format!("/{}", rel); let rel = format!("/{}", rel);
trace!("ImageCompiler squash write {}", rel); trace!("ImageCompiler squash write {}", rel);
let typ = entry.file_type(); let typ = entry.file_type();
@ -373,7 +369,7 @@ impl ImageCompiler<'_> {
let symlink = fs::read_link(entry.path())?; let symlink = fs::read_link(entry.path())?;
let symlink = symlink let symlink = symlink
.to_str() .to_str()
.ok_or_else(|| HyphaError::new("failed to read symlink"))?; .ok_or_else(|| anyhow!("failed to read symlink"))?;
writer.push_symlink(symlink, rel, header)?; writer.push_symlink(symlink, rel, header)?;
} else if typ.is_dir() { } else if typ.is_dir() {
writer.push_dir(rel, header)?; writer.push_dir(rel, header)?;
@ -393,7 +389,7 @@ impl ImageCompiler<'_> {
let device = metadata.dev(); let device = metadata.dev();
writer.push_char_device(device as u32, rel, header)?; writer.push_char_device(device as u32, rel, header)?;
} else { } else {
return Err(HyphaError::new("invalid file type")); return Err(anyhow!("invalid file type"));
} }
} }
@ -401,7 +397,7 @@ impl ImageCompiler<'_> {
let squash_file_path = squash_file let squash_file_path = squash_file
.to_str() .to_str()
.ok_or_else(|| HyphaError::new("failed to convert squashfs string"))?; .ok_or_else(|| anyhow!("failed to convert squashfs string"))?;
let mut file = File::create(squash_file)?; let mut file = File::create(squash_file)?;
trace!("ImageCompiler squash generate: {}", squash_file_path); trace!("ImageCompiler squash generate: {}", squash_file_path);

View File

@ -1,4 +1,4 @@
use crate::error::Result; use anyhow::Result;
use std::fmt; use std::fmt;
use url::Url; use url::Url;

View File

@ -1,6 +1,5 @@
pub mod autoloop; pub mod autoloop;
pub mod container; pub mod container;
pub mod ctl; pub mod ctl;
pub mod error;
pub mod image; pub mod image;
mod shared; pub mod shared;