From 91d67d8b0bf4fd1e9a14a8d68f599be60049038c Mon Sep 17 00:00:00 2001 From: Andre Detsch Date: Wed, 28 Aug 2024 16:14:34 -0300 Subject: [PATCH] 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 | 34 +++++++++++++++++++++++++++------- src/composeappmanager.h | 5 +++-- src/liteclient.cc | 14 ++++++++------ src/liteclient.h | 1 + 5 files changed, 45 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..22a2dc19 100644 --- a/src/composeappmanager.cc +++ b/src/composeappmanager.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include "bootloader/bootloaderlite.h" #include "docker/restorableappengine.h" @@ -193,7 +194,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, std::unordered_map& apps_and_reasons) const { AppsContainer apps_to_update; auto currently_installed_target_apps = Target::appsJson(OstreeManager::getCurrent()); @@ -206,6 +208,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 +216,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 +225,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 +250,20 @@ 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); +// true if the vector contains any string starting with the given prefix +std::unordered_map ComposeAppManager::checkForAppsToUpdate(const Uptane::Target& target) { + std::unordered_map 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..67cf272e 100644 --- a/src/composeappmanager.h +++ b/src/composeappmanager.h @@ -58,8 +58,9 @@ 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, + std::unordered_map& apps_and_reasons) const; + std::unordered_map 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..5af572e6 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(); } + +std::unordered_map 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..428fa594 100644 --- a/src/liteclient.h +++ b/src/liteclient.h @@ -68,6 +68,7 @@ class LiteClient { void reportAppsState(); bool isTargetActive(const Uptane::Target& target) const; bool appsInSync(const Uptane::Target& target) const; + std::unordered_map 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,