From ea9dd722ff4ebc650c8e0d4981f4162fbf0898a2 Mon Sep 17 00:00:00 2001 From: RCmerci Date: Thu, 14 Feb 2019 22:32:29 +0800 Subject: [PATCH] feature: add rm & put --rename --- db/src/containeritem.rs | 2 +- db/src/docker.rs | 48 ++++++++++++++++++++++++++++++++--------- db/src/hostitem.rs | 9 +++++--- db/src/lib.rs | 9 ++++++-- db/src/utils.rs | 8 +++++++ dockerclient/src/lib.rs | 34 ++++++++++++++++------------- front/src/lib.rs | 11 ++++++++-- src/main.rs | 39 +++++++++++++++++++++------------ 8 files changed, 113 insertions(+), 47 deletions(-) diff --git a/db/src/containeritem.rs b/db/src/containeritem.rs index a1b058b..f88c7e4 100644 --- a/db/src/containeritem.rs +++ b/db/src/containeritem.rs @@ -22,7 +22,7 @@ impl<'a, 'b> ContainerItem<'a, 'b> { dockercli: &'b dockerclient::DockerClient, image: &str, ) -> Result { - let mut filename = "".into(); + let filename; match path.file_name() { None => { return Err(Error::BadPath(path.display().to_string())); diff --git a/db/src/docker.rs b/db/src/docker.rs index 4c2d072..536e70d 100644 --- a/db/src/docker.rs +++ b/db/src/docker.rs @@ -1,4 +1,4 @@ -use crate::utils::get_or_run; +use crate::utils::{get_or_run, run}; use crate::*; use hex; use std::fmt; @@ -35,6 +35,34 @@ impl ImageDrive { dockercli: dockercli, } } + // cat /checksum/data/*/* | sort + fn get_checksum_str(&self, container: &str) -> String { + let cmd = "cat /checksum/data/*/* | sort"; + self.dockercli + .exec(container, cmd) + .map_err(Error::DockerError) + .map(|(out, err)| { + ( + String::from_utf8_lossy(&out).to_owned().to_string(), + String::from_utf8_lossy(&err).to_owned().to_string(), + ) + }) + .and_then(|(out, err)| { + if err != "" { + return Err(Error::ExecError(err)); + } + Ok(out.trim().to_owned()) + }) + .expect("get checksum") + } + // diff_container_with_image return true if different + fn diff_container_with_image(&self) -> bool { + let c = get_or_run(&self.dockercli, &self.image_name).expect("get or run"); + let image_c = run(&self.dockercli, &self.image_name).expect("run"); + let c_checksum = self.get_checksum_str(&c.id); + let image_c_checksum = self.get_checksum_str(&image_c.id); + c_checksum != image_c_checksum + } } impl fmt::Display for ImageDrive { @@ -55,11 +83,11 @@ impl DB for ImageDrive { ) } - fn add(&self, entry: &str, itempath: &Path) -> Result { + fn add(&self, entry: &str, itempath: &Path, rename: Option<&str>) -> Result { if !itempath.exists() { return Err(Error::NotExistItem(format!("{}", itempath.display()))); } - let mut item = hostitem::HostItem::new(itempath).map_err(Error::HostItemError)?; + let mut item = hostitem::HostItem::new(itempath, rename).map_err(Error::HostItemError)?; let c = get_or_run(&self.dockercli, &self.image_name).map_err(Error::DockerError)?; let path = Path::new("/data").join(entry); @@ -126,9 +154,12 @@ impl DB for ImageDrive { Ok(AddResult::Succ) }) } - fn delete(&self, entry: &str, item: &str) -> Result<(), Error> { + fn delete(&self, entry: &str, item: Option<&str>) -> Result<(), Error> { let c = get_or_run(&self.dockercli, &self.image_name).map_err(Error::DockerError)?; - let dstpath = Path::new("/data").join(entry).join(item); + let dstpath = match item { + None => Path::new("/data").join(entry), + Some(file) => Path::new("/data").join(entry).join(file), + }; self.dockercli .remove_file(&c.id, &dstpath) .map_err(|e| Error::ExecError(format!("{:?}", e))) @@ -143,13 +174,10 @@ impl DB for ImageDrive { } fn sync(&self) -> Result<(), Error> { - // TODO: check image exist, if not , pull from registry - - // image existed, so push to registry - // 1. commit all changed data in container to image let c = get_or_run(&self.dockercli, &self.image_name).map_err(Error::DockerError)?; - if self.dockercli.diff_container_image_content(&c.id) { + if self.diff_container_with_image() { + println!("something changed in localDB , so need to sync to remote"); let _ = self .dockercli .commit(&c.id, "commit by sync", &self.image_name) diff --git a/db/src/hostitem.rs b/db/src/hostitem.rs index d8a8af4..f8d776c 100644 --- a/db/src/hostitem.rs +++ b/db/src/hostitem.rs @@ -18,15 +18,18 @@ pub struct HostItem<'a> { } impl<'a> HostItem<'a> { - pub fn new(path: &'a Path) -> Result { - let mut filename = "".into(); + pub fn new(path: &'a Path, rename: Option<&str>) -> Result { + let filename; match path.file_name() { None => { return Err(Error::BadPath(path.display().to_string())); } Some(s) => filename = s.to_string_lossy().to_owned(), } - let id = format!("{:?}", filename); + let mut id = format!("{:?}", filename); + if rename.is_some() { + id = format!("{:?}", rename.unwrap()) + } let mut fs = vec![]; // host file or dir if path.is_file() { diff --git a/db/src/lib.rs b/db/src/lib.rs index febba53..57f3d38 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -36,9 +36,14 @@ where /// items list items under `entry` fn items(&self, entry: &str) -> Result, E>; /// add `item` to DB under `entry` - fn add(&self, entry: &str, itempath: &std::path::Path) -> Result; + fn add( + &self, + entry: &str, + itempath: &std::path::Path, + rename: Option<&str>, + ) -> Result; /// delete item from DB, which is located by entry and reference - fn delete(&self, entry: &str, item: &str) -> Result<(), E>; + fn delete(&self, entry: &str, item: Option<&str>) -> Result<(), E>; /// export_to_dir export `entry` to `dir` fn export_to_dir(&self, dir: &std::path::Path, entry: &str) -> Result<(), E>; /// sync local DB to remote DB diff --git a/db/src/utils.rs b/db/src/utils.rs index 0358c3a..e4aaf56 100644 --- a/db/src/utils.rs +++ b/db/src/utils.rs @@ -17,3 +17,11 @@ pub fn get_or_run( cli.start(&c.id)?; Ok(c) } +pub fn run( + cli: &dockerclient::DockerClient, + image: &str, +) -> Result { + let c = cli.create(image)?; + cli.start(&c.id)?; + Ok(c) +} diff --git a/dockerclient/src/lib.rs b/dockerclient/src/lib.rs index f6263f6..77d0c1b 100644 --- a/dockerclient/src/lib.rs +++ b/dockerclient/src/lib.rs @@ -265,17 +265,21 @@ impl DockerClient { }) } + // it maybe long duration, so print docker push's processing stdout pub fn push(&self, image: &str) -> Result<(), Error> { let cmd = format!("docker push {}", image); - let r = std::process::Command::new("sh") + let mut r = std::process::Command::new("sh") .arg("-c") .arg(&cmd) - .output() + .stderr(std::process::Stdio::null()) + .stdout(std::process::Stdio::inherit()) + .spawn() .map_err(|e| Error::DefaultError(e.to_string()))?; - if r.status.success() { + let status = r.wait().map_err(|e| Error::DefaultError(e.to_string()))?; + if status.success() { return Ok(()); } - if (!r.status.success()) + if (!status.success()) && self.server.is_some() && self.username.is_some() && self.password.is_some() @@ -295,8 +299,8 @@ impl DockerClient { let errstr = String::from_utf8_lossy(&output.stderr).to_string(); return Err(Error::LoginError(errstr)); } - } else if !r.status.success() { - return Err(Error::PushError(format!("code: {:?}", r.status.code()))); + } else if !status.success() { + return Err(Error::PushError(format!("code: {:?}", status.code()))); } // login succ, retry push @@ -304,21 +308,21 @@ impl DockerClient { std::process::Command::new("sh") .arg("-c") .arg(&cmd) - .output() - .map_err(|e| Error::DefaultError(e.to_string())) + .stderr(std::process::Stdio::piped()) + .stdout(std::process::Stdio::inherit()) + .spawn() + .map_err(|e| Error::PushError(e.to_string())) .and_then(|r| { - if !r.status.success() { - let errstr = String::from_utf8_lossy(&r.stderr).to_string(); + let output = r + .wait_with_output() + .map_err(|e| Error::PushError(e.to_string()))?; + if !output.status.success() { + let errstr = String::from_utf8_lossy(&output.stderr).to_string(); return Err(Error::PushError(errstr)); } Ok(()) }) } - - /// return true if container's content is different with its image. - pub fn diff_container_image_content(&self, _container: &str) -> bool { - true - } } fn copy( diff --git a/front/src/lib.rs b/front/src/lib.rs index 482db07..7f6a3c1 100644 --- a/front/src/lib.rs +++ b/front/src/lib.rs @@ -36,8 +36,8 @@ pub fn list_entry_item(db: &ImageDrive, entry: &str) { } } -pub fn put(db: &ImageDrive, entry: &str, item: &str) { - match db.add(entry, Path::new(item)) { +pub fn put(db: &ImageDrive, entry: &str, item: &str, rename: Option<&str>) { + match db.add(entry, Path::new(item), rename) { Err(e) => println!("put item fail: {:?}", e), Ok(r) => println!("{:?}", r), } @@ -61,6 +61,13 @@ pub fn sync(db: &ImageDrive, from_remote: bool) { } } +pub fn rm(db: &ImageDrive, entry: &str, file: Option<&str>) { + match db.delete(entry, file) { + Err(e) => println!("rm entry (or file) fail: {:?}", e), + Ok(_) => println!("rm [{:?}]", entry.to_owned() + "/" + file.unwrap_or("")), + } +} + #[cfg(test)] mod tests { #[test] diff --git a/src/main.rs b/src/main.rs index 1d1a199..28838b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,7 +36,8 @@ fn main() { SubCommand::with_name("put") .about("put host file to imagedrive") .arg(Arg::with_name("entry").help("entry name").required(true)) - .arg(Arg::with_name("file").help("file path").required(true)), + .arg(Arg::with_name("file").help("file path").required(true)) + .arg(Arg::with_name("name").help("rename file")), ) .subcommand( SubCommand::with_name("export") @@ -54,9 +55,15 @@ fn main() { .long("remote"), ), ) + .subcommand( + SubCommand::with_name("rm") + .about("remove entry or file") + .arg(Arg::with_name("entry").help("entry name").required(true)) + .arg(Arg::with_name("file").help("file name")), + ) .get_matches(); - let mut config_path = None; + let mut config_path; if matches.is_present("config") { config_path = Some(std::path::Path::new(matches.value_of("config").unwrap()).to_path_buf()); } else { @@ -79,7 +86,6 @@ fn main() { let image_name = &cfg.image_name; if let Some(matches) = matches.subcommand_matches("ls") { - // "$ myapp test" was run if matches.is_present("entry") { front::list_entry_item( &ImageDrive::new(image_name, server, username, password), @@ -88,19 +94,17 @@ fn main() { } else { front::list_entry(&ImageDrive::new(image_name, server, username, password)); } - } - - if let Some(matches) = matches.subcommand_matches("put") { + } else if let Some(matches) = matches.subcommand_matches("put") { let entry = matches.value_of("entry").unwrap(); let filepath = matches.value_of("file").unwrap(); + let rename = matches.value_of("name"); front::put( &ImageDrive::new(image_name, server, username, password), entry, filepath, + rename, ); - } - - if let Some(matches) = matches.subcommand_matches("export") { + } else if let Some(matches) = matches.subcommand_matches("export") { let entry = matches.value_of("entry").unwrap(); let filepath = matches.value_of("dir").unwrap(); front::export( @@ -108,14 +112,21 @@ fn main() { entry, filepath, ); - } - - if let Some(matches) = matches.subcommand_matches("sync") { + } else if let Some(matches) = matches.subcommand_matches("sync") { front::sync( &ImageDrive::new(image_name, server, username, password), matches.is_present("from_remote"), ); + } else if let Some(matches) = matches.subcommand_matches("rm") { + let entry = matches.value_of("entry").unwrap(); + let file = matches.value_of("file"); + front::rm( + &ImageDrive::new(image_name, server, username, password), + entry, + file, + ); + } else { + // default + front::list_entry(&ImageDrive::new(image_name, server, username, password)); } - - // Continued program logic goes here... }