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

Commit

Permalink
Add complete cycle ostree install test
Browse files Browse the repository at this point in the history
With some parts mocked (emulate booted deployments)

Signed-off-by: Laurent Bonnans <[email protected]>
  • Loading branch information
lbonn committed Apr 26, 2019
1 parent 80fe8ec commit bca0838
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/aktualizr_repo/image_repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void ImageRepo::addImage(const std::string &name, const Json::Value &target, con
}

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

boost::filesystem::path targets_path = repo_dir / "targets";
Expand Down
2 changes: 1 addition & 1 deletion src/aktualizr_repo/image_repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ImageRepo : public Repo {
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 addBinaryImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
const Delegation &delegation = {});
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
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
167 changes: 167 additions & 0 deletions src/libaktualizr/primary/aktualizr_fullostree_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include <gtest/gtest.h>

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

#include <boost/process.hpp>

#include "uptane_test_common.h"

#include "config/config.h"
#include "http/httpclient.h"
#include "httpfake.h"
#include "logging/logging.h"
#include "package_manager/ostreemanager.h"
#include "package_manager/packagemanagerfactory.h"
#include "primary/aktualizr.h"
#include "primary/events.h"
#include "primary/sotauptaneclient.h"
#include "storage/sqlstorage.h"
#include "test_utils.h"
#include "uptane/tuf.h"
#include "utilities/apiqueue.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;
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();
}

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.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
conf.provision.primary_ecu_hardware_id = "primary_hw";
conf.provision.provision_path = "tests/test_data/cred.zip";
conf.tls.server = server;

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

#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"));
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__
17 changes: 15 additions & 2 deletions tests/fake_http_server/fake_http_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,27 @@ def do_POST(self):
return
else:
last_fails = False
if self.path == '/token':
if self.path == '/devices':
self.send_response(200)
self.end_headers()
with open('tests/test_data/cred.p12', 'rb') as source:
while True:
data = source.read(1024)
if not data:
break
self.wfile.write(data)
elif self.path == '/token':
self.send_response(200)
self.end_headers()
if 'Authorization' in self.headers:
self.wfile.write(b"{\"access_token\": \"token\"}")
else:
self.wfile.write(b'')

elif self.path == "/director/ecus":
self.send_response(200)
self.end_headers()
self.wfile.write(b"{}")
return
else:
self.send_response(200)
self.end_headers()
Expand Down
85 changes: 52 additions & 33 deletions tests/fake_http_server/fake_test_server.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,55 @@
#!/usr/bin/python3

import argparse
import sys
import socket

from http.server import SimpleHTTPRequestHandler, HTTPServer
from time import sleep

file_path = None
meta_path = None


class Handler(SimpleHTTPRequestHandler):
def _serve_simple(self, uri):
with open(uri, 'rb') as source:
while True:
data = source.read(1024)
if not data:
break
self.wfile.write(data)

def serve_meta(self, uri):
if meta_path is None:
raise RuntimeError("Please supply a path for metadata")
self._serve_simple(meta_path + uri)

def serve_target(self, filename):
if file_path is None:
raise RuntimeError("Please supply a path for targets")
self._serve_simple(file_path + filename)

def do_GET(self):
print("path: " + self.path)

if self.path.startswith("/director/") and self.path.endswith(".json"):
self.send_response(200)
self.end_headers()
role = self.path[len("/director/"):]
with open(meta_path + '/repo/director/' + role, 'rb') as source:
while True:
data = source.read(1024)
if not data:
break
self.wfile.write(data)
return
self.serve_meta("/repo/director/" + role)
elif self.path.startswith("/repo/") and self.path.endswith(".json"):
self.send_response(200)
self.end_headers()
role = self.path[len("/repo/"):]
with open(meta_path + '/repo/image/' + role, 'rb') as source:
while True:
data = source.read(1024)
if not data:
break
self.wfile.write(data)
return
self.serve_meta('/repo/image/' + role)
elif self.path.startswith("/repo/targets"):
self.send_response(200)
self.end_headers()
filename = self.path[len("/repo/targets"):]
with open(file_path + filename, 'rb') as source:
while True:
data = source.read(1024)
if not data:
break
self.wfile.write(data)
return
self.serve_target(filename)

if self.path == '/download':
elif self.path == '/download':
self.send_response(301)
self.send_header('Location', '/download/file')
self.end_headers()
Expand Down Expand Up @@ -85,12 +92,12 @@ def do_GET(self):
self.send_response(200)
self.end_headers()
for i in range(5):
self.wfile.write(b'aa')
sleep(1)
self.wfile.write(b'aa')
sleep(1)
else:
self.send_response(200)
self.end_headers()
self.wfile.write(b'{"path": "%b"}'%bytes(self.path, "utf8"))
self.wfile.write(b'{"path": "%b"}' % bytes(self.path, "utf8"))

def do_POST(self):
if self.path == '/devices':
Expand Down Expand Up @@ -137,13 +144,25 @@ def server_bind(self):
HTTPServer.server_bind(self)


server_address = ('', int(sys.argv[1]))
file_path = sys.argv[2]
meta_path = sys.argv[3]
def main():
global file_path, meta_path

parser = argparse.ArgumentParser(description='Run a fake OTA backend')
parser.add_argument('port', type=int, help='server port')
parser.add_argument('-t', '--targets', help='targets directory', default=None)
parser.add_argument('-m', '--meta', help='meta directory', default=None)
args = parser.parse_args()

server_address = ('', args.port)
file_path = args.targets
meta_path = args.meta

httpd = ReUseHTTPServer(server_address, Handler)
try:
httpd.serve_forever()
except KeyboardInterrupt:
httpd.server_close()

httpd = ReUseHTTPServer(server_address, Handler)
try:
httpd.serve_forever()
except KeyboardInterrupt:
httpd.server_close()

if __name__ == "__main__":
sys.exit(main())
4 changes: 2 additions & 2 deletions tests/ostree_mock.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <ostree.h>
#include <stdio.h>

OstreeDeployment *ostree_sysroot_get_booted_deployment (OstreeSysroot *self) {
OstreeDeployment* ostree_sysroot_get_booted_deployment(OstreeSysroot* self) {
OstreeDeployment* (*orig)(OstreeSysroot*) = dlsym(RTLD_NEXT, __func__);
char mocked_name[100];
snprintf(mocked_name, sizeof(mocked_name), "%s_mock", __func__);
Expand All @@ -15,7 +15,7 @@ OstreeDeployment *ostree_sysroot_get_booted_deployment (OstreeSysroot *self) {
return orig(self);
}

const char *ostree_deployment_get_bootcsum (OstreeDeployment *self) {
const char* ostree_deployment_get_bootcsum(OstreeDeployment* self) {
char* (*orig)(OstreeDeployment*) = dlsym(RTLD_NEXT, __func__);
char mocked_name[100];
snprintf(mocked_name, sizeof(mocked_name), "%s_mock", __func__);
Expand Down
Loading

0 comments on commit bca0838

Please sign in to comment.