From: Ian Jackson Date: Mon, 3 May 2021 11:59:28 +0000 (+0100) Subject: bundles: Implement DownloadBundle X-Git-Tag: otter-0.6.0~451 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=d30caee7d7946ea87479c017cc93bcaae271adda;p=otter.git bundles: Implement DownloadBundle Signed-off-by: Ian Jackson --- diff --git a/daemon/cmdlistener.rs b/daemon/cmdlistener.rs index b85f7ff4..9fdb5166 100644 --- a/daemon/cmdlistener.rs +++ b/daemon/cmdlistener.rs @@ -80,7 +80,7 @@ fn execute_and_respond(cs: &mut CommandStreamData, cmd: MgmtCommand, -> Result<(), CSE> where R: Read, W: Write { - let /*mut*/ bulk_download: Option> = None; + let mut bulk_download: Option> = None; let mut cmd_s = log_enabled!(log::Level::Info) .as_some_from(|| format!("{:?}", &cmd)) @@ -251,6 +251,16 @@ fn execute_and_respond(cs: &mut CommandStreamData, cmd: MgmtCommand, let bundles = bundles.list(); MR::Bundles { bundles } } + MC::DownloadBundle { game, id } => { + let ag = AccountsGuard::lock(); + let gref = Instance::lookup_by_name_unauth(&game)?; + let mut igu = gref.lock()?; + let (ig, _) = cs.check_acl(&ag, &mut igu, PCH::Instance, + TP_ACCESS_BUNDLES)?; + let f = id.open(&ig.name)?.ok_or_else(|| ME::BundleNotFound)?; + bulk_download = Some(Box::new(f)); + Fine + } MC::ListGames { all } => { let ag = AccountsGuard::lock(); diff --git a/src/bin/otter.rs b/src/bin/otter.rs index 780b6406..a1e012a6 100644 --- a/src/bin/otter.rs +++ b/src/bin/otter.rs @@ -1402,6 +1402,75 @@ mod list_bundles { )} } +//---------- download-bundle ---------- + +mod download_bundle { + use super::*; + + #[derive(Default,Debug)] + struct Args { + table_name: String, + index: bundles::Index, + output: Option, + } + + fn subargs(sa: &mut Args) -> ArgumentParser { + use argparse::*; + let mut ap = ArgumentParser::new(); + ap.refer(&mut sa.table_name).required() + .add_argument("TABLE-NAME",Store,"table name"); + ap.refer(&mut sa.index).required() + .add_argument("INDEX",Store,"bundle number"); + ap.refer(&mut sa.output).metavar("OUTPUT") + .add_option(&["-o","--output"],StoreOption, + "write output to OUTPUT (rather than NNNNN.zip"); + ap + } + + #[throws(AE)] + fn call(_sc: &Subcommand, ma: MainOpts, args: Vec) { + let args = parse_args::(args, &subargs, &ok_id, None); + let instance_name = ma.instance_name(&args.table_name); + let mut chan = access_game(&ma, &args.table_name)?; + let kind = bundles::Kind::only(); + let id = bundles::Id { kind, index: args.index }; + let path = args.output.unwrap_or_else(|| id.to_string().into()); + + let (f, path_tmp): (Box, _) = + if path.as_os_str().as_bytes() == b"-" + { + (Box::new(io::stdout()), None) + } else { + let tmp = { + let mut w = path.as_os_str().to_owned(); + w.push(".tmp"); + PathBuf::from(w) + }; + let f = fs::File::create(&tmp) + .with_context(|| tmp.to_debug()).context("create temporary")?; + (Box::new(f), Some((path, tmp))) + }; + let mut f = BufWriter::new(f); + let cmd = MC::DownloadBundle { + game: instance_name.clone(), + id, + }; + chan.cmd_withbulk(&cmd, &mut io::empty(), &mut f) + .context("download bundle")?; + f.flush().context("flush bundle file")?; + if let Some((path, tmp)) = path_tmp { + fs::rename(&tmp,&path) + .with_context(|| path.to_debug()).context("rename after download")?; + } + } + + inventory::submit!{Subcommand( + "download-bundle", + "download bundle", + call, + )} +} + //---------- list-accounts ---------- mod list_accounts { diff --git a/src/commands.rs b/src/commands.rs index 15218613..0d221c4a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -49,12 +49,10 @@ pub enum MgmtCommand { ListBundles { game: InstanceName, }, - /* DownloadBundle { game: InstanceName, id: bundles::Id, }, - */ LibraryListByGlob { glob: shapelib::ItemSpec, @@ -233,6 +231,7 @@ pub enum MgmtError { UploadCorrupted, TooManyBundles, BadBundle(String), + BundleNotFound, } impl Display for MgmtError { #[throws(fmt::Error)]