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