Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
uptane_network_test.cc
Go to the documentation of this file.
1 /**
2  * \file
3  *
4  * Check that aktualizr can complete provisioning after encountering various
5  * network issues.
6  */
7 #include <gtest/gtest.h>
8 #include <gtest/internal/gtest-port.h>
9 
10 #include <boost/process.hpp>
11 #include <fstream>
12 #include <iostream>
13 #include <string>
14 #include <utility>
15 #include <vector>
16 
17 #include "http/httpclient.h"
18 #include "httpfake.h"
19 #include "logging/logging.h"
20 #include "primary/initializer.h"
21 #include "primary/sotauptaneclient.h"
22 #include "storage/invstorage.h"
23 #include "test_utils.h"
24 #include "uptane/uptanerepository.h"
25 #include "uptane_test_common.h"
26 #include "utilities/utils.h"
27 
28 Config conf("tests/config/basic.toml");
29 std::string port;
30 
31 bool doTestInit(StorageType storage_type, const std::string &device_register_state,
32  const std::string &ecu_register_state) {
33  LOG_INFO << "First attempt to initialize.";
34  TemporaryDirectory temp_dir;
35  conf.storage.type = storage_type;
36  conf.storage.path = temp_dir.Path();
37  conf.provision.expiry_days = device_register_state;
38  conf.provision.primary_ecu_serial = ecu_register_state;
39  const std::string good_url = conf.provision.server;
40  if (device_register_state == "noconnection") {
41  conf.provision.server = conf.provision.server.substr(conf.provision.server.size() - 2) + "11";
42  }
43 
44  bool result;
45  auto http = std::make_shared<HttpClient>();
46  auto store = INvStorage::newStorage(conf.storage);
47  {
48  KeyManager keys(store, conf.keymanagerConfig());
49  try {
50  Initializer initializer(conf.provision, store, http, keys, {});
51  result = true;
52  } catch (const std::exception &e) {
53  result = false;
54  }
55  }
56  if (device_register_state != "noerrors" || ecu_register_state != "noerrors") {
57  EXPECT_FALSE(result);
58  LOG_INFO << "Second attempt to initialize.";
59  conf.provision.server = good_url;
60  conf.provision.expiry_days = "noerrors";
61  conf.provision.primary_ecu_serial = "noerrors";
62 
63  if (device_register_state == "noerrors" && ecu_register_state != "noerrors") {
64  // restore a "good" ECU serial in the ECU register fault injection case
65  // (the bad value has been cached in storage)
66  EcuSerials serials;
67  store->loadEcuSerials(&serials);
68  serials[0].first = Uptane::EcuSerial(conf.provision.primary_ecu_serial);
69  store->storeEcuSerials(serials);
70  }
71 
72  KeyManager keys(store, conf.keymanagerConfig());
73  try {
74  Initializer initializer(conf.provision, store, http, keys, {});
75  result = true;
76  } catch (const std::exception &e) {
77  result = false;
78  }
79  }
80 
81  return result;
82 }
83 
84 TEST(UptaneNetwork, device_drop_request_sqlite) {
85  RecordProperty("zephyr_key", "OTA-991,TST-158");
86  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "drop_request", "noerrors"));
87 }
88 
89 TEST(UptaneNetwork, device_drop_body_sqlite) {
90  RecordProperty("zephyr_key", "OTA-991,TST-158");
91  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "drop_body", "noerrors"));
92 }
93 
94 TEST(UptaneNetwork, device_503_sqlite) {
95  RecordProperty("zephyr_key", "OTA-991,TST-158");
96  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "status_503", "noerrors"));
97 }
98 
99 TEST(UptaneNetwork, device_408_sqlite) {
100  RecordProperty("zephyr_key", "OTA-991,TST-158");
101  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "status_408", "noerrors"));
102 }
103 
104 TEST(UptaneNetwork, ecu_drop_request_sqlite) {
105  RecordProperty("zephyr_key", "OTA-991,TST-158");
106  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "noerrors", "drop_request"));
107 }
108 
109 TEST(UptaneNetwork, ecu_503_sqlite) {
110  RecordProperty("zephyr_key", "OTA-991,TST-158");
111  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "noerrors", "status_503"));
112 }
113 
114 TEST(UptaneNetwork, ecu_408_sqlite) {
115  RecordProperty("zephyr_key", "OTA-991,TST-158");
116  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "noerrors", "status_408"));
117 }
118 
119 TEST(UptaneNetwork, no_connection_sqlite) {
120  RecordProperty("zephyr_key", "OTA-991,TST-158");
121  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "noconnection", "noerrors"));
122 }
123 
124 TEST(UptaneNetwork, no_errors_sqlite) {
125  RecordProperty("zephyr_key", "OTA-991,TST-158");
126  EXPECT_TRUE(doTestInit(StorageType::kSqlite, "noerrors", "noerrors"));
127 }
128 
129 TEST(UptaneNetwork, DownloadFailure) {
130  TemporaryDirectory temp_dir;
131  conf.storage.path = temp_dir.Path();
132  conf.provision.expiry_days = "download_failure";
133  conf.provision.primary_ecu_serial = "download_failure";
134  conf.provision.primary_ecu_hardware_id = "hardware_id";
135 
136  auto storage = INvStorage::newStorage(conf.storage);
137  auto up = std_::make_unique<SotaUptaneClient>(conf, storage);
138  EXPECT_NO_THROW(up->initialize());
139 
140  Json::Value ot_json;
141  ot_json["custom"]["ecuIdentifiers"][conf.provision.primary_ecu_serial]["hardwareId"] =
142  conf.provision.primary_ecu_hardware_id;
143  ot_json["custom"]["targetFormat"] = "binary";
144  ot_json["length"] = 2048;
145  ot_json["hashes"]["sha256"] = "d03b1a2081755f3a5429854cc3e700f8cbf125db2bd77098ae79a7d783256a7d";
146  Uptane::Target package_to_install{conf.provision.primary_ecu_serial, ot_json};
147 
148  std::pair<bool, Uptane::Target> result = up->downloadImage(package_to_install);
149  EXPECT_TRUE(result.first);
150 }
151 
152 /*
153  * Output a log when connectivity is restored.
154  */
155 class HttpUnstable : public HttpFake {
156  public:
157  HttpUnstable(const boost::filesystem::path &test_dir_in) : HttpFake(test_dir_in, "hasupdates") {}
158  HttpResponse get(const std::string &url, int64_t maxsize) override {
159  if (!connectSwitch) {
160  return HttpResponse({}, 503, CURLE_OK, "");
161  } else {
162  return HttpFake::get(url, maxsize);
163  }
164  }
165 
166  HttpResponse put(const std::string &url, const Json::Value &data) override {
167  if (!connectSwitch) {
168  (void)data;
169  return HttpResponse(url, 503, CURLE_OK, "");
170  } else {
171  return HttpFake::put(url, data);
172  }
173  }
174 
175  HttpResponse post(const std::string &url, const Json::Value &data) override {
176  if (!connectSwitch) {
177  (void)data;
178  return HttpResponse(url, 503, CURLE_OK, "");
179  } else {
180  return HttpFake::post(url, data);
181  }
182  }
183 
184  bool connectSwitch = true;
185 };
186 
187 TEST(UptaneNetwork, LogConnectivityRestored) {
188  TemporaryDirectory temp_dir;
189  auto http = std::make_shared<HttpUnstable>(temp_dir.Path());
190  Config config = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
191  config.uptane.director_server = http->tls_server + "director";
192  config.uptane.repo_server = http->tls_server + "repo";
193  config.pacman.type = PACKAGE_MANAGER_NONE;
194  config.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
195  config.provision.primary_ecu_hardware_id = "primary_hw";
196  config.storage.path = temp_dir.Path();
197  config.tls.server = http->tls_server;
198  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hw");
199 
200  auto storage = INvStorage::newStorage(config.storage);
201  auto up = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
202  EXPECT_NO_THROW(up->initialize());
203 
204  result::UpdateCheck result = up->fetchMeta();
205  EXPECT_EQ(result.status, result::UpdateStatus::kUpdatesAvailable);
206 
207  http->connectSwitch = false;
208  result = up->fetchMeta();
209  EXPECT_EQ(result.status, result::UpdateStatus::kError);
210 
211  http->connectSwitch = true;
212  testing::internal::CaptureStdout();
213  result = up->fetchMeta();
214  EXPECT_EQ(result.status, result::UpdateStatus::kUpdatesAvailable);
215  EXPECT_NE(std::string::npos, testing::internal::GetCapturedStdout().find("Connectivity is restored."));
216 }
217 
218 #ifndef __NO_MAIN__
219 int main(int argc, char **argv) {
220  ::testing::InitGoogleTest(&argc, argv);
221  logger_set_threshold(boost::log::trivial::trace);
222 
223  port = TestUtils::getFreePort();
224  boost::process::child server_process("tests/fake_http_server/fake_uptane_server.py", port);
225  TestUtils::waitForServer("http://127.0.0.1:" + port + "/");
226 
227  conf.provision.server = "http://127.0.0.1:" + port;
228  conf.tls.server = "http://127.0.0.1:" + port;
229  conf.provision.ecu_registration_endpoint = conf.tls.server + "/director/ecus";
230  conf.uptane.repo_server = conf.tls.server + "/repo";
231  conf.uptane.director_server = conf.tls.server + "/director";
232  conf.pacman.ostree_server = conf.tls.server + "/treehub";
233 
234  return RUN_ALL_TESTS();
235 }
236 #endif
HttpFake
Definition: httpfake.h:20
HttpUnstable
Definition: uptane_network_test.cc:155
KeyManager
Definition: keymanager.h:13
result::UpdateCheck
Container for information about available updates.
Definition: results.h:38
data
General data structures.
Definition: types.cc:55
HttpResponse
Definition: httpinterface.h:17
Config
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:74
Uptane::EcuSerial
Definition: tuf.h:177
TemporaryDirectory
Definition: utils.h:82
result
Results of libaktualizr API calls.
Definition: results.h:13
Initializer
Definition: initializer.h:11
Uptane::Target
Definition: tuf.h:210