diff --git a/CMakeLists.txt b/CMakeLists.txt index 890aee06e9..17514e7e6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ option(PEDANTIC_WARNINGS "Compile with pedantic warnings" OFF) option(BUILD_WITH_CODE_COVERAGE "Enable gcov code coverage" OFF) option(BUILD_OSTREE "Set to ON to compile with ostree support" OFF) option(BUILD_DEB "Set to ON to compile with debian packages support" OFF) +option(BUILD_DOCKERAPP "Set to ON to compile with package manager support of docker-app" OFF) option(BUILD_P11 "Support for key storage in a HSM via PKCS#11" OFF) option(BUILD_SOTA_TOOLS "Set to ON to build SOTA tools" OFF) option(BUILD_PARTIAL "Set to ON to compile with partial secondaries support" OFF) @@ -122,6 +123,10 @@ else(BUILD_DEB) unset(LIBDPKG_LIBRARIES CACHE) endif(BUILD_DEB) +if(BUILD_DOCKERAPP) + add_definitions(-DBUILD_DOCKERAPP) +endif(BUILD_DOCKERAPP) + if(BUILD_P11) find_package(LibP11 REQUIRED) add_definitions(-DBUILD_P11) diff --git a/src/aktualizr_lite/main.cc b/src/aktualizr_lite/main.cc index c490c26896..8aeba4b107 100644 --- a/src/aktualizr_lite/main.cc +++ b/src/aktualizr_lite/main.cc @@ -38,7 +38,7 @@ static int list_main(Config &config, const bpo::variables_map &unused) { } } - LOG_INFO << "Updates for available to " << hwid << ":"; + LOG_INFO << "Updates available to " << hwid << ":"; for (auto &t : client->allTargets()) { for (auto const &it : t.hardwareIds()) { if (it == hwid) { @@ -47,6 +47,21 @@ static int list_main(Config &config, const bpo::variables_map &unused) { name = t.custom_version(); } LOG_INFO << name << "\tsha256:" << t.sha256Hash(); + if (config.pacman.type == PackageManager::kOstreeDockerApp) { + bool shown = false; + auto apps = t.custom_data()["docker_apps"]; + for (Json::ValueIterator i = apps.begin(); i != apps.end(); ++i) { + if (!shown) { + shown = true; + LOG_INFO << "\tDocker Apps:"; + } + if ((*i).isObject() && (*i).isMember("filename")) { + LOG_INFO << "\t\t" << i.key().asString() << " -> " << (*i)["filename"].asString(); + } else { + LOG_ERROR << "\t\tInvalid custom data for docker-app: " << i.key().asString(); + } + } + } break; } } diff --git a/src/libaktualizr/package_manager/CMakeLists.txt b/src/libaktualizr/package_manager/CMakeLists.txt index f1543f8665..181c19af12 100644 --- a/src/libaktualizr/package_manager/CMakeLists.txt +++ b/src/libaktualizr/package_manager/CMakeLists.txt @@ -40,6 +40,11 @@ if(BUILD_OSTREE) add_aktualizr_test(NAME ostree SOURCES ostreemanager_test.cc PROJECT_WORKING_DIRECTORY NO_VALGRIND ARGS ${PROJECT_BINARY_DIR}/ostree_repo) + if(BUILD_DOCKERAPP) + target_sources(package_manager PRIVATE dockerappmanager.cc) + add_aktualizr_test(NAME dockerapp SOURCES dockerappmanager_test.cc PROJECT_WORKING_DIRECTORY NO_VALGRIND + ARGS ${PROJECT_BINARY_DIR}/ostree_repo "$") + endif(BUILD_DOCKERAPP) endif(BUILD_OSTREE) add_aktualizr_test(NAME packagemanager_factory SOURCES packagemanagerfactory_test.cc NO_VALGRIND @@ -55,6 +60,7 @@ aktualizr_source_file_checks(ostreemanager.cc ostreereposync.cc ostreemanager.h ostreereposync.h) aktualizr_source_file_checks(androidmanager.cc androidmanager.h) +aktualizr_source_file_checks(dockerappmanager.cc dockerappmanager.h dockerappmanager_test.cc) if(ANDROID) target_sources(package_manager PRIVATE androidmanager.cc) diff --git a/src/libaktualizr/package_manager/docker_fake.sh b/src/libaktualizr/package_manager/docker_fake.sh new file mode 100755 index 0000000000..4e50b6ff1e --- /dev/null +++ b/src/libaktualizr/package_manager/docker_fake.sh @@ -0,0 +1,29 @@ +#! /bin/bash +set -eEuo pipefail + +if [ "$1" = "app" ] ; then + echo "DOCKER-APP RENDER OUTPUT" + if [ ! -f app1.dockerapp ] ; then + echo "Missing docker app file!" + exit 1 + fi + cat app1.dockerapp + exit 0 +fi +if [ "$1" = "up" ] ; then + echo "DOCKER-COMPOSE UP" + if [ ! -f docker-compose.yml ] ; then + echo "Missing docker-compose file!" + exit 1 + fi + # the content of dockerapp includes the sha of the target, so this should + # be present in the docker-compose.yml it creates. + if ! grep primary docker-compose.yml ; then + echo "Could not find expected content in docker-compose.yml" + cat docker-compose.yml + exit 1 + fi + exit 0 +fi +echo "Unknown command: $*" +exit 1 diff --git a/src/libaktualizr/package_manager/dockerapp_test_repo.sh b/src/libaktualizr/package_manager/dockerapp_test_repo.sh new file mode 100755 index 0000000000..517bc81e22 --- /dev/null +++ b/src/libaktualizr/package_manager/dockerapp_test_repo.sh @@ -0,0 +1,34 @@ +#! /bin/bash +set -eEuo pipefail + +if [ "$#" -lt 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +AKTUALIZR_REPO="$1" +DEST_DIR="$2" +PORT="$3" + +akrepo() { + "$AKTUALIZR_REPO" --path "$DEST_DIR" "$@" +} + +mkdir -p "$DEST_DIR" +trap 'rm -rf "$DEST_DIR"' ERR + +IMAGES=$(mktemp -d) +trap 'rm -rf "$IMAGES"' exit +DOCKER_APP="$IMAGES/foo.dockerapp" +echo "fake contents of a docker app" > "$DOCKER_APP" + +akrepo --command generate --expires 2021-07-04T16:33:27Z +akrepo --command image --filename "$DOCKER_APP" --targetname foo.dockerapp +akrepo --command addtarget --hwid primary_hw --serial CA:FE:A6:D2:84:9D --targetname foo.dockerapp +akrepo --command signtargets + +cd $DEST_DIR +echo "Target.json is: " +cat repo/image/targets.json +echo "Running repo server port on $PORT" +exec python3 -m http.server $PORT diff --git a/src/libaktualizr/package_manager/dockerappmanager.cc b/src/libaktualizr/package_manager/dockerappmanager.cc new file mode 100644 index 0000000000..30d83d0ef4 --- /dev/null +++ b/src/libaktualizr/package_manager/dockerappmanager.cc @@ -0,0 +1,116 @@ +#include "dockerappmanager.h" + +#include + +struct DockerApp { + DockerApp(const std::string app_name, const PackageConfig &config) + : name(std::move(app_name)), + app_root(std::move(config.docker_apps_root / app_name)), + app_params(std::move(config.docker_app_params)), + app_bin(std::move(config.docker_app_bin)), + compose_bin(std::move(config.docker_compose_bin)) {} + + bool render(const std::string &app_content) { + auto bin = boost::filesystem::canonical(app_bin).string(); + Utils::writeFile(app_root / (name + ".dockerapp"), app_content); + std::string cmd("cd " + app_root.string() + " && " + bin + " app render " + name); + if (!app_params.empty()) { + cmd += " -f " + app_params.string(); + } + std::string yaml; + if (Utils::shell(cmd, &yaml, true) != 0) { + LOG_ERROR << "Unable to run " << cmd << " output:\n" << yaml; + return false; + } + Utils::writeFile(app_root / "docker-compose.yml", yaml); + return true; + } + + bool start() { + // Depending on the number and size of the containers in the docker-app, + // this command can take a bit of time to complete. Rather than using, + // Utils::shell which isn't interactive, we'll use std::system so that + // stdout/stderr is streamed while docker sets things up. + auto bin = boost::filesystem::canonical(compose_bin).string(); + std::string cmd("cd " + app_root.string() + " && " + bin + " up --remove-orphans -d"); + if (std::system(cmd.c_str()) != 0) { + return false; + } + return true; + } + + std::string name; + boost::filesystem::path app_root; + boost::filesystem::path app_params; + boost::filesystem::path app_bin; + boost::filesystem::path compose_bin; +}; + +bool DockerAppManager::iterate_apps(const Uptane::Target &target, DockerAppCb cb) const { + auto apps = target.custom_data()["docker_apps"]; + bool res = true; + Uptane::ImagesRepository repo; + // checkMetaOffline pulls in data from INvStorage to properly initialize + // the targets member of the instance so that we can use the LazyTargetList + repo.checkMetaOffline(*storage_); + + if (!apps) { + LOG_DEBUG << "Detected an update target from Director with no docker-apps data"; + for (const auto t : Uptane::LazyTargetsList(repo, storage_, fake_fetcher_)) { + if (t == target) { + LOG_DEBUG << "Found the match " << t; + apps = t.custom_data()["docker_apps"]; + break; + } + } + } + + for (const auto t : Uptane::LazyTargetsList(repo, storage_, fake_fetcher_)) { + for (Json::ValueIterator i = apps.begin(); i != apps.end(); ++i) { + if ((*i).isObject() && (*i).isMember("filename")) { + for (auto app : config.docker_apps) { + if (i.key().asString() == app && (*i)["filename"].asString() == t.filename()) { + if (!cb(app, t)) { + res = false; + } + } + } + } else { + LOG_ERROR << "Invalid custom data for docker-app: " << i.key().asString() << " -> " << *i; + } + } + } + return res; +} + +bool DockerAppManager::fetchTarget(const Uptane::Target &target, Uptane::Fetcher &fetcher, const KeyManager &keys, + FetcherProgressCb progress_cb, const api::FlowControlToken *token) { + if (!OstreeManager::fetchTarget(target, fetcher, keys, progress_cb, token)) { + return false; + } + + LOG_INFO << "Looking for DockerApps to fetch"; + auto cb = [this, &fetcher, &keys, progress_cb, token](const std::string &app, const Uptane::Target &app_target) { + LOG_INFO << "Fetching " << app << " -> " << app_target; + return PackageManagerInterface::fetchTarget(app_target, fetcher, keys, progress_cb, token); + }; + return iterate_apps(target, cb); +} + +data::InstallationResult DockerAppManager::install(const Uptane::Target &target) const { + auto res = OstreeManager::install(target); + auto cb = [this](const std::string &app, const Uptane::Target &app_target) { + LOG_INFO << "Installing " << app << " -> " << app_target; + std::stringstream ss; + ss << *storage_->openTargetFile(app_target); + DockerApp dapp(app, config); + if (!dapp.render(ss.str()) || !dapp.start()) { + return false; + } + return true; + }; + if (!iterate_apps(target, cb)) { + return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "Could not render docker app"); + } + return res; +} diff --git a/src/libaktualizr/package_manager/dockerappmanager.h b/src/libaktualizr/package_manager/dockerappmanager.h new file mode 100644 index 0000000000..a8e5a851a0 --- /dev/null +++ b/src/libaktualizr/package_manager/dockerappmanager.h @@ -0,0 +1,28 @@ +#ifndef DOCKERAPPMGR_H_ +#define DOCKERAPPMGR_H_ + +#include "ostreemanager.h" +#include "uptane/iterator.h" + +using DockerAppCb = std::function; + +class DockerAppManager : public OstreeManager { + public: + DockerAppManager(PackageConfig pconfig, std::shared_ptr storage, std::shared_ptr bootloader, + std::shared_ptr http) + : OstreeManager(pconfig, storage, bootloader, http) { + fake_fetcher_ = std::make_shared(Config(), http_); + } + bool fetchTarget(const Uptane::Target &target, Uptane::Fetcher &fetcher, const KeyManager &keys, + FetcherProgressCb progress_cb, const api::FlowControlToken *token = nullptr) override; + data::InstallationResult install(const Uptane::Target &target) const override; + std::string name() const override { return "ostree+docker-app"; } + + private: + bool iterate_apps(const Uptane::Target &target, DockerAppCb cb) const; + + // iterate_apps needs an Uptane::Fetcher. However, its an unused parameter + // and we just need to construct a dummy one to make the compiler happy. + std::shared_ptr fake_fetcher_; +}; +#endif // DOCKERAPPMGR_H_ diff --git a/src/libaktualizr/package_manager/dockerappmanager_test.cc b/src/libaktualizr/package_manager/dockerappmanager_test.cc new file mode 100644 index 0000000000..353a6a0375 --- /dev/null +++ b/src/libaktualizr/package_manager/dockerappmanager_test.cc @@ -0,0 +1,122 @@ +#include + +#include +#include + +#include "config/config.h" +#include "http/httpclient.h" +#include "package_manager/packagemanagerfactory.h" +#include "package_manager/packagemanagerinterface.h" +#include "primary/sotauptaneclient.h" +#include "storage/invstorage.h" +#include "test_utils.h" +#include "uptane/fetcher.h" + +static std::string repo_server = "http://127.0.0.1:"; +static std::string treehub_server = "http://127.0.0.1:"; +static boost::filesystem::path test_sysroot; +static boost::filesystem::path akrepo; + +static void progress_cb(const Uptane::Target& target, const std::string& description, unsigned int progress) { + (void)description; + LOG_INFO << "progress_cb " << target << " " << progress; +} + +static std::unique_ptr create_repo(const boost::filesystem::path& repo_path) { + std::string port = TestUtils::getFreePort(); + repo_server += port; + auto p = std_::make_unique("src/libaktualizr/package_manager/dockerapp_test_repo.sh", akrepo, + repo_path, port); + TestUtils::waitForServer(repo_server + "/"); + return p; +} + +TEST(DockerAppManager, PackageManager_Factory_Good) { + Config config; + config.pacman.type = PackageManager::kOstreeDockerApp; + config.pacman.sysroot = test_sysroot; + TemporaryDirectory dir; + config.storage.path = dir.Path(); + + std::shared_ptr storage = INvStorage::newStorage(config.storage); + auto pacman = PackageManagerFactory::makePackageManager(config.pacman, storage, nullptr, nullptr); + EXPECT_TRUE(pacman); +} + +TEST(DockerAppManager, DockerApp_Fetch) { + std::string sha = Utils::readFile(test_sysroot / "ostree/repo/refs/heads/ostree/1/1/0", true); + Json::Value target_json; + target_json["hashes"]["sha256"] = sha; + target_json["custom"]["targetFormat"] = "OSTREE"; + target_json["length"] = 0; + target_json["custom"]["docker_apps"]["app1"]["filename"] = "foo.dockerapp"; + Uptane::Target target("pull", target_json); + + TemporaryDirectory temp_dir; + auto repo = temp_dir.Path(); + auto repod = create_repo(repo); + + Config config; + config.pacman.type = PackageManager::kOstreeDockerApp; + config.pacman.sysroot = test_sysroot; + config.pacman.docker_apps_root = temp_dir / "docker_apps"; + config.pacman.docker_apps.push_back("app1"); + config.pacman.docker_app_bin = config.pacman.docker_compose_bin = "src/libaktualizr/package_manager/docker_fake.sh"; + config.pacman.ostree_server = treehub_server; + config.uptane.repo_server = repo_server + "/repo/image"; + TemporaryDirectory dir; + config.storage.path = dir.Path(); + + std::shared_ptr storage = INvStorage::newStorage(config.storage); + KeyManager keys(storage, config.keymanagerConfig()); + auto http = std::make_shared(); + auto client = SotaUptaneClient::newTestClient(config, storage, http, nullptr); + ASSERT_TRUE(client->updateImagesMeta()); + + std::string targets = Utils::readFile(repo / "repo/image/targets.json"); + LOG_INFO << "Repo targets " << targets; + + bool result = client->package_manager_->fetchTarget(target, *(client->uptane_fetcher), keys, progress_cb, nullptr); + ASSERT_TRUE(result); + + auto hashes = std::vector{ + Uptane::Hash(Uptane::Hash::Type::kSha256, "dfca385c923400228c8ddd3c2d572919985e48a9409a2d71dab33148017231c3"), + Uptane::Hash(Uptane::Hash::Type::kSha512, + "76b183d51f53613a450825afc6f984077b68ae7b321ba041a2b3871f3c25a9a20d964ad0b60352e5fdd09b78fd53879f4e3" + "fa3dcc8335b26d3bbf455803d2ecb")}; + Uptane::Target app_target("foo.dockerapp", hashes, 8); + ASSERT_TRUE(storage->checkTargetFile(app_target)); + + client->package_manager_->install(target); + std::string content = Utils::readFile(config.pacman.docker_apps_root / "app1/docker-compose.yml"); + ASSERT_EQ("DOCKER-APP RENDER OUTPUT\nfake contents of a docker app\n", content); +} + +#ifndef __NO_MAIN__ +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (argc != 3) { + std::cerr << "Error: " << argv[0] + << " requires the path to an OSTree sysroot and aktualizr-repo as an input argument.\n"; + return EXIT_FAILURE; + } + akrepo = argv[2]; + + std::string port = TestUtils::getFreePort(); + treehub_server += port; + boost::process::child server_process("tests/fake_http_server/fake_test_server.py", port); + + TemporaryDirectory temp_dir; + // Utils::copyDir doesn't work here. Complaints about non existent symlink path + int r = system((std::string("cp -r ") + argv[1] + std::string(" ") + temp_dir.PathString()).c_str()); + if (r != 0) { + return -1; + } + test_sysroot = (temp_dir.Path() / "ostree_repo").string(); + + TestUtils::waitForServer(treehub_server + "/"); + + return RUN_ALL_TESTS(); +} +#endif diff --git a/src/libaktualizr/package_manager/packagemanagerconfig.cc b/src/libaktualizr/package_manager/packagemanagerconfig.cc index 5777cb06fa..53677bd81e 100644 --- a/src/libaktualizr/package_manager/packagemanagerconfig.cc +++ b/src/libaktualizr/package_manager/packagemanagerconfig.cc @@ -1,5 +1,8 @@ #include "package_manager/packagemanagerconfig.h" +#include +#include +#include #include std::ostream& operator<<(std::ostream& os, PackageManager pm) { @@ -11,6 +14,9 @@ std::ostream& operator<<(std::ostream& os, PackageManager pm) { case PackageManager::kDebian: pm_str = "debian"; break; + case PackageManager::kOstreeDockerApp: + pm_str = "ostree+docker-app"; + break; case PackageManager::kNone: default: pm_str = "none"; @@ -27,6 +33,18 @@ void PackageConfig::updateFromPropertyTree(const boost::property_tree::ptree& pt CopyFromConfig(ostree_server, "ostree_server", pt); CopyFromConfig(packages_file, "packages_file", pt); CopyFromConfig(fake_need_reboot, "fake_need_reboot", pt); +#ifdef BUILD_DOCKERAPP + std::string val; + CopyFromConfig(val, "docker_apps", pt); + if (val.length() > 0) { + // token_compress_on allows lists like: "foo,bar", "foo, bar", or "foo bar" + boost::split(docker_apps, val, boost::is_any_of(", "), boost::token_compress_on); + CopyFromConfig(docker_apps_root, "docker_apps_root", pt); + CopyFromConfig(docker_app_params, "docker_app_params", pt); + CopyFromConfig(docker_app_bin, "docker_app_bin", pt); + CopyFromConfig(docker_compose_bin, "docker_compose_bin", pt); + } +#endif } void PackageConfig::writeToStream(std::ostream& out_stream) const { @@ -36,4 +54,11 @@ void PackageConfig::writeToStream(std::ostream& out_stream) const { writeOption(out_stream, ostree_server, "ostree_server"); writeOption(out_stream, packages_file, "packages_file"); writeOption(out_stream, fake_need_reboot, "fake_need_reboot"); +#ifdef BUILD_DOCKERAPP + writeOption(out_stream, boost::algorithm::join(docker_apps, ","), "docker_apps"); + writeOption(out_stream, docker_apps_root, "docker_apps_root"); + writeOption(out_stream, docker_app_params, "docker_app_params"); + writeOption(out_stream, docker_app_bin, "docker_app_bin"); + writeOption(out_stream, docker_compose_bin, "docker_compose_bin"); +#endif } diff --git a/src/libaktualizr/package_manager/packagemanagerconfig.h b/src/libaktualizr/package_manager/packagemanagerconfig.h index bf4d25ecbe..d19bdd87c2 100644 --- a/src/libaktualizr/package_manager/packagemanagerconfig.h +++ b/src/libaktualizr/package_manager/packagemanagerconfig.h @@ -7,7 +7,7 @@ #include "utilities/config_utils.h" -enum class PackageManager { kNone = 0, kOstree, kDebian, kAndroid }; +enum class PackageManager { kNone = 0, kOstree, kDebian, kAndroid, kOstreeDockerApp }; std::ostream& operator<<(std::ostream& os, PackageManager pm); struct PackageConfig { @@ -17,6 +17,14 @@ struct PackageConfig { std::string ostree_server; boost::filesystem::path packages_file{"/usr/package.manifest"}; +#ifdef BUILD_DOCKERAPP + std::vector docker_apps; + boost::filesystem::path docker_apps_root; + boost::filesystem::path docker_app_params; + boost::filesystem::path docker_app_bin{"/usr/bin/docker-app"}; + boost::filesystem::path docker_compose_bin{"/usr/bin/docker-compose"}; +#endif + // Options for simulation (to be used with kNone) bool fake_need_reboot{false}; @@ -36,6 +44,8 @@ inline void CopyFromConfig(PackageManager& dest, const std::string& option_name, dest = PackageManager::kDebian; } else if (pm_type == "android") { dest = PackageManager::kAndroid; + } else if (pm_type == "ostree+docker-app") { + dest = PackageManager::kOstreeDockerApp; } else { dest = PackageManager::kNone; } diff --git a/src/libaktualizr/package_manager/packagemanagerfactory.cc b/src/libaktualizr/package_manager/packagemanagerfactory.cc index 582e33cdae..2bf78d7245 100644 --- a/src/libaktualizr/package_manager/packagemanagerfactory.cc +++ b/src/libaktualizr/package_manager/packagemanagerfactory.cc @@ -3,6 +3,9 @@ #ifdef BUILD_OSTREE #include "package_manager/ostreemanager.h" +#ifdef BUILD_DOCKERAPP +#include "package_manager/dockerappmanager.h" +#endif #endif #ifdef BUILD_DEB @@ -37,6 +40,12 @@ std::shared_ptr PackageManagerFactory::makePackageManag return std::make_shared(pconfig, storage, bootloader, http); #else throw std::runtime_error("aktualizr was compiled without android support!"); +#endif + case PackageManager::kOstreeDockerApp: +#if defined(BUILD_DOCKERAPP) && defined(BUILD_OSTREE) + return std::make_shared(pconfig, storage, bootloader, http); +#else + throw std::runtime_error("aktualizr was compiled without ostree+docker-app support!"); #endif case PackageManager::kNone: return std::make_shared(pconfig, storage, bootloader, http); diff --git a/src/libaktualizr/primary/sotauptaneclient.cc b/src/libaktualizr/primary/sotauptaneclient.cc index acca85ec76..20c272948d 100644 --- a/src/libaktualizr/primary/sotauptaneclient.cc +++ b/src/libaktualizr/primary/sotauptaneclient.cc @@ -178,7 +178,8 @@ void SotaUptaneClient::finalizeAfterReboot() { data::InstallationResult SotaUptaneClient::PackageInstallSetResult(const Uptane::Target &target) { data::InstallationResult result; Uptane::EcuSerial ecu_serial = uptane_manifest.getPrimaryEcuSerial(); - if (!target.IsOstree() && config.pacman.type == PackageManager::kOstree) { + if (!target.IsOstree() && + (config.pacman.type == PackageManager::kOstree || config.pacman.type == PackageManager::kOstreeDockerApp)) { result = data::InstallationResult(data::ResultCode::Numeric::kValidationFailed, "Cannot install a non-OSTree package on an OSTree system"); } else { diff --git a/src/libaktualizr/primary/sotauptaneclient.h b/src/libaktualizr/primary/sotauptaneclient.h index 36fdc47977..63d7ddd39f 100644 --- a/src/libaktualizr/primary/sotauptaneclient.h +++ b/src/libaktualizr/primary/sotauptaneclient.h @@ -74,6 +74,7 @@ class SotaUptaneClient { FRIEND_TEST(Aktualizr, AutoRebootAfterUpdate); FRIEND_TEST(Aktualizr, EmptyTargets); FRIEND_TEST(Aktualizr, FullOstreeUpdate); + FRIEND_TEST(DockerAppManager, DockerApp_Fetch); FRIEND_TEST(Uptane, AssembleManifestGood); FRIEND_TEST(Uptane, AssembleManifestBad); FRIEND_TEST(Uptane, InstallFake); diff --git a/src/libaktualizr/uptane/tuf.cc b/src/libaktualizr/uptane/tuf.cc index e619f5ab76..9c01812bf9 100644 --- a/src/libaktualizr/uptane/tuf.cc +++ b/src/libaktualizr/uptane/tuf.cc @@ -118,25 +118,24 @@ std::vector Hash::decodeVector(std::string hashes_str) { Target::Target(std::string filename, const Json::Value &content) : filename_(std::move(filename)) { if (content.isMember("custom")) { - Json::Value custom = content["custom"]; + custom_ = content["custom"]; - Json::Value hwids = custom["hardwareIds"]; + Json::Value hwids = custom_["hardwareIds"]; for (Json::ValueIterator i = hwids.begin(); i != hwids.end(); ++i) { hwids_.emplace_back(HardwareIdentifier((*i).asString())); } - custom_version_ = custom["version"].asString(); - Json::Value ecus = custom["ecuIdentifiers"]; + Json::Value ecus = custom_["ecuIdentifiers"]; for (Json::ValueIterator i = ecus.begin(); i != ecus.end(); ++i) { ecus_.insert({EcuSerial(i.key().asString()), HardwareIdentifier((*i)["hardwareId"].asString())}); } - if (custom.isMember("targetFormat")) { - type_ = custom["targetFormat"].asString(); + if (custom_.isMember("targetFormat")) { + type_ = custom_["targetFormat"].asString(); } - if (custom.isMember("uri")) { - std::string custom_uri = custom["uri"].asString(); + if (custom_.isMember("uri")) { + std::string custom_uri = custom_["uri"].asString(); // Ignore this exact URL for backwards compatibility with old defaults that inserted it. if (custom_uri != "https://example.com/") { uri_ = std::move(custom_uri); diff --git a/src/libaktualizr/uptane/tuf.h b/src/libaktualizr/uptane/tuf.h index 21b89ce990..94783d44c8 100644 --- a/src/libaktualizr/uptane/tuf.h +++ b/src/libaktualizr/uptane/tuf.h @@ -234,7 +234,8 @@ class Target { std::string sha512Hash() const; std::vector hashes() const { return hashes_; }; std::vector hardwareIds() const { return hwids_; }; - std::string custom_version() const { return custom_version_; } + std::string custom_version() const { return custom_["version"].asString(); } + Json::Value custom_data() const { return custom_; } std::string correlation_id() const { return correlation_id_; }; void setCorrelationId(std::string correlation_id) { correlation_id_ = std::move(correlation_id); }; uint64_t length() const { return length_; } @@ -293,7 +294,7 @@ class Target { std::map ecus_; std::vector hashes_; std::vector hwids_; - std::string custom_version_; + Json::Value custom_; uint64_t length_{0}; std::string correlation_id_; std::string uri_;