Aktualizr
C++ SOTA Client
empty_targets_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <string>
4 
5 #include "httpfake.h"
6 #include "libaktualizr/aktualizr.h"
7 #include "test_utils.h"
8 #include "uptane_test_common.h"
9 #include "utilities/fault_injection.h"
10 
11 boost::filesystem::path uptane_generator_path;
12 
14  public:
15  HttpRejectEmptyCorrId(const boost::filesystem::path &test_dir_in, const boost::filesystem::path &meta_dir_in)
16  : HttpFake(test_dir_in, "", meta_dir_in) {}
17 
18  HttpResponse put(const std::string &url, const Json::Value &data) override {
19  last_manifest = data;
20  if (url.find("/manifest") != std::string::npos) {
21  if (data["signed"].isMember("installation_report") &&
22  data["signed"]["installation_report"]["report"]["correlation_id"].asString().empty()) {
23  EXPECT_FALSE(data["signed"]["installation_report"]["report"]["correlation_id"].asString().empty());
24  return HttpResponse(url, 400, CURLE_OK, "");
25  }
26  }
27  return HttpResponse(url, 200, CURLE_OK, "");
28  }
29 };
30 
31 /*
32  * Verify that we can successfully install an update after receiving
33  * subsequent Targets metadata that is empty.
34  */
35 TEST(Aktualizr, EmptyTargets) {
36  TemporaryDirectory temp_dir;
37  TemporaryDirectory meta_dir;
38  auto http = std::make_shared<HttpRejectEmptyCorrId>(temp_dir.Path(), meta_dir.Path() / "repo");
39  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
40  conf.pacman.fake_need_reboot = true;
41  logger_set_threshold(boost::log::trivial::trace);
42 
43  Process uptane_gen(uptane_generator_path.string());
44  uptane_gen.run({"generate", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
45  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware.txt",
46  "--targetname", "firmware.txt", "--hwid", "primary_hw"});
47  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware.txt", "--hwid", "primary_hw",
48  "--serial", "CA:FE:A6:D2:84:9D"});
49  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
50 
51  auto storage = INvStorage::newStorage(conf.storage);
52  {
53  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
54  aktualizr.Initialize();
55 
56  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
57  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
58 
59  result::Download download_result = aktualizr.Download(update_result.updates).get();
60  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
61 
62  uptane_gen.run({"emptytargets", "--path", meta_dir.PathString()});
63  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
64 
65  result::UpdateCheck update_result2 = aktualizr.CheckUpdates().get();
66  EXPECT_EQ(update_result2.status, result::UpdateStatus::kUpdatesAvailable);
67 
68  result::Install install_result = aktualizr.Install(update_result2.updates).get();
69  ASSERT_EQ(install_result.ecu_reports.size(), 1);
70  EXPECT_EQ(install_result.ecu_reports[0].install_res.result_code.num_code,
71  data::ResultCode::Numeric::kNeedCompletion);
72 
73  aktualizr.uptane_client()->package_manager_->completeInstall();
74  }
75  {
76  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
77  aktualizr.Initialize();
78 
79  const Json::Value manifest = http->last_manifest["signed"];
80  const Json::Value manifest_versions = manifest["ecu_version_manifests"];
81 
82  EXPECT_EQ(manifest["installation_report"]["report"]["items"].size(), 1);
83  EXPECT_EQ(manifest["installation_report"]["report"]["items"][0]["ecu"].asString(), "CA:FE:A6:D2:84:9D");
84  EXPECT_TRUE(manifest["installation_report"]["report"]["items"][0]["result"]["success"].asBool());
85 
86  EXPECT_EQ(manifest_versions["CA:FE:A6:D2:84:9D"]["signed"]["installed_image"]["filepath"].asString(),
87  "firmware.txt");
88  EXPECT_EQ(manifest_versions["CA:FE:A6:D2:84:9D"]["signed"]["installed_image"]["fileinfo"]["length"].asUInt(), 17);
89 
90  result::UpdateCheck update_result3 = aktualizr.CheckUpdates().get();
91  EXPECT_EQ(update_result3.status, result::UpdateStatus::kNoUpdatesAvailable);
92  }
93 }
94 
95 /* Check that Aktualizr switches back to empty targets after failing to verify
96  * the Target matching. Also check that no updates are reported if any are
97  * invalid. */
98 TEST(Aktualizr, EmptyTargetsAfterVerification) {
99  TemporaryDirectory temp_dir;
100  TemporaryDirectory meta_dir;
101  auto http = std::make_shared<HttpRejectEmptyCorrId>(temp_dir.Path(), meta_dir.Path() / "repo");
102  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
103  logger_set_threshold(boost::log::trivial::trace);
104 
105  // Add two images: a valid one for the Primary and an invalid for the
106  // Secondary. The Primary will get verified first and should succeed.
107  Process uptane_gen(uptane_generator_path.string());
108  uptane_gen.run({"generate", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
109  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware.txt",
110  "--targetname", "firmware.txt", "--hwid", "primary_hw"});
111  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware.txt", "--hwid", "primary_hw",
112  "--serial", "CA:FE:A6:D2:84:9D"});
113  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware_name.txt",
114  "--targetname", "firmware_name.txt", "--hwid", "bad"});
115  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware_name.txt", "--hwid",
116  "secondary_hw", "--serial", "secondary_ecu_serial"});
117  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
118 
119  // failing verification
120  auto storage = INvStorage::newStorage(conf.storage);
121  {
122  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
123  aktualizr.Initialize();
124 
125  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
126  EXPECT_EQ(update_result.status, result::UpdateStatus::kError);
127  EXPECT_EQ(update_result.ecus_count, 0);
128  EXPECT_TRUE(update_result.updates.empty());
129  }
130 
131  // Backend reacts to failure: no need to install the target anymore
132  uptane_gen.run({"emptytargets", "--path", meta_dir.PathString()});
133  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
134 
135  // check that no update is available
136  {
137  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
138  aktualizr.Initialize();
139 
140  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
141  EXPECT_EQ(update_result.status, result::UpdateStatus::kNoUpdatesAvailable);
142  }
143 }
144 
145 #ifdef FIU_ENABLE
146 
147 /* Check that Aktualizr switches back to empty targets after failing a
148  * download attempt (OTA-3169) */
149 TEST(Aktualizr, EmptyTargetsAfterDownload) {
150  TemporaryDirectory temp_dir;
151  TemporaryDirectory meta_dir;
152  auto http = std::make_shared<HttpRejectEmptyCorrId>(temp_dir.Path(), meta_dir.Path() / "repo");
153  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
154  logger_set_threshold(boost::log::trivial::trace);
155 
156  Process uptane_gen(uptane_generator_path.string());
157  uptane_gen.run({"generate", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
158  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware.txt",
159  "--targetname", "firmware.txt", "--hwid", "primary_hw"});
160  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware.txt", "--hwid", "primary_hw",
161  "--serial", "CA:FE:A6:D2:84:9D"});
162  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
163 
164  // failing download
165  fault_injection_init();
166  auto storage = INvStorage::newStorage(conf.storage);
167  {
168  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
169  aktualizr.Initialize();
170 
171  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
172  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
173 
174  fault_injection_enable("fake_package_download", 1, "", 0);
175  result::Download download_result = aktualizr.Download(update_result.updates).get();
176  EXPECT_EQ(download_result.status, result::DownloadStatus::kError);
177  fault_injection_disable("fake_package_download");
178  }
179 
180  // Backend reacts to failure: no need to install the target anymore
181  uptane_gen.run({"emptytargets", "--path", meta_dir.PathString()});
182  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
183 
184  // check that no update is available
185  {
186  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
187  aktualizr.Initialize();
188 
189  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
190  EXPECT_EQ(update_result.status, result::UpdateStatus::kNoUpdatesAvailable);
191  }
192 }
193 
194 /* Check that Aktualizr switches back to empty targets after failing an
195  * installation attempt (OTA-2587) */
196 TEST(Aktualizr, EmptyTargetsAfterInstall) {
197  TemporaryDirectory temp_dir;
198  TemporaryDirectory meta_dir;
199  auto http = std::make_shared<HttpRejectEmptyCorrId>(temp_dir.Path(), meta_dir.Path() / "repo");
200  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
201  logger_set_threshold(boost::log::trivial::trace);
202 
203  Process uptane_gen(uptane_generator_path.string());
204  uptane_gen.run({"generate", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
205  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware.txt",
206  "--targetname", "firmware.txt", "--hwid", "primary_hw"});
207  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware.txt", "--hwid", "primary_hw",
208  "--serial", "CA:FE:A6:D2:84:9D"});
209  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
210 
211  // failing install
212  fault_injection_init();
213  auto storage = INvStorage::newStorage(conf.storage);
214  {
215  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
216  aktualizr.Initialize();
217 
218  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
219  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
220 
221  result::Download download_result = aktualizr.Download(update_result.updates).get();
222  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
223 
224  fault_injection_enable("fake_package_install", 1, "", 0);
225  result::Install install_result = aktualizr.Install(download_result.updates).get();
226  EXPECT_EQ(install_result.ecu_reports.size(), 1);
227  EXPECT_EQ(install_result.ecu_reports[0].install_res.result_code.num_code,
229  fault_injection_disable("fake_package_install");
230  }
231 
232  // Backend reacts to failure: no need to install the target anymore
233  uptane_gen.run({"emptytargets", "--path", meta_dir.PathString()});
234  uptane_gen.run({"signtargets", "--path", meta_dir.PathString(), "--correlationid", "abc123"});
235 
236  // check that no update is available
237  {
238  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
239  aktualizr.Initialize();
240 
241  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
242  EXPECT_EQ(update_result.status, result::UpdateStatus::kNoUpdatesAvailable);
243  }
244 }
245 
246 #endif
247 
248 #ifndef __NO_MAIN__
249 int main(int argc, char **argv) {
250  ::testing::InitGoogleTest(&argc, argv);
251  if (argc != 2) {
252  std::cerr << "Error: " << argv[0] << " requires the path to the uptane-generator utility\n";
253  return EXIT_FAILURE;
254  }
255  uptane_generator_path = argv[1];
256 
257  logger_init();
258  logger_set_threshold(boost::log::trivial::trace);
259 
260  return RUN_ALL_TESTS();
261 }
262 #endif
263 
264 // vim: set tabstop=2 shiftwidth=2 expandtab:
HttpFake
Definition: httpfake.h:20
UptaneTestCommon::TestAktualizr
Definition: uptane_test_common.h:21
result::UpdateCheck
Container for information about available updates.
Definition: results.h:37
data
General data structures.
Definition: types.h:217
HttpResponse
Definition: httpinterface.h:17
Config
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:208
HttpRejectEmptyCorrId
Definition: empty_targets_test.cc:13
Aktualizr
This class provides the main APIs necessary for launching and controlling libaktualizr.
Definition: aktualizr.h:24
result::Download
Container for information about downloading an update.
Definition: results.h:116
Process
Definition: test_utils.h:19
TemporaryDirectory
Definition: utils.h:82
result::Install
Container for information about installing an update.
Definition: results.h:129
data::ResultCode::Numeric::kInstallFailed
@ kInstallFailed
Package installation failed.