chiark / gitweb /
bundles: Implement ClearBundles
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 15 May 2021 15:19:41 +0000 (16:19 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 15 May 2021 15:22:59 +0000 (16:22 +0100)
We need a command line UI and a test case, still.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
daemon/cmdlistener.rs
src/bundles.rs
src/commands.rs
src/spec.rs

index e6dbeb3c463b355acaf3ef9e6bf89ac1f98d5302..5813298a7b64cedfc06062f44b98f7c7ed48f8b0 100644 (file)
@@ -290,6 +290,15 @@ fn execute_and_respond<R,W>(cs: &mut CommandStreamData, cmd: MgmtCommand,
       bulk_download = Some(Box::new(f));
       Fine
     }
+    MC::ClearBundles { game } => {
+      let (ag, gref) = start_modify_game(&game)?;
+      modify_bundles(
+        cs, &ag, &gref, &[TP::ClearBundles],
+        &mut |mut ig, mut bundles: MutexGuard<'_, InstanceBundles>| {
+          bundles.clear(&mut ig)
+        })?;
+      Fine
+    }
 
     MC::ListGames { all } => {
       let ag = AccountsGuard::lock();
index 4c95ebde4f205deb141987e69df1eb882d4d37d1..f3b464224ce3b78a73b1cbc3798fa05ed39e2814 100644 (file)
@@ -904,6 +904,70 @@ impl InstanceBundles {
   }
 }
 
+//---------- clearing ----------
+
+impl InstanceBundles {
+  #[throws(MgmtError)]
+  pub fn clear(&mut self, ig: &mut Instance) {
+
+    // Check we are not in bundle state USED
+    for (m, ok) in [
+      ( "pieces",            ig.gs.pieces.is_empty()  ),
+      ( "pieces - occults",  ig.gs.occults.is_empty() ),
+      ( "pieces - ipieces",  ig.ipieces.is_empty()    ),
+      ( "piece aliases",     ig.pcaliases.is_empty()  ),
+      ( "pieces - ioccults", ig.ioccults.is_empty()   ),
+    ] {
+      if ! ok { throw!(ME::BundlesInUse(m.to_string())) }
+    }
+
+    // If we are in UNUSED, become BROKEN
+    // xxx: make shape libs and specs inaccessible to mgmt commands
+    let _ = ();
+
+    (||{
+      // If we are in BROKEN, become WRECKAGE
+      let mut to_clean
+        : Vec<(&dyn Fn(&str) -> io::Result<()>, String)>
+        = vec![];
+      for (index, slot) in self.bundles.iter_mut().enumerate() {
+        if_let!{ Some(note) = slot; else continue }
+        let id = Id {
+          index: index.try_into()
+            .map_err(|_e| internal_error_bydebug(&(index, &note)))?,
+          kind: note.kind,
+        };
+        let tmp = id.path_tmp(&ig.name);
+        let install = id.path_(&ig.name);
+        match fs::rename(&install, &tmp) {
+          Err(e) if e.kind() == ErrorKind::NotFound => { }
+          x => x.with_context(|| tmp.clone())
+            .with_context(|| install.clone())
+            .context("rename away")?
+        }
+        note.state = State::Uploading;
+        // These will happen in order, turning each bundle from
+        // WRECKAGE into NEARLY-ABSENT
+        to_clean.push((&|p| fs::remove_dir_all(p), id.path_dir(&ig.name) ));
+        to_clean.push((&|p| fs::remove_file   (p), tmp                   ));
+      }
+
+      // Actually try to clean up WRECKAGE into NEARLY-ABSENT
+      for (f,p) in to_clean {
+        match f(&p) {
+          Err(e) if e.kind() == ErrorKind::NotFound => { }
+          x => x.with_context(|| p.clone()).context("clean up")?
+        }
+      }
+
+      // Right, everything is at most NEARLY-ASENT, make them ABSENT
+      self.bundles.clear();
+
+      Ok::<_,IE>(())
+    })()?;
+  }
+}
+
 #[test]
 fn id_file_parse() {
   let check_y = |s,index,kind| {
index 8ca26f144e2ad3d671c0b7f7923d7fe27d11b195..786402c698063aa9838759bb8f3c48ecc659bf26 100644 (file)
@@ -37,10 +37,9 @@ pub enum MgmtCommand {
     game: InstanceName,
   },
 
-  /*
   ClearBundles {
     game: InstanceName,
-  },*/
+  },
   UploadBundle {
     game: InstanceName,
     size: usize,
@@ -236,6 +235,7 @@ pub enum MgmtError {
   TooManyBundles,
   BadBundle(String),
   BundleNotFound,
+  BundlesInUse(String),
 }
 impl Display for MgmtError {
   #[throws(fmt::Error)]
index 9eb59dcba28cfa6c670efc31099f500e7e608a6c..88a75bbdec58d9e1ae075203e1354ecfe651cf0c 100644 (file)
@@ -138,6 +138,7 @@ pub enum TablePermission {
   ChangePieces,
   UploadBundles,
   SetLinks,
+  ClearBundles,
   ResetOthersAccess,
   RedeliverOthersAccess,
   ModifyOtherPlayer,