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

Test/ostree full fake install #1195

Merged
merged 9 commits into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ These are the primary actions that a user of libaktualizr can perform through th
- [x] Send EcuInstallationStartedReport to server for primary (uptane_test.cc, aktualizr_test.cc)
- [x] Send an event report (see below)
- [x] Install an update on the primary
- [ ] Install an OSTree update on the primary
- [x] Install an OSTree update on the primary (aktualizr_fullostree_test.cc)
- [ ] Notify "reboot needed" after an OSTree update trigger
- [x] Set new version to pending status after an OSTree update trigger (aktualizr_test.cc)
- [x] Send EcuInstallationAppliedReport to server after an OSTree update trigger (aktualizr_test.cc)
Expand Down
57 changes: 29 additions & 28 deletions src/aktualizr_repo/image_repo.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
#include "image_repo.h"

void ImageRepo::addImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
const Delegation &delegation) {
void ImageRepo::addImage(const std::string &name, const Json::Value &target, const Delegation &delegation) {
boost::filesystem::path repo_dir(path_ / "repo/image");

boost::filesystem::path targets_path =
delegation ? ((repo_dir / "delegations") / delegation.name).string() + ".json" : repo_dir / "targets.json";
Json::Value targets = Utils::parseJSONFile(targets_path)["signed"];
targets["targets"][name] = target;
targets["version"] = (targets["version"].asUInt()) + 1;

auto role = delegation ? Uptane::Role(delegation.name, true) : Uptane::Role::Targets();
std::string signed_targets = Utils::jsonToCanonicalStr(signTuf(role, targets));
Utils::writeFile(targets_path, signed_targets);
updateRepo();
}

void ImageRepo::addBinaryImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
const Delegation &delegation) {
boost::filesystem::path repo_dir(path_ / "repo/image");

boost::filesystem::path targets_path = repo_dir / "targets";
Expand All @@ -18,22 +33,21 @@ void ImageRepo::addImage(const boost::filesystem::path &image_path, const boost:
target["length"] = Json::UInt64(image.size());
target["hashes"]["sha256"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(image)));
target["hashes"]["sha512"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(image)));
target["custom"]["targetFormat"] = "BINARY";
addImage(targetname.string(), target, delegation);
}

void ImageRepo::addImage(const std::string &name, const Json::Value &target, const Delegation &delegation) {
boost::filesystem::path repo_dir(path_ / "repo/image");

boost::filesystem::path targets_path =
delegation ? ((repo_dir / "delegations") / delegation.name).string() + ".json" : repo_dir / "targets.json";
Json::Value targets = Utils::parseJSONFile(targets_path)["signed"];
targets["targets"][name] = target;
targets["version"] = (targets["version"].asUInt()) + 1;

auto role = delegation ? Uptane::Role(delegation.name, true) : Uptane::Role::Targets();
std::string signed_targets = Utils::jsonToCanonicalStr(signTuf(role, targets));
Utils::writeFile(targets_path, signed_targets);
updateRepo();
void ImageRepo::addCustomImage(const std::string &name, const Uptane::Hash &hash, const uint64_t length,
const Delegation &delegation, const Json::Value &custom) {
Json::Value target;
target["length"] = Json::UInt(length);
if (hash.type() == Uptane::Hash::Type::kSha256) {
target["hashes"]["sha256"] = hash.HashString();
} else if (hash.type() == Uptane::Hash::Type::kSha512) {
target["hashes"]["sha512"] = hash.HashString();
}
target["custom"] = custom;
addImage(name, target, delegation);
}

void ImageRepo::addDelegation(const Uptane::Role &name, const Uptane::Role &parent_role, const std::string &path,
Expand Down Expand Up @@ -137,16 +151,3 @@ std::vector<std::string> ImageRepo::getDelegationTargets(const Uptane::Role &nam
}
return result;
}

void ImageRepo::addCustomImage(const std::string &name, const Uptane::Hash &hash, const uint64_t length,
const Delegation &delegation, const Json::Value &custom) {
Json::Value target;
target["length"] = Json::UInt(length);
if (hash.type() == Uptane::Hash::Type::kSha256) {
target["hashes"]["sha256"] = hash.HashString();
} else if (hash.type() == Uptane::Hash::Type::kSha512) {
target["hashes"]["sha512"] = hash.HashString();
}
target["custom"] = custom;
addImage(name, target, delegation);
}
4 changes: 2 additions & 2 deletions src/aktualizr_repo/image_repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class ImageRepo : public Repo {
public:
ImageRepo(boost::filesystem::path path, const std::string &expires, std::string correlation_id)
: Repo(Uptane::RepositoryType::Image(), std::move(path), expires, std::move(correlation_id)) {}
void addImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
const Delegation &delegation = {});
void addBinaryImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
const Delegation &delegation = {});
void addCustomImage(const std::string &name, const Uptane::Hash &hash, uint64_t length, const Delegation &delegation,
const Json::Value &custom = {});
void addDelegation(const Uptane::Role &name, const Uptane::Role &parent_role, const std::string &path,
Expand Down
8 changes: 8 additions & 0 deletions src/aktualizr_repo/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ int main(int argc, char **argv) {
("path", po::value<boost::filesystem::path>(), "path to the repository")
("filename", po::value<boost::filesystem::path>(), "path to the image")
("hwid", po::value<std::string>(), "target hardware identifier")
("targetformat", po::value<std::string>(), "format of target for 'image' command")
("targetcustom", po::value<boost::filesystem::path>(), "path to custom JSON for 'image' command")
("serial", po::value<std::string>(), "target ECU serial")
("expires", po::value<std::string>(), "expiration time")
Expand Down Expand Up @@ -117,9 +118,16 @@ int main(int argc, char **argv) {
hash = std_::make_unique<Uptane::Hash>(Uptane::Hash::Type::kSha512, vm["targetsha512"].as<std::string>());
}
Json::Value custom;
if (vm.count("targetcustom") > 0 && vm.count("targetformat") > 0) {
std::cerr << "--targetcustom and --targetformat cannot be used together";
exit(EXIT_FAILURE);
}
if (vm.count("targetcustom") > 0) {
std::ifstream custom_file(vm["targetcustom"].as<boost::filesystem::path>().c_str());
custom_file >> custom;
} else if (vm.count("targetformat") > 0) {
custom = Json::Value();
custom["targetFormat"] = vm["targetformat"].as<std::string>();
}
repo.addCustomImage(targetname.string(), *hash, vm["targetlength"].as<uint64_t>(), delegation, custom);
}
Expand Down
2 changes: 1 addition & 1 deletion src/aktualizr_repo/uptane_repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void UptaneRepo::revokeDelegation(const Uptane::Role &name) {

void UptaneRepo::addImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
const Delegation &delegation) {
image_repo_.addImage(image_path, targetname, delegation);
image_repo_.addBinaryImage(image_path, targetname, delegation);
}
void UptaneRepo::addCustomImage(const std::string &name, const Uptane::Hash &hash, uint64_t length,
const Delegation &delegation, const Json::Value &custom) {
Expand Down
2 changes: 2 additions & 0 deletions src/libaktualizr/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,6 @@ class Config : public BaseConfig {
bool loglevel_from_cmdline{false};
};

std::ostream& operator<<(std::ostream& os, const Config& cfg);

#endif // CONFIG_H_
2 changes: 1 addition & 1 deletion src/libaktualizr/http/httpclient_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ int main(int argc, char** argv) {

std::string port = TestUtils::getFreePort();
server += port;
boost::process::child server_process("tests/fake_http_server/fake_http_server.py", port);
boost::process::child server_process("tests/fake_http_server/fake_test_server.py", port, "-f");
TestUtils::waitForServer(server + "/");

return RUN_ALL_TESTS();
Expand Down
2 changes: 1 addition & 1 deletion src/libaktualizr/package_manager/fetcher_death_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ int main(int argc, char** argv) {
std::string port = TestUtils::getFreePort();
server = "http://127.0.0.1:" + port;
config.uptane.repo_server = server;
boost::process::child http_server_process("tests/fake_http_server/fake_http_server.py", port);
boost::process::child http_server_process("tests/fake_http_server/fake_test_server.py", port, "-f");
TestUtils::waitForServer(server + "/");
return RUN_ALL_TESTS();
}
Expand Down
2 changes: 1 addition & 1 deletion src/libaktualizr/package_manager/fetcher_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ int main(int argc, char** argv) {

std::string port = TestUtils::getFreePort();
server += port;
boost::process::child http_server_process("tests/fake_http_server/fake_http_server.py", port);
boost::process::child http_server_process("tests/fake_http_server/fake_test_server.py", port, "-f");
TestUtils::waitForServer(server + "/");
#ifdef BUILD_OSTREE
std::string treehub_port = TestUtils::getFreePort();
Expand Down
9 changes: 9 additions & 0 deletions src/libaktualizr/primary/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ add_library(primary OBJECT ${SOURCES})
add_aktualizr_test(NAME aktualizr SOURCES aktualizr_test.cc PROJECT_WORKING_DIRECTORY ARGS ${PROJECT_BINARY_DIR}/uptane_repos)
add_dependencies(t_aktualizr uptane_repo_full_no_correlation_id)

if (BUILD_OSTREE)
add_aktualizr_test(NAME aktualizr_fullostree SOURCES aktualizr_fullostree_test.cc PROJECT_WORKING_DIRECTORY ARGS $<TARGET_FILE:aktualizr-repo> ${PROJECT_BINARY_DIR}/ostree_repo)
set_target_properties(t_aktualizr_fullostree PROPERTIES LINK_FLAGS -Wl,--export-dynamic)
add_dependencies(t_aktualizr_fullostree ostree_mock aktualizr-repo make_ostree_sysroot)
set_tests_properties(test_aktualizr_fullostree PROPERTIES ENVIRONMENT LD_PRELOAD=$<TARGET_FILE:ostree_mock>)
else (BUILD_OSTREE)
aktualizr_source_file_checks(aktualizr_fullostree_test.cc)
endif (BUILD_OSTREE)

add_aktualizr_test(NAME reportqueue SOURCES reportqueue_test.cc PROJECT_WORKING_DIRECTORY)
add_aktualizr_test(NAME emptytargets SOURCES empty_targets_test.cc PROJECT_WORKING_DIRECTORY
ARGS "$<TARGET_FILE:aktualizr-repo>")
Expand Down
1 change: 1 addition & 0 deletions src/libaktualizr/primary/aktualizr.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class Aktualizr {
FRIEND_TEST(Aktualizr, AddSecondary);
FRIEND_TEST(Aktualizr, EmptyTargets);
FRIEND_TEST(Aktualizr, EmptyTargetsAfterInstall);
FRIEND_TEST(Aktualizr, FullOstreeUpdate);
FRIEND_TEST(Delegation, Basic);
FRIEND_TEST(Delegation, RevokeAfterCheckUpdates);
FRIEND_TEST(Delegation, RevokeAfterInstall);
Expand Down
163 changes: 163 additions & 0 deletions src/libaktualizr/primary/aktualizr_fullostree_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include <gtest/gtest.h>

#include <future>
#include <iostream>
#include <string>
#include <thread>

#include <boost/process.hpp>

#include "uptane_test_common.h"

#include "config/config.h"
#include "logging/logging.h"
#include "package_manager/ostreemanager.h"
#include "primary/aktualizr.h"
#include "storage/sqlstorage.h"
#include "test_utils.h"

boost::filesystem::path aktualizr_repo_path;
static std::string server = "http://127.0.0.1:";
static std::string treehub_server = "http://127.0.0.1:";
static boost::filesystem::path sysroot;

static struct {
int serial{0};
std::string rev;
} ostree_deployment;
static std::string new_rev;

#include <ostree.h>
extern "C" OstreeDeployment *ostree_sysroot_get_booted_deployment_mock(OstreeSysroot *self) {
(void)self;
static GObjectUniquePtr<OstreeDeployment> dep;

dep.reset(ostree_deployment_new(0, "dummy-os", ostree_deployment.rev.c_str(), ostree_deployment.serial,
ostree_deployment.rev.c_str(), ostree_deployment.serial));
return dep.get();
}

extern "C" const char *ostree_deployment_get_csum(OstreeDeployment *self) {
(void)self;
return ostree_deployment.rev.c_str();
}

/*
* Install an OSTree update on the primary.
*/
TEST(Aktualizr, FullOstreeUpdate) {
TemporaryDirectory temp_dir;
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, server);
conf.pacman.type = PackageManager::kOstree;
conf.pacman.sysroot = sysroot.string();
conf.pacman.ostree_server = treehub_server;
conf.pacman.os = "dummy-os";
conf.provision.device_id = "device_id";
conf.provision.ecu_registration_endpoint = server + "/director/ecus";
conf.tls.server = server;
lbonn marked this conversation as resolved.
Show resolved Hide resolved

LOG_INFO << "conf: " << conf;

{
Aktualizr aktualizr(conf);

aktualizr.Initialize();

result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
ASSERT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);

result::Download download_result = aktualizr.Download(update_result.updates).get();
EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);

result::Install install_result = aktualizr.Install(update_result.updates).get();
EXPECT_EQ(install_result.ecu_reports.size(), 1);
EXPECT_EQ(install_result.ecu_reports[0].install_res.result_code.num_code,
data::ResultCode::Numeric::kNeedCompletion);
}

// do "reboot" and finalize
ostree_deployment.serial = 1;
ostree_deployment.rev = new_rev;
boost::filesystem::remove(conf.bootloader.reboot_sentinel_dir / conf.bootloader.reboot_sentinel_name);

{
Aktualizr aktualizr(conf);

aktualizr.Initialize();

result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
ASSERT_EQ(update_result.status, result::UpdateStatus::kNoUpdatesAvailable);

// check new version
const auto target = aktualizr.uptane_client_->package_manager_->getCurrent();
EXPECT_EQ(target.sha256Hash(), new_rev);
}
lbonn marked this conversation as resolved.
Show resolved Hide resolved
}

#ifndef __NO_MAIN__
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);

logger_init();

if (argc != 3) {
std::cerr << "Error: " << argv[0] << " requires the path to the aktualizr-repo utility "
<< "and an OStree sysroot\n";
return EXIT_FAILURE;
}
aktualizr_repo_path = argv[1];

Process ostree("ostree");

TemporaryDirectory meta_dir;
TemporaryDirectory temp_sysroot;
sysroot = temp_sysroot / "sysroot";
// uses cp, as boost doesn't like to copy bad symlinks
int res = system((std::string("cp -r ") + argv[2] + std::string(" ") + sysroot.string()).c_str());
if (res != 0) {
return -1;
}
auto r = ostree.run(
{"rev-parse", std::string("--repo"), (sysroot / "/ostree/repo").string(), "generate-remote/generated"});
if (std::get<0>(r) != 0) {
return -1;
}
ostree_deployment.serial = 0;
ostree_deployment.rev = ostree.lastStdOut();
boost::trim_if(ostree_deployment.rev, boost::is_any_of(" \t\r\n"));
LOG_INFO << "ORIG: " << ostree_deployment.rev;

std::string port = TestUtils::getFreePort();
server += port;
boost::process::child http_server_process("tests/fake_http_server/fake_test_server.py", port, "-m", meta_dir.Path());
TestUtils::waitForServer(server + "/");

std::string treehub_port = TestUtils::getFreePort();
treehub_server += treehub_port;
TemporaryDirectory treehub_dir;
boost::process::child ostree_server_process("tests/sota_tools/treehub_server.py", std::string("-p"), treehub_port,
std::string("-d"), treehub_dir.PathString(), std::string("-s0.5"),
std::string("--create"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you still need the treehub_server if you are using the fake_test_server? I'd hoped when I wrote that for it to be able to handle all server tasks in one script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried, to be honest. This test is mostly a mix-and-match of other tests that we have.

However:

  • treehub_server also generates a repository at startup which the test uses
  • there is the cgi / multipart business that I don't quite understand.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem. It'd be nice to clean up our various test servers, but it doesn't need to be done now. We can come to back to that later if you'd like.

TestUtils::waitForServer(treehub_server + "/");
r = ostree.run({"rev-parse", std::string("--repo"), treehub_dir.PathString(), "master"});
if (std::get<0>(r) != 0) {
return -1;
}
new_rev = ostree.lastStdOut();
boost::trim_if(new_rev, boost::is_any_of(" \t\r\n"));
LOG_INFO << "DEST: " << new_rev;

Process akt_repo(aktualizr_repo_path.string());
akt_repo.run({"generate", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
akt_repo.run({"image", "--path", meta_dir.PathString(), "--targetname", "update_1.0", "--targetsha256", new_rev,
"--targetlength", "0", "--targetformat", "OSTREE"});
akt_repo.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "update_1.0", "--hwid", "primary_hw",
"--serial", "CA:FE:A6:D2:84:9D"});
akt_repo.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
LOG_INFO << akt_repo.lastStdOut();
// Work around inconsistent directory naming.
Utils::copyDir(meta_dir.Path() / "repo/image", meta_dir.Path() / "repo/repo");

return RUN_ALL_TESTS();
}
#endif // __NO_MAIN__
5 changes: 0 additions & 5 deletions src/libaktualizr/primary/aktualizr_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,6 @@ TEST(Aktualizr, FullWithUpdatesNeedReboot) {
auto http = std::make_shared<HttpFakePutCounter>(temp_dir.Path());
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
conf.pacman.fake_need_reboot = true;
conf.bootloader.reboot_sentinel_dir = temp_dir.Path();

{
// first run: do the install
Expand Down Expand Up @@ -602,7 +601,6 @@ TEST(Aktualizr, FinalizationFailure) {
conf.pacman.fake_need_reboot = true;
conf.uptane.force_install_completion = true;
conf.uptane.polling_sec = 0;
conf.bootloader.reboot_sentinel_dir = temp_dir.Path();
auto storage = INvStorage::newStorage(conf.storage);

std::vector<std::string> expected_event_order = {
Expand Down Expand Up @@ -781,7 +779,6 @@ TEST(Aktualizr, InstallationFailure) {
TemporaryDirectory temp_dir;
auto http_server_mock = std::make_shared<HttpInstallationFailed>(temp_dir.Path());
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http_server_mock->tls_server);
conf.bootloader.reboot_sentinel_dir = temp_dir.Path();
auto storage = INvStorage::newStorage(conf.storage);

fiu_init(0);
Expand Down Expand Up @@ -841,7 +838,6 @@ TEST(Aktualizr, InstallationFailure) {
TemporaryDirectory temp_dir;
auto http_server_mock = std::make_shared<HttpInstallationFailed>(temp_dir.Path());
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http_server_mock->tls_server);
conf.bootloader.reboot_sentinel_dir = temp_dir.Path();
auto storage = INvStorage::newStorage(conf.storage);
const std::string sec_fault_name = std::string("secondary_install_") + secondary_ecu_id;

Expand Down Expand Up @@ -927,7 +923,6 @@ TEST(Aktualizr, AutoRebootAfterUpdate) {
conf.pacman.fake_need_reboot = true;
conf.uptane.force_install_completion = true;
conf.uptane.polling_sec = 0;
conf.bootloader.reboot_sentinel_dir = temp_dir.Path();

{
// first run: do the install, exit UptaneCycle and emulate reboot since force_install_completion is set
Expand Down
Loading