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

campaign consent declined/postponed #1225

Merged
merged 2 commits into from
Jun 6, 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
8 changes: 8 additions & 0 deletions actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ These are the primary actions that a user of libaktualizr can perform through th
- [x] Send campaign acceptance report
- [x] Send an event report (see below)
- [x] Send CampaignAcceptComplete event
- [x] Decline a campaign
- [x] Send campaign decline report
- [x] Send an event report (see below)
- [x] Send CampaignDeclineComplete event
- [x] Postpone a campaign
- [x] Send campaign postpone report
- [x] Send an event report (see below)
- [x] Send CampaignPostponeComplete event
- [x] Fetch metadata from server
- [x] Generate and send manifest (see below)
- [x] Fetch metadata from the director (uptane_test.cc, uptane_vector_tests.cc)
Expand Down
9 changes: 5 additions & 4 deletions src/aktualizr_primary/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ bpo::variables_map parse_options(int argc, char *argv[]) {
("version,v", "Current aktualizr version")
("config,c", bpo::value<std::vector<boost::filesystem::path> >()->composing(), "configuration file or directory")
("loglevel", bpo::value<int>(), "set log level 0-5 (trace, debug, info, warning, error, fatal)")
("run-mode", bpo::value<std::string>(), "run mode of aktualizr: full, once, campaign_check, campaign_accept, check, download, or install")
("run-mode", bpo::value<std::string>(), "run mode of aktualizr: full, once, campaign_check, campaign_accept, campaign_decline, campaign_postpone, check, download, or install")
("tls-server", bpo::value<std::string>(), "url of device gateway")
("repo-server", bpo::value<std::string>(), "url of the uptane repo repository")
("director-server", bpo::value<std::string>(), "url of the uptane director repository")
Expand Down Expand Up @@ -136,11 +136,12 @@ int main(int argc, char *argv[]) {
// launch the first event
if (run_mode == "campaign_check") {
aktualizr.CampaignCheck().get();
} else if (run_mode == "campaign_accept") {
} else if (run_mode == "campaign_accept" || run_mode == "campaign_decline" || run_mode == "campaign_postpone") {
if (commandline_map.count("campaign-id") == 0) {
throw std::runtime_error("Accepting a campaign requires a campaign ID");
throw std::runtime_error(run_mode + " requires a campaign ID");
}
aktualizr.CampaignAccept(commandline_map["campaign-id"].as<std::string>()).get();
aktualizr.CampaignControl(commandline_map["campaign-id"].as<std::string>(), campaign::cmdFromName(run_mode))
.get();
} else if (run_mode == "check") {
aktualizr.SendDeviceData().get();
aktualizr.CheckUpdates().get();
Expand Down
12 changes: 12 additions & 0 deletions src/libaktualizr/campaign/campaign.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ class CampaignParseError : std::exception {
const char *what() const noexcept override { return "Could not parse Campaign metadata"; }
};

enum class Cmd {
Accept,
Decline,
Postpone,
};

static inline Cmd cmdFromName(const std::string &name) {
return std::map<std::string, Cmd>{
{"campaign_accept", Cmd::Accept}, {"campaign_decline", Cmd::Decline}, {"campaign_postpone", Cmd::Postpone}}
.at(name);
}

// Out of uptane concept: update campaign for a device
class Campaign {
public:
Expand Down
21 changes: 18 additions & 3 deletions src/libaktualizr/primary/aktualizr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,22 @@ std::future<result::CampaignCheck> Aktualizr::CampaignCheck() {
return api_queue_.enqueue(task);
}

std::future<void> Aktualizr::CampaignAccept(const std::string &campaign_id) {
std::function<void()> task([this, &campaign_id] { uptane_client_->campaignAccept(campaign_id); });
std::future<void> Aktualizr::CampaignControl(const std::string &campaign_id, campaign::Cmd cmd) {
std::function<void()> task([this, &campaign_id, cmd] {
switch (cmd) {
case campaign::Cmd::Accept:
uptane_client_->campaignAccept(campaign_id);
break;
case campaign::Cmd::Decline:
uptane_client_->campaignDecline(campaign_id);
break;
case campaign::Cmd::Postpone:
uptane_client_->campaignPostpone(campaign_id);
break;
default:
break;
}
});
return api_queue_.enqueue(task);
}

Expand Down Expand Up @@ -147,7 +161,8 @@ result::Pause Aktualizr::Resume() {

void Aktualizr::Abort() { api_queue_.abort(); }

boost::signals2::connection Aktualizr::SetSignalHandler(std::function<void(shared_ptr<event::BaseEvent>)> &handler) {
boost::signals2::connection Aktualizr::SetSignalHandler(
const std::function<void(shared_ptr<event::BaseEvent>)> &handler) {
return sig_->connect(handler);
}

Expand Down
15 changes: 9 additions & 6 deletions src/libaktualizr/primary/aktualizr.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ class Aktualizr {
std::future<result::CampaignCheck> CampaignCheck();

/**
* Accept a campaign for the current device.
* Campaigns are a concept outside of Uptane, and allow for user approval of
* updates before the contents of the update are known.
* Act on campaign: accept, decline or postpone.
* Accepted campaign will be removed from the campaign list but no guarantee
* is made for declined or postponed items. Applications are responsible for
* tracking their state but this method will notify the server for device
* state monitoring purposes.
* @param campaign_id Campaign ID as provided by CampaignCheck.
* @param cmd action to apply on the campaign: accept, decline or postpone
* @return Empty std::future object
*/
std::future<void> CampaignAccept(const std::string& campaign_id);
std::future<void> CampaignControl(const std::string& campaign_id, campaign::Cmd cmd);

/**
* Send local device data to the server.
Expand Down Expand Up @@ -153,7 +156,7 @@ class Aktualizr {
* @param handler a function that can receive event objects.
* @return a signal connection object, which can be disconnected if desired.
*/
boost::signals2::connection SetSignalHandler(std::function<void(std::shared_ptr<event::BaseEvent>)>& handler);
boost::signals2::connection SetSignalHandler(const std::function<void(std::shared_ptr<event::BaseEvent>)>& handler);

private:
FRIEND_TEST(Aktualizr, FullNoUpdates);
Expand All @@ -169,7 +172,7 @@ class Aktualizr {
FRIEND_TEST(Aktualizr, DownloadFailures);
FRIEND_TEST(Aktualizr, InstallWithUpdates);
FRIEND_TEST(Aktualizr, ReportDownloadProgress);
FRIEND_TEST(Aktualizr, CampaignCheckAndAccept);
FRIEND_TEST(Aktualizr, CampaignCheckAndControl);
FRIEND_TEST(Aktualizr, FullNoCorrelationId);
FRIEND_TEST(Aktualizr, ManifestCustom);
FRIEND_TEST(Aktualizr, APICheck);
Expand Down
77 changes: 53 additions & 24 deletions src/libaktualizr/primary/aktualizr_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,8 @@ class HttpFakeCampaign : public HttpFake {
(void)url;
for (auto it = data.begin(); it != data.end(); it++) {
auto ev = *it;
if (ev["eventType"]["id"] == "campaign_accepted") {
auto id = ev["eventType"]["id"];
if (id == "campaign_accepted" || id == "campaign_declined" || id == "campaign_postponed") {
seen_events.push_back(ev);
}
}
Expand All @@ -1556,52 +1557,80 @@ class HttpFakeCampaign : public HttpFake {
std::vector<Json::Value> seen_events;
};

bool campaignaccept_seen = false;
void CampaignCheck_events(const std::shared_ptr<event::BaseEvent>& event) {
std::cout << event->variant << "\n";
if (event->variant == "CampaignCheckComplete") {
auto concrete_event = std::static_pointer_cast<event::CampaignCheckComplete>(event);
EXPECT_EQ(concrete_event->result.campaigns.size(), 1);
EXPECT_EQ(concrete_event->result.campaigns[0].name, "campaign1");
EXPECT_EQ(concrete_event->result.campaigns[0].id, "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
EXPECT_EQ(concrete_event->result.campaigns[0].size, 62470);
EXPECT_EQ(concrete_event->result.campaigns[0].autoAccept, true);
EXPECT_EQ(concrete_event->result.campaigns[0].description, "this is my message to show on the device");
} else if (event->variant == "CampaignAcceptComplete") {
campaignaccept_seen = true;
class CampaignEvents {
public:
bool campaignaccept_seen{false};
bool campaigndecline_seen{false};
bool campaignpostpone_seen{false};

void handler(const std::shared_ptr<event::BaseEvent>& event) {
std::cout << event->variant << "\n";
if (event->variant == "CampaignCheckComplete") {
auto concrete_event = std::static_pointer_cast<event::CampaignCheckComplete>(event);
EXPECT_EQ(concrete_event->result.campaigns.size(), 1);
EXPECT_EQ(concrete_event->result.campaigns[0].name, "campaign1");
EXPECT_EQ(concrete_event->result.campaigns[0].id, "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
EXPECT_EQ(concrete_event->result.campaigns[0].size, 62470);
EXPECT_EQ(concrete_event->result.campaigns[0].autoAccept, true);
EXPECT_EQ(concrete_event->result.campaigns[0].description, "this is my message to show on the device");
} else if (event->variant == "CampaignAcceptComplete") {
campaignaccept_seen = true;
} else if (event->variant == "CampaignDeclineComplete") {
campaigndecline_seen = true;
} else if (event->variant == "CampaignPostponeComplete") {
campaignpostpone_seen = true;
}
}
}
};

/* Check for campaigns with manual control.
* Accept a campaign.
* Send CampaignCheckComplete event with campaign data.
* Fetch campaigns from the server.
*
* Accept a campaign.
* Send campaign acceptance report.
* Send CampaignAcceptComplete event.
* Send CampaignCheckComplete event with campaign data.
*
* Decline a campaign.
* Send campaign decline report.
* Send CampaignDeclineComplete event.
*
* Postpone a campaign.
* Send campaign postpone report.
* Send CampaignPostponeComplete event.
*/
TEST(Aktualizr, CampaignCheckAndAccept) {
TEST(Aktualizr, CampaignCheckAndControl) {
TemporaryDirectory temp_dir;
auto http = std::make_shared<HttpFakeCampaign>(temp_dir.Path());
Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);

CampaignEvents campaign_events;

{
auto storage = INvStorage::newStorage(conf.storage);
Aktualizr aktualizr(conf, storage, http);
std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = CampaignCheck_events;
aktualizr.SetSignalHandler(f_cb);
aktualizr.SetSignalHandler(std::bind(&CampaignEvents::handler, &campaign_events, std::placeholders::_1));
aktualizr.Initialize();

// check for campaign
auto result = aktualizr.CampaignCheck().get();
EXPECT_EQ(result.campaigns.size(), 1);

// accept the campaign
aktualizr.CampaignAccept("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493").get();
aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Accept).get();

aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Decline).get();

aktualizr.CampaignControl("c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493", campaign::Cmd::Postpone).get();
}

ASSERT_EQ(http->seen_events.size(), 1);
ASSERT_EQ(http->seen_events[0]["event"]["campaignId"], "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
ASSERT_TRUE(campaignaccept_seen);
ASSERT_EQ(http->seen_events.size(), 3);
for (const auto& ev : http->seen_events) {
EXPECT_EQ(ev["event"]["campaignId"], "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
}
EXPECT_TRUE(campaign_events.campaignaccept_seen);
EXPECT_TRUE(campaign_events.campaigndecline_seen);
EXPECT_TRUE(campaign_events.campaignpostpone_seen);
}

class HttpFakeNoCorrelationId : public HttpFake {
Expand Down
10 changes: 10 additions & 0 deletions src/libaktualizr/primary/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ class CampaignAcceptComplete : public BaseEvent {
CampaignAcceptComplete() { variant = "CampaignAcceptComplete"; }
};

class CampaignDeclineComplete : public BaseEvent {
public:
CampaignDeclineComplete() { variant = "CampaignDeclineComplete"; }
};

class CampaignPostponeComplete : public BaseEvent {
public:
CampaignPostponeComplete() { variant = "CampaignPostponeComplete"; }
};

using Channel = boost::signals2::signal<void(std::shared_ptr<event::BaseEvent>)>;

} // namespace event
Expand Down
9 changes: 9 additions & 0 deletions src/libaktualizr/primary/reportqueue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ CampaignAcceptedReport::CampaignAcceptedReport(const std::string& campaign_id) :
custom["campaignId"] = campaign_id;
}

CampaignDeclinedReport::CampaignDeclinedReport(const std::string& campaign_id) : ReportEvent("campaign_declined", 0) {
custom["campaignId"] = campaign_id;
}

CampaignPostponedReport::CampaignPostponedReport(const std::string& campaign_id)
: ReportEvent("campaign_postponed", 0) {
custom["campaignId"] = campaign_id;
}

DevicePausedReport::DevicePausedReport(const std::string& correlation_id) : ReportEvent("DevicePaused", 0) {
setCorrelationId(correlation_id);
}
Expand Down
10 changes: 10 additions & 0 deletions src/libaktualizr/primary/reportqueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class CampaignAcceptedReport : public ReportEvent {
CampaignAcceptedReport(const std::string& campaign_id);
};

class CampaignDeclinedReport : public ReportEvent {
public:
CampaignDeclinedReport(const std::string& campaign_id);
};

class CampaignPostponedReport : public ReportEvent {
public:
CampaignPostponedReport(const std::string& campaign_id);
};

class DevicePausedReport : public ReportEvent {
public:
DevicePausedReport(const std::string& correlation_id);
Expand Down
10 changes: 10 additions & 0 deletions src/libaktualizr/primary/sotauptaneclient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,16 @@ void SotaUptaneClient::campaignAccept(const std::string &campaign_id) {
report_queue->enqueue(std_::make_unique<CampaignAcceptedReport>(campaign_id));
}

void SotaUptaneClient::campaignDecline(const std::string &campaign_id) {
sendEvent<event::CampaignDeclineComplete>();
report_queue->enqueue(std_::make_unique<CampaignDeclinedReport>(campaign_id));
}

void SotaUptaneClient::campaignPostpone(const std::string &campaign_id) {
sendEvent<event::CampaignPostponeComplete>();
report_queue->enqueue(std_::make_unique<CampaignPostponedReport>(campaign_id));
}

bool SotaUptaneClient::isInstallCompletionRequired() {
bool force_install_completion = (hasPendingUpdates() && config.uptane.force_install_completion);
return force_install_completion;
Expand Down
2 changes: 2 additions & 0 deletions src/libaktualizr/primary/sotauptaneclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class SotaUptaneClient {
result::Install uptaneInstall(const std::vector<Uptane::Target> &updates);
result::CampaignCheck campaignCheck();
void campaignAccept(const std::string &campaign_id);
void campaignDecline(const std::string &campaign_id);
void campaignPostpone(const std::string &campaign_id);
bool hasPendingUpdates();
bool isInstallCompletionRequired();
void completeInstall();
Expand Down