Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
uptane_vector_tests.cc
1 #include <gtest/gtest.h>
2 
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <cstdlib>
6 #include <iostream>
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "config/config.h"
12 #include "http/httpclient.h"
13 #include "logging/logging.h"
14 #include "primary/sotauptaneclient.h"
15 #include "storage/invstorage.h"
16 #include "utilities/utils.h"
17 
18 std::string address;
19 
21  public:
22  VectorWrapper(Json::Value vector) : vector_(std::move(vector)) {}
23 
24  bool matchError(const Uptane::Exception& e) {
25  auto me = [this, &e](const std::string r) {
26  if (vector_[r]["update"]["err_msg"].asString() == e.what()) {
27  return true;
28  }
29  const Json::Value& targets = vector_[r]["targets"];
30  for (Json::Value::const_iterator it = targets.begin(); it != targets.end(); it++) {
31  if ((*it)["err_msg"].asString() == e.what()) {
32  return true;
33  }
34  }
35  return false;
36  };
37  if (me("director") || me("image_repo")) {
38  return true;
39  }
40  std::cout << "aktualizr failed with unmatched exception " << typeid(e).name() << ": " << e.what() << "\n";
41  std::cout << "Expected error: " << vector_ << "\n";
42  return false;
43  }
44 
45  bool shouldFail() {
46  bool should_fail = false;
47  if (!vector_["director"]["update"]["is_success"].asBool() ||
48  !vector_["image_repo"]["update"]["is_success"].asBool()) {
49  should_fail = true;
50  } else {
51  for (const auto& t : vector_["director"]["targets"]) {
52  if (!t["is_success"].asBool()) {
53  should_fail = true;
54  break;
55  }
56  }
57  for (const auto& t : vector_["image_repo"]["targets"]) {
58  if (!t["is_success"].asBool()) {
59  should_fail = true;
60  break;
61  }
62  }
63  }
64  return should_fail;
65  }
66 
67  void printExpectedFailure() {
68  std::cout << "No exceptions occurred, but expected ";
69  if (!vector_["director"]["update"]["is_success"].asBool()) {
70  std::cout << "exception from director: '" << vector_["director"]["update"]["err"]
71  << " with message: " << vector_["director"]["update"]["err_msg"] << "\n";
72  } else if (!vector_["image_repo"]["update"]["is_success"].asBool()) {
73  std::cout << "exception from image_repo: '" << vector_["image_repo"]["update"]["err"]
74  << " with message: " << vector_["image_repo"]["update"]["err_msg"] << "\n";
75  } else {
76  std::cout << "an exception while fetching Targets metadata.\n";
77  }
78  }
79 
80  private:
81  Json::Value vector_;
82 };
83 
84 class UptaneVector : public ::testing::TestWithParam<std::string> {};
85 
86 /**
87  * Check that aktualizr fails on expired metadata.
88  * RecordProperty("zephyr_key", "REQ-150,TST-49");
89  * Check that aktualizr fails on bad threshold.
90  * RecordProperty("zephyr_key", "REQ-153,TST-52");
91  */
92 TEST_P(UptaneVector, Test) {
93  const std::string test_name = GetParam();
94  std::cout << "Running test vector " << test_name << "\n";
95 
96  TemporaryDirectory temp_dir;
97  Config config;
98  config.provision.primary_ecu_serial = "test_primary_ecu_serial";
99  config.provision.primary_ecu_hardware_id = "test_primary_hardware_id";
100  config.uptane.director_server = address + test_name + "/director";
101  config.uptane.repo_server = address + test_name + "/image_repo";
102  config.storage.path = temp_dir.Path();
103  config.storage.uptane_metadata_path = BasedPath(temp_dir.Path() / "metadata");
104  config.pacman.type = PACKAGE_MANAGER_NONE;
105  logger_set_threshold(boost::log::trivial::trace);
106 
107  auto storage = INvStorage::newStorage(config.storage);
108  auto uptane_client = std_::make_unique<SotaUptaneClient>(config, storage);
109  Uptane::EcuSerial ecu_serial(config.provision.primary_ecu_serial);
110  Uptane::HardwareIdentifier hw_id(config.provision.primary_ecu_hardware_id);
111  uptane_client->primary_ecu_serial_ = ecu_serial;
112  uptane_client->primary_ecu_hw_id_ = hw_id;
113  Uptane::EcuMap ecu_map{{ecu_serial, hw_id}};
114  Uptane::Target target("test_filename", ecu_map, {{Hash::Type::kSha256, "sha256"}}, 1, "");
115  storage->saveInstalledVersion(ecu_serial.ToString(), target, InstalledVersionUpdateMode::kCurrent);
116 
117  HttpClient http_client;
118  while (true) {
119  HttpResponse response = http_client.post(address + test_name + "/step", Json::Value());
120  if (response.http_status_code == 204) {
121  return;
122  }
123  const auto vector_json(response.getJson());
124  std::cout << "VECTOR: " << vector_json;
125  VectorWrapper vector(vector_json);
126 
127  bool should_fail = vector.shouldFail();
128 
129  try {
130  /* Fetch metadata from the Director.
131  * Check metadata from the Director.
132  * Identify targets for known ECUs.
133  * Fetch metadata from the Image repo.
134  * Check metadata from the Image repo.
135  *
136  * It would be simpler to just call fetchMeta() here, but that calls
137  * putManifestSimple(), which will fail here. */
138  uptane_client->uptaneIteration(nullptr, nullptr);
139 
140  result::UpdateCheck updates = uptane_client->checkUpdates();
141  if (updates.status == result::UpdateStatus::kError) {
142  ASSERT_TRUE(should_fail) << "checkUpdates unexpectedly failed.";
143  if (uptane_client->getLastException() != nullptr) {
144  std::rethrow_exception(uptane_client->getLastException());
145  }
146  }
147  if (updates.ecus_count > 0) {
148  /* Download a binary package.
149  * Verify a binary package. */
150  result::Download result = uptane_client->downloadImages(updates.updates);
151  if (result.status != result::DownloadStatus::kSuccess) {
152  ASSERT_TRUE(should_fail) << "downloadImages unexpectedly failed.";
153  if (uptane_client->getLastException() != nullptr) {
154  std::rethrow_exception(uptane_client->getLastException());
155  }
156  }
157  }
158 
159  } catch (const Uptane::Exception& e) {
160  ASSERT_TRUE(vector.matchError(e)) << "libaktualizr threw a different exception than expected!";
161  continue;
162  } catch (const std::exception& e) {
163  FAIL() << "libaktualizr failed with unrecognized exception " << typeid(e).name() << ": " << e.what();
164  }
165 
166  if (should_fail) {
167  vector.printExpectedFailure();
168  FAIL();
169  }
170  }
171  FAIL() << "Step sequence unexpectedly aborted.";
172 }
173 
174 std::vector<std::string> GetVectors() {
175  HttpClient http_client;
176  const Json::Value json_vectors = http_client.get(address, HttpInterface::kNoLimit).getJson();
177  std::vector<std::string> vectors;
178  for (Json::ValueConstIterator it = json_vectors.begin(); it != json_vectors.end(); it++) {
179  vectors.emplace_back((*it).asString());
180  }
181  return vectors;
182 }
183 
184 INSTANTIATE_TEST_SUITE_P(UptaneVectorSuite, UptaneVector, ::testing::ValuesIn(GetVectors()));
185 
186 int main(int argc, char* argv[]) {
187  logger_init();
188  logger_set_threshold(boost::log::trivial::trace);
189 
190  if (argc < 2) {
191  std::cerr << "This program is intended to be run from run_vector_tests.sh!\n";
192  return 1;
193  }
194 
195  /* Use ports to distinguish both the server connection and local storage so
196  * that parallel runs of this code don't cause problems that are difficult to
197  * debug. */
198  const std::string port = argv[1];
199  address = "http://localhost:" + port + "/";
200 
201  ::testing::InitGoogleTest(&argc, argv);
202  return RUN_ALL_TESTS();
203 }
Container for information about downloading an update.
Definition: results.h:117
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:74
Container for information about available updates.
Definition: results.h:38
Results of libaktualizr API calls.
Definition: results.h:13