Allow update_engine to skip post-install

The postinstall steps take long time to finish, even though most of them
are optional. Therefore, reuse the flag run_post_install in InstallPlan
to allow skipping optional postinstalls.

Bug: 136185424
Test: cancel the update during post-install, apply again with the header
"RUN_POST_INSTALL=0", check the optional post-installs are skipped

Change-Id: Ic5ab89b079dfd547714fd3d1664e044900f9eebe
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index c0c3956..0f48493 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -70,10 +70,17 @@
   partition_weight_.resize(install_plan_.partitions.size());
   total_weight_ = 0;
   for (size_t i = 0; i < install_plan_.partitions.size(); ++i) {
+    auto& partition = install_plan_.partitions[i];
+    if (!install_plan_.run_post_install && partition.postinstall_optional) {
+      partition.run_postinstall = false;
+      LOG(INFO) << "Skipping optional post-install for partition "
+                << partition.name << " according to install plan.";
+    }
+
     // TODO(deymo): This code sets the weight to all the postinstall commands,
     // but we could remember how long they took in the past and use those
     // values.
-    partition_weight_[i] = install_plan_.partitions[i].run_postinstall;
+    partition_weight_[i] = partition.run_postinstall;
     total_weight_ += partition_weight_[i];
   }
   accumulated_weight_ = 0;
@@ -83,11 +90,6 @@
 }
 
 void PostinstallRunnerAction::PerformPartitionPostinstall() {
-  if (!install_plan_.run_post_install) {
-    LOG(INFO) << "Skipping post-install according to install plan.";
-    return CompletePostinstall(ErrorCode::kSuccess);
-  }
-
   if (install_plan_.download_url.empty()) {
     LOG(INFO) << "Skipping post-install during rollback";
     return CompletePostinstall(ErrorCode::kSuccess);
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index caee5e2..e9313f1 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -102,6 +102,8 @@
                             bool powerwash_required,
                             bool is_rollback);
 
+  void RunPostinstallActionWithInstallPlan(const InstallPlan& install_plan);
+
  public:
   void ResumeRunningAction() {
     ASSERT_NE(nullptr, postinstall_action_);
@@ -171,9 +173,6 @@
     const string& postinstall_program,
     bool powerwash_required,
     bool is_rollback) {
-  ActionProcessor processor;
-  processor_ = &processor;
-  auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = device_path;
@@ -184,6 +183,14 @@
   install_plan.download_url = "http://127.0.0.1:8080/update";
   install_plan.powerwash_required = powerwash_required;
   install_plan.is_rollback = is_rollback;
+  RunPostinstallActionWithInstallPlan(install_plan);
+}
+
+void PostinstallRunnerActionTest::RunPostinstallActionWithInstallPlan(
+    const chromeos_update_engine::InstallPlan& install_plan) {
+  ActionProcessor processor;
+  processor_ = &processor;
+  auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
   feeder_action->set_obj(install_plan);
   auto runner_action = std::make_unique<PostinstallRunnerAction>(
       &fake_boot_control_, &fake_hardware_);
@@ -305,6 +312,27 @@
   EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
+TEST_F(PostinstallRunnerActionTest, RunAsRootSkipOptionalPostinstallTest) {
+  InstallPlan::Partition part;
+  part.name = "part";
+  part.target_path = "/dev/null";
+  part.run_postinstall = true;
+  part.postinstall_path = kPostinstallDefaultScript;
+  part.postinstall_optional = true;
+  InstallPlan install_plan;
+  install_plan.partitions = {part};
+  install_plan.download_url = "http://127.0.0.1:8080/update";
+
+  // Optional postinstalls will be skipped, and the postinstall action succeeds.
+  RunPostinstallActionWithInstallPlan(install_plan);
+  EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+
+  part.postinstall_optional = false;
+  install_plan.partitions = {part};
+  RunPostinstallActionWithInstallPlan(install_plan);
+  EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
+}
+
 // Check that the failures from the postinstall script cause the action to
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {