aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2022-05-11 20:41:03 -0500
committerToby Vincent <tobyv13@gmail.com>2022-05-11 20:41:03 -0500
commit03675f76b53abd63ea99e9e4af614d73615a496b (patch)
treea07ecc37b62db3147c971e12dced2cc5c8727de5
parentbd0793b71f557049f252e0256297e0407c065bad (diff)
feat: create Volumes impl for overlay file system
-rw-r--r--Cargo.lock58
-rw-r--r--Cargo.toml2
-rw-r--r--zone_overlay/Cargo.toml14
-rw-r--r--zone_overlay/src/config.rs94
-rw-r--r--zone_overlay/src/error.rs29
-rw-r--r--zone_overlay/src/lib.rs16
-rw-r--r--zone_overlay/src/overlay.rs87
7 files changed, 289 insertions, 11 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b931472..94c8fc3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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 = [
diff --git a/Cargo.toml b/Cargo.toml
index 20679e1..191b691 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)
+ }
+}