aboutsummaryrefslogtreecommitdiffstats
path: root/zone_zfs
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2022-02-17 22:55:33 -0600
committerToby Vincent <tobyv13@gmail.com>2022-02-17 22:55:33 -0600
commit22039b683f3c111e42c9eb212c45d304b9c8ac10 (patch)
treec6de813929f40f228b056ce7d55ae2ade0a27809 /zone_zfs
parent239cf15875b88281873d0b443c288e4906733579 (diff)
refactor(zone_zfs): convert zfs crate to singleton
Refactored the zone_zfs to be a singleton with a psudo-builder pattern rocket is now managing the singleton, instead of the config.
Diffstat (limited to 'zone_zfs')
-rw-r--r--zone_zfs/Cargo.toml3
-rw-r--r--zone_zfs/src/config.rs48
-rw-r--r--zone_zfs/src/error.rs20
-rw-r--r--zone_zfs/src/file_system.rs84
-rw-r--r--zone_zfs/src/lib.rs105
-rw-r--r--zone_zfs/src/snapshot.rs29
-rw-r--r--zone_zfs/src/zfs.rs60
7 files changed, 196 insertions, 153 deletions
diff --git a/zone_zfs/Cargo.toml b/zone_zfs/Cargo.toml
index 472dda1..41ef655 100644
--- a/zone_zfs/Cargo.toml
+++ b/zone_zfs/Cargo.toml
@@ -5,8 +5,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+bytesize = { version = "1.1.0", features = ["serde"] }
chrono = "0.4.19"
figment = "0.10.6"
serde = "1.0.136"
thiserror = "1.0.30"
tracing = "0.1.29"
+
+[features]
diff --git a/zone_zfs/src/config.rs b/zone_zfs/src/config.rs
new file mode 100644
index 0000000..f3dc390
--- /dev/null
+++ b/zone_zfs/src/config.rs
@@ -0,0 +1,48 @@
+use bytesize::ByteSize;
+use figment::{
+ error::Result,
+ providers::{Env, Format, Serialized, Toml},
+ value::{Dict, Map},
+ Figment, Metadata, Profile, Provider,
+};
+use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct Config {
+ pub quota: ByteSize,
+ pub pool_name: String,
+ pub mountpoint: PathBuf,
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Config {
+ quota: ByteSize::gb(16),
+ pool_name: String::from("pool"),
+ mountpoint: PathBuf::from("/svr"),
+ }
+ }
+}
+
+impl Config {
+ pub fn from<T: Provider>(provider: T) -> Result<Config> {
+ Figment::from(provider).extract()
+ }
+
+ pub fn figment() -> Figment {
+ Figment::from(Config::default())
+ .merge(Toml::file(Env::var_or("ZFS_CONFIG", "ZFS.toml")).nested())
+ .merge(Env::prefixed("ZFS_").global())
+ }
+}
+
+impl Provider for Config {
+ fn metadata(&self) -> Metadata {
+ Metadata::named("ZFS Config")
+ }
+
+ fn data(&self) -> Result<Map<Profile, Dict>> {
+ Serialized::defaults(Config::default()).data()
+ }
+}
diff --git a/zone_zfs/src/error.rs b/zone_zfs/src/error.rs
new file mode 100644
index 0000000..0275384
--- /dev/null
+++ b/zone_zfs/src/error.rs
@@ -0,0 +1,20 @@
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[allow(clippy::large_enum_variant)]
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error("ZFS error")]
+ ZFS(String),
+
+ #[error("Snapshot Error: {0:?}")]
+ Snapshot(String),
+
+ #[error("File System Error: {0:?}")]
+ FileSystem(String),
+
+ #[error("IO Error: Failed to run command")]
+ IO(#[from] std::io::Error),
+
+ #[error("Config Error: {0:?}")]
+ Config(#[from] figment::Error),
+}
diff --git a/zone_zfs/src/file_system.rs b/zone_zfs/src/file_system.rs
index 83164c9..6bee10b 100644
--- a/zone_zfs/src/file_system.rs
+++ b/zone_zfs/src/file_system.rs
@@ -1,3 +1,8 @@
+use crate::{
+ error::{Error, Result},
+ snapshot::Snapshot,
+};
+use bytesize::ByteSize;
use std::{
ffi::{OsStr, OsString},
fmt::Display,
@@ -5,12 +10,21 @@ use std::{
process::Command,
};
-use crate::{snapshot::Snapshot, Error, Result};
-
-#[derive(Debug)]
+macro_rules! opt [
+ ($name:expr, $value:expr) => ({
+ let mut _temp = OsString::new();
+ _temp.push($name);
+ _temp.push("=");
+ _temp.push($value);
+ _temp
+ })
+];
+
+#[derive(Debug, Default, Clone)]
pub struct FileSystem {
pub(crate) value: OsString,
- pub(crate) mountpoint: Option<PathBuf>,
+ pub(crate) mountpoint: PathBuf,
+ pub(crate) quota: ByteSize,
}
impl Display for FileSystem {
@@ -19,14 +33,13 @@ impl Display for FileSystem {
}
}
-// pool/000.0/nkollack-1
impl TryFrom<OsString> for FileSystem {
type Error = Error;
fn try_from(value: OsString) -> Result<Self> {
Ok(FileSystem {
value,
- mountpoint: None,
+ ..FileSystem::default()
})
}
}
@@ -59,26 +72,43 @@ impl From<FileSystem> for String {
impl FileSystem {
pub(super) fn get_name(&self) -> Result<String> {
- Ok(PathBuf::from(self.value.clone())
+ PathBuf::from(&self.value)
.file_name()
- .ok_or_else(|| Error::FileSystem(format!("Invalid path for filesystem: {:?}", self)))?
- .to_string_lossy()
- .into_owned())
+ .ok_or_else(|| Error::FileSystem(format!("Invalid path for filesystem: {:?}", self)))
+ .map(|s| s.to_string_lossy().into_owned())
}
- pub(super) fn set_quota(&self, quota: &str) -> Result<()> {
+ pub(super) fn set_opt<T, U>(&self, name: T, value: U) -> Result<&Self>
+ where
+ T: AsRef<OsStr>,
+ U: AsRef<OsStr>,
+ {
Command::new("zfs")
.arg("set")
- .arg(format!("quota={}", quota))
+ .arg(opt!(&name, value))
.arg(&self.value)
.status()?
.success()
- .then(|| ())
- .ok_or_else(|| Error::FileSystem(format!("Failed to set quota: {:?}", self)))
+ .then(|| self)
+ .ok_or_else(|| {
+ Error::FileSystem(format!("Failed to set {:?}: {:?}", name.as_ref(), self))
+ })
+ }
+
+ pub(super) fn set_quota(&mut self, quota: ByteSize) -> Result<Self> {
+ self.set_opt("quota", quota.to_string())?;
+ self.quota = quota;
+ Ok(self.to_owned())
+ }
+
+ pub(super) fn set_mountpoint(&mut self, mountpoint: PathBuf) -> Result<Self> {
+ self.set_opt("mountpoint", &mountpoint)?;
+ self.mountpoint = mountpoint;
+ Ok(self.to_owned())
}
pub(super) fn get_snapshots(&self) -> Result<Vec<Snapshot>> {
- let stdout = Command::new("zfs")
+ Command::new("zfs")
.arg("list")
.arg("-H")
.arg("-o")
@@ -86,13 +116,11 @@ impl FileSystem {
.arg("-t")
.arg("snapshot")
.arg(self)
- .output()?
- .stdout;
-
- String::from_utf8(stdout)
+ .output()
+ .map(|o| String::from_utf8(o.stdout))?
.map_err(|err| Error::FileSystem(format!("Failed to parse command output: {:?}", err)))?
.split_whitespace()
- .map(|s| s.try_into())
+ .map(Snapshot::try_from)
.collect()
}
@@ -104,22 +132,6 @@ impl FileSystem {
.max_by_key(|s| s.timestamp))
}
- pub(super) fn get_file_systems() -> Result<Vec<FileSystem>> {
- let stdout = Command::new("zfs")
- .arg("list")
- .arg("-H")
- .arg("-o")
- .arg("name")
- .output()?
- .stdout;
-
- String::from_utf8(stdout)
- .map_err(|err| Error::FileSystem(format!("Failed to parse command output: {:?}", err)))?
- .split_whitespace()
- .map(|s| s.try_into())
- .collect()
- }
-
pub fn mount(&self) -> Result<()> {
Command::new("zfs")
.arg("mount")
diff --git a/zone_zfs/src/lib.rs b/zone_zfs/src/lib.rs
index c430cba..7c50861 100644
--- a/zone_zfs/src/lib.rs
+++ b/zone_zfs/src/lib.rs
@@ -1,104 +1,19 @@
-use self::file_system::FileSystem;
-use figment::{
- providers::{Env, Format, Serialized, Toml},
- Figment, Metadata, Profile, Provider,
-};
-use serde::{Deserialize, Serialize};
-use std::{io, path::PathBuf, result};
+pub use zfs::ZFS;
+pub use error::{Error, Result};
+pub use file_system::FileSystem;
+pub use config::Config;
+pub mod config;
+pub mod error;
pub mod file_system;
-
pub mod snapshot;
-
-type Result<T> = result::Result<T, Error>;
-
-#[derive(thiserror::Error, Debug)]
-pub enum Error {
- #[error("ZFS error")]
- ZFS(String),
-
- #[error("Snapshot Error: {0:?}")]
- Snapshot(String),
-
- #[error("File System Error: {0:?}")]
- FileSystem(String),
-
- #[error("IO Error: Failed to run command")]
- IO(#[from] io::Error),
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Config {
- pub quota: String,
-}
-
-impl Default for Config {
- fn default() -> Self {
- Config {
- quota: "16G".to_string(),
- }
- }
-}
-
-impl Config {
- pub fn from<T: Provider>(provider: T) -> result::Result<Config, figment::Error> {
- Figment::from(provider).extract()
- }
-
- pub fn figment() -> Figment {
- Figment::from(Config::default())
- .merge(Toml::file(Env::var_or("ZFS_CONFIG", "ZFS.toml")).nested())
- .merge(Env::prefixed("ZFS_").global())
- }
-}
-
-impl Provider for Config {
- fn metadata(&self) -> Metadata {
- Metadata::named("ZFS Config")
- }
-
- fn data(
- &self,
- ) -> result::Result<figment::value::Map<Profile, figment::value::Dict>, figment::Error> {
- Serialized::defaults(Config::default()).data()
- }
-}
-
-pub fn create_file_system(
- base_fs_name: String,
- name: String,
- config: &Config,
-) -> Result<FileSystem> {
- let fs = FileSystem::get_file_systems()?
- .into_iter()
- .find_map(|fs| match fs.get_name() {
- Ok(n) if n == base_fs_name => Some(fs),
- _ => None,
- })
- .ok_or_else(|| Error::FileSystem("No ".to_string()))?;
-
- let snapshot = fs
- .get_latest_snapshot()?
- .ok_or_else(|| Error::Snapshot("No snapshot found".to_string()))?;
-
- let mut mountpoint = fs.value;
- mountpoint.push(name);
-
- let cloned_fs = snapshot.clone_into_file_system(
- snapshot.file_system.get_name()?,
- Some(PathBuf::from(mountpoint)),
- )?;
-
- cloned_fs.set_quota(&config.quota)?;
-
- Ok(cloned_fs)
-}
+pub mod zfs;
#[cfg(test)]
mod tests {
#[test]
- fn zfs_list() {
- use super::*;
- assert!(FileSystem::get_file_systems().is_ok());
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
}
}
diff --git a/zone_zfs/src/snapshot.rs b/zone_zfs/src/snapshot.rs
index 14fafb4..b0fabb9 100644
--- a/zone_zfs/src/snapshot.rs
+++ b/zone_zfs/src/snapshot.rs
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
use std::{ffi::OsString, path::PathBuf, process::Command};
use tracing::warn;
-use crate::{file_system::FileSystem, Error, Result};
+use crate::{error::Error, error::Result, file_system::FileSystem, Config};
#[derive(Debug)]
pub struct Snapshot {
@@ -38,32 +38,17 @@ impl TryFrom<&str> for Snapshot {
}
impl Snapshot {
- pub fn clone_into_file_system(
- &self,
- name: String,
- mountpoint: Option<PathBuf>,
- ) -> Result<FileSystem> {
- let new_fs = FileSystem {
- value: PathBuf::from(&self.file_system.value).join(name).into(),
- mountpoint,
- };
+ pub fn clone_into_file_system(&self, new_fs: PathBuf) -> Result<FileSystem> {
+ let mut file_system = self.file_system.clone();
+ file_system.value.push(&new_fs);
- let mut command = Command::new("zfs");
-
- command.arg("clone");
-
- if let Some(mp) = &new_fs.mountpoint {
- command
- .arg("-o")
- .arg(format!("mountpoint={}", mp.to_string_lossy()));
- };
-
- command
+ Command::new("zfs")
+ .arg("clone")
.arg(&self.value)
.arg(&new_fs)
.status()?
.success()
- .then(|| new_fs)
+ .then(|| file_system)
.ok_or_else(|| Error::Snapshot(format!("Failed to clone snapshot: {:?}", self)))
}
}
diff --git a/zone_zfs/src/zfs.rs b/zone_zfs/src/zfs.rs
new file mode 100644
index 0000000..7f34951
--- /dev/null
+++ b/zone_zfs/src/zfs.rs
@@ -0,0 +1,60 @@
+use figment::{Figment, Provider};
+use std::path::PathBuf;
+use std::process::Command;
+
+use crate::{Config, Error, FileSystem, Result};
+
+#[derive(Default, Debug)]
+pub struct ZFS {
+ pub config: Config,
+}
+
+impl ZFS {
+ pub fn new() -> Result<Self> {
+ Self::custom(Config::figment())
+ }
+
+ pub fn custom<T: Provider>(provider: T) -> Result<Self> {
+ Config::from(Figment::from(provider))
+ .map_err(Error::from)
+ .map(|config| Self { config })
+ }
+
+ pub fn clone_from_latest(&self, name: PathBuf, parent: PathBuf) -> Result<FileSystem> {
+ Self::get_file_system(parent)?
+ .get_latest_snapshot()?
+ .ok_or_else(|| Error::Snapshot("No snapshot found".into()))?
+ .clone_into_file_system(name)?
+ .set_quota(self.config.quota)
+ }
+
+ pub(super) fn get_file_systems() -> Result<Vec<FileSystem>> {
+ Command::new("zfs")
+ .arg("list")
+ .arg("-H")
+ .arg("-o")
+ .arg("name")
+ .output()
+ .map(|o| String::from_utf8(o.stdout))?
+ .map_err(|err| Error::FileSystem(format!("Failed to parse command output: {:?}", err)))?
+ .split_whitespace()
+ .map(FileSystem::try_from)
+ .collect()
+ }
+
+ fn get_file_system(name: PathBuf) -> Result<FileSystem> {
+ Self::get_file_systems()?
+ .into_iter()
+ .find(|fs| PathBuf::from(&fs.value).ends_with(&name))
+ .ok_or_else(|| Error::FileSystem("No file system found".into()))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn zfs_list() {
+ use super::*;
+ assert!(ZFS::get_file_systems().is_ok());
+ }
+}