chiark / gitweb /
otter(1) reset: Bundle uploading: Scan to see if we have to
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 20 May 2021 11:51:57 +0000 (12:51 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 21 May 2021 13:03:20 +0000 (14:03 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/bin/otter.rs
src/bundles.rs

index d0745dfd05ceb89d49a52d3b6141e38983b066cb..76b398cd2ee5e860fde319b9735d85a165bf82b5 100644 (file)
@@ -729,8 +729,9 @@ mod reset_game {
 
   #[derive(Default,Debug)]
   struct Args {
-    game_spec: String,
     table_file: Option<String>,
+    game_spec: String,
+    bundles: Vec<String>,
   }
 
   fn subargs(sa: &mut Args) -> ArgumentParser {
@@ -743,6 +744,11 @@ mod reset_game {
       .add_argument("GAME-SPEC",Store,
                     "game spec, as found in server, \
                      or local filename if it contains a '/')");
+    ap.refer(&mut sa.bundles).required()
+      .add_argument("BUNDLES",Collect,
+                    "Bundle files to use.  If any are specified, \
+                     all needed bundles must be specified, as any \
+                     not mentioned will be cleared from the server.");
     ap
   }
 
@@ -778,6 +784,50 @@ mod reset_game {
       insns.extend(setup_table(&ma, &instance_name, &table_spec)?);
     }
 
+    if args.bundles.len() != 0 {
+      let local = args.bundles.into_iter().map(|file| {
+        BundleForUpload::prepare(file)
+      }).collect::<Result<Vec<_>,_>>()?;
+
+      let resp = chan.cmd(&MgmtCommand::ListBundles { game: ma.instance() })?;
+      let remote = match resp {
+        MR::Bundles { bundles } => bundles,
+        x => throw!(anyhow!("unexpected response to ListBundles: {:?}",x)),
+      };
+
+      match Itertools::zip_longest(
+        local.iter().rev(),
+        remote.iter().rev(),
+      ).map(|eob| {
+        use EitherOrBoth::*;
+        use bundles::State::*;
+        match eob {
+          Left(local) => Err(format!("server is missing {}", local.file)),
+          Right(_) => Ok(()),
+          Both(_local, (id, Uploading))
+            => Err(format!("server has incomplete upload :{}", id)),
+          Both(local, (id, Loaded(remote))) => {
+            if (local.size,  local.hash) !=
+               (remote.size, remote.hash) {
+               Err(format!("server's {} does not match {}", id, &local.file))
+            } else {
+               Ok(())
+            }
+          }
+        }
+      }).find_map(Result::err) {
+        None => {
+          eprintln!("Reusing server's existing bundles");
+        },
+        Some(why) => {
+          if ma.verbose >= 0 {
+            eprintln!("Re-uploading bundles: {}", why);
+          }
+          todo!();
+        },
+      }
+    }
+
     insns.push(reset_insn);
 
     chan.alter_game(insns, None)?;
@@ -1507,6 +1557,7 @@ mod alter_game_adhoc {
 //---------- upload-bundle ----------
 
 struct BundleForUpload {
+  file: String,
   f: BufReader<File>,
   size: usize,
   hash: bundles::Hash,
@@ -1515,9 +1566,9 @@ struct BundleForUpload {
 
 impl BundleForUpload {
   #[throws(AE)]
-  fn prepare(file: &str) -> Self {
-    let f = File::open(file.clone())
-      .with_context(|| file.to_owned())
+  fn prepare(file: String) -> Self {
+    let f = File::open(&file)
+      .with_context(|| file.clone())
       .context("open bundle file")?;
     let size = f
       .metadata().context("fstat bundle file")?
@@ -1529,14 +1580,14 @@ impl BundleForUpload {
     let hash = bundles::Hash(hash.into());
     let kind = bundles::Kind::only();
     f.rewind().context("rewind bundle file")?;
-    BundleForUpload { f, size, hash, kind }
+    BundleForUpload { file, f, size, hash, kind }
   }
 
   #[throws(AE)]
   fn upload(self, ma: &MainOpts, chan: &mut MgmtChannelForGame,
             progress: &mut dyn termprogress::Reporter)
             -> bundles::Id {
-    let BundleForUpload { mut f, size, hash, kind } = self;
+    let BundleForUpload { mut f, size, hash, kind,.. } = self;
     let cmd = MC::UploadBundle {
       size,
       game: ma.instance(),
@@ -1573,7 +1624,7 @@ mod upload_bundle {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
     let mut chan = ma.access_game()?;
     let mut progress = termprogress::new();
-    let for_upload = BundleForUpload::prepare(&args.bundle_file)?;
+    let for_upload = BundleForUpload::prepare(args.bundle_file)?;
     let bundle = for_upload.upload(&ma, &mut chan, &mut *progress)?;
     println!("{}", bundle);
   }
index cda5f6bea7c55580696bde86721f4088d2530154..1114dd9b219ab318aeb904855b22a180c8a4749b 100644 (file)
@@ -46,8 +46,8 @@ pub enum State {
 #[derive(Debug,Clone,Serialize,Deserialize)]
 pub struct Loaded {
   pub meta: BundleMeta,
-  size: usize,
-  hash: bundles::Hash,
+  pub size: usize,
+  pub hash: bundles::Hash,
 }
 
 #[derive(Debug,Clone,Serialize,Deserialize,Default)]