From dc4ef3d9e384a8204286d9aeb48019326fffffe0 Mon Sep 17 00:00:00 2001 From: Mike Sul Date: Wed, 11 Dec 2024 11:15:28 +0100 Subject: [PATCH] appengine: Check app install along with app run check Check whether an app is installed while verifying whether an app is running or not in `appengine.isRunning()` call. It guarantees that the running app is exactly what is expected to be running. Signed-off-by: Mike Sul --- src/composeapp/appengine.cc | 41 ++++++++++++++++++++++++++++++++++--- src/composeappmanager.cc | 4 ++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/composeapp/appengine.cc b/src/composeapp/appengine.cc index a6d94ec1..637d1476 100644 --- a/src/composeapp/appengine.cc +++ b/src/composeapp/appengine.cc @@ -10,6 +10,8 @@ namespace composeapp { enum class ExitCode { ExitCodeInsufficientSpace = 100 }; static bool checkAppStatus(const AppEngine::App& app, const Json::Value& status); +static bool checkAppInstallationStatus(const AppEngine::App& app, const Json::Value& status); +static bool isNullOrEmptyIfMember(const Json::Value& val, const std::string& field); AppEngine::Result AppEngine::fetch(const App& app) { Result res{false}; @@ -66,10 +68,16 @@ bool AppEngine::isRunning(const App& app) const { bool res{false}; try { std::future output; - exec(boost::format{"%s --store %s ps %s --format json"} % composectl_cmd_ % storeRoot() % app.uri, "", - boost::process::std_out > output); + exec(boost::format{"%s --store %s --compose %s ps %s --format json --install"} % composectl_cmd_ % storeRoot() % + installRoot() % app.uri, + "", boost::process::std_out > output); const auto app_status{Utils::parseJSON(output.get())}; - res = checkAppStatus(app, app_status); + // Make sure app images and bundle are properly installed + res = checkAppInstallationStatus(app, app_status); + if (res) { + // Make sure app is running + res = checkAppStatus(app, app_status); + } } catch (const std::exception& exc) { LOG_ERROR << "failed to verify whether app is running; app: " << app.name << ", err: " << exc.what(); } @@ -227,4 +235,31 @@ static bool checkAppStatus(const AppEngine::App& app, const Json::Value& status) return is_running; } +static bool checkAppInstallationStatus(const AppEngine::App& app, const Json::Value& status) { + const auto& app_status{status.get(app.uri, Json::Value())}; + if (!app_status.isObject()) { + LOG_ERROR << "could not get app status; uri: " << app.uri; + return false; + } + if (!isNullOrEmptyIfMember(app_status, "missing_images")) { + LOG_INFO << app.name << " is not fully installed; missing images:\n" << app_status["missing_images"]; + return false; + } + if (!isNullOrEmptyIfMember(app_status, "bundle_errors")) { + LOG_INFO << app.name << " is not fully installed; invalid bundle installation:\n" << app_status["bundle_errors"]; + return false; + } + return true; +} + +static bool isNullOrEmptyIfMember(const Json::Value& val, const std::string& field) { + bool res{false}; + if (val.isMember(field)) { + res = val[field].isNull() || val[field].empty(); + } else { + res = true; + } + return res; +} + } // namespace composeapp diff --git a/src/composeappmanager.cc b/src/composeappmanager.cc index 4a29d1cb..688b3b6b 100644 --- a/src/composeappmanager.cc +++ b/src/composeappmanager.cc @@ -223,14 +223,14 @@ ComposeAppManager::AppsContainer ComposeAppManager::getAppsToUpdate(const Uptane LOG_DEBUG << app_name << " performing full status check"; if (!app_engine_->isRunning({app_name, app_pair.second})) { - // an App that is supposed to running is not running + // an App that is supposed to be running is not running or is not fully installed 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; } if (!app_engine_->isFetched({app_name, app_pair.second})) { - // an App that is supposed to be installed is not fully installed + // an App that is supposed to be installed is not fully fetched apps_to_update.insert(app_pair); apps_and_reasons[app_pair.first] = "not fetched"; LOG_INFO << app_name << " is not fully fetched; missing blobs will be fetched";