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 dirty version of an ostree direct update support fo…
Browse files Browse the repository at this point in the history
…r IP Secondary

Signed-off-by: Mike Sul <[email protected]>
  • Loading branch information
Mike Sul committed Jan 13, 2020
1 parent 4541b02 commit e100348
Show file tree
Hide file tree
Showing 26 changed files with 654 additions and 301 deletions.
269 changes: 159 additions & 110 deletions src/aktualizr_secondary/aktualizr_secondary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class SecondaryAdapter : public Uptane::SecondaryInterface {
bool sendFirmware(const std::shared_ptr<std::string>& data) override {
return secondary.AktualizrSecondary::sendFirmwareResp(data);
}
data::ResultCode::Numeric install(const std::string& target_name) override {
return secondary.AktualizrSecondary::installResp(target_name);
}

private:
AktualizrSecondary& secondary;
Expand All @@ -43,6 +46,29 @@ AktualizrSecondary::AktualizrSecondary(const AktualizrSecondaryConfig& config,
LOG_ERROR << "Failed to initialize";
return;
}

if (rebootDetected()) {
LOG_INFO << "Reboot has been detected, applying the new ostree revision: " << pending_target_.sha256Hash();
// TODO: refactor this
std::vector<Uptane::Target> installed_versions;
boost::optional<Uptane::Target> pending_target;
storage->loadInstalledVersions(ecu_serial_.ToString(), nullptr, &pending_target);

if (!!pending_target) {
data::InstallationResult install_res = pacman->finalizeInstall(*pending_target);
storage->saveEcuInstallationResult(ecu_serial_, install_res);
if (install_res.success) {
storage->saveInstalledVersion(ecu_serial_.ToString(), *pending_target, InstalledVersionUpdateMode::kCurrent);
} else {
// finalize failed
// unset pending flag so that the rest of the uptane process can
// go forward again
storage->saveInstalledVersion(ecu_serial_.ToString(), *pending_target, InstalledVersionUpdateMode::kNone);
director_repo_.dropTargets(*storage);
}
}
pacman->rebootFlagClear();
}
}

void AktualizrSecondary::run() {
Expand All @@ -65,8 +91,6 @@ Json::Value AktualizrSecondary::getManifestResp() const {
return keys_.signTuf(manifest);
}

bool AktualizrSecondary::putMetadataResp(const Metadata& metadata) { return doFullVerification(metadata); }

int32_t AktualizrSecondary::getRootVersionResp(bool director) const {
std::string root_meta;
if (!storage_->loadLatestRoot(&root_meta,
Expand All @@ -85,123 +109,59 @@ bool AktualizrSecondary::putRootResp(const std::string& root, bool director) {
return false;
}

bool AktualizrSecondary::sendFirmwareResp(const std::shared_ptr<std::string>& firmware) {
auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHardwareID());
bool AktualizrSecondary::putMetadataResp(const Metadata& metadata) { return doFullVerification(metadata); }

if (targetsForThisEcu.size() != 1) {
LOG_ERROR << "Invalid number of targets (should be one): " << targetsForThisEcu.size();
bool AktualizrSecondary::sendFirmwareResp(const std::shared_ptr<std::string>& firmware) {
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;
}
}

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);

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;
std::unique_ptr<Downloader> downloader;
// TBD: this stuff has to be refactored and improved
if (pending_target_.IsOstree()) {
downloader = std_::make_unique<OstreeDirectDownloader>(config_.pacman.sysroot, keys_);
} else {
downloader = std_::make_unique<FakeDownloader>();
}

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();
if (!downloader->download(pending_target_, *firmware)) {
LOG_ERROR << "Failed to pull/store an update data";
pending_target_ = Uptane::Target::Unknown();
return false;
}

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;
}
return true;
}

} catch (const std::exception& exc) {
LOG_ERROR << "Failed to generate a hash of the received image data: " << exc.what();
return false;
}
data::ResultCode::Numeric AktualizrSecondary::installResp(const std::string& target_name) {
if (!pending_target_.IsValid()) {
LOG_ERROR << "No any pending target to receive update data/image for";
return data::ResultCode::Numeric::kInternalError;
}

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 (pending_target_.filename() != target_name) {
LOG_ERROR << "Targte name to install and the pending target name does not match";
return data::ResultCode::Numeric::kInternalError;
}

if (install_res.result_code.num_code == data::ResultCode::Numeric::kNeedCompletion) {
storage_->saveInstalledVersion(getSerialResp().ToString(), target_to_apply, InstalledVersionUpdateMode::kPending);
} else {
storage_->saveInstalledVersion(getSerialResp().ToString(), target_to_apply, InstalledVersionUpdateMode::kCurrent);
}
auto install_result = pacman->install(pending_target_);

// 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);
switch (install_result.result_code.num_code) {
case data::ResultCode::Numeric::kOk: {
storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kCurrent);
pending_target_ = Uptane::Target::Unknown();
break;
}
case data::ResultCode::Numeric::kNeedCompletion: {
storage_->saveInstalledVersion(ecu_serial_.ToString(), pending_target_, InstalledVersionUpdateMode::kPending);
break;
}
default: {}
}

LOG_INFO << "Target has been successfully installed: " << target_name;
return install_result.result_code.num_code;
}

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

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

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 @@ -274,5 +227,101 @@ bool AktualizrSecondary::doFullVerification(const Metadata& metadata) {
return false;
}

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

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

pending_target_ = targetsForThisEcu[0];

return true;
}

bool OstreeDirectDownloader::download(const Uptane::Target& target, const std::string& data) {
std::string treehub_server;
bool download_result = false;

try {
std::string ca, cert, pkey, server_url;
extractCredentialsArchive(data, &ca, &cert, &pkey, &server_url);
_keyMngr.loadKeys(&pkey, &cert, &ca);
boost::trim(server_url);
treehub_server = server_url;
} catch (std::runtime_error& exc) {
LOG_ERROR << exc.what();
return false;
}

auto install_res = OstreeManager::pull(_sysrootPath, treehub_server, _keyMngr, target);

switch (install_res.result_code.num_code) {
case data::ResultCode::Numeric::kOk: {
LOG_INFO << "The target revision has been successfully downloaded: " << target.sha256Hash();
download_result = true;
break;
}
case data::ResultCode::Numeric::kAlreadyProcessed: {
LOG_INFO << "The target revision is already present on the local ostree repo: " << target.sha256Hash();
download_result = true;
break;
}
default: {
LOG_ERROR << "Failed to download the target revision: " << target.sha256Hash() << " ( "
<< install_res.result_code.toString() << " ): " << install_res.description;
}
}

return download_result;
}

void OstreeDirectDownloader::extractCredentialsArchive(const std::string& archive, std::string* ca, std::string* cert,
std::string* pkey, std::string* treehub_server) {
::extractCredentialsArchive(archive, ca, cert, pkey, treehub_server);
}

void 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);
}
}

bool FakeDownloader::download(const Uptane::Target& target, const std::string& data) {
auto target_hashes = target.hashes();
if (target_hashes.size() == 0) {
LOG_ERROR << "No hash found in the target metadata: " << target.filename();
return false;
}

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

if (!target.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;
}

} catch (const std::exception& exc) {
LOG_ERROR << "Failed to generate a hash of the received image data: " << exc.what();
return false;
}

return true;
}
47 changes: 45 additions & 2 deletions src/aktualizr_secondary/aktualizr_secondary.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ class AktualizrSecondary : public AktualizrSecondaryInterface, private Aktualizr
int32_t getRootVersionResp(bool director) const;
bool putRootResp(const std::string& root, bool director);
bool sendFirmwareResp(const std::shared_ptr<std::string>& firmware);
data::ResultCode::Numeric installResp(const std::string& target_name);

static void extractCredentialsArchive(const std::string& archive, std::string* ca, std::string* cert,
std::string* pkey, std::string* treehub_server);
bool pendingRebootToApplyUpdate() {
// TODO: it's up to a pack man to know whether there is a pending install or not
return storage_->hasPendingInstall() && pending_target_.IsValid();
}

bool rebootDetected() { return pacman->rebootDetected() && storage_->hasPendingInstall(); }

private:
void connectToPrimary();
Expand All @@ -45,6 +50,44 @@ class AktualizrSecondary : public AktualizrSecondaryInterface, private Aktualizr
SocketServer socket_server_;
Uptane::DirectorRepository director_repo_;
Uptane::ImagesRepository image_repo_;
Uptane::Target pending_target_{Uptane::Target::Unknown()};
};

// TBD: consider moving this and SotaUptaneClient::secondaryTreehubCredentials() to encapsulate them in one place that
// is shared between IP Secondary's component
void extractCredentialsArchive(const std::string& archive, std::string* ca, std::string* cert, std::string* pkey,
std::string* treehub_server);

class Downloader {
public:
Downloader() = default;
virtual ~Downloader() = default;

Downloader(const Downloader&) = delete;
Downloader& operator=(const Downloader&) = delete;

virtual bool download(const Uptane::Target& target, const std::string& data) = 0;
};

class OstreeDirectDownloader : public Downloader {
public:
OstreeDirectDownloader(const boost::filesystem::path& sysroot_path, KeyManager& key_mngr)
: _sysrootPath(sysroot_path), _keyMngr(key_mngr) {}

bool download(const Uptane::Target& target, const std::string& data) override;

private:
static void extractCredentialsArchive(const std::string& archive, std::string* ca, std::string* cert,
std::string* pkey, std::string* treehub_server);

private:
boost::filesystem::path _sysrootPath;
KeyManager& _keyMngr;
};

class FakeDownloader : public Downloader {
public:
bool download(const Uptane::Target& target, const std::string& data) override;
};

#endif // AKTUALIZR_SECONDARY_H
Loading

0 comments on commit e100348

Please sign in to comment.