From 21f25da47df67f0d6121cc37704b26bd26cff62d Mon Sep 17 00:00:00 2001 From: Mike Sul Date: Wed, 15 Nov 2023 14:05:37 +0100 Subject: [PATCH] cli: Add param to delay App installation - Extend the CLI implementation to support update with delayed Apps installation. - Add a new CLI param to specify the delayed App installation mode. Signed-off-by: Mike Sul --- include/aktualizr-lite/api.h | 4 +++- src/api.cc | 8 ++++++-- src/cli/cli.cc | 14 ++++++++++++-- src/cli/cli.h | 4 +++- src/main.cc | 7 ++++++- tests/apiclient_test.cc | 10 +++++++--- 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/include/aktualizr-lite/api.h b/include/aktualizr-lite/api.h index 93007d5c..1573d6d8 100644 --- a/include/aktualizr-lite/api.h +++ b/include/aktualizr-lite/api.h @@ -106,6 +106,7 @@ class InstallResult { Ok = 0, OkBootFwNeedsCompletion, NeedsCompletion, + AppsNeedCompletion, BootFwNeedsCompletion, Failed, DownloadFailed, @@ -116,7 +117,8 @@ class InstallResult { // NOLINTNEXTLINE(hicpp-explicit-conversions,google-explicit-constructor) operator bool() const { - return status == Status::Ok || status == Status::OkBootFwNeedsCompletion || status == Status::NeedsCompletion; + return status == Status::Ok || status == Status::OkBootFwNeedsCompletion || status == Status::NeedsCompletion || + status == Status::AppsNeedCompletion; } }; diff --git a/src/api.cc b/src/api.cc index e73a965e..46eeaf99 100644 --- a/src/api.cc +++ b/src/api.cc @@ -206,7 +206,7 @@ std::string AkliteClient::GetDeviceID() const { return client_->getDeviceID(); } class LiteInstall : public InstallContext { public: LiteInstall(std::shared_ptr client, std::unique_ptr t, std::string& reason, - InstallMode install_mode) + InstallMode install_mode = InstallMode::All) : client_(std::move(client)), target_(std::move(t)), reason_(reason), mode_{install_mode} {} InstallResult Install() override { @@ -216,7 +216,11 @@ class LiteInstall : public InstallContext { auto status = InstallResult::Status::Failed; if (rc == data::ResultCode::Numeric::kNeedCompletion) { if (client_->isPendingTarget(*target_)) { - status = InstallResult::Status::NeedsCompletion; + if (client_->getCurrent().sha256Hash() == target_->sha256Hash()) { + status = InstallResult::Status::AppsNeedCompletion; + } else { + status = InstallResult::Status::NeedsCompletion; + } } else { // If the install returns `kNeedCompletion` and the target being installed is not pending, // then it means that the previous boot fw update requires reboot prior to running the new target update diff --git a/src/cli/cli.cc b/src/cli/cli.cc index b0347887..12adfd83 100644 --- a/src/cli/cli.cc +++ b/src/cli/cli.cc @@ -27,11 +27,21 @@ static const std::unordered_map i2s = { {InstallResult::Status::Ok, StatusCode::Ok}, {InstallResult::Status::OkBootFwNeedsCompletion, StatusCode::OkNeedsRebootForBootFw}, {InstallResult::Status::NeedsCompletion, StatusCode::InstallNeedsReboot}, + {InstallResult::Status::AppsNeedCompletion, StatusCode::InstallAppsNeedFinalization}, {InstallResult::Status::BootFwNeedsCompletion, StatusCode::InstallNeedsRebootForBootFw}, {InstallResult::Status::DownloadFailed, StatusCode::InstallAppPullFailure}, }; -StatusCode Install(AkliteClient &client, int version, const std::string &target_name) { +StatusCode Install(AkliteClient &client, int version, const std::string &target_name, const std::string &install_mode) { + const static std::unordered_map str2Mode = {{"delay-app-install", InstallMode::OstreeOnly}}; + InstallMode mode{InstallMode::All}; + if (!install_mode.empty()) { + if (str2Mode.count(install_mode) == 0) { + LOG_WARNING << "Unsupported installation mode: " << install_mode << "; falling back to the default install mode"; + } else { + mode = str2Mode.at(install_mode); + } + } // Check if the device is in a correct state to start a new update if (client.IsInstallationInProgress()) { LOG_ERROR << "Cannot start Target installation since there is ongoing installation; target: " @@ -75,7 +85,7 @@ StatusCode Install(AkliteClient &client, int version, const std::string &target_ LOG_INFO << "To New Target: " << target.Name(); } - const auto installer = client.Installer(target); + const auto installer = client.Installer(target, "", "", mode); if (installer == nullptr) { LOG_ERROR << "Unexpected error: installer couldn't find Target in the DB; try again later"; return StatusCode::UnknownError; diff --git a/src/cli/cli.h b/src/cli/cli.h index ee684676..f8e8507b 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -22,12 +22,14 @@ enum class StatusCode { InstallAppPullFailure = 80, InstallNeedsRebootForBootFw = 90, InstallNeedsReboot = 100, + InstallAppsNeedFinalization = 105, InstallRollbackOk = 110, InstallRollbackNeedsReboot = 120, InstallRollbackFailed = 130, }; -StatusCode Install(AkliteClient &client, int version = -1, const std::string &target_name = ""); +StatusCode Install(AkliteClient &client, int version = -1, const std::string &target_name = "", + const std::string &install_mode = ""); StatusCode CompleteInstall(AkliteClient &client); } // namespace cli diff --git a/src/main.cc b/src/main.cc index f3479cd4..8cf4b036 100644 --- a/src/main.cc +++ b/src/main.cc @@ -435,11 +435,15 @@ static int cli_install(LiteClient& client, const bpo::variables_map& params) { target_name = version_str; } } + std::string install_mode; + if (params.count("install-mode") > 0) { + install_mode = params.at("install-mode").as(); + } std::shared_ptr client_ptr{&client, [](LiteClient* /*unused*/) {}}; AkliteClient akclient{client_ptr, false, true}; - return static_cast(cli::Install(akclient, version, target_name)); + return static_cast(cli::Install(akclient, version, target_name, install_mode)); } static int cli_complete_install(LiteClient& client, const bpo::variables_map& params) { @@ -498,6 +502,7 @@ bpo::variables_map parse_options(int argc, char** argv) { ("ostree-server", bpo::value(), "URL of the Ostree repository") ("primary-ecu-hardware-id", bpo::value(), "hardware ID of primary ecu") ("update-name", bpo::value(), "optional name of the update when running \"update\". default=latest") + ("install-mode", bpo::value(), "Optional install mode. Supported modes: [delay-app-install]. By default both ostree and apps are installed before reboot") #ifdef ALLOW_MANUAL_ROLLBACK ("clear-installed-versions", "DANGER - clear the history of installed updates before applying the given update. This is handy when doing test/debug and you need to rollback to an old version manually.") #endif diff --git a/tests/apiclient_test.cc b/tests/apiclient_test.cc index 0c7b023a..32beae10 100644 --- a/tests/apiclient_test.cc +++ b/tests/apiclient_test.cc @@ -287,10 +287,14 @@ TEST_F(ApiClientTest, InstallModeOstreeOnlyIfJustApps) { ASSERT_EQ(DownloadResult::Status::Ok, dresult.status); auto iresult = installer->Install(); - ASSERT_EQ(InstallResult::Status::NeedsCompletion, iresult.status); + ASSERT_EQ(InstallResult::Status::AppsNeedCompletion, iresult.status); - auto ciresult = client.CompleteInstallation(); - ASSERT_EQ(InstallResult::Status::Ok, ciresult.status); + { + liteclient = createLiteClient(); + AkliteClient client(liteclient); + auto ciresult = client.CompleteInstallation(); + ASSERT_EQ(InstallResult::Status::Ok, ciresult.status); + } } TEST_F(ApiClientTest, Secondaries) {