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