From b8cba304154ccd6483590258a5cbb6c3cb019830 Mon Sep 17 00:00:00 2001 From: Andre Detsch Date: Wed, 28 Aug 2024 16:08:28 -0300 Subject: [PATCH 1/2] cli: Use appropriate reason when initiating update in pullAndInstall This affects aklite CLI pull, install, and update commands. Signed-off-by: Andre Detsch --- src/cli/cli.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/cli.cc b/src/cli/cli.cc index fd7988bc..c8ceed94 100644 --- a/src/cli/cli.cc +++ b/src/cli/cli.cc @@ -300,7 +300,7 @@ static StatusCode pullAndInstall(AkliteClientExt &client, int version, const std LOG_WARNING << "Downgrading from " << current.Version() << " to " << gti_res.selected_target.Version() << "..."; } - auto pi_res = client.PullAndInstall(gti_res.selected_target, "", "", install_mode, local_update_source, + auto pi_res = client.PullAndInstall(gti_res.selected_target, gti_res.reason, "", install_mode, local_update_source, pull_mode == PullMode::All, do_install); return res2StatusCode(i2s, pi_res.status); } From 05b018d8f2fc472df52da317e0da9938a6d88e7b Mon Sep 17 00:00:00 2001 From: Andre Detsch Date: Wed, 28 Aug 2024 16:14:34 -0300 Subject: [PATCH 2/2] api: extended: Report apps and reasons for update in event Adds additional information to the DownloadStarted event detailing the reasons for an Apps Sync update to be initiated. Signed-off-by: Andre Detsch --- src/aklite_client_ext.cc | 8 ++++++-- src/composeappmanager.cc | 32 +++++++++++++++++++++++++------- src/composeappmanager.h | 5 +++-- src/liteclient.cc | 14 ++++++++------ src/liteclient.h | 2 ++ 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/aklite_client_ext.cc b/src/aklite_client_ext.cc index b57237e2..c9290c73 100644 --- a/src/aklite_client_ext.cc +++ b/src/aklite_client_ext.cc @@ -120,7 +120,8 @@ GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const CheckInResult << " Skipping its installation."; } - if (force_apps_sync || !client_->appsInSync(Target::fromTufTarget(current))) { + auto apps_to_update = client_->appsToUpdate(Target::fromTufTarget(current)); + if (force_apps_sync || !apps_to_update.empty()) { // Force installation of apps res.selected_target = current; LOG_INFO @@ -128,7 +129,10 @@ GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const CheckInResult << res.selected_target.Name(); res.status = GetTargetToInstallResult::Status::UpdateSyncApps; - res.reason = "Syncing Active Target Apps"; + res.reason = "Syncing Active Target Apps\n"; + for (const auto& app_to_update : apps_to_update) { + res.reason += "- " + app_to_update.first + ": " + app_to_update.second + "\n"; + } } else { // No targets to install res.selected_target = TufTarget(); diff --git a/src/composeappmanager.cc b/src/composeappmanager.cc index 9e5238c7..c7ffacd1 100644 --- a/src/composeappmanager.cc +++ b/src/composeappmanager.cc @@ -193,7 +193,8 @@ ComposeAppManager::AppsContainer ComposeAppManager::getApps(const Uptane::Target return apps; } -ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane::Target& t) const { +ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane::Target& t, + AppsSyncReason& apps_and_reasons) const { AppsContainer apps_to_update; auto currently_installed_target_apps = Target::appsJson(OstreeManager::getCurrent()); @@ -206,6 +207,7 @@ ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane if (app_data.empty()) { // new app in Target apps_to_update.insert(app_pair); + apps_and_reasons[app_pair.first] = "new app in target"; LOG_INFO << app_name << " will be installed"; continue; } @@ -213,6 +215,7 @@ ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane if (app_pair.second != app_data["uri"].asString()) { // an existing App update apps_to_update.insert(app_pair); + apps_and_reasons[app_pair.first] = "new version in target"; LOG_INFO << app_name << " will be updated"; continue; } @@ -221,15 +224,24 @@ ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane !boost::filesystem::exists(cfg_.apps_root / app_name / Docker::ComposeAppEngine::ComposeFile)) { // an App that is supposed to be installed has been removed somehow, let's install it again apps_to_update.insert(app_pair); + apps_and_reasons[app_pair.first] = "missing installation, to be re-installed"; LOG_INFO << app_name << " will be re-installed"; continue; } LOG_DEBUG << app_name << " performing full status check"; - if (!app_engine_->isFetched({app_name, app_pair.second}) || !app_engine_->isRunning({app_name, app_pair.second})) { - // an App that is supposed to be installed and running is not fully installed or running + if (!app_engine_->isFetched({app_name, app_pair.second})) { + // an App that is supposed to be installed is not fully installed apps_to_update.insert(app_pair); - LOG_INFO << app_name << " update will be re-installed or completed"; + apps_and_reasons[app_pair.first] = "not fetched"; + LOG_INFO << app_name << " is not fully fetched; missing blobs will be fetched"; + continue; + } + if (!app_engine_->isRunning({app_name, app_pair.second})) { + // an App that is supposed to running is not running + apps_to_update.insert(app_pair); + apps_and_reasons[app_pair.first] = "not running"; + LOG_INFO << app_name << " is not installed or not running; will be installed and started"; continue; } } @@ -237,13 +249,19 @@ ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane return apps_to_update; } -bool ComposeAppManager::checkForAppsToUpdate(const Uptane::Target& target) { - cur_apps_to_fetch_and_update_ = getAppsToUpdate(target); +ComposeAppManager::AppsSyncReason ComposeAppManager::checkForAppsToUpdate(const Uptane::Target& target) { + AppsSyncReason apps_and_reasons; + cur_apps_to_fetch_and_update_ = getAppsToUpdate(target, apps_and_reasons); if (!!cfg_.reset_apps) { cur_apps_to_fetch_ = getAppsToFetch(target); } are_apps_checked_ = true; - return cur_apps_to_fetch_and_update_.empty() && cur_apps_to_fetch_.empty(); + for (const auto& app : cur_apps_to_fetch_) { + if (apps_and_reasons.count(app.first) == 0) { + apps_and_reasons[app.first] = "not fetched (reset apps)"; + } + } + return apps_and_reasons; } DownloadResult ComposeAppManager::Download(const TufTarget& target) { diff --git a/src/composeappmanager.h b/src/composeappmanager.h index e7e3eb3c..fc59469c 100644 --- a/src/composeappmanager.h +++ b/src/composeappmanager.h @@ -39,6 +39,7 @@ class ComposeAppManager : public RootfsTreeManager { }; using AppsContainer = std::unordered_map; + using AppsSyncReason = std::unordered_map; ComposeAppManager(const PackageConfig& pconfig, const BootloaderConfig& bconfig, const std::shared_ptr& storage, const std::shared_ptr& http, @@ -58,8 +59,8 @@ class ComposeAppManager : public RootfsTreeManager { // Returns an intersection of Target's Apps and Apps listed in the config (sota.toml:compose_apps) // If Apps are not specified in the config then all Target's Apps are returned AppsContainer getApps(const Uptane::Target& t) const; - AppsContainer getAppsToUpdate(const Uptane::Target& t) const; - bool checkForAppsToUpdate(const Uptane::Target& target); + AppsContainer getAppsToUpdate(const Uptane::Target& t, AppsSyncReason& apps_and_reasons) const; + AppsSyncReason checkForAppsToUpdate(const Uptane::Target& target); void setAppsNotChecked() { are_apps_checked_ = false; } void handleRemovedApps(const Uptane::Target& target) const; Json::Value getAppsState() const; diff --git a/src/liteclient.cc b/src/liteclient.cc index d3a47791..0d1c1a22 100644 --- a/src/liteclient.cc +++ b/src/liteclient.cc @@ -771,22 +771,24 @@ bool LiteClient::isTargetActive(const Uptane::Target& target) const { return target.filename() == current.filename() && target.sha256Hash() == current.sha256Hash(); } -bool LiteClient::appsInSync(const Uptane::Target& target) const { +bool LiteClient::appsInSync(const Uptane::Target& target) const { return appsToUpdate(target).empty(); } + +ComposeAppManager::AppsSyncReason LiteClient::appsToUpdate(const Uptane::Target& target) const { if (package_manager_->name() == ComposeAppManager::Name) { auto* compose_pacman = dynamic_cast(package_manager_.get()); if (compose_pacman == nullptr) { LOG_ERROR << "Cannot downcast the package manager to a specific type"; - return false; + return {}; } LOG_INFO << "Checking status of Active Target (" << target.filename() << ")"; - auto no_any_app_to_update = compose_pacman->checkForAppsToUpdate(target); - if (no_any_app_to_update) { + auto apps_to_update = compose_pacman->checkForAppsToUpdate(target); + if (apps_to_update.empty()) { compose_pacman->handleRemovedApps(getCurrent()); } - return no_any_app_to_update; + return apps_to_update; } - return true; + return {}; } void LiteClient::setAppsNotChecked() { diff --git a/src/liteclient.h b/src/liteclient.h index 79bf5284..b5cf5a38 100644 --- a/src/liteclient.h +++ b/src/liteclient.h @@ -1,6 +1,7 @@ #ifndef AKTUALIZR_LITE_CLIENT_H_ #define AKTUALIZR_LITE_CLIENT_H_ +#include "composeappmanager.h" #include "downloader.h" #include "gtest/gtest_prod.h" #include "libaktualizr/config.h" @@ -68,6 +69,7 @@ class LiteClient { void reportAppsState(); bool isTargetActive(const Uptane::Target& target) const; bool appsInSync(const Uptane::Target& target) const; + ComposeAppManager::AppsSyncReason appsToUpdate(const Uptane::Target& target) const; void setAppsNotChecked(); std::string getDeviceID() const; static void update_request_headers(std::shared_ptr& http_client, const Uptane::Target& target,