Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Commit

Permalink
OTA-4174: Initial implementation of an ostree update on IP Secondary
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Sul <[email protected]>
  • Loading branch information
Mike Sul committed Jan 16, 2020
1 parent de61528 commit 6baf1c2
Show file tree
Hide file tree
Showing 46 changed files with 1,162 additions and 462 deletions.
12 changes: 6 additions & 6 deletions src/aktualizr_primary/secondary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ class SecondaryWaiter {

LOG_INFO << "Accepted connection from a secondary: (" << sec_ip << ":" << sec_port << ")";
try {
auto sec_creation_res = Uptane::IpUptaneSecondary::create(sec_ip, sec_port, con_socket_.native_handle());
if (sec_creation_res.first) {
connected_secondaries_.push_back(sec_creation_res.second);
auto secondary = Uptane::IpUptaneSecondary::create(sec_ip, sec_port, con_socket_.native_handle());
if (secondary) {
connected_secondaries_.push_back(secondary);
}
} catch (const std::exception& exc) {
LOG_ERROR << "Failed to initialize a secondary: " << exc.what();
Expand Down Expand Up @@ -146,9 +146,9 @@ static Secondaries createIPSecondaries(const IPSecondariesConfig& config) {
SecondaryWaiter sec_waiter{config.secondaries_wait_port, config.secondaries_timeout_s, result};

for (auto& ip_sec_cfg : config.secondaries_cfg) {
auto sec_creation_res = Uptane::IpUptaneSecondary::connectAndCreate(ip_sec_cfg.ip, ip_sec_cfg.port);
if (sec_creation_res.first) {
result.push_back(sec_creation_res.second);
auto secondary = Uptane::IpUptaneSecondary::connectAndCreate(ip_sec_cfg.ip, ip_sec_cfg.port);
if (secondary) {
result.push_back(secondary);
} else {
sec_waiter.addSecondary(ip_sec_cfg.ip, ip_sec_cfg.port);
}
Expand Down
10 changes: 9 additions & 1 deletion src/aktualizr_secondary/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set(AKTUALIZR_SECONDARY_SRC main.cc)

set(AKTUALIZR_SECONDARY_LIB_SRC
update_agent_file.cc
aktualizr_secondary.cc
aktualizr_secondary_factory.cc
aktualizr_secondary_config.cc
Expand Down Expand Up @@ -46,6 +47,8 @@ install(TARGETS aktualizr-secondary
RUNTIME DESTINATION bin)

set(ALL_AKTUALIZR_SECONDARY_HEADERS
update_agent.h
update_agent_file.h
aktualizr_secondary.h
aktualizr_secondary_factory.h
aktualizr_secondary_config.h
Expand All @@ -65,6 +68,10 @@ add_aktualizr_test(NAME secondary_tcp_server
SOURCES secondary_tcp_server_test.cc PROJECT_WORKING_DIRECTORY)

if(BUILD_OSTREE)
target_sources(aktualizr_secondary_lib PRIVATE update_agent_ostree.cc)
list(APPEND AKTUALIZR_SECONDARY_LIB_SRC update_agent_ostree.cc)
list(APPEND ALL_AKTUALIZR_SECONDARY_HEADERS update_agent_ostree.h)

add_aktualizr_test(NAME aktualizr_secondary_ostree
SOURCES aktualizr_secondary_ostree_test.cc
ARGS ${PROJECT_BINARY_DIR}/ostree_repo
Expand All @@ -73,7 +80,8 @@ if(BUILD_OSTREE)

set_target_properties(t_aktualizr_secondary_ostree PROPERTIES LINK_FLAGS -Wl,--export-dynamic)
else(BUILD_OSTREE)
list(APPEND TEST_SOURCES aktualizr_secondary_ostree_test.cc)
list(APPEND TEST_SOURCES aktualizr_secondary_ostree_test.cc update_agent_ostree.cc)
list(APPEND ALL_AKTUALIZR_SECONDARY_HEADERS update_agent_ostree.h)
endif(BUILD_OSTREE)

add_aktualizr_test(NAME aktualizr_secondary
Expand Down
226 changes: 96 additions & 130 deletions src/aktualizr_secondary/aktualizr_secondary.cc
Original file line number Diff line number Diff line change
@@ -1,26 +1,56 @@
#include "aktualizr_secondary.h"

#include <sys/types.h>
#include <memory>

#include "crypto/keymanager.h"
#include "logging/logging.h"
#ifdef BUILD_OSTREE
#include "package_manager/ostreemanager.h" // TODO: Hide behind PackageManagerInterface
#endif
#include "update_agent.h"
#include "uptane/manifest.h"
#include "utilities/utils.h"

AktualizrSecondary::AktualizrSecondary(const AktualizrSecondaryConfig& config,
const std::shared_ptr<INvStorage>& storage,
const std::shared_ptr<KeyManager>& key_mngr,
const std::shared_ptr<PackageManagerInterface>& pacman)
: config_{config}, storage_{storage}, keys_{key_mngr}, pacman_{pacman} {
// note: we don't use TlsConfig here and supply the default to
// KeyManagerConf. Maybe we should figure a cleaner way to do that
// (split KeyManager?)
#include <sys/types.h>
#include <memory>

AktualizrSecondary::AktualizrSecondary(AktualizrSecondaryConfig config, std::shared_ptr<INvStorage> storage,
std::shared_ptr<KeyManager> key_mngr, std::shared_ptr<UpdateAgent> update_agent)
: config_(std::move(config)),
storage_(std::move(storage)),
keys_(std::move(key_mngr)),
update_agent_(std::move(update_agent)) {
if (!uptaneInitialize()) {
LOG_ERROR << "Failed to initialize";
return;
}

manifest_issuer_ = std::make_shared<Uptane::ManifestIssuer>(keys_, ecu_serial_);

if (hasPendingUpdate()) {
// TODO: refactor this to make it simpler as we don't need to persist/store
// an installation status of each ECU but store it just for a given secondary ECU
std::vector<Uptane::Target> installed_versions;
boost::optional<Uptane::Target> pending_target;
storage_->loadInstalledVersions(ecu_serial_.ToString(), nullptr, &pending_target);
data::InstallationResult install_res =
data::InstallationResult(data::ResultCode::Numeric::kUnknown, "Unknown installation error");
LOG_INFO << "There is a pending update, try to apply it, update hash: " << pending_target_.sha256Hash();

if (!!pending_target) {
install_res = update_agent_->applyPendingInstall(*pending_target);

if (install_res.result_code != data::ResultCode::Numeric::kNeedCompletion) {
storage_->saveEcuInstallationResult(ecu_serial_, install_res);
if (install_res.success) {
LOG_INFO << "Pending update has been successfully applied: " << pending_target_.sha256Hash();
storage_->saveInstalledVersion(ecu_serial_.ToString(), *pending_target, InstalledVersionUpdateMode::kCurrent);
} else {
LOG_ERROR << "Application of the pending update has failed: (" << install_res.result_code.toString() << ")"
<< install_res.description;
storage_->saveInstalledVersion(ecu_serial_.ToString(), *pending_target, InstalledVersionUpdateMode::kNone);
director_repo_.dropTargets(*storage_);
}
} else {
LOG_INFO << "Pending update hasn't been applied because a reboot hasn't been detected";
}
}
}
}

Uptane::EcuSerial AktualizrSecondary::getSerial() const { return ecu_serial_; }
Expand All @@ -29,14 +59,14 @@ Uptane::HardwareIdentifier AktualizrSecondary::getHwId() const { return hardware

PublicKey AktualizrSecondary::getPublicKey() const { return keys_->UptanePublicKey(); }

Json::Value AktualizrSecondary::getManifest() const {
Json::Value manifest = pacman_->getManifest(getSerial());

return keys_->signTuf(manifest);
}
Uptane::Manifest AktualizrSecondary::getManifest() const {
Uptane::InstalledImageInfo installed_image_info;
Uptane::Manifest manifest;
if (update_agent_->getInstalledImageInfo(installed_image_info)) {
manifest = manifest_issuer_->assembleAndSignManifest(installed_image_info);
}

bool AktualizrSecondary::putMetadata(const Uptane::RawMetaPack& meta_pack) {
return doFullVerification(Metadata(meta_pack));
return manifest;
}

int32_t AktualizrSecondary::getRootVersion(bool director) const {
Expand All @@ -60,122 +90,51 @@ bool AktualizrSecondary::putRoot(const std::string& root, bool director) {
bool AktualizrSecondary::putMetadata(const Metadata& metadata) { return doFullVerification(metadata); }

bool AktualizrSecondary::sendFirmware(const std::string& firmware) {
auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHwId());

if (targetsForThisEcu.size() != 1) {
LOG_ERROR << "Invalid number of targets (should be one): " << targetsForThisEcu.size();
// TODO: how to handle the case when secondary is rebooted after metadata are received
if (!pending_target_.IsValid()) {
LOG_ERROR << "No any pending target to receive update data/image for";
return false;
}
auto target_to_apply = targetsForThisEcu[0];

std::string treehub_server;
std::size_t firmware_size = firmware.length();

if (target_to_apply.IsOstree()) {
// this is the ostree specific case
try {
std::string ca, cert, pkey, server_url;
extractCredentialsArchive(firmware, &ca, &cert, &pkey, &server_url);
keys_->loadKeys(&ca, &cert, &pkey);
boost::trim(server_url);
treehub_server = server_url;
firmware_size = 0;
} catch (std::runtime_error& exc) {
LOG_ERROR << exc.what();

return false;
}
if (!update_agent_->download(pending_target_, firmware)) {
LOG_ERROR << "Failed to pull/store an update data";
pending_target_ = Uptane::Target::Unknown();
return false;
}

data::InstallationResult install_res;

if (target_to_apply.IsOstree()) {
#ifdef BUILD_OSTREE
install_res = OstreeManager::pull(config_.pacman.sysroot, treehub_server, *keys_, target_to_apply);
return true;
}

if (install_res.result_code.num_code != data::ResultCode::Numeric::kOk) {
LOG_ERROR << "Could not pull from OSTree (" << install_res.result_code.toString()
<< "): " << install_res.description;
return false;
}
#else
LOG_ERROR << "Could not pull from OSTree. Aktualizr was built without OSTree support!";
return false;
#endif
} else if (pacman_->name() == "debian") {
// TODO save debian package here.
LOG_ERROR << "Installation of debian images is not suppotrted yet.";
return false;
data::ResultCode::Numeric AktualizrSecondary::install(const std::string& target_name) {
// TODO: how to handle the case when secondary is rebooted after metadata are received
if (!pending_target_.IsValid()) {
LOG_ERROR << "No any pending target to receive update data/image for";
return data::ResultCode::Numeric::kInternalError;
}

if (target_to_apply.length() != firmware_size) {
LOG_ERROR << "The target image size specified in metadata " << target_to_apply.length()
<< " does not match actual size " << firmware.length();
return false;
if (pending_target_.filename() != target_name) {
LOG_ERROR << "name of the target to install and a name of the pending target do not match";
return data::ResultCode::Numeric::kInternalError;
}

if (!target_to_apply.IsOstree()) {
auto target_hashes = target_to_apply.hashes();
if (target_hashes.size() == 0) {
LOG_ERROR << "No hash found in the target metadata: " << target_to_apply.filename();
return false;
}

try {
auto received_image_data_hash = Uptane::Hash::generate(target_hashes[0].type(), firmware);

if (!target_to_apply.MatchHash(received_image_data_hash)) {
LOG_ERROR << "The received image data hash doesn't match the hash specified in the target metadata,"
" hash type: "
<< target_hashes[0].TypeString();
return false;
}
auto install_result = update_agent_->install(pending_target_);

} catch (const std::exception& exc) {
LOG_ERROR << "Failed to generate a hash of the received image data: " << exc.what();
return false;
switch (install_result) {
case data::ResultCode::Numeric::kOk: {
storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kCurrent);
pending_target_ = Uptane::Target::Unknown();
LOG_INFO << "The target has been successfully installed: " << target_name;
break;
}
case data::ResultCode::Numeric::kNeedCompletion: {
storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kPending);
LOG_INFO << "The target has been successfully installed, but a reboot is required to be applied: " << target_name;
break;
}
default: { LOG_INFO << "Failed to install the target: " << target_name; }
}

install_res = pacman_->install(target_to_apply);
if (install_res.result_code.num_code != data::ResultCode::Numeric::kOk &&
install_res.result_code.num_code != data::ResultCode::Numeric::kNeedCompletion) {
LOG_ERROR << "Could not install target (" << install_res.result_code.toString() << "): " << install_res.description;
return false;
}

if (install_res.result_code.num_code == data::ResultCode::Numeric::kNeedCompletion) {
storage_->saveInstalledVersion(getSerial().ToString(), target_to_apply, InstalledVersionUpdateMode::kPending);
} else {
storage_->saveInstalledVersion(getSerial().ToString(), target_to_apply, InstalledVersionUpdateMode::kCurrent);
}

// TODO: https://saeljira.it.here.com/browse/OTA-4174
// - return data::InstallationResult to Primary
// - add finaliztion support, pacman_->finalizeInstall() must be called at secondary startup if there is pending
// version

return true;
}

void AktualizrSecondary::extractCredentialsArchive(const std::string& archive, std::string* ca, std::string* cert,
std::string* pkey, std::string* treehub_server) {
{
std::stringstream as(archive);
*ca = Utils::readFileFromArchive(as, "ca.pem");
}
{
std::stringstream as(archive);
*cert = Utils::readFileFromArchive(as, "client.pem");
}
{
std::stringstream as(archive);
*pkey = Utils::readFileFromArchive(as, "pkey.pem");
}
{
std::stringstream as(archive);
*treehub_server = Utils::readFileFromArchive(as, "server.url", true);
}
return install_result;
}

bool AktualizrSecondary::doFullVerification(const Metadata& metadata) {
Expand Down Expand Up @@ -207,13 +166,6 @@ bool AktualizrSecondary::doFullVerification(const Metadata& metadata) {
return false;
}

auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHwId());

if (targetsForThisEcu.size() != 1) {
LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size();
return false;
}

// 6. Download and check the Root metadata file from the Image repository, following the procedure in Section 5.4.4.3.
// 7. Download and check the Timestamp metadata file from the Image repository, following the procedure in
// Section 5.4.4.4.
Expand All @@ -232,6 +184,20 @@ bool AktualizrSecondary::doFullVerification(const Metadata& metadata) {
return false;
}

auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHwId());

if (targetsForThisEcu.size() != 1) {
LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size();
return false;
}

if (!update_agent_->isTargetSupported(targetsForThisEcu[0])) {
LOG_ERROR << "The given target type is not supported: " << targetsForThisEcu[0].type();
return false;
}

pending_target_ = targetsForThisEcu[0];

return true;
}

Expand Down
Loading

0 comments on commit 6baf1c2

Please sign in to comment.