Apache Mesos
http.hpp
Go to the documentation of this file.
1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #ifndef __COMMON_HTTP_HPP__
18 #define __COMMON_HTTP_HPP__
19 
20 #include <vector>
21 
22 #include <mesos/http.hpp>
23 #include <mesos/mesos.hpp>
24 
26 
27 #include <mesos/quota/quota.hpp>
28 
30 #include <process/future.hpp>
31 #include <process/http.hpp>
32 #include <process/owned.hpp>
33 
34 #include <stout/hashmap.hpp>
35 #include <stout/hashset.hpp>
36 #include <stout/json.hpp>
37 #include <stout/jsonify.hpp>
38 #include <stout/protobuf.hpp>
39 #include <stout/recordio.hpp>
40 #include <stout/unreachable.hpp>
41 #include <stout/uuid.hpp>
42 
43 #include "internal/evolve.hpp"
44 
45 // TODO(benh): Remove this once we get C++14 as an enum should have a
46 // default hash.
47 namespace std {
48 
49 template <>
50 struct hash<mesos::authorization::Action>
51 {
52  typedef size_t result_type;
53 
54  typedef mesos::authorization::Action argument_type;
55 
56  result_type operator()(const argument_type& action) const
57  {
58  size_t seed = 0;
59  boost::hash_combine(
60  seed, static_cast<std::underlying_type<argument_type>::type>(action));
61  return seed;
62  }
63 };
64 
65 } // namespace std {
66 
67 namespace mesos {
68 
69 class Attributes;
70 class Resources;
71 class Task;
72 
73 namespace internal {
74 
75 // Name of the default, basic authenticator.
76 constexpr char DEFAULT_BASIC_HTTP_AUTHENTICATOR[] = "basic";
77 
78 // Name of the default, basic authenticatee.
79 constexpr char DEFAULT_BASIC_HTTP_AUTHENTICATEE[] = "basic";
80 
81 // Name of the default, JWT authenticator.
82 constexpr char DEFAULT_JWT_HTTP_AUTHENTICATOR[] = "jwt";
83 
84 
85 // Contains the media types corresponding to some of the "Content-*",
86 // "Accept-*" and "Message-*" prefixed request headers in our internal
87 // representation.
89 {
90  ContentType content; // 'Content-Type' header.
91  ContentType accept; // 'Accept' header.
92  Option<ContentType> messageContent; // 'Message-Content-Type' header.
93  Option<ContentType> messageAccept; // 'Message-Accept' header.
94 };
95 
96 
97 // Serializes a protobuf message for transmission
98 // based on the HTTP content type.
99 // NOTE: For streaming `contentType`, `message` would not
100 // be serialized in "Record-IO" format.
101 std::string serialize(
102  ContentType contentType,
103  const google::protobuf::Message& message);
104 
105 
106 // Deserializes a string message into a protobuf message based on the
107 // HTTP content type.
108 template <typename Message>
110  ContentType contentType,
111  const std::string& body)
112 {
113  switch (contentType) {
114  case ContentType::PROTOBUF: {
115  Message message;
116  if (!message.ParseFromString(body)) {
117  return Error("Failed to parse body into a protobuf object");
118  }
119  return message;
120  }
121  case ContentType::JSON: {
122  Try<JSON::Value> value = JSON::parse(body);
123  if (value.isError()) {
124  return Error("Failed to parse body into JSON: " + value.error());
125  }
126 
127  return ::protobuf::parse<Message>(value.get());
128  }
129  case ContentType::RECORDIO: {
130  return Error("Deserializing a RecordIO stream is not supported");
131  }
132  }
133 
134  UNREACHABLE();
135 }
136 
137 
138 // Returns true if the media type can be used for
139 // streaming requests/responses.
140 bool streamingMediaType(ContentType contentType);
141 
142 
143 // Represents the streaming HTTP connection to a client, such as a framework,
144 // executor, or operator subscribed to the '/api/vX' endpoint.
145 // The `Event` template is the evolved message being sent to the client,
146 // e.g. `v1::scheduler::Event`, `v1::master::Event`, or `v1::executor::Event`.
147 template <typename Event>
149 {
151  const process::http::Pipe::Writer& _writer,
152  ContentType _contentType,
153  id::UUID _streamId = id::UUID::random())
154  : writer(_writer),
155  contentType(_contentType),
156  streamId(_streamId) {}
157 
158  template <typename Message>
159  bool send(const Message& message)
160  {
161  // TODO(bmahler): Remove this evolve(). Could we still
162  // somehow assert that evolve(message) produces a result
163  // of type Event without calling evolve()?
164  Event e = evolve(message);
165 
166  std::string record = serialize(contentType, e);
167 
168  return writer.write(::recordio::encode(record));
169  }
170 
171  // Like the above send, but for already serialized data.
172  bool send(const std::string& event)
173  {
174  return writer.write(::recordio::encode(event));
175  }
176 
177  bool close()
178  {
179  return writer.close();
180  }
181 
183  {
184  return writer.readerClosed();
185  }
186 
190 };
191 
192 
193 // The representation of generic v0 protobuf => v1 protobuf as JSON,
194 // e.g., `jsonify(asV1Protobuf(message))`.
195 //
196 // Specifically, this acts the same as JSON::Protobuf, except that
197 // it remaps "slave" to "agent" in field names and enum values.
198 struct asV1Protobuf : Representation<google::protobuf::Message>
199 {
201 };
202 
203 void json(JSON::ObjectWriter* writer, const asV1Protobuf& protobuf);
204 
205 
206 JSON::Object model(const Resources& resources);
208 JSON::Object model(const Attributes& attributes);
209 JSON::Object model(const CommandInfo& command);
210 JSON::Object model(const ExecutorInfo& executorInfo);
211 JSON::Array model(const Labels& labels);
212 JSON::Object model(const Task& task);
213 JSON::Object model(const FileInfo& fileInfo);
214 
215 void json(JSON::ObjectWriter* writer, const Task& task);
216 
217 } // namespace internal {
218 
219 void json(JSON::ObjectWriter* writer, const Attributes& attributes);
220 void json(JSON::ObjectWriter* writer, const CommandInfo& command);
221 void json(JSON::ObjectWriter* writer, const DomainInfo& domainInfo);
222 void json(JSON::ObjectWriter* writer, const ExecutorInfo& executorInfo);
223 void json(
224  JSON::StringWriter* writer, const FrameworkInfo::Capability& capability);
225 void json(JSON::ArrayWriter* writer, const Labels& labels);
226 void json(JSON::ObjectWriter* writer, const MasterInfo& info);
227 void json(
228  JSON::StringWriter* writer, const MasterInfo::Capability& capability);
229 void json(JSON::ObjectWriter* writer, const Offer& offer);
230 void json(JSON::ObjectWriter* writer, const Resources& resources);
231 void json(
232  JSON::ObjectWriter* writer,
233  const google::protobuf::RepeatedPtrField<Resource>& resources);
234 void json(JSON::ObjectWriter* writer, const ResourceQuantities& quantities);
235 void json(JSON::ObjectWriter* writer, const ResourceLimits& limits);
236 void json(JSON::ObjectWriter* writer, const SlaveInfo& slaveInfo);
237 void json(
238  JSON::StringWriter* writer, const SlaveInfo::Capability& capability);
239 void json(JSON::ObjectWriter* writer, const Task& task);
240 void json(JSON::ObjectWriter* writer, const TaskStatus& status);
241 
242 
243 // Implementation of the `ObjectApprover` interface authorizing all objects.
245 {
246 public:
248  const Option<ObjectApprover::Object>& object) const noexcept override
249  {
250  return true;
251  }
252 };
253 
254 
256 {
257 public:
259  const Option<Authorizer*>& authorizer,
261  std::initializer_list<authorization::Action> actions);
262 
263  template <authorization::Action action, typename... Args>
264  bool approved(const Args&... args)
265  {
266  if (!approvers.contains(action)) {
267  LOG(WARNING) << "Attempted to authorize " << principal
268  << " for unexpected action " << stringify(action);
269  return false;
270  }
271 
272  Try<bool> approved = approvers[action]->approved(
273  ObjectApprover::Object(args...));
274 
275  if (approved.isError()) {
276  // TODO(joerg84): Expose these errors back to the caller.
277  LOG(WARNING) << "Failed to authorize principal " << principal
278  << "for action " << stringify(action) << ": "
279  << approved.error();
280  return false;
281  }
282 
283  return approved.get();
284  }
285 
286 private:
288  hashmap<
289  authorization::Action,
290  process::Owned<ObjectApprover>>&& _approvers,
292  : approvers(std::move(_approvers)),
293  principal(_principal.isSome()
294  ? "'" + stringify(_principal.get()) + "'"
295  : "")
296  {}
297 
299  const std::string principal; // Only used for logging.
300 };
301 
302 
303 template <>
304 inline bool ObjectApprovers::approved<authorization::VIEW_ROLE>(
305  const Resource& resource)
306 {
307  // Necessary because recovered agents are presented in old format.
308  if (resource.has_role() && resource.role() != "*" &&
309  !approved<authorization::VIEW_ROLE>(resource.role())) {
310  return false;
311  }
312 
313  // Reservations follow a path model where each entry is a child of the
314  // previous one. Therefore, to accept the resource the acceptor has to
315  // accept all entries.
316  foreach (Resource::ReservationInfo reservation, resource.reservations()) {
317  if (!approved<authorization::VIEW_ROLE>(reservation.role())) {
318  return false;
319  }
320  }
321 
322  if (resource.has_allocation_info() &&
323  !approved<authorization::VIEW_ROLE>(
324  resource.allocation_info().role())) {
325  return false;
326  }
327 
328  return true;
329 }
330 
331 
337 template <typename T>
339 {
340 public:
342  {
343  if (id.isSome()) {
344  T targetId_;
345  targetId_.set_value(id.get());
346  targetId = targetId_;
347  }
348  }
349 
350  bool accept(const T& candidateId) const
351  {
352  if (targetId.isNone()) {
353  return true;
354  }
355 
356  return candidateId.value() == targetId->value();
357  }
358 
359 protected:
361 };
362 
363 
364 // Authorizes access to an HTTP endpoint. The `method` parameter
365 // determines which ACL action will be used in the authorization.
366 // It is expected that the caller has validated that `method` is
367 // supported by this function. Currently "GET" is supported.
368 //
369 // TODO(nfnt): Prefer types instead of strings
370 // for `endpoint` and `method`, see MESOS-5300.
372  const std::string& endpoint,
373  const std::string& method,
374  const Option<Authorizer*>& authorizer,
376 
377 
391  const std::string& realm,
392  const std::vector<std::string>& httpAuthenticatorNames,
393  const Option<Credentials>& credentials = None(),
394  const Option<std::string>& jwtSecretKey = None());
395 
396 
397 // Logs the request. Route handlers can compose this with the
398 // desired request handler to get consistent request logging.
400 
401 
402 // Log the response for the corresponding request together with the request
403 // processing time. Route handlers can compose this with the desired request
404 // handler to get consistent request/response logging.
405 //
406 // TODO(alexr): Consider taking `response` as a future to allow logging for
407 // cases when response has not been generated.
408 void logResponse(
410  const process::http::Response& response);
411 
412 } // namespace mesos {
413 
414 #endif // __COMMON_HTTP_HPP__
size_t result_type
Definition: http.hpp:52
Definition: http.hpp:244
ContentType accept
Definition: http.hpp:91
ContentType
Definition: http.hpp:43
Definition: errorbase.hpp:36
Definition: resource_quantities.hpp:192
Future< Response > request(const Request &request, bool streamedResponse=false)
Asynchronously sends an HTTP request to the process and returns the HTTP response once the entire res...
T & get()&
Definition: try.hpp:80
Definition: authorizer.hpp:46
id::UUID streamId
Definition: http.hpp:189
Definition: check.hpp:33
std::string encode(const std::string &record)
Returns the "Record-IO" encoded record.
Definition: recordio.hpp:63
Definition: http.hpp:198
bool streamingMediaType(ContentType contentType)
Definition: resource_quantities.hpp:63
constexpr char DEFAULT_BASIC_HTTP_AUTHENTICATEE[]
Definition: http.hpp:79
process::Future< Nothing > closed() const
Definition: http.hpp:182
Result< ProcessStatus > status(pid_t pid)
Definition: proc.hpp:166
Definition: resources.hpp:83
Definition: type_utils.hpp:598
Definition: json.hpp:198
result_type operator()(const argument_type &action) const
Definition: http.hpp:56
Capability
Definition: capabilities.hpp:35
Definition: jsonify.hpp:254
bool close()
Definition: http.hpp:177
Option< T > targetId
Definition: http.hpp:360
bool isSome() const
Definition: option.hpp:116
Definition: http.hpp:533
Definition: json.hpp:158
mesos::v1::scheduler::Event Event
Definition: mesos.hpp:2783
Definition: hashmap.hpp:38
constexpr char DEFAULT_JWT_HTTP_AUTHENTICATOR[]
Definition: http.hpp:82
void logResponse(const process::http::Request &request, const process::http::Response &response)
void logRequest(const process::http::Request &request)
Used to filter results for API handlers.
Definition: http.hpp:338
static UUID random()
Definition: uuid.hpp:38
Try< Message > deserialize(ContentType contentType, const std::string &body)
Definition: http.hpp:109
Definition: http.hpp:88
Definition: http.hpp:354
void json(JSON::ObjectWriter *writer, const TaskStatus &status)
bool send(const Message &message)
Definition: http.hpp:159
Definition: uuid.hpp:35
StreamingHttpConnection(const process::http::Pipe::Writer &_writer, ContentType _contentType, id::UUID _streamId=id::UUID::random())
Definition: http.hpp:150
JSON::Object model(const FileInfo &fileInfo)
Definition: agent.hpp:25
Definition: http.hpp:255
Definition: jsonify.hpp:325
const T & get() const &
Definition: option.hpp:119
ContentType contentType
Definition: http.hpp:188
bool accept(const T &candidateId) const
Definition: http.hpp:350
Try< Value > parse(const std::string &s)
Returns the OCI v1 descriptor, image index, image manifest and image configuration from the given str...
Definition: json.hpp:978
ContentType content
Definition: http.hpp:90
Option< ContentType > messageContent
Definition: http.hpp:92
Definition: protobuf.hpp:61
static Try error(const E &e)
Definition: try.hpp:43
Try< Nothing > initializeHttpAuthenticators(const std::string &realm, const std::vector< std::string > &httpAuthenticatorNames, const Option< Credentials > &credentials=None(), const Option< std::string > &jwtSecretKey=None())
Helper function to create HTTP authenticators for a given realm and register in libprocess.
#define UNREACHABLE()
Definition: unreachable.hpp:22
constexpr char DEFAULT_BASIC_HTTP_AUTHENTICATOR[]
Definition: http.hpp:76
Definition: none.hpp:27
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:78
Definition: http.hpp:612
VolumeCapability evolve(const types::VolumeCapability &capability)
bool send(const std::string &event)
Definition: http.hpp:172
Try< uint32_t > type(const std::string &path)
IDAcceptor(const Option< std::string > &id=None())
Definition: http.hpp:341
Option< ContentType > messageAccept
Definition: http.hpp:93
bool approved(const Args &...args)
Definition: http.hpp:264
Try< Nothing > create(const std::string &hierarchy, const std::string &cgroup, bool recursive=false)
std::string serialize(ContentType contentType, const google::protobuf::Message &message)
process::Future< bool > authorizeEndpoint(const std::string &endpoint, const std::string &method, const Option< Authorizer * > &authorizer, const Option< process::http::authentication::Principal > &principal)
This interface represents a function object returned by the authorizer which can be used locally (and...
Definition: authorizer.hpp:40
std::string stringify(int flags)
Definition: owned.hpp:36
process::http::Pipe::Writer writer
Definition: http.hpp:187
mesos::authorization::Action argument_type
Definition: http.hpp:54
Definition: representation.hpp:72
Try< bool > approved(const Option< ObjectApprover::Object > &object) const noexceptoverride
NOTE: As this function can be used synchronously by actors it is essential that it does not block! ...
Definition: http.hpp:247
Definition: jsonify.hpp:295
Definition: attributes.hpp:32