Skip to content

Commit

Permalink
[WIP] api, cli: Do not perform TUF checkin on every Pull or Install
Browse files Browse the repository at this point in the history
Add a new CheckStoredMetadata method that is used for getting stored
targets metadata. The GetTargetToInstall method now receives a
CheckInResult parameter instead of doing a CheckIn internally.
This allow the user to either use the original CheckIn method, or use
the new CheckStoredMetadata to feed targets information into
GetTargetToInstall.

Signed-off-by: Andre Detsch <andre.detsch@foundries.io>
  • Loading branch information
detsch committed Jul 2, 2024
1 parent 47cb328 commit 3edc017
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 68 deletions.
7 changes: 6 additions & 1 deletion apps/aklite-offline/cmds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ int CheckCmd::checkSrcDir(const po::variables_map& vm, const boost::filesystem::

int InstallCmd::installUpdate(const po::variables_map& vm, const boost::filesystem::path& src_dir,
const std::string& target_name, bool force_downgrade) const {
AkliteClientExt client(vm, false, false);
AkliteClientExt client(vm, false, false, false);
const LocalUpdateSource local_update_source{.tuf_repo = (src_dir / "tuf").string(),
.ostree_repo = (src_dir / "ostree_repo").string(),
.app_store = (src_dir / "apps").string()};
auto ci_ret_code{aklite::cli::CheckIn(client, &local_update_source)};
if (ci_ret_code != aklite::cli::StatusCode::CheckinOkCached && ci_ret_code != aklite::cli::StatusCode::Ok) {
return static_cast<int>(ci_ret_code);
}

auto ret_code{
aklite::cli::Install(client, -1, target_name, InstallMode::OstreeOnly, force_downgrade, &local_update_source)};
switch (ret_code) {
Expand Down
16 changes: 14 additions & 2 deletions include/aktualizr-lite/aklite_client_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,21 @@ class AkliteClientExt : public AkliteClient {
} state_when_download_failed{"", "", {.err = "undefined"}};

public:
GetTargetToInstallResult GetTargetToInstall(const LocalUpdateSource *local_update_source = nullptr, int version = -1,
explicit AkliteClientExt(std::shared_ptr<LiteClient> client, bool read_only = false, bool apply_lock = false,
bool invoke_post_cb_at_checkin = true)
: AkliteClient(std::move(client), read_only, apply_lock) {
invoke_post_cb_at_checkin_ = invoke_post_cb_at_checkin;
}

explicit AkliteClientExt(const boost::program_options::variables_map &cmdline_args, bool read_only = false,
bool finalize = true, bool invoke_post_cb_at_checkin = true)
: AkliteClient(cmdline_args, read_only, finalize) {
invoke_post_cb_at_checkin_ = invoke_post_cb_at_checkin;
}

GetTargetToInstallResult GetTargetToInstall(const CheckInResult &check_in_result, int version = -1,
const std::string &target_name = "", bool allow_bad_target = false,
bool force_apps_sync = false);
bool force_apps_sync = false, bool offline_mode = false);
InstallResult PullAndInstall(const TufTarget &target, const std::string &reason = "",
const std::string &correlation_id = "", InstallMode install_mode = InstallMode::All,
const LocalUpdateSource *local_update_source = nullptr, bool do_download = true,
Expand Down
5 changes: 4 additions & 1 deletion include/aktualizr-lite/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ class AkliteClient {
*/
CheckInResult CheckInLocal(const LocalUpdateSource *local_update_source) const;

CheckInResult CheckStoredMetadata(const LocalUpdateSource *local_update_source = nullptr) const;

/**
* Return the active aktualizr-lite configuration.
*/
Expand Down Expand Up @@ -362,7 +364,8 @@ class AkliteClient {
static const std::vector<boost::filesystem::path> CONFIG_DIRS;

protected:
bool usingUpdateClientApi{false};
/* check-for-update-post success callback may be called at the end of CheckIn, or the end of GetTargetToInstall */
bool invoke_post_cb_at_checkin_{true};
bool is_booted_env{true};
std::shared_ptr<LiteClient> client_;

Expand Down
43 changes: 25 additions & 18 deletions src/aklite_client_ext.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "http/httpclient.h"
#include "libaktualizr/config.h"
#include "liteclient.h"
#include "logging/logging.h"
#include "primary/reportqueue.h"

#include "aktualizr-lite/tuf/tuf.h"
Expand All @@ -26,25 +27,25 @@
// Returns the target that should be installed, if any.
// It might be an updated version, a rollback target, or even the currently installed target, in case we need to sync
// apps
GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const LocalUpdateSource* local_update_source, int version,
GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const CheckInResult& check_in_result, int version,
const std::string& target_name, bool allow_bad_target,
bool force_apps_sync) {
// Tell CheckIn and CheckInLocal methods to not call notifyTufUpdateFinished in case of success
usingUpdateClientApi = true;
bool force_apps_sync, bool is_offline_mode) {
std::string err;
if (!check_in_result) {
err = "Can't select target to install using a failed check in result";
LOG_WARNING << err << " " << static_cast<int>(check_in_result.status);
return {GetTargetToInstallResult::Status::Failed, TufTarget(), err};
}

client_->setAppsNotChecked();
bool rollback_operation = false;

CheckInResult checkin_res = local_update_source == nullptr ? CheckIn() : CheckInLocal(local_update_source);

if (!checkin_res) {
LOG_WARNING << "Unable to update latest metadata";
return GetTargetToInstallResult(checkin_res);
}

auto candidate_target = checkin_res.SelectTarget(version, target_name);
auto candidate_target = check_in_result.SelectTarget(version, target_name);
if (candidate_target.IsUnknown()) {
std::string err = "No matching target";
client_->notifyTufUpdateFinished(err);
err = "No matching target";
if (!invoke_post_cb_at_checkin_) {
client_->notifyTufUpdateFinished(err);
}
LOG_WARNING << err;
return {GetTargetToInstallResult::Status::TufTargetNotFound, TufTarget(), err};
}
Expand All @@ -62,7 +63,9 @@ GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const LocalUpdateSo
current.Name() % current.Sha256Hash())};

LOG_ERROR << err;
client_->notifyTufUpdateFinished(err);
if (!invoke_post_cb_at_checkin_) {
client_->notifyTufUpdateFinished(err);
}
return {GetTargetToInstallResult::Status::RollbackTargetNotFound, TufTarget(), err};
}
candidate_target = rollback_target;
Expand All @@ -83,7 +86,9 @@ GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const LocalUpdateSo
boost::str(boost::format("A bad target (%s) was selected for rollback of %s. This should not happen") %
candidate_target.Name() % current.Name())};
LOG_ERROR << err;
client_->notifyTufUpdateFinished(err);
if (!invoke_post_cb_at_checkin_) {
client_->notifyTufUpdateFinished(err);
}
return {GetTargetToInstallResult::Status::Failed, TufTarget(), err};
}

Expand Down Expand Up @@ -121,7 +126,7 @@ GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const LocalUpdateSo
} else {
// No targets to install
res.selected_target = TufTarget();
if (local_update_source == nullptr) {
if (!is_offline_mode) {
// Online mode
LOG_INFO << "Device is up-to-date";
} else {
Expand All @@ -133,7 +138,9 @@ GetTargetToInstallResult AkliteClientExt::GetTargetToInstall(const LocalUpdateSo
client_->setAppsNotChecked();
}

client_->notifyTufUpdateFinished("", Target::fromTufTarget(candidate_target));
if (!invoke_post_cb_at_checkin_) {
client_->notifyTufUpdateFinished("", Target::fromTufTarget(candidate_target));
}

return res;
}
Expand Down
53 changes: 51 additions & 2 deletions src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ CheckInResult AkliteClient::CheckIn() const {
}
LOG_INFO << "Latest targets metadata contains " << matchingTargets.size() << " entries for tag=\""
<< boost::algorithm::join(client_->tags, ",") << "\" and hardware id=\"" << hw_id_ << "\"";
if (!usingUpdateClientApi) {
if (invoke_post_cb_at_checkin_) {
client_->notifyTufUpdateFinished();
}
return CheckInResult(CheckInResult::Status::Ok, hw_id_, matchingTargets);
Expand Down Expand Up @@ -573,12 +573,61 @@ CheckInResult AkliteClient::CheckInLocal(const LocalUpdateSource* local_update_s
return CheckInResult(CheckInResult::Status::NoTargetContent, hw_id_, std::vector<TufTarget>{});
}

if (!usingUpdateClientApi) {
if (invoke_post_cb_at_checkin_) {
client_->notifyTufUpdateFinished();
}
return CheckInResult(check_status, hw_id_, toTufTargets(available_targets));
}

CheckInResult AkliteClient::CheckStoredMetadata(const LocalUpdateSource* local_update_source) const {
std::string err_msg;
LOG_INFO << "Checking the stored TUF metadata...";
try {
tuf_repo_->CheckMeta();
LOG_INFO << "The stored TUF metadata is valid";
} catch (const std::exception& exc) {
err_msg = std::string("Stored TUF metadata is invalid: ") + exc.what();
LOG_WARNING << err_msg;
return CheckInResult{CheckInResult::Status::SecurityError, hw_id_, {}};
}

LOG_INFO << "Searching for matching TUF Targets...";
auto matchingTargets = filterTargets(tuf_repo_->GetTargets(), hw_id_, client_->tags, secondary_hwids_);
if (matchingTargets.empty()) {
// TODO: consider reporting about it to the backend to make it easier to figure out
// why specific devices are not picking up a new Target
err_msg = boost::str(boost::format("No Target found for the device; hw ID: %s; tags: %s") % hw_id_ %
boost::algorithm::join(client_->tags, ","));
LOG_ERROR << err_msg;
return CheckInResult{CheckInResult::Status::NoMatchingTargets, hw_id_, {}};
}
LOG_INFO << "Latest targets metadata contains " << matchingTargets.size() << " entries for tag=\""
<< boost::algorithm::join(client_->tags, ",") << "\" and hardware id=\"" << hw_id_ << "\"";

if (local_update_source != nullptr) {
LOG_INFO << "Looking for update content for each matching Target...";
UpdateSrc src{
.TufDir = local_update_source->tuf_repo,
.OstreeRepoDir = local_update_source->ostree_repo,
.AppsDir = local_update_source->app_store,
};
std::vector<Uptane::Target> available_targets =
getAvailableTargets(client_->config.pacman, fromTufTargets(matchingTargets), src,
false /* get all available targets, not just latest */);
if (available_targets.empty()) {
err_msg = "No update content found in ostree dir " + src.OstreeRepoDir.string() + " and app dir " +
src.AppsDir.string();
LOG_ERROR << err_msg;
client_->notifyTufUpdateFinished(err_msg);
return CheckInResult(CheckInResult::Status::NoTargetContent, hw_id_, std::vector<TufTarget>{});
}
return CheckInResult(CheckInResult::Status::OkCached, hw_id_, toTufTargets(available_targets));

} else {
return CheckInResult(CheckInResult::Status::OkCached, hw_id_, matchingTargets);
}
}

boost::property_tree::ptree AkliteClient::GetConfig() const {
std::stringstream ss;
ss << client_->config;
Expand Down
23 changes: 12 additions & 11 deletions src/cli/cli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,12 @@ static StatusCode pullAndInstall(AkliteClientExt &client, int version, const std
return StatusCode::InstallationInProgress;
}

auto cr = client.GetTargetToInstall(local_update_source, version, target_name, true, true);
auto csm_res = client.CheckStoredMetadata(local_update_source);
auto gti_res = client.GetTargetToInstall(csm_res, version, target_name, true, true, local_update_source != nullptr);

//
if (cr.selected_target.IsUnknown()) {
if (cr.status == GetTargetToInstallResult::Status::NoMatchingTargets) {
if (gti_res.selected_target.IsUnknown()) {
if (gti_res.status == GetTargetToInstallResult::Status::NoMatchingTargets) {
std::string target_string;
if (version == -1 && target_name.empty()) {
target_string = " version: latest,";
Expand All @@ -141,27 +142,27 @@ static StatusCode pullAndInstall(AkliteClientExt &client, int version, const std
LOG_ERROR << "No Target found;" << target_string
<< " hardware ID: " << client.GetConfig().get("provision.primary_ecu_hardware_id", "")
<< ", tag: " << client.GetConfig().get("pacman.tags", "");
} else if (cr) {
} else if (gti_res) {
LOG_INFO << "No target to update";
}
return res2StatusCode<GetTargetToInstallResult::Status>(t2s, cr.status);
return res2StatusCode<GetTargetToInstallResult::Status>(t2s, gti_res.status);
}

const auto current{client.GetCurrent()};
if (current.Version() > cr.selected_target.Version()) {
if (current.Version() > gti_res.selected_target.Version()) {
LOG_WARNING << "Found TUF Target is lower version than the current on; "
<< "current: " << current.Version() << ", found Target: " << cr.selected_target.Version();
<< "current: " << current.Version() << ", found Target: " << gti_res.selected_target.Version();

if (!force_downgrade) {
LOG_ERROR << "Downgrade is not allowed by default, re-run the command with `--force` option to force downgrade";
return StatusCode::InstallDowngradeAttempt;
}
LOG_WARNING << "Downgrading from " << current.Version() << " to " << cr.selected_target.Version() << "...";
LOG_WARNING << "Downgrading from " << current.Version() << " to " << gti_res.selected_target.Version() << "...";
}

auto ir = client.PullAndInstall(cr.selected_target, "", "", install_mode, local_update_source,
pull_mode == PullMode::All, do_install);
return res2StatusCode<InstallResult::Status>(i2s, ir.status);
auto pi_res = client.PullAndInstall(gti_res.selected_target, "", "", install_mode, local_update_source,
pull_mode == PullMode::All, do_install);
return res2StatusCode<InstallResult::Status>(i2s, pi_res.status);
}

StatusCode Pull(AkliteClientExt &client, int version, const std::string &target_name, bool force_downgrade,
Expand Down
33 changes: 18 additions & 15 deletions src/daemon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ int run_daemon(LiteClient& client, uint64_t interval, bool return_on_sleep, bool
}

std::shared_ptr<LiteClient> client_ptr{&client, [](LiteClient* /*unused*/) {}};
AkliteClientExt akclient{client_ptr, false, acquire_lock};
AkliteClientExt akclient{client_ptr, false, acquire_lock, false};
if (akclient.IsInstallationInProgress()) {
auto finalize_result = akclient.CompleteInstallation();
if (finalize_result.status == InstallResult::Status::NeedsCompletion) {
Expand All @@ -35,20 +35,23 @@ int run_daemon(LiteClient& client, uint64_t interval, bool return_on_sleep, bool
auto current = akclient.GetCurrent();
LOG_INFO << "Active Target: " << current.Name() << ", sha256: " << current.Sha256Hash();
LOG_INFO << "Checking for a new Target...";
auto cir = akclient.GetTargetToInstall();
if (!cir.selected_target.IsUnknown()) {
LOG_INFO << "Going to install " << cir.selected_target.Name() << ". Reason: " << cir.reason;
// A target is supposed to be installed
auto install_result = akclient.PullAndInstall(cir.selected_target, cir.reason);
if (akclient.RebootIfRequired()) {
// no point to continue running TUF cycle (check for update, download, install)
// since reboot is required to apply/finalize the currently installed update (aka pending update)
// If a reboot command is set in configuration, and is executed successfully, we will not get to this point
break;
}
if (install_result) {
// After a successful install, do not sleep, go directly to the next iteration
continue;
auto ci_res = akclient.CheckIn();
if (ci_res) {
auto gti_res = akclient.GetTargetToInstall(ci_res);
if (!gti_res.selected_target.IsUnknown()) {
LOG_INFO << "Going to install " << gti_res.selected_target.Name() << ". Reason: " << gti_res.reason;
// A target is supposed to be installed
auto install_result = akclient.PullAndInstall(gti_res.selected_target, gti_res.reason);
if (akclient.RebootIfRequired()) {
// no point to continue running TUF cycle (check for update, download, install)
// since reboot is required to apply/finalize the currently installed update (aka pending update)
// If a reboot command is set in configuration, and is executed successfully, we will not get to this point
break;
}
if (install_result) {
// After a successful install, do not sleep, go directly to the next iteration
continue;
}
}
}

Expand Down
19 changes: 14 additions & 5 deletions src/main.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <sys/file.h>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <thread>
Expand All @@ -20,6 +21,7 @@
#include "helpers.h"
#include "http/httpclient.h"
#include "libaktualizr/config.h"
#include "logging/logging.h"
#include "storage/invstorage.h"
#include "target.h"
#include "utilities/aktualizr_version.h"
Expand Down Expand Up @@ -65,15 +67,19 @@ static int status_main(LiteClient& client, const bpo::variables_map& unused) {
return 0;
}

static int list_main(LiteClient& client, const bpo::variables_map& unused) {
(void)unused;
static int checkin(LiteClient& client) {
std::shared_ptr<LiteClient> client_ptr{&client, [](LiteClient* /*unused*/) {}};
AkliteClient akclient(client_ptr, false, true);

auto status = aklite::cli::CheckIn(akclient, nullptr);
auto status = aklite::cli::CheckIn(akclient);
return aklite::cli::IsSuccessCode(status) ? EXIT_SUCCESS : EXIT_FAILURE;
}

static int list_main(LiteClient& client, const bpo::variables_map& unused) {
(void)unused;
return checkin(client);
}

static int daemon_main(LiteClient& client, const bpo::variables_map& variables_map) {
uint64_t interval = client.config.uptane.polling_sec;
if (variables_map.count("interval") > 0) {
Expand All @@ -94,7 +100,7 @@ static void get_target_id(const bpo::variables_map& params, int& version, std::s
version = std::stoi(version_str);
} catch (const std::invalid_argument& exc) {
LOG_DEBUG << "Failed to convert the input target version to integer, consider it as a name of Target: "
<< exc.what();
<< exc.what();
name = version_str;
}
}
Expand Down Expand Up @@ -125,8 +131,11 @@ static int install(LiteClient& client, const bpo::variables_map& params, aklite:
return static_cast<int>(aklite::cli::Install(akclient, version, target_name, mode, true, nullptr, pull_mode));
}

// Pull and Install
// CheckIn, Pull and Install
static int cli_update(LiteClient& client, const bpo::variables_map& params) {
if (checkin(client) != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return install(client, params, aklite::cli::PullMode::All);
}

Expand Down
Loading

0 comments on commit 3edc017

Please sign in to comment.