Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
dockerappmanager_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <boost/filesystem.hpp>
4 #include <boost/process.hpp>
5 
6 #include "config/config.h"
7 #include "http/httpclient.h"
8 #include "package_manager/packagemanagerfactory.h"
9 #include "package_manager/packagemanagerinterface.h"
10 #include "primary/sotauptaneclient.h"
11 #include "storage/invstorage.h"
12 #include "test_utils.h"
13 #include "uptane/fetcher.h"
14 
15 static std::string repo_server = "http://127.0.0.1:";
16 static std::string treehub_server = "http://127.0.0.1:";
17 static boost::filesystem::path test_sysroot;
18 static boost::filesystem::path uptane_gen;
19 
20 static void progress_cb(const Uptane::Target& target, const std::string& description, unsigned int progress) {
21  (void)description;
22  LOG_INFO << "progress_cb " << target << " " << progress;
23 }
24 
25 static std::unique_ptr<boost::process::child> create_repo(const boost::filesystem::path& repo_path) {
26  std::string port = TestUtils::getFreePort();
27  repo_server += port;
28  auto p = std_::make_unique<boost::process::child>("src/libaktualizr/package_manager/dockerapp_test_repo.sh",
29  uptane_gen, repo_path, port);
30  TestUtils::waitForServer(repo_server + "/");
31  return p;
32 }
33 
34 TEST(DockerAppManager, PackageManager_Factory_Good) {
35  Config config;
36  config.pacman.type = PACKAGE_MANAGER_OSTREEDOCKERAPP;
37  config.pacman.sysroot = test_sysroot;
39  config.storage.path = dir.Path();
40 
41  std::shared_ptr<INvStorage> storage = INvStorage::newStorage(config.storage);
42  auto pacman = PackageManagerFactory::makePackageManager(config.pacman, config.bootloader, storage, nullptr);
43  EXPECT_TRUE(pacman);
44 }
45 
47  std::string sha = Utils::readFile(test_sysroot / "ostree/repo/refs/heads/ostree/1/1/0", true);
48  Json::Value target_json;
49  target_json["hashes"]["sha256"] = sha;
50  target_json["custom"]["targetFormat"] = "OSTREE";
51  target_json["length"] = 0;
52  target_json["custom"]["docker_apps"]["app1"]["filename"] = "foo.dockerapp";
53  // Add a docker-app bundle entry to make sure we don't get confused
54  target_json["custom"]["docker_apps"]["app1"]["uri"] = "http://foo.com";
55  Uptane::Target target("pull", target_json);
56 
57  TemporaryDirectory temp_dir;
58  auto repo = temp_dir.Path();
59  auto repod = create_repo(repo);
60 
61  boost::filesystem::path apps_root = temp_dir / "docker_apps";
62 
63  Config config;
64  config.pacman.type = PACKAGE_MANAGER_OSTREEDOCKERAPP;
65  config.pacman.sysroot = test_sysroot.string();
66  config.pacman.ostree_server = treehub_server;
67  config.pacman.extra["docker_apps_root"] = apps_root.string();
68  config.pacman.extra["docker_apps"] = "app1 app2";
69  config.pacman.extra["docker_app_bin"] = config.pacman.extra["docker_compose_bin"] =
70  "src/libaktualizr/package_manager/docker_fake.sh";
71  config.uptane.repo_server = repo_server + "/repo/repo";
73  config.storage.path = dir.Path();
74 
75  // Create a fake "docker-app" that's not configured. We'll test below
76  // to ensure its removed
77  boost::filesystem::create_directories(apps_root / "delete-this-app");
78  // This is app is configured, but not a part of the install Target, so
79  // it should get removed below
80  boost::filesystem::create_directories(apps_root / "app2");
81 
82  std::shared_ptr<INvStorage> storage = INvStorage::newStorage(config.storage);
83  KeyManager keys(storage, config.keymanagerConfig());
84  auto client = std_::make_unique<SotaUptaneClient>(config, storage);
85  ASSERT_TRUE(client->updateImageMeta());
86 
87  std::string targets = Utils::readFile(repo / "repo/repo/targets.json");
88  LOG_INFO << "Repo targets " << targets;
89 
90  bool result = client->package_manager_->fetchTarget(target, *(client->uptane_fetcher), keys, progress_cb, nullptr);
91  ASSERT_TRUE(result);
92 
93  auto hashes = std::vector<Uptane::Hash>{
94  Uptane::Hash(Uptane::Hash::Type::kSha256, "dfca385c923400228c8ddd3c2d572919985e48a9409a2d71dab33148017231c3"),
95  Uptane::Hash(Uptane::Hash::Type::kSha512,
96  "76b183d51f53613a450825afc6f984077b68ae7b321ba041a2b3871f3c25a9a20d964ad0b60352e5fdd09b78fd53879f4e3"
97  "fa3dcc8335b26d3bbf455803d2ecb")};
98  Uptane::Target app_target("foo.dockerapp", Uptane::EcuMap{}, hashes, 8);
99  ASSERT_TRUE(storage->checkTargetFile(app_target));
100 
101  client->package_manager_->install(target);
102  std::string content = Utils::readFile(apps_root / "app1/docker-compose.yml");
103  ASSERT_EQ("DOCKER-APP RENDER OUTPUT\nfake contents of a docker app\n", content);
104 
105  // Make sure the unconfigured docker app has been removed:
106  ASSERT_FALSE(boost::filesystem::exists(apps_root / "delete-this-app"));
107  ASSERT_FALSE(boost::filesystem::exists(apps_root / "app2"));
108  ASSERT_TRUE(boost::filesystem::exists(apps_root / "docker-compose-down-called"));
109 
110  setenv("DOCKER_APP_FAIL", "1", 1);
111  result = client->package_manager_->fetchTarget(target, *(client->uptane_fetcher), keys, progress_cb, nullptr);
112  ASSERT_FALSE(result);
113 }
114 
116  // Define the target we want to update to
117  unsetenv("DOCKER_APP_FAIL"); // Make sure this is cleared from previous test
118  std::string sha = Utils::readFile(test_sysroot / "ostree/repo/refs/heads/ostree/1/1/0", true);
119  Json::Value target_json;
120  target_json["hashes"]["sha256"] = sha;
121  target_json["custom"]["targetFormat"] = "OSTREE";
122  target_json["length"] = 0;
123  target_json["custom"]["docker_apps"]["app1"]["uri"] = "hub.docker.io/user/hello@sha256:deadbeef";
124  // Add a standalone entry to make sure we don't get confused
125  target_json["custom"]["docker_apps"]["app1"]["filename"] = "foo.dockerapp";
126  Uptane::Target target("pull", target_json);
127 
128  TemporaryDirectory temp_dir;
129 
130  // Insert a facke "docker" binary into our path
131  boost::filesystem::path docker =
132  boost::filesystem::system_complete("src/libaktualizr/package_manager/docker_fake.sh");
133  ASSERT_EQ(0, symlink(docker.c_str(), (temp_dir.Path() / "docker").c_str()));
134  std::string path(temp_dir.PathString() + ":" + getenv("PATH"));
135  setenv("PATH", path.c_str(), 1);
136 
137  // Set up a SotaUptaneClient to test with
138  boost::filesystem::path apps_root = temp_dir / "docker_apps";
139  Config config;
140  config.pacman.type = PACKAGE_MANAGER_OSTREEDOCKERAPP;
141  config.pacman.sysroot = test_sysroot;
142  config.pacman.extra["docker_apps_root"] = apps_root.string();
143  config.pacman.extra["docker_apps"] = "app1 app2";
144  config.pacman.extra["docker_compose_bin"] = "src/libaktualizr/package_manager/docker_fake.sh";
145  config.pacman.ostree_server = treehub_server;
146  config.uptane.repo_server = repo_server + "/repo/repo";
147  TemporaryDirectory dir;
148  config.storage.path = dir.Path();
149 
150  // Create a fake "docker-app" that's not configured. We'll test below
151  // to ensure its removed
152  // boost::filesystem::create_directories(apps_root / "delete-this-app");
153  // This is app is configured, but not a part of the install Target, so
154  // it should get removed below
155  boost::filesystem::create_directories(apps_root / "app2");
156 
157  std::shared_ptr<INvStorage> storage = INvStorage::newStorage(config.storage);
158  storage->savePrimaryInstalledVersion(target, InstalledVersionUpdateMode::kCurrent);
159  KeyManager keys(storage, config.keymanagerConfig());
160  auto client = std_::make_unique<SotaUptaneClient>(config, storage);
161 
162  ASSERT_TRUE(client->package_manager_->fetchTarget(target, *(client->uptane_fetcher), keys, progress_cb, nullptr));
163  std::string content = Utils::readFile(temp_dir / "docker-app-pull");
164  ASSERT_EQ("PULL CALLED app pull hub.docker.io/user/hello@sha256:deadbeef\n", content);
165 
166  client->package_manager_->install(target);
167  content = Utils::readFile(apps_root / "app1/docker-compose.yml");
168  ASSERT_EQ("FAKE CONTENT FOR IMAGE RENDER\nhub.docker.io/user/hello@sha256:deadbeef\n", content);
169 
170  // Make sure the unconfigured docker app has been removed:
171  ASSERT_FALSE(boost::filesystem::exists(apps_root / "delete-this-app"));
172  ASSERT_FALSE(boost::filesystem::exists(apps_root / "app2"));
173  ASSERT_TRUE(boost::filesystem::exists(apps_root / "docker-compose-down-called"));
174 }
175 
176 #ifndef __NO_MAIN__
177 int main(int argc, char** argv) {
178  ::testing::InitGoogleTest(&argc, argv);
179 
180  if (argc != 3) {
181  std::cerr << "Error: " << argv[0]
182  << " requires the path to an OSTree sysroot and uptane-generator as an input argument.\n";
183  return EXIT_FAILURE;
184  }
185  uptane_gen = argv[2];
186 
187  std::string port = TestUtils::getFreePort();
188  treehub_server += port;
189  boost::process::child server_process("tests/fake_http_server/fake_test_server.py", port);
190 
191  TemporaryDirectory temp_dir;
192  // Utils::copyDir doesn't work here. Complaints about non existent symlink path
193  int r = system((std::string("cp -r ") + argv[1] + std::string(" ") + temp_dir.PathString()).c_str());
194  if (r != 0) {
195  return -1;
196  }
197  test_sysroot = (temp_dir.Path() / "ostree_repo").string();
198 
199  TestUtils::waitForServer(treehub_server + "/");
200 
201  return RUN_ALL_TESTS();
202 }
203 #endif
KeyManager
Definition: keymanager.h:13
Uptane::Hash
The hash of a file or TUF metadata.
Definition: tuf.h:209
DockerAppBundles
Definition: dockerappmanager.h:44
Config
Configuration object for an aktualizr instance running on a primary ECU.
Definition: config.h:74
TemporaryDirectory
Definition: utils.h:82
DockerAppStandalone
Definition: dockerappmanager.h:21
result
Results of libaktualizr API calls.
Definition: results.h:13
Uptane::Target
Definition: tuf.h:238
DockerAppManager
Definition: dockerappmanager.h:67