Aktualizr
C++ SOTA Client
metadata_fetch_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 
10 boost::filesystem::path uptane_generator_path;
11 
12 class HttpFakeMetaCounter : public HttpFake {
13  public:
14  HttpFakeMetaCounter(const boost::filesystem::path &test_dir_in, const boost::filesystem::path &meta_dir_in)
15  : HttpFake(test_dir_in, "", meta_dir_in) {}
16 
17  HttpResponse get(const std::string &url, int64_t maxsize) override {
18  if (url.find("director/1.root.json") != std::string::npos) {
19  ++director_1root_count;
20  }
21  if (url.find("director/2.root.json") != std::string::npos) {
22  ++director_2root_count;
23  }
24  if (url.find("director/targets.json") != std::string::npos) {
25  ++director_targets_count;
26  }
27  if (url.find("repo/1.root.json") != std::string::npos) {
28  ++image_1root_count;
29  }
30  if (url.find("repo/2.root.json") != std::string::npos) {
31  ++image_2root_count;
32  }
33  if (url.find("repo/timestamp.json") != std::string::npos) {
34  ++image_timestamp_count;
35  }
36  if (url.find("repo/snapshot.json") != std::string::npos) {
37  ++image_snapshot_count;
38  }
39  if (url.find("repo/targets.json") != std::string::npos) {
40  ++image_targets_count;
41  }
42 
43  return HttpFake::get(url, maxsize);
44  }
45 
46  int director_1root_count{0};
47  int director_2root_count{0};
48  int director_targets_count{0};
49  int image_1root_count{0};
50  int image_2root_count{0};
51  int image_timestamp_count{0};
52  int image_snapshot_count{0};
53  int image_targets_count{0};
54 };
55 
56 /*
57  * Don't download Image repo metadata if Director reports no new targets. Don't
58  * download Snapshot and Targets metadata from the Image repo if the Timestamp
59  * indicates nothing has changed.
60  */
61 TEST(Aktualizr, MetadataFetch) {
62  TemporaryDirectory temp_dir;
63  TemporaryDirectory meta_dir;
64  auto http = std::make_shared<HttpFakeMetaCounter>(temp_dir.Path(), meta_dir.Path() / "repo");
65  Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server);
66  logger_set_threshold(boost::log::trivial::trace);
67 
68  auto storage = INvStorage::newStorage(conf.storage);
69  UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http);
70  aktualizr.Initialize();
71 
72  // No updates scheduled: only download Director Root and Targets metadata.
73  Process uptane_gen(uptane_generator_path.string());
74  uptane_gen.run({"generate", "--path", meta_dir.PathString()});
75 
76  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
77  EXPECT_EQ(update_result.status, result::UpdateStatus::kNoUpdatesAvailable);
78  EXPECT_EQ(http->director_1root_count, 1);
79  EXPECT_EQ(http->director_2root_count, 1);
80  EXPECT_EQ(http->director_targets_count, 1);
81  EXPECT_EQ(http->image_1root_count, 0);
82  EXPECT_EQ(http->image_2root_count, 0);
83  EXPECT_EQ(http->image_timestamp_count, 0);
84  EXPECT_EQ(http->image_snapshot_count, 0);
85  EXPECT_EQ(http->image_targets_count, 0);
86 
87  // Two images added, but only one update scheduled: all metadata objects
88  // should be fetched once.
89  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware.txt",
90  "--targetname", "firmware.txt", "--hwid", "primary_hw"});
91  uptane_gen.run({"image", "--path", meta_dir.PathString(), "--filename", "tests/test_data/firmware_name.txt",
92  "--targetname", "firmware_name.txt", "--hwid", "primary_hw"});
93  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware.txt", "--hwid", "primary_hw",
94  "--serial", "CA:FE:A6:D2:84:9D"});
95  uptane_gen.run({"adddelegation", "--path", meta_dir.PathString(), "--dname", "role-abc", "--dpattern", "abc/*"});
96  uptane_gen.run({"signtargets", "--path", meta_dir.PathString()});
97 
98  update_result = aktualizr.CheckUpdates().get();
99  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
100  EXPECT_EQ(http->director_1root_count, 1);
101  EXPECT_EQ(http->director_2root_count, 2);
102  EXPECT_EQ(http->director_targets_count, 2);
103  EXPECT_EQ(http->image_1root_count, 1);
104  EXPECT_EQ(http->image_2root_count, 1);
105  EXPECT_EQ(http->image_timestamp_count, 1);
106  EXPECT_EQ(http->image_snapshot_count, 1);
107  EXPECT_EQ(http->image_targets_count, 1);
108 
109  // Update scheduled with pre-existing image: no need to refetch Image repo
110  // Snapshot or Targets metadata.
111  uptane_gen.run({"emptytargets", "--path", meta_dir.PathString()});
112  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware_name.txt", "--hwid",
113  "primary_hw", "--serial", "CA:FE:A6:D2:84:9D"});
114  uptane_gen.run({"signtargets", "--path", meta_dir.PathString()});
115 
116  update_result = aktualizr.CheckUpdates().get();
117  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
118  EXPECT_EQ(http->director_1root_count, 1);
119  EXPECT_EQ(http->director_2root_count, 3);
120  EXPECT_EQ(http->director_targets_count, 3);
121  EXPECT_EQ(http->image_1root_count, 1);
122  EXPECT_EQ(http->image_2root_count, 2);
123  EXPECT_EQ(http->image_timestamp_count, 2);
124  EXPECT_EQ(http->image_snapshot_count, 1);
125  EXPECT_EQ(http->image_targets_count, 1);
126 
127  // Delegation added to an existing delegation; update scheduled with
128  // pre-existing image: Snapshot must be refetched, but Targets are unchanged.
129  uptane_gen.run({"emptytargets", "--path", meta_dir.PathString()});
130  uptane_gen.run({"addtarget", "--path", meta_dir.PathString(), "--targetname", "firmware.txt", "--hwid", "primary_hw",
131  "--serial", "CA:FE:A6:D2:84:9D"});
132  uptane_gen.run({"adddelegation", "--path", meta_dir.PathString(), "--dname", "role-def", "--dpattern", "def/*",
133  "--dparent", "role-abc"});
134  uptane_gen.run({"signtargets", "--path", meta_dir.PathString()});
135 
136  update_result = aktualizr.CheckUpdates().get();
137  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
138  EXPECT_EQ(http->director_1root_count, 1);
139  EXPECT_EQ(http->director_2root_count, 4);
140  EXPECT_EQ(http->director_targets_count, 4);
141  EXPECT_EQ(http->image_1root_count, 1);
142  EXPECT_EQ(http->image_2root_count, 3);
143  EXPECT_EQ(http->image_timestamp_count, 3);
144  EXPECT_EQ(http->image_snapshot_count, 2);
145  EXPECT_EQ(http->image_targets_count, 1);
146 }
147 
148 #ifndef __NO_MAIN__
149 int main(int argc, char **argv) {
150  ::testing::InitGoogleTest(&argc, argv);
151  if (argc != 2) {
152  std::cerr << "Error: " << argv[0] << " requires the path to the uptane-generator utility\n";
153  return EXIT_FAILURE;
154  }
155  uptane_generator_path = argv[1];
156 
157  logger_init();
158  logger_set_threshold(boost::log::trivial::trace);
159 
160  return RUN_ALL_TESTS();
161 }
162 #endif
163 
164 // 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
HttpResponse
Definition: httpinterface.h:17
Config
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:208
Aktualizr
This class provides the main APIs necessary for launching and controlling libaktualizr.
Definition: aktualizr.h:24
Process
Definition: test_utils.h:19
TemporaryDirectory
Definition: utils.h:82
HttpFakeMetaCounter
Definition: metadata_fetch_test.cc:12