Skip to content

Commit 4d60691

Browse files
authored
Merge pull request #441 from foundriesio/add-metadata-update-event
Inform backend about TUF metadata updates
2 parents a5f4f5a + 4d8e793 commit 4d60691

File tree

3 files changed

+105
-8
lines changed

3 files changed

+105
-8
lines changed

src/liteclient.cc

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,18 @@ bool LiteClient::wasTargetInstalled(const Uptane::Target& target) const {
412412
});
413413
}
414414

415+
class MetadataUpdateCompletedReport : public ReportEvent {
416+
public:
417+
MetadataUpdateCompletedReport(const Uptane::EcuSerial& ecu, const std::string& correlation_id, bool success,
418+
const std::string& details)
419+
: ReportEvent("MetadataUpdateCompleted", 0) {
420+
setEcu(ecu);
421+
setCorrelationId(correlation_id);
422+
custom["success"] = success;
423+
custom["details"] = details;
424+
}
425+
};
426+
415427
class DetailedDownloadReport : public EcuDownloadStartedReport {
416428
public:
417429
DetailedDownloadReport(const Uptane::EcuSerial& ecu, const std::string& correlation_id, const std::string& details)
@@ -429,11 +441,63 @@ class DetailedDownloadCompletedReport : public EcuDownloadCompletedReport {
429441
}
430442
};
431443

432-
void LiteClient::notifyTufUpdateStarted() { callback("check-for-update-pre", Uptane::Target::Unknown(), ""); }
444+
void LiteClient::notifyTufUpdateStarted() {
445+
callback("check-for-update-pre", Uptane::Target::Unknown(), "");
446+
447+
forEachRoleVersion(storage, [&](const Uptane::Role& role, int version) {
448+
auto& d = tuf_update_details_[role.ToString()];
449+
d.from = version;
450+
});
451+
}
433452

434453
void LiteClient::notifyTufUpdateFinished(const std::string& err, const Uptane::Target& t) {
435454
auto msg = err.empty() ? "OK" : "FAILED: " + err;
436455
callback("check-for-update-post", t, msg);
456+
457+
static thread_local boost::uuids::random_generator uuid_gen;
458+
const std::string prefix{"tuf-meta"};
459+
auto cor_id = prefix + "-" + boost::uuids::to_string(uuid_gen());
460+
461+
bool was_updated;
462+
std::string details;
463+
bool need_separator{false};
464+
465+
forEachRoleVersion(storage, [&](const Uptane::Role& role, int version) {
466+
auto& d = tuf_update_details_[role.ToString()];
467+
d.to = version;
468+
was_updated = d.from != d.to;
469+
});
470+
471+
if (!err.empty() || was_updated) {
472+
// Compose the "details" string with info about metadata if there is an error or metadata was updated
473+
for (const auto& d : tuf_update_details_) {
474+
if (need_separator) {
475+
details += "; ";
476+
}
477+
details += d.first + ": " + std::to_string(d.second.from);
478+
if (d.second.from != d.second.to) {
479+
details += " -> " + std::to_string(d.second.to);
480+
}
481+
need_separator = true;
482+
}
483+
}
484+
485+
if (!err.empty()) {
486+
if (need_separator) {
487+
details += "; err: ";
488+
}
489+
details += err;
490+
}
491+
if (!details.empty()) {
492+
// If there is an error or the TUF metadata was updated ("details" is not empty), then send the metadata update
493+
// completed event
494+
auto update_target{t};
495+
if (!update_target.IsValid()) {
496+
update_target = getCurrent();
497+
}
498+
notify(update_target,
499+
std_::make_unique<MetadataUpdateCompletedReport>(primary_ecu.first, cor_id, err.empty(), details));
500+
}
437501
}
438502

439503
void LiteClient::notifyDownloadStarted(const Uptane::Target& t, const std::string& reason) {
@@ -871,3 +935,24 @@ LiteClient::Type LiteClient::getClientType(const KeyManager& key_manager) {
871935
boost::optional<std::vector<std::string>> LiteClient::getAppShortlist() const {
872936
return config.pacman.type == ComposeAppManager::Name ? ComposeAppManager::Config(config.pacman).apps : boost::none;
873937
}
938+
939+
void LiteClient::forEachRoleVersion(std::shared_ptr<const INvStorage> storage,
940+
const std::function<void(const Uptane::Role&, int)>& func) {
941+
for (const auto& role : Uptane::Role::Roles()) {
942+
int ver{-1};
943+
std::string meta;
944+
bool loaded;
945+
946+
if (role == Uptane::Role::Root()) {
947+
loaded = storage->loadRoot(&meta, Uptane::RepositoryType::Image(), Uptane::Version());
948+
} else {
949+
loaded = storage->loadNonRoot(&meta, Uptane::RepositoryType::Image(), role);
950+
}
951+
952+
if (loaded) {
953+
ver = Uptane::extractVersionUntrusted(meta);
954+
}
955+
956+
func(role, ver);
957+
}
958+
}

src/liteclient.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ class LiteClient {
9595
void disableHwInfoReporting() { hwinfo_reported_ = true; }
9696

9797
private:
98+
struct Diff {
99+
int from;
100+
int to;
101+
};
102+
using TufUpdateDetails = std::map<std::string, Diff>;
103+
98104
FRIEND_TEST(helpers, locking);
99105
FRIEND_TEST(helpers, callback);
100106
FRIEND_TEST(AkliteTest, RollbackIfAppsInstallFails);
@@ -115,6 +121,9 @@ class LiteClient {
115121
static bool isRegistered(const KeyManager& key_manager);
116122
static Type getClientType(const KeyManager& key_manager);
117123

124+
static void forEachRoleVersion(std::shared_ptr<const INvStorage> storage,
125+
const std::function<void(const Uptane::Role&, int)>& func);
126+
118127
boost::filesystem::path callback_program;
119128
std::unique_ptr<KeyManager> key_manager_;
120129
std::shared_ptr<PackageManagerInterface> package_manager_;
@@ -135,6 +144,8 @@ class LiteClient {
135144
const int report_queue_run_pause_s_{10};
136145
const int report_queue_event_limit_{6};
137146
Type type_{Type::Undefined};
147+
148+
TufUpdateDetails tuf_update_details_;
138149
};
139150

140151
#endif // AKTUALIZR_LITE_CLIENT_H_

tests/apiclient_test.cc

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ TEST_F(ApiClientTest, CheckIn) {
126126
auto result = client.CheckIn();
127127

128128
auto events = getDeviceGateway().getEvents();
129-
ASSERT_EQ(2, events.size());
129+
ASSERT_EQ(3, events.size());
130130
auto val = getDeviceGateway().readSotaToml();
131131
ASSERT_NE(std::string::npos, val.find("[pacman]"));
132132

@@ -141,7 +141,7 @@ TEST_F(ApiClientTest, CheckIn) {
141141
EXPECT_CALL(*lite_client, callback(testing::StrEq("check-for-update-pre"), testing::_, testing::StrEq(""))).Times(1);
142142
EXPECT_CALL(*lite_client, callback(testing::StrEq("check-for-update-post"), testing::_, testing::StrEq("OK")));
143143
result = client.CheckIn();
144-
ASSERT_EQ(0, getDeviceGateway().getEvents().size());
144+
ASSERT_EQ(1, getDeviceGateway().getEvents().size());
145145
ASSERT_EQ("", getDeviceGateway().readSotaToml());
146146
ASSERT_EQ(CheckInResult::Status::Ok, result.status);
147147
ASSERT_EQ(2, result.Targets().size());
@@ -164,7 +164,7 @@ TEST_F(ApiClientTest, CheckInLocal) {
164164

165165
// No communication is done with the device gateway inside CheckInLocal
166166
auto events = getDeviceGateway().getEvents();
167-
ASSERT_EQ(0, events.size());
167+
ASSERT_EQ(1, events.size());
168168
ASSERT_EQ("", getDeviceGateway().readSotaToml());
169169

170170
ASSERT_EQ(CheckInResult::Status::Ok, result.status);
@@ -173,7 +173,7 @@ TEST_F(ApiClientTest, CheckInLocal) {
173173
auto new_target = createTarget();
174174
tuf_repo_.updateBundleMeta(new_target.filename());
175175
result = client.CheckInLocal(&local_update_source_);
176-
ASSERT_EQ(0, getDeviceGateway().getEvents().size());
176+
ASSERT_EQ(2, getDeviceGateway().getEvents().size());
177177
ASSERT_EQ("", getDeviceGateway().readSotaToml());
178178
ASSERT_EQ(CheckInResult::Status::Ok, result.status);
179179
ASSERT_EQ(2, result.Targets().size());
@@ -187,7 +187,7 @@ TEST_F(ApiClientTest, CheckInWithoutTargetImport) {
187187
auto result = client.CheckIn();
188188

189189
auto events = getDeviceGateway().getEvents();
190-
ASSERT_EQ(2, events.size());
190+
ASSERT_EQ(3, events.size());
191191
auto val = getDeviceGateway().readSotaToml();
192192
ASSERT_NE(std::string::npos, val.find("[pacman]"));
193193

@@ -199,7 +199,7 @@ TEST_F(ApiClientTest, CheckInWithoutTargetImport) {
199199

200200
auto new_target = createTarget();
201201
result = client.CheckIn();
202-
ASSERT_EQ(0, getDeviceGateway().getEvents().size());
202+
ASSERT_EQ(1, getDeviceGateway().getEvents().size());
203203
ASSERT_EQ("", getDeviceGateway().readSotaToml());
204204
ASSERT_EQ(CheckInResult::Status::Ok, result.status);
205205
ASSERT_EQ(1, result.Targets().size());
@@ -603,7 +603,8 @@ TEST_F(ApiClientTest, CheckInCurrent) {
603603
auto result = client.CheckIn();
604604

605605
auto events = getDeviceGateway().getEvents();
606-
ASSERT_EQ(2, events.size());
606+
// Network info + System info + MetadataUpdateCompleted event
607+
ASSERT_EQ(3, events.size());
607608
auto val = getDeviceGateway().readSotaToml();
608609
ASSERT_NE(std::string::npos, val.find("[pacman]"));
609610

0 commit comments

Comments
 (0)