Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
ostree_object_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <curl/curl.h>
4 #include <boost/process.hpp>
5 
6 #include "authenticate.h"
7 #include "garage_common.h"
8 #include "ostree_dir_repo.h"
9 #include "ostree_object.h"
10 #include "request_pool.h"
11 #include "server_credentials.h"
12 #include "test_utils.h"
13 
14 std::string port;
15 std::string repo_path;
16 
17 /* Verify that constructor does not accept a nonexistent repo. */
18 TEST(OstreeObject, ConstructorBad) {
19  OSTreeDirRepo bad_repo("nonexistentrepo");
20  EXPECT_THROW(OSTreeObject(bad_repo, "bad"), std::runtime_error);
21 }
22 
23 /* Verify that constructor accepts a valid repo and commit hash. */
24 TEST(OstreeObject, ConstructorGood) {
25  OSTreeDirRepo good_repo(repo_path);
26  OSTreeHash hash = good_repo.GetRef("master").GetHash();
27  boost::filesystem::path objpath = hash.string().insert(2, 1, '/');
28  OSTreeObject(good_repo, objpath.string() + ".commit");
29 }
30 
31 // This is a class solely for the purpose of being a FRIEND_TEST to
32 // OSTreeObject. The name is carefully constructed for this purpose.
34  public:
35  static void MakeTestRequest(const OSTreeRepo::ptr src_repo, const OSTreeHash& hash, const long expected) {
36  curl_global_init(CURL_GLOBAL_DEFAULT);
37  CURLM* multi = curl_multi_init();
38  curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX);
39 
40  TreehubServer push_server;
41  push_server.root_url("http://localhost:" + port);
42  OSTreeObject::ptr object = src_repo->GetObject(hash, OstreeObjectType::OSTREE_OBJECT_TYPE_COMMIT);
43 
44  object->MakeTestRequest(push_server, multi);
45 
46  // This bit is basically copied from RequestPool::LoopListen().
47  int running_requests;
48  do {
49  CURLMcode mc = curl_multi_perform(multi, &running_requests);
50  EXPECT_EQ(mc, CURLM_OK);
51  } while (running_requests > 0);
52 
53  int msgs_in_queue;
54  do {
55  CURLMsg* msg = curl_multi_info_read(multi, &msgs_in_queue);
56  if ((msg != nullptr) && msg->msg == CURLMSG_DONE) {
57  OSTreeObject::ptr h = ostree_object_from_curl(msg->easy_handle);
58  EXPECT_EQ(object, h);
59  EXPECT_EQ(h->current_operation_, CurrentOp::kOstreeObjectPresenceCheck);
60 
61  // This bit is basically copied from OSTreeObject::CurlDone().
62  h->refcount_--;
63  EXPECT_GE(h->refcount_, 1);
64  long rescode = 0; // NOLINT(google-runtime-int)
65  curl_easy_getinfo(h->curl_handle_, CURLINFO_RESPONSE_CODE, &rescode);
66  EXPECT_EQ(rescode, expected);
67  curl_multi_remove_handle(multi, h->curl_handle_);
68  curl_easy_cleanup(h->curl_handle_);
69  h->curl_handle_ = nullptr;
70  }
71  } while (msgs_in_queue > 0);
72 
73  curl_multi_cleanup(multi);
74  curl_global_cleanup();
75  }
76 };
77 
78 /* Query destination repository for OSTree commit object.
79  * Expect HTTP 200 for a hash that we expect the server to know about. */
80 TEST(OstreeObject, MakeTestRequestPresent) {
81  OSTreeRepo::ptr src_repo = std::make_shared<OSTreeDirRepo>(repo_path);
82  OSTreeHash hash = src_repo->GetRef("master").GetHash();
83  OstreeObject_Request_Test::MakeTestRequest(src_repo, hash, 200);
84 }
85 
86 /* Query destination repository for OSTree commit object.
87  * Expect HTTP 404 for a hash that we expect the server not to know about.
88  *
89  * In this case, the hash is from a different repo. */
90 TEST(OstreeObject, MakeTestRequestMissing) {
91  OSTreeRepo::ptr src_repo = std::make_shared<OSTreeDirRepo>("tests/sota_tools/bigger_repo");
92  OSTreeHash hash = src_repo->GetRef("master").GetHash();
93  OstreeObject_Request_Test::MakeTestRequest(src_repo, hash, 404);
94 }
95 
96 /* Skip upload if dry run was specified. */
97 TEST(OstreeObject, UploadDryRun) {
98  TreehubServer push_server;
99  push_server.root_url("http://localhost:" + port);
100 
101  OSTreeRepo::ptr src_repo = std::make_shared<OSTreeDirRepo>(repo_path);
102  OSTreeHash hash = src_repo->GetRef("master").GetHash();
103  OSTreeObject::ptr object = src_repo->GetObject(hash, OstreeObjectType::OSTREE_OBJECT_TYPE_COMMIT);
104 
105  object->is_on_server_ = PresenceOnServer::kObjectStateUnknown;
106  object->current_operation_ = CurrentOp::kOstreeObjectPresenceCheck;
107  object->Upload(push_server, nullptr, RunMode::kDryRun);
108  EXPECT_EQ(object->is_on_server_, PresenceOnServer::kObjectPresent);
109  // This currently does not get reset.
110  EXPECT_EQ(object->current_operation_, CurrentOp::kOstreeObjectPresenceCheck);
111 }
112 
113 /* Detect curl misconfiguration.
114  *
115  * Specifically, verify that a null curl pointer will cause the upload to fail. */
116 TEST(OstreeObject, UploadFail) {
117  TreehubServer push_server;
118  push_server.root_url("http://localhost:" + port);
119 
120  OSTreeRepo::ptr src_repo = std::make_shared<OSTreeDirRepo>(repo_path);
121  OSTreeHash hash = src_repo->GetRef("master").GetHash();
122  OSTreeObject::ptr object = src_repo->GetObject(hash, OstreeObjectType::OSTREE_OBJECT_TYPE_COMMIT);
123 
124  object->is_on_server_ = PresenceOnServer::kObjectStateUnknown;
125  object->current_operation_ = CurrentOp::kOstreeObjectPresenceCheck;
126  object->Upload(push_server, nullptr, RunMode::kDefault);
127  EXPECT_EQ(object->is_on_server_, PresenceOnServer::kObjectStateUnknown);
128  EXPECT_EQ(object->current_operation_, CurrentOp::kOstreeObjectUploading);
129 }
130 
131 /* Upload missing OSTree objects to destination repository. */
132 TEST(OstreeObject, UploadSuccess) {
133  curl_global_init(CURL_GLOBAL_DEFAULT);
134  CURLM* multi = curl_multi_init();
135  curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX);
136 
137  TemporaryDirectory temp_dir;
138  const std::string dp = TestUtils::getFreePort();
139  Json::Value auth;
140  auth["ostree"]["server"] = std::string("https://localhost:") + dp;
141  Utils::writeFile(temp_dir.Path() / "auth.json", auth);
142  boost::process::child deploy_server_process("tests/sota_tools/treehub_server.py", std::string("-p"), dp,
143  std::string("-d"), temp_dir.Path().string(), std::string("--tls"));
144  TestUtils::waitForServer("https://localhost:" + dp + "/");
145 
146  TreehubServer push_server;
147  push_server.root_url("https://localhost:" + dp);
148 
149  boost::filesystem::path filepath = (temp_dir.Path() / "auth.json").string();
150  boost::filesystem::path cert_path = "tests/fake_http_server/server.crt";
151  EXPECT_EQ(authenticate(cert_path.string(), ServerCredentials(filepath), push_server), EXIT_SUCCESS);
152 
153  OSTreeRepo::ptr src_repo = std::make_shared<OSTreeDirRepo>(repo_path);
154  OSTreeHash hash = src_repo->GetRef("master").GetHash();
155  OSTreeObject::ptr object = src_repo->GetObject(hash, OstreeObjectType::OSTREE_OBJECT_TYPE_COMMIT);
156 
157  object->Upload(push_server, multi, RunMode::kDefault);
158 
159  // This bit is basically copied from RequestPool::LoopListen().
160  int running_requests;
161  do {
162  CURLMcode mc = curl_multi_perform(multi, &running_requests);
163  EXPECT_EQ(mc, CURLM_OK);
164  } while (running_requests > 0);
165 
166  int msgs_in_queue;
167  do {
168  CURLMsg* msg = curl_multi_info_read(multi, &msgs_in_queue);
169  if ((msg != nullptr) && msg->msg == CURLMSG_DONE) {
170  OSTreeObject::ptr h = ostree_object_from_curl(msg->easy_handle);
171  EXPECT_EQ(object, h);
172  EXPECT_EQ(h->current_operation_, CurrentOp::kOstreeObjectUploading);
173 
174  // This bit is basically copied from OSTreeObject::CurlDone().
175  h->refcount_--;
176  EXPECT_GE(h->refcount_, 1);
177  long rescode = 0; // NOLINT(google-runtime-int)
178  curl_easy_getinfo(h->curl_handle_, CURLINFO_RESPONSE_CODE, &rescode);
179  EXPECT_EQ(rescode, 204);
180 
181  curl_multi_remove_handle(multi, h->curl_handle_);
182  curl_easy_cleanup(h->curl_handle_);
183  h->curl_handle_ = nullptr;
184  }
185  } while (msgs_in_queue > 0);
186 
187  curl_multi_cleanup(multi);
188  curl_global_cleanup();
189 }
190 
191 #ifndef __NO_MAIN__
192 int main(int argc, char** argv) {
193  ::testing::InitGoogleTest(&argc, argv);
194 
195  std::string server = "tests/sota_tools/treehub_server.py";
196  port = TestUtils::getFreePort();
197  TemporaryDirectory repo_dir;
198  repo_path = repo_dir.PathString();
199 
200  boost::process::child server_process(server, std::string("-p"), port, std::string("-d"), repo_path,
201  std::string("--create"));
202  TestUtils::waitForServer("http://localhost:" + port + "/");
203 
204  return RUN_ALL_TESTS();
205 }
206 #endif
207 
208 // vim: set tabstop=2 shiftwidth=2 expandtab:
OSTreeHash
Definition: ostree_hash.h:10
ServerCredentials
Definition: server_credentials.h:25
OSTreeObject
Definition: ostree_object.h:30
OstreeObject_Request_Test
Definition: ostree_object_test.cc:33
TreehubServer
Definition: treehub_server.h:11
TemporaryDirectory
Definition: utils.h:82
garage_common.h
RunMode::kDefault
@ kDefault
Default operation.
RunMode::kDryRun
@ kDryRun
Dry run.
OSTreeDirRepo
Definition: ostree_dir_repo.h:9