updater: Cleanup Active blob to support multiple stages

Without the change, the update will fail due to existing active blob.
```
./burn_my_bmc --command update --interface ipmipci  \
  --image test.txt --sig test.txt --type bios
Sending over the firmware image.
sendFile with /flash/bios and test.txt
Opening the cleanup blob
Committing to the cleanup blob
Closing cleanup blob
Exception received: blob exception received: Received IPMI_CC: 255
```

With the Change, it will delete the active blob to continue the update.
```
./burn_my_bmc --command update --interface ipmipci  \
  --image test.txt --sig test.txt --type bios
Found an active blob, deleting /flash/active/image
Opening the cleanup blob
Committing to the cleanup blob
Closing cleanup blob
Committing to the cleanup blob
Closing cleanup blob
Sending over the firmware image.
sendFile with /flash/bios and test.txt
000Find [0x1050 0x750]
bar0[0x90100000]
Progress: 100.00%
001Sending over the hash file.
sendFile with /flash/hash and test.txt
000Find [0x1050 0x750]
bar0[0x90100000]
Progress: 100.00%
001Opening the verification file
Committing to /flash/verify to trigger service
Calling stat on /flash/verify session to check status
running
running
failed
Returned non-success (could still be running (unlikely))
failed
Opening the cleanup blob
Committing to the cleanup blob
Closing cleanup blob
Exception received: Verification failed
```

Signed-off-by: Willy Tu <wltu@google.com>
Change-Id: I46a802c7faece60a9b5354db47860e796af99107
diff --git a/tools/main.cpp b/tools/main.cpp
index f749e3b..1ce68a4 100644
--- a/tools/main.cpp
+++ b/tools/main.cpp
@@ -274,8 +274,8 @@
         try
         {
             host_tool::UpdateHandler updater(&blob, handler.get());
-            host_tool::updaterMain(&updater, imagePath, signaturePath, type,
-                                   ignoreUpdate);
+            host_tool::updaterMain(&updater, &blob, imagePath, signaturePath,
+                                   type, ignoreUpdate);
         }
         catch (const host_tool::ToolException& e)
         {
diff --git a/tools/test/tools_updater_unittest.cpp b/tools/test/tools_updater_unittest.cpp
index 824cec9..60cc077 100644
--- a/tools/test/tools_updater_unittest.cpp
+++ b/tools/test/tools_updater_unittest.cpp
@@ -294,8 +294,32 @@
         .WillOnce(Return(true));
     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
         .WillOnce(Return(true));
+    EXPECT_CALL(blobMock, getBlobList())
+        .WillOnce(Return(std::vector<std::string>({})));
 
-    updaterMain(&handler, image, signature, layout, defaultIgnore);
+    updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
+}
+
+TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob)
+{
+    UpdateHandlerMock handler;
+
+    EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
+    EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
+    EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
+        .WillOnce(Return());
+    EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
+        .WillOnce(Return(true));
+    EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
+        .WillOnce(Return(true));
+    EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
+    EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId))
+        .WillOnce(Return());
+    EXPECT_CALL(blobMock, getBlobList())
+        .WillOnce(Return(std::vector<std::string>(
+            {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId})));
+
+    updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
 }
 
 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate)
@@ -311,8 +335,10 @@
         .WillOnce(Return(true));
     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore))
         .WillOnce(Return(true));
+    EXPECT_CALL(blobMock, getBlobList())
+        .WillOnce(Return(std::vector<std::string>({})));
 
-    updaterMain(&handler, image, signature, layout, updateIgnore);
+    updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore);
 }
 
 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure)
@@ -326,8 +352,11 @@
     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
         .WillOnce(Return(false));
     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
+    EXPECT_CALL(blobMock, getBlobList())
+        .WillOnce(Return(std::vector<std::string>({})));
 
-    EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
+    EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
+                             defaultIgnore),
                  ToolException);
 }
 
@@ -344,8 +373,11 @@
     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
         .WillOnce(Return(false));
     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
+    EXPECT_CALL(blobMock, getBlobList())
+        .WillOnce(Return(std::vector<std::string>({})));
 
-    EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
+    EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
+                             defaultIgnore),
                  ToolException);
 }
 
@@ -355,7 +387,8 @@
 
     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
 
-    EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
+    EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
+                             defaultIgnore),
                  ToolException);
 }
 
diff --git a/tools/updater.cpp b/tools/updater.cpp
index 87962b9..4963103 100644
--- a/tools/updater.cpp
+++ b/tools/updater.cpp
@@ -35,8 +35,8 @@
 namespace host_tool
 {
 
-void updaterMain(UpdateHandlerInterface* updater, const std::string& imagePath,
-                 const std::string& signaturePath,
+void updaterMain(UpdateHandlerInterface* updater, ipmiblob::BlobInterface* blob,
+                 const std::string& imagePath, const std::string& signaturePath,
                  const std::string& layoutType, bool ignoreUpdate)
 {
     /* TODO: validate the layoutType isn't a special value such as: 'update',
@@ -50,6 +50,23 @@
         throw ToolException("Goal firmware not supported");
     }
 
+    // Clean all active blobs to support multiple stages
+    // Check for any active blobs and delete the first one found to reset the
+    // BMC's phosphor-ipmi-flash state machine then clean any leftover artifacts
+    const auto blobList = blob->getBlobList();
+    for (const auto& activeBlob : blobList)
+    {
+        // Prefix is /flash/active/
+        if (activeBlob.find("/flash/active/", 0) == 0)
+        {
+            std::fprintf(stderr, "Found an active blob, deleting %s\n",
+                         activeBlob.c_str());
+            blob->deleteBlob(activeBlob);
+            updater->cleanArtifacts();
+            break;
+        }
+    }
+
     /* Yay, our layout type is supported. */
     try
     {
diff --git a/tools/updater.hpp b/tools/updater.hpp
index 739d3e9..bcadd31 100644
--- a/tools/updater.hpp
+++ b/tools/updater.hpp
@@ -13,14 +13,15 @@
  * Attempt to update the BMC's firmware using the interface provided.
  *
  * @param[in] updater - update handler object.
+ * @param[in] blob - ipmi blob object.
  * @param[in] imagePath - the path to the image file.
  * @param[in] signaturePath - the path to the signature file.
  * @param[in] layoutType - the image update layout type (static/ubi/other)
  * @param[in] ignoreUpdate - determines whether to ignore the update status
  * @throws ToolException on failures.
  */
-void updaterMain(UpdateHandlerInterface* updater, const std::string& imagePath,
-                 const std::string& signaturePath,
+void updaterMain(UpdateHandlerInterface* updater, ipmiblob::BlobInterface* blob,
+                 const std::string& imagePath, const std::string& signaturePath,
                  const std::string& layoutType, bool ignoreUpdate);
 
 } // namespace host_tool