1 #include <gtest/gtest.h>
5 #include "boost/algorithm/string/trim.hpp"
6 #include "boost/process.hpp"
8 #include "logging/logging.h"
9 #include "test_utils.h"
11 #include "aktualizr_secondary_ostree.h"
12 #include "update_agent_ostree.h"
13 #include "uptane_repo.h"
17 Treehub(
const std::string& server_path)
18 : port_(TestUtils::getFreePort()),
19 url_(
"http://127.0.0.1:" + port_),
20 process_(server_path,
"-p", port_,
"-d", root_dir_.PathString(),
"-s0.5",
"--create") {
21 TestUtils::waitForServer(url() +
"/");
22 auto rev_process =
Process(
"ostree").run({
"rev-parse",
"--repo", root_dir_.PathString(),
"master"});
23 EXPECT_EQ(std::get<0>(rev_process), 0) << std::get<2>(rev_process);
24 cur_rev_ = std::get<1>(rev_process);
25 boost::trim_right_if(cur_rev_, boost::is_any_of(
" \t\r\n"));
27 LOG_INFO <<
"Treehub is running on: " << port_ <<
" current revision: " << cur_rev_;
32 process_.wait_for(std::chrono::seconds(10));
33 if (process_.running()) {
34 LOG_ERROR <<
"Failed to stop Treehub server";
36 LOG_INFO <<
"Treehub server has been stopped";
41 const std::string& url()
const {
return url_; }
42 const std::string& curRev()
const {
return cur_rev_; }
46 const std::string port_;
47 const std::string url_;
48 boost::process::child process_;
55 auto sysroot_copy =
Process(
"cp").run({
"-r", rootfs_template, getPath().c_str()});
56 EXPECT_EQ(std::get<0>(sysroot_copy), 0) << std::get<1>(sysroot_copy);
58 auto deployment_rev =
Process(
"ostree").run(
59 {
"rev-parse", std::string(
"--repo"), getPath().string() +
"/ostree/repo",
"generate-remote/generated"});
61 EXPECT_EQ(std::get<0>(deployment_rev), 0) << std::get<2>(deployment_rev);
63 rev_ = std::get<1>(deployment_rev);
64 boost::trim_right_if(rev_, boost::is_any_of(
" \t\r\n"));
66 deployment_.reset(ostree_deployment_new(0, getOSName(), getDeploymentRev(), getDeploymentSerial(),
67 getDeploymentRev(), getDeploymentSerial()));
70 const boost::filesystem::path& getPath()
const {
return sysroot_dir_; }
71 const char* getDeploymentRev()
const {
return rev_.c_str(); }
72 int getDeploymentSerial()
const {
return 0; }
73 const char* getOSName()
const {
return os_name_.c_str(); }
75 OstreeDeployment* getDeployment()
const {
return deployment_.get(); }
76 void setNewDeploymentRev(
const std::string& new_rev) { rev_ = new_rev; }
79 struct OstreeDeploymentDeleter {
80 void operator()(OstreeDeployment* e)
const { g_object_unref(
reinterpret_cast<gpointer
>(e)); }
84 const std::string os_name_{
"dummy-os"};
86 boost::filesystem::path sysroot_dir_{tmp_dir_ /
"ostree-rootfs"};
88 std::unique_ptr<OstreeDeployment, OstreeDeploymentDeleter> deployment_;
96 config_.pacman.type = PACKAGE_MANAGER_OSTREE;
97 config_.pacman.os = sysroot.getOSName();
98 config_.pacman.sysroot = sysroot.getPath();
99 config_.pacman.ostree_server = treehub.url();
101 config_.bootloader.reboot_sentinel_dir = storage_dir_.Path();
102 config_.bootloader.reboot_sentinel_name =
"need_reboot";
104 config_.storage.path = storage_dir_.Path();
105 config_.storage.type = StorageType::kSqlite;
107 storage_ = INvStorage::newStorage(config_.storage);
108 secondary_ = std::make_shared<AktualizrSecondaryOstree>(config_, storage_);
109 secondary_->initialize();
113 std::shared_ptr<AktualizrSecondaryOstree>& operator->() {
return secondary_; }
115 Uptane::Target getPendingVersion()
const {
return getVersion().first; }
117 Uptane::Target getCurrentVersion()
const {
return getVersion().second; }
119 std::pair<Uptane::Target, Uptane::Target> getVersion()
const {
120 boost::optional<Uptane::Target> current_target;
121 boost::optional<Uptane::Target> pending_target;
123 storage_->loadInstalledVersions(secondary_->serial().ToString(), ¤t_target, &pending_target);
125 return std::make_pair(!pending_target ? Uptane::Target::Unknown() : *pending_target,
126 !current_target ? Uptane::Target::Unknown() : *current_target);
129 std::string hardwareID()
const {
return secondary_->hwID().ToString(); }
131 std::string serial()
const {
return secondary_->serial().ToString(); }
134 boost::filesystem::remove(storage_dir_ / config_.bootloader.reboot_sentinel_name);
135 secondary_ = std::make_shared<AktualizrSecondaryOstree>(config_, storage_);
136 secondary_->initialize();
142 std::shared_ptr<INvStorage> storage_;
143 std::shared_ptr<AktualizrSecondaryOstree> secondary_;
150 Metadata addOstreeRev(
const std::string& rev,
const std::string& hardware_id,
const std::string& serial) {
152 auto custom = Json::Value();
153 custom[
"targetFormat"] =
"OSTREE";
154 uptane_repo_.addCustomImage(rev,
Hash(Hash::Type::kSha256, rev), 0, hardware_id,
"",
Delegation(), custom);
156 uptane_repo_.addTarget(rev, hardware_id, serial,
"");
157 uptane_repo_.signTargets();
159 return getCurrentMetadata();
162 Uptane::MetaBundle getCurrentMetadata()
const {
163 Uptane::MetaBundle meta_bundle;
164 std::string metadata;
166 boost::filesystem::load_string_file(director_dir_ /
"root.json", metadata);
167 meta_bundle.insert({std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()), std::move(metadata)});
168 boost::filesystem::load_string_file(director_dir_ /
"targets.json", metadata);
170 {std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()), std::move(metadata)});
172 boost::filesystem::load_string_file(imagerepo_dir_ /
"root.json", metadata);
173 meta_bundle.insert({std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()), std::move(metadata)});
174 boost::filesystem::load_string_file(imagerepo_dir_ /
"timestamp.json", metadata);
176 {std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()), std::move(metadata)});
177 boost::filesystem::load_string_file(imagerepo_dir_ /
"snapshot.json", metadata);
179 {std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()), std::move(metadata)});
180 boost::filesystem::load_string_file(imagerepo_dir_ /
"targets.json", metadata);
181 meta_bundle.insert({std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets()), std::move(metadata)});
186 std::shared_ptr<std::string> getImageData(
const std::string& targetname)
const {
187 auto image_data = std::make_shared<std::string>();
188 boost::filesystem::load_string_file(root_dir_ / targetname, *image_data);
194 boost::filesystem::path director_dir_{root_dir_ /
"repo/director"};
195 boost::filesystem::path imagerepo_dir_{root_dir_ /
"repo/repo"};
196 UptaneRepo uptane_repo_{root_dir_.Path(),
"",
""};
201 static const char* curOstreeRootfsRev(OstreeDeployment* ostree_depl) {
203 return sysroot_->getDeploymentRev();
206 static OstreeDeployment* curOstreeDeployment(OstreeSysroot* ostree_sysroot) {
207 (void)ostree_sysroot;
208 return sysroot_->getDeployment();
211 static void setOstreeRootfsTemplate(
const std::string& ostree_rootfs_template) {
212 ostree_rootfs_template_ = ostree_rootfs_template;
216 static void SetUpTestSuite() {
217 treehub_ = std::make_shared<Treehub>(
"tests/sota_tools/treehub_server.py");
218 sysroot_ = std::make_shared<OstreeRootfs>(ostree_rootfs_template_);
221 static void TearDownTestSuite() {
229 Uptane::MetaBundle addDefaultTarget() {
return addTarget(treehub_->curRev()); }
231 Uptane::MetaBundle addTarget(
const std::string& rev =
"",
const std::string& hardware_id =
"",
232 const std::string& serial =
"") {
233 auto rev_to_apply = rev.empty() ? treehub_->curRev() : rev;
234 auto hw_id = hardware_id.empty() ? secondary_.hardwareID() : hardware_id;
235 auto serial_id = serial.empty() ? secondary_.serial() : serial;
237 uptane_repo_.addOstreeRev(rev, hw_id, serial_id);
239 return currentMetadata();
242 Uptane::MetaBundle currentMetadata()
const {
return uptane_repo_.getCurrentMetadata(); }
244 std::string getCredsToSend()
const {
245 std::map<std::string, std::string> creds_map = {
246 {
"ca.pem",
""}, {
"client.pem",
""}, {
"pkey.pem",
""}, {
"server.url", treehub_->url()}};
248 std::stringstream creads_strstream;
249 Utils::writeArchive(creds_map, creads_strstream);
251 return creads_strstream.str();
254 Hash treehubCurRevHash()
const {
return Hash(Hash::Type::kSha256, treehub_->curRev()); }
255 Hash sysrootCurRevHash()
const {
return Hash(Hash::Type::kSha256, sysroot_->getDeploymentRev()); }
256 const std::string& treehubCurRev()
const {
return treehub_->curRev(); }
259 static std::shared_ptr<Treehub> treehub_;
260 static std::string ostree_rootfs_template_;
261 static std::shared_ptr<OstreeRootfs> sysroot_;
267 std::shared_ptr<Treehub> SecondaryOstreeTest::treehub_{
nullptr};
268 std::string SecondaryOstreeTest::ostree_rootfs_template_{
"./build/ostree_repo"};
269 std::shared_ptr<OstreeRootfs> SecondaryOstreeTest::sysroot_{
nullptr};
272 EXPECT_TRUE(secondary_->putMetadata(addTarget(
"invalid-revision")).isSuccess());
273 EXPECT_FALSE(secondary_->downloadOstreeUpdate(getCredsToSend()).isSuccess());
277 EXPECT_FALSE(secondary_->putMetadata(addTarget(
"",
"invalid-hardware-id",
"")).isSuccess());
281 EXPECT_FALSE(secondary_->putMetadata(addTarget(
"",
"",
"invalid-serial-id")).isSuccess());
287 EXPECT_TRUE(manifest.verifySignature(secondary_->publicKey()));
288 EXPECT_EQ(manifest.installedImageHash(), sysrootCurRevHash());
291 EXPECT_TRUE(secondary_->putMetadata(addDefaultTarget()).isSuccess());
296 EXPECT_TRUE(secondary_->downloadOstreeUpdate(getCredsToSend()).isSuccess());
297 EXPECT_EQ(secondary_->install().result_code.num_code, data::ResultCode::Numeric::kNeedCompletion);
300 EXPECT_TRUE(secondary_.getPendingVersion().MatchHash(treehubCurRevHash()));
302 manifest = secondary_->getManifest();
303 EXPECT_TRUE(manifest.verifySignature(secondary_->publicKey()));
304 EXPECT_EQ(manifest.installedImageHash(), sysrootCurRevHash());
307 sysroot_->setNewDeploymentRev(treehubCurRev());
311 EXPECT_FALSE(secondary_.getPendingVersion().IsValid());
312 EXPECT_TRUE(secondary_.getCurrentVersion().MatchHash(treehubCurRevHash()));
313 manifest = secondary_->getManifest();
314 EXPECT_TRUE(manifest.verifySignature(secondary_->publicKey()));
315 EXPECT_EQ(manifest.installedImageHash(), treehubCurRevHash());
320 EXPECT_FALSE(secondary_.getPendingVersion().IsValid());
321 EXPECT_TRUE(secondary_.getCurrentVersion().MatchHash(treehubCurRevHash()));
322 manifest = secondary_->getManifest();
323 EXPECT_TRUE(manifest.verifySignature(secondary_->publicKey()));
324 EXPECT_EQ(manifest.installedImageHash(), treehubCurRevHash());
327 int main(
int argc,
char** argv) {
328 ::testing::InitGoogleTest(&argc, argv);
331 std::cerr <<
"Error: " << argv[0] <<
" <ostree rootfs path>\n";
335 SecondaryOstreeTest::setOstreeRootfsTemplate(argv[1]);
338 logger_set_threshold(boost::log::trivial::info);
340 return RUN_ALL_TESTS();
343 extern "C" OstreeDeployment* ostree_sysroot_get_booted_deployment(OstreeSysroot* ostree_sysroot) {
344 return SecondaryOstreeTest::curOstreeDeployment(ostree_sysroot);
347 extern "C" const char* ostree_deployment_get_csum(OstreeDeployment* ostree_deployment) {
348 return SecondaryOstreeTest::curOstreeRootfsRev(ostree_deployment);