diff options
author | Toby Vincent <tobyv13@gmail.com> | 2022-05-11 20:41:03 -0500 |
---|---|---|
committer | Toby Vincent <tobyv13@gmail.com> | 2022-05-11 20:41:03 -0500 |
commit | 03675f76b53abd63ea99e9e4af614d73615a496b (patch) | |
tree | a07ecc37b62db3147c971e12dced2cc5c8727de5 | |
parent | bd0793b71f557049f252e0256297e0407c065bad (diff) |
feat: create Volumes impl for overlay file system
-rw-r--r-- | Cargo.lock | 58 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | zone_overlay/Cargo.toml | 14 | ||||
-rw-r--r-- | zone_overlay/src/config.rs | 94 | ||||
-rw-r--r-- | zone_overlay/src/error.rs | 29 | ||||
-rw-r--r-- | zone_overlay/src/lib.rs | 16 | ||||
-rw-r--r-- | zone_overlay/src/overlay.rs | 87 |
7 files changed, 289 insertions, 11 deletions
@@ -450,6 +450,12 @@ dependencies = [ ] [[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + +[[package]] name = "futures" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1311,6 +1317,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] name = "schannel" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1351,18 +1366,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -1505,9 +1520,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "04066589568b72ec65f42d65a1a52436e954b168773148893c020269563decf2" dependencies = [ "proc-macro2", "quote", @@ -1572,18 +1587,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -1985,6 +2000,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2248,6 +2274,18 @@ dependencies = [ ] [[package]] +name = "zone_overlay" +version = "0.1.0" +dependencies = [ + "figment", + "fs_extra", + "serde", + "thiserror", + "walkdir", + "zone_core", +] + +[[package]] name = "zone_zfs" version = "0.1.0" dependencies = [ @@ -1,3 +1,3 @@ [workspace] -members = ["zone", "zone_core", "zone_nspawn", "zone_zfs", "zoned"] +members = ["zone", "zone_core", "zone_nspawn", "zone_zfs", "zone_overlay", "zoned"] default-members = ["zone", "zoned"] diff --git a/zone_overlay/Cargo.toml b/zone_overlay/Cargo.toml new file mode 100644 index 0000000..97f3590 --- /dev/null +++ b/zone_overlay/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "zone_overlay" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +figment = { version = "0.10.6", features = ["toml", "env"] } +fs_extra = "1.2.0" +serde = { version = "1.0.137", features = ["derive"] } +thiserror = "1.0.31" +walkdir = "2.3.2" +zone_core = { version = "0.1.0", path = "../zone_core" } diff --git a/zone_overlay/src/config.rs b/zone_overlay/src/config.rs new file mode 100644 index 0000000..b7a7821 --- /dev/null +++ b/zone_overlay/src/config.rs @@ -0,0 +1,94 @@ +use figment::{ + error::Result, + providers::Serialized, + value::{Dict, Map}, + Figment, Metadata, Profile, Provider, +}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct Config { + pub volumes: PathBuf, + pub templates: PathBuf, +} + +impl Default for Config { + fn default() -> Self { + Config { + volumes: "/tmp/zone/volumes".into(), + templates: "/tmp/zone/templates".into(), + } + } +} + +impl Config { + pub fn from<T: Provider>(provider: T) -> Result<Config> { + Figment::from(provider).extract() + } +} + +impl Provider for Config { + fn metadata(&self) -> Metadata { + Metadata::named("zone_overlay Config") + } + + fn data(&self) -> Result<Map<Profile, Dict>> { + Serialized::defaults(Config::default()).data() + } +} + +#[cfg(test)] +mod tests { + use figment::providers::{Format, Serialized, Toml}; + + use super::*; + + #[test] + fn defaults() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "Config.toml", + r#" + volumes = "/tmp/zone/volumes" + templates = "/tmp/zone/templates" + "#, + )?; + + let config: Config = Figment::from(Serialized::defaults(Config::default())) + .merge(Toml::file("Config.toml")) + .extract()?; + + assert_eq!(config, Config::default()); + + Ok(()) + }); + } + + #[test] + fn non_defaults() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "Config.toml", + r#" + volumes = "/svr/zone/volumes" + templates = "/svr/zone/templates" + "#, + )?; + + let config: Config = Figment::from(Serialized::defaults(Config::default())) + .merge(Toml::file("Config.toml")) + .extract()?; + + assert_eq!( + config, + Config { + volumes: "/svr/zone/volumes".into(), + templates: "/svr/zone/templates".into(), + } + ); + + Ok(()) + }); + } +} diff --git a/zone_overlay/src/error.rs b/zone_overlay/src/error.rs new file mode 100644 index 0000000..65663ad --- /dev/null +++ b/zone_overlay/src/error.rs @@ -0,0 +1,29 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Volume error: {0:?}")] + Volume(#[from] VolumeError), + + #[error("Template error: {0:?}")] + Template(#[from] TemplateError), +} + +#[derive(thiserror::Error, Debug)] +pub enum VolumeError { + #[error("Volume does not exist")] + NotFound, + + #[error("Volume directory does not exist")] + BasePath, +} + +#[derive(thiserror::Error, Debug)] +pub enum TemplateError { + #[error("Template does not exist")] + NotFound, + + #[error("Template directory does not exist: {0:?}")] + BasePath(std::io::Error), + + #[error("Copy error: {0:?}")] + Copy(#[from] fs_extra::error::Error), +} diff --git a/zone_overlay/src/lib.rs b/zone_overlay/src/lib.rs new file mode 100644 index 0000000..a0cdfcf --- /dev/null +++ b/zone_overlay/src/lib.rs @@ -0,0 +1,16 @@ +pub use config::Config; +pub use error::Error; +pub use overlay::Overlay; + +mod config; +mod error; +mod overlay; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +} diff --git a/zone_overlay/src/overlay.rs b/zone_overlay/src/overlay.rs new file mode 100644 index 0000000..5f2cf58 --- /dev/null +++ b/zone_overlay/src/overlay.rs @@ -0,0 +1,87 @@ +use fs_extra::dir; +use std::path::PathBuf; +use walkdir::WalkDir; +use zone_core::{Container, Templates, Volumes}; + +use crate::{ + error::{TemplateError, VolumeError}, + Config, Error, +}; + +#[derive(Default, Debug)] +pub struct Overlay { + pub config: Config, +} + +impl Volumes for Overlay { + type Error = Error; + + fn list_volumes(&self) -> Result<Vec<Container>, Self::Error> { + if !&self.config.volumes.is_dir() { + return Err(VolumeError::BasePath.into()); + } + + let containers = WalkDir::new(&self.config.volumes) + .max_depth(2) + .into_iter() + .filter_entry(|entry| entry.file_type().is_dir() && entry.depth() == 2) + .filter_map(|res| res.map(|entry| entry.into_path()).ok()) + .filter_map( + |path_buf| match path_buf.strip_prefix(&self.config.volumes) { + Ok(p) => Container::try_from(PathBuf::from(p)).ok(), + Err(_) => None, + }, + ) + .collect(); + + Ok(containers) + } + + fn get_volume(&self, container: &Container) -> Result<PathBuf, Self::Error> { + match self + .config + .volumes + .join(PathBuf::from(container.to_owned())) + { + path if path.is_dir() => Ok(path), + _ => Err(VolumeError::NotFound.into()), + } + } +} + +impl Templates for Overlay { + type Error = Error; + + fn list_templates(&self) -> Result<Vec<String>, Self::Error> { + let templates = std::fs::read_dir(&self.config.templates) + .map_err(TemplateError::BasePath)? + .into_iter() + .filter_map(|e| match e { + Ok(e) if e.path().is_dir() => e.file_name().to_str().map(String::from), + _ => None, + }) + .collect(); + + Ok(templates) + } + + fn clone_from(&self, template: String, owner: String) -> Result<Container, Self::Error> { + let container = Container { + id: self.get_last_id(&template, &owner)?, + template, + owner, + }; + + let to = &self + .config + .volumes + .join(PathBuf::from(container.to_owned())); + + let from = &self.config.templates.join(&container.template); + + dir::copy(from, to, &Default::default()) + .map(|_| container) + .map_err(TemplateError::from) + .map_err(Error::from) + } +} |