Aktualizr
C++ SOTA Client
check.cc
1 #include <curl/curl.h>
2 
3 #include "authenticate.h"
4 #include "check.h"
5 #include "garage_common.h"
6 #include "json/json.h"
7 #include "libaktualizr/types.h"
8 #include "logging/logging.h"
9 #include "ostree_http_repo.h"
10 #include "ostree_object.h"
11 #include "rate_controller.h"
12 #include "request_pool.h"
13 #include "treehub_server.h"
14 #include "utilities/utils.h"
15 
16 // helper function to download data to a string
17 static size_t writeString(void *contents, size_t size, size_t nmemb, void *userp) {
18  assert(userp);
19  // append the writeback data to the provided string
20  (static_cast<std::string *>(userp))->append(static_cast<char *>(contents), size * nmemb);
21 
22  // return size of written data
23  return size * nmemb;
24 }
25 
26 int CheckRefValid(TreehubServer &treehub, const std::string &ref, RunMode mode, int max_curl_requests,
27  const boost::filesystem::path &tree_dir) {
28  // Check if the ref is present on treehub. The traditional use case is that it
29  // should be a commit object, but we allow walking the tree given any OSTree
30  // ref.
31  CurlEasyWrapper curl;
32  if (curl.get() == nullptr) {
33  LOG_FATAL << "Error initializing curl";
34  return EXIT_FAILURE;
35  }
36  curlEasySetoptWrapper(curl.get(), CURLOPT_VERBOSE, get_curlopt_verbose());
37  curlEasySetoptWrapper(curl.get(), CURLOPT_NOBODY, 1L); // HEAD
38 
39  treehub.InjectIntoCurl("objects/" + ref.substr(0, 2) + "/" + ref.substr(2) + ".commit", curl.get());
40 
41  CURLcode result = curl_easy_perform(curl.get());
42  if (result != CURLE_OK) {
43  LOG_FATAL << "Error connecting to treehub: " << result << ": " << curl_easy_strerror(result);
44  return EXIT_FAILURE;
45  }
46 
47  long http_code; // NOLINT(google-runtime-int)
48  OstreeObjectType type = OstreeObjectType::OSTREE_OBJECT_TYPE_COMMIT;
49  curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
50  if (http_code == 404) {
51  if (mode != RunMode::kWalkTree) {
52  LOG_FATAL << "OSTree commit " << ref << " is missing in treehub";
53  return EXIT_FAILURE;
54  } else {
55  type = OstreeObjectType::OSTREE_OBJECT_TYPE_UNKNOWN;
56  }
57  } else if (http_code != 200) {
58  LOG_FATAL << "Error " << http_code << " getting OSTree ref " << ref << " from treehub";
59  return EXIT_FAILURE;
60  }
61  if (mode != RunMode::kWalkTree) {
62  LOG_INFO << "OSTree commit " << ref << " is found on treehub";
63  }
64 
65  if (mode == RunMode::kWalkTree) {
66  // Walk the entire tree and check for all objects.
67  OSTreeHttpRepo dest_repo(&treehub, tree_dir);
68  OSTreeHash hash = OSTreeHash::Parse(ref);
69  OSTreeObject::ptr input_object = dest_repo.GetObject(hash, type);
70 
71  RequestPool request_pool(treehub, max_curl_requests, mode);
72 
73  // Add input object to the queue.
74  request_pool.AddQuery(input_object);
75 
76  // Main curl event loop.
77  // request_pool takes care of holding number of outstanding requests below.
78  // OSTreeObject::CurlDone() adds new requests to the pool and stops the pool
79  // on error.
80  do {
81  request_pool.Loop();
82  } while (!request_pool.is_idle() && !request_pool.is_stopped());
83 
84  if (input_object->is_on_server() == PresenceOnServer::kObjectPresent) {
85  LOG_INFO << "Dry run. No objects uploaded.";
86  } else {
87  LOG_ERROR << "One or more errors while pushing";
88  }
89  }
90 
91  // If we have a commit object, check if the ref is present in targets.json.
92  if (type == OstreeObjectType::OSTREE_OBJECT_TYPE_COMMIT) {
93  curlEasySetoptWrapper(curl.get(), CURLOPT_VERBOSE, get_curlopt_verbose());
94  curlEasySetoptWrapper(curl.get(), CURLOPT_HTTPGET, 1L);
95  curlEasySetoptWrapper(curl.get(), CURLOPT_NOBODY, 0L);
96  treehub.InjectIntoCurl("/api/v1/user_repo/targets.json", curl.get(), true);
97 
98  std::string targets_str;
99  curlEasySetoptWrapper(curl.get(), CURLOPT_WRITEFUNCTION, writeString);
100  curlEasySetoptWrapper(curl.get(), CURLOPT_WRITEDATA, static_cast<void *>(&targets_str));
101  result = curl_easy_perform(curl.get());
102 
103  if (result != CURLE_OK) {
104  LOG_FATAL << "Error connecting to TUF repo: " << result << ": " << curl_easy_strerror(result);
105  return EXIT_FAILURE;
106  }
107 
108  curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
109  if (http_code != 200) {
110  LOG_FATAL << "Error " << http_code << " getting targets.json from TUF repo: " << targets_str;
111  return EXIT_FAILURE;
112  }
113 
114  Json::Value targets_json = Utils::parseJSON(targets_str);
115  std::string expiry_time_str = targets_json["signed"]["expires"].asString();
116  TimeStamp timestamp(expiry_time_str);
117 
118  if (timestamp.IsExpiredAt(TimeStamp::Now())) {
119  LOG_FATAL << "targets.json has been expired.";
120  return EXIT_FAILURE;
121  }
122 
123  Json::Value target_list = targets_json["signed"]["targets"];
124  for (auto t_it = target_list.begin(); t_it != target_list.end(); t_it++) {
125  if ((*t_it)["hashes"]["sha256"].asString() == ref) {
126  LOG_INFO << "OSTree commit " << ref << " is found in targets.json";
127  return EXIT_SUCCESS;
128  }
129  }
130  LOG_FATAL << "OSTree ref " << ref << " was not found in targets.json";
131  return EXIT_FAILURE;
132  } else {
133  LOG_INFO << "OSTree ref " << ref << " is not a commit object. Skipping targets.json check.";
134  return EXIT_SUCCESS;
135  }
136 }
CurlEasyWrapper
Definition: utils.h:146
types.h
OSTreeHash
Definition: ostree_hash.h:10
RunMode::kWalkTree
@ kWalkTree
Walk the entire tree (without uploading).
TimeStamp
Definition: types.h:188
TreehubServer
Definition: treehub_server.h:11
RequestPool
Definition: request_pool.h:12
OSTreeHash::Parse
static OSTreeHash Parse(const std::string &hash)
Parse an OSTree hash from a string.
Definition: ostree_hash.cc:7
result
Results of libaktualizr API calls.
Definition: results.h:12
garage_common.h
OSTreeHttpRepo
Definition: ostree_http_repo.h:12
OstreeObjectType
OstreeObjectType
Types of OSTree objects, borrowed from libostree/ostree-core.h.
Definition: garage_common.h:34
RunMode
RunMode
Execution mode to run garage tools in.
Definition: garage_common.h:6