New setting to mark postinstall as optional.

This setting allows to mark a postinstall script as optional. This
allows the postinstall program to fail when it is not strictly required
to run for the update to succeed.

Bug: 27178350
TEST=Added unittest. Sideloaded an update with an optional postinstall.

Change-Id: I41956d3308f3458b6bf94b835c9b5e470c84ca41
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a1cf2ab..507ad8c 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -841,6 +841,7 @@
           (partition.has_postinstall_path() ? partition.postinstall_path()
                                             : kPostinstallDefaultScript);
       install_part.filesystem_type = partition.filesystem_type();
+      install_part.postinstall_optional = partition.postinstall_optional();
     }
 
     if (partition.has_old_partition_info()) {
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 51e85b3..b04da74 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -115,7 +115,8 @@
           target_hash == that.target_hash &&
           run_postinstall == that.run_postinstall &&
           postinstall_path == that.postinstall_path &&
-          filesystem_type == that.filesystem_type);
+          filesystem_type == that.filesystem_type &&
+          postinstall_optional == that.postinstall_optional);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index f9240c7..3f0005c 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -91,6 +91,7 @@
     bool run_postinstall{false};
     std::string postinstall_path;
     std::string filesystem_type;
+    bool postinstall_optional{false};
   };
   std::vector<Partition> partitions;
 
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 47b1947..9681e8c 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -304,7 +304,14 @@
       // to get back to FW A.
       error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable;
     }
-    return CompletePostinstall(error_code);
+
+    // If postinstall script for this partition is optional we can ignore the
+    // result.
+    if (install_plan_.partitions[current_partition_].postinstall_optional) {
+      LOG(INFO) << "Ignoring postinstall failure since it is optional";
+    } else {
+      return CompletePostinstall(error_code);
+    }
   }
   accumulated_weight_ += partition_weight_[current_partition_];
   current_partition_++;
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index de81a39..2f95b21 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -138,6 +138,7 @@
           partition->set_postinstall_path(part.postinstall.path);
         if (!part.postinstall.filesystem_type.empty())
           partition->set_filesystem_type(part.postinstall.filesystem_type);
+        partition->set_postinstall_optional(part.postinstall.optional);
       }
       for (const AnnotatedOperation& aop : part.aops) {
         *partition->add_operations() = aop.op;
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index c705f60..38a72a9 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -28,7 +28,7 @@
 namespace chromeos_update_engine {
 
 bool PostInstallConfig::IsEmpty() const {
-  return run == false && path.empty() && filesystem_type.empty();
+  return !run && path.empty() && filesystem_type.empty() && !optional;
 }
 
 bool PartitionConfig::ValidateExists() const {
@@ -90,6 +90,8 @@
     store.GetString("POSTINSTALL_PATH_" + part.name, &part.postinstall.path);
     store.GetString("FILESYSTEM_TYPE_" + part.name,
                     &part.postinstall.filesystem_type);
+    store.GetBoolean("POSTINSTALL_OPTIONAL_" + part.name,
+                     &part.postinstall.optional);
   }
   if (!found_postinstall) {
     LOG(ERROR) << "No valid postinstall config found.";
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 2601821..373c7cd 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -46,6 +46,9 @@
   // The filesystem type used to mount the partition in order to run the
   // post-install program.
   std::string filesystem_type;
+
+  // Whether this postinstall script should be ignored if it fails.
+  bool optional = false;
 };
 
 struct PartitionConfig {
diff --git a/payload_generator/payload_generation_config_unittest.cc b/payload_generator/payload_generation_config_unittest.cc
index 122d94a..3545056 100644
--- a/payload_generator/payload_generation_config_unittest.cc
+++ b/payload_generator/payload_generation_config_unittest.cc
@@ -29,12 +29,14 @@
   EXPECT_TRUE(
       store.LoadFromString("RUN_POSTINSTALL_root=true\n"
                            "POSTINSTALL_PATH_root=postinstall\n"
-                           "FILESYSTEM_TYPE_root=ext4"));
+                           "FILESYSTEM_TYPE_root=ext4\n"
+                           "POSTINSTALL_OPTIONAL_root=true"));
   EXPECT_TRUE(image_config.LoadPostInstallConfig(store));
   EXPECT_FALSE(image_config.partitions[0].postinstall.IsEmpty());
   EXPECT_EQ(true, image_config.partitions[0].postinstall.run);
   EXPECT_EQ("postinstall", image_config.partitions[0].postinstall.path);
   EXPECT_EQ("ext4", image_config.partitions[0].postinstall.filesystem_type);
+  EXPECT_TRUE(image_config.partitions[0].postinstall.optional);
 }
 
 TEST_F(PayloadGenerationConfigTest, LoadPostInstallConfigNameMismatchTest) {
diff --git a/update_metadata.proto b/update_metadata.proto
index 222542f..454c736 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -235,6 +235,10 @@
   // associated operation blobs (in operations[i].data_offset, data_length)
   // should be stored contiguously and in the same order.
   repeated InstallOperation operations = 8;
+
+  // Whether a failure in the postinstall step for this partition should be
+  // ignored.
+  optional bool postinstall_optional = 9;
 }
 
 message DeltaArchiveManifest {