Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
test_utils.cc
1 #include "test_utils.h"
2 
3 #include <signal.h>
4 #include <sys/wait.h>
5 #include <thread>
6 
7 #if __linux__
8 #include <sys/prctl.h>
9 #endif
10 #include <chrono>
11 #include <fstream>
12 #include <iostream>
13 #include <string>
14 #include <thread>
15 
16 #include <boost/asio/io_service.hpp>
17 #include <boost/filesystem.hpp>
18 #include <boost/process.hpp>
19 
20 #include "logging/logging.h"
21 
22 in_port_t TestUtils::getFreePortAsInt() {
23  int s = socket(AF_INET, SOCK_STREAM, 0);
24  if (s == -1) {
25  std::cout << "socket() failed: " << errno;
26  throw std::runtime_error("Could not open socket");
27  }
28  struct sockaddr_in soc_addr;
29  memset(&soc_addr, 0, sizeof(struct sockaddr_in));
30  soc_addr.sin_family = AF_INET;
31  soc_addr.sin_addr.s_addr = INADDR_ANY;
32  soc_addr.sin_port = htons(INADDR_ANY);
33 
34  if (bind(s, (struct sockaddr *)&soc_addr, sizeof(soc_addr)) == -1) {
35  std::cout << "bind() failed: " << errno;
36  throw std::runtime_error("Could not bind socket");
37  }
38 
39  struct sockaddr_in sa;
40  unsigned int sa_len = sizeof(sa);
41  if (getsockname(s, (struct sockaddr *)&sa, &sa_len) == -1) {
42  throw std::runtime_error("getsockname failed");
43  }
44  close(s);
45  return sa.sin_port;
46 }
47 
48 std::string TestUtils::getFreePort() { return std::to_string(ntohs(getFreePortAsInt())); }
49 
50 void TestUtils::writePathToConfig(const boost::filesystem::path &toml_in, const boost::filesystem::path &toml_out,
51  const boost::filesystem::path &storage_path) {
52  // Append our temp_dir path as storage.path to the config file. This is a hack
53  // but less annoying than the alternatives.
54  boost::filesystem::copy_file(toml_in, toml_out);
55  std::string conf_path_str = toml_out.string();
56  std::ofstream cs(conf_path_str.c_str(), std::ofstream::app);
57  cs << "\n[storage]\npath = " << storage_path.string() << "\n";
58 }
59 
60 void TestUtils::waitForServer(const std::string &address) {
61  CurlEasyWrapper curl;
62 
63  curlEasySetoptWrapper(curl.get(), CURLOPT_URL, address.c_str());
64  curlEasySetoptWrapper(curl.get(), CURLOPT_CONNECTTIMEOUT, 3L);
65  curlEasySetoptWrapper(curl.get(), CURLOPT_NOBODY, 1L);
66  curlEasySetoptWrapper(curl.get(), CURLOPT_VERBOSE, 1L);
67  curlEasySetoptWrapper(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);
68 
69  CURLcode result;
70  for (size_t counter = 1; counter <= 100; counter++) {
71  result = curl_easy_perform(curl.get());
72  if (result == 0) {
73  // connection successful
74  break;
75  }
76  if (counter % 5 == 0) {
77  std::cout << "Unable to connect to " << address << " after " << counter << " tries.\n";
78  }
79  std::this_thread::sleep_for(std::chrono::milliseconds(200));
80  }
81 
82  if (result != 0) {
83  throw std::runtime_error("Wait for server timed out");
84  }
85 }
86 
87 Process::Result Process::spawn(const std::string &executable_to_run, const std::vector<std::string> &executable_args) {
88  std::future<std::string> output;
89  std::future<std::string> err_output;
90  std::future<int> child_process_exit_code;
91  boost::asio::io_service io_service;
92 
93  try {
94  std::string executable_path;
95  if (boost::filesystem::exists(executable_to_run)) {
96  executable_path = executable_to_run;
97  } else {
98  executable_path = boost::process::search_path(executable_to_run).string();
99  }
100  boost::process::child child_process(boost::process::exe = executable_path, boost::process::args = executable_args,
101  boost::process::std_out > output, boost::process::std_err > err_output,
102  boost::process::on_exit = child_process_exit_code, io_service);
103 
104  io_service.run();
105 
106  bool wait_successfull = child_process.wait_for(std::chrono::seconds(60));
107  if (!wait_successfull) {
108  throw std::runtime_error("Timeout occured while waiting for a child process completion");
109  }
110 
111  // Issue with getting an exit code if 'on_exit' handler is not specified
112  // https://github.com/boostorg/process/issues/39
113  // child_process_exit_code = child_process.exit_code();
114 
115  } catch (const std::exception &exc) {
116  throw std::runtime_error("Failed to spawn process " + executable_to_run + " exited with an error: " + exc.what());
117  }
118 
119  return std::make_tuple(child_process_exit_code.get(), output.get(), err_output.get());
120 }
121 
122 Process::Result Process::run(const std::vector<std::string> &args) {
123  last_exit_code_ = 0;
124  last_stdout_.clear();
125  last_stderr_.clear();
126 
127  auto cred_gen_result = Process::spawn(exe_path_, args);
128  std::tie(last_exit_code_, last_stdout_, last_stderr_) = cred_gen_result;
129  if (last_exit_code_ != 0) {
130  LOG_WARNING << last_stderr_;
131  }
132 
133  return cred_gen_result;
134 }
CurlEasyWrapper
Definition: utils.h:146
result
Results of libaktualizr API calls.
Definition: results.h:12