Ejemplo n.º 1
0
Future<http::Response> Master::QuotaHandler::remove(
    const http::Request& request,
    const Option<string>& principal) const
{
  VLOG(1) << "Removing quota for request path: '" << request.url.path << "'";

  // Check that the request type is DELETE which is guaranteed by the master.
  CHECK_EQ("DELETE", request.method);

  // Extract role from url.
  vector<string> tokens = strings::tokenize(request.url.path, "/");

  // Check that there are exactly 3 parts: {master,quota,'role'}.
  if (tokens.size() != 3u) {
    return BadRequest(
        "Failed to parse request path '" + request.url.path +
        "': 3 tokens ('master', 'quota', 'role') required, found " +
        stringify(tokens.size()) + " token(s)");
  }

  // Check that "quota" is the second to last token.
  if (tokens.end()[-2] != "quota") {
    return BadRequest(
        "Failed to parse request path '" + request.url.path +
        "': Missing 'quota' endpoint");
  }

  const string& role = tokens.back();

  // Check that the role is on the role whitelist, if it exists.
  if (!master->isWhitelistedRole(role)) {
    return BadRequest(
        "Failed to validate remove quota request for path '" +
        request.url.path +"': Unknown role '" + role + "'");
  }

  // Check that we are removing an existing quota.
  if (!master->quotas.contains(role)) {
    return BadRequest(
        "Failed to remove quota for path '" + request.url.path +
        "': Role '" + role + "' has no quota set");
  }

  Option<string> quota_principal = master->quotas[role].info.has_principal()
    ? master->quotas[role].info.principal()
    : Option<string>::none();

  return authorizeRemoveQuota(principal, quota_principal)
    .then(defer(master->self(), [=](bool authorized) -> Future<http::Response> {
      if (!authorized) {
        return Forbidden();
      }

      return _remove(role);
    }));
}
Ejemplo n.º 2
0
Future<http::Response> Master::QuotaHandler::set(
    const http::Request& request) const
{
  VLOG(1) << "Setting quota from request: '" << request.body << "'";

  // Authenticate and authorize the request.
  // TODO(alexr): Check Master::Http::authenticate() for an example.

  // Check that the request type is POST which is guaranteed by the master.
  CHECK_EQ("POST", request.method);

  // Validate request and extract JSON.

  Try<hashmap<string, string>> decode = http::query::decode(request.body);
  if (decode.isError()) {
    return BadRequest("Failed to decode set quota request query string ('" +
                      request.body + "'): " +
                      decode.error());
  }

  hashmap<string, string> values = decode.get();

  if (!values.contains("resources")) {
    return BadRequest("Failed to parse set quota request query string ('" +
                      request.body + "'): Missing 'resources'");
  }

  Try<google::protobuf::RepeatedPtrField<Resource>> resources =
    parseResources(values["resources"]);

  if (resources.isError()) {
    return BadRequest("Failed to parse set quota request query string ('" +
                      request.body + "'): " + resources.error());
  }

  // Create the `QuotaInfo` protobuf message from the request JSON.
  Try<QuotaInfo> create = createQuotaInfo(resources.get());
  if (create.isError()) {
    return BadRequest("Failed to create QuotaInfo from set quota request "
                      "query string '(" + request.body + "'): " +
                      create.error());
  }

  // Check that the `QuotaInfo` is a valid quota request.
  Try<Nothing> validate = quota::validation::quotaInfo(create.get());
  if (validate.isError()) {
    return BadRequest("Failed to validate set quota request query string: ('" +
                      request.body + "'): " + validate.error());
  }

  // Check that the role is known by the master.
  // TODO(alexr): Once we are able to dynamically add roles, we should stop
  // checking whether the requested role is known to the master, because an
  // operator may set quota for a role that is about to be introduced.
  if (!master->roles.contains(create.get().role())) {
    return BadRequest("Failed to validate set quota request query string: ('" +
                      request.body +"')': Unknown role: '" +
                      create.get().role() + "'");
  }

  // Check that we are not updating an existing quota.
  // TODO(joerg84): Update error message once quota update is in place.
  if (master->quotas.contains(create.get().role())) {
    return BadRequest("Failed to validate set quota request query string: ('" +
                      request.body + "')': "
                      "Can not set quota for a role that already has quota");
  }

  const QuotaInfo& quotaInfo = create.get();

  // Validate whether a quota request can be satisfied.
  Option<Error> error = capacityHeuristic(quotaInfo);
  if (error.isSome()) {
    return Conflict("Heuristic capacity check for set quota request failed: " +
                    error.get().message);
  }

  // Populate master's quota-related local state. We do this before updating
  // the registry in order to make sure that we are not already trying to
  // satisfy a request for this role (since this is a multi-phase event).
  // NOTE: We do not need to remove quota for the role if the registry update
  // fails because in this case the master fails as well.
  master->quotas[quotaInfo.role()] = Quota{quotaInfo};

  // Update the registry with the new quota and acknowledge the request.
  return master->registrar->apply(Owned<Operation>(
      new quota::UpdateQuota(quotaInfo)))
    .then(defer(master->self(), [=](bool result) -> Future<http::Response> {
      // See the top comment in "master/quota.hpp" for why this check is here.
      CHECK(result);

      master->allocator->setQuota(quotaInfo.role(), quotaInfo);

      return OK();
    }));
}
Ejemplo n.º 3
0
Future<http::Response> Master::QuotaHandler::set(
    const http::Request& request) const
{
  VLOG(1) << "Setting quota from request: '" << request.body << "'";

  // Authenticate the request.
  Result<Credential> credential = master->http.authenticate(request);
  if (credential.isError()) {
    return Unauthorized("Mesos master", credential.error());
  }

  // Check that the request type is POST which is guaranteed by the master.
  CHECK_EQ("POST", request.method);

  // Validate request and extract JSON.
  // TODO(alexr): Create a type (e.g. a protobuf) for the request JSON. If we
  // move the `force` field out of the request JSON, we can reuse `QuotaInfo`.
  Try<JSON::Object> parse = JSON::parse<JSON::Object>(request.body);
  if (parse.isError()) {
    return BadRequest(
        "Failed to parse set quota request JSON '" + request.body + "': " +
        parse.error());
  }

  Result<JSON::Array> resourcesJSON =
    parse.get().find<JSON::Array>("resources");

  if (resourcesJSON.isError()) {
    // An `Error` usually indicates that a search string is malformed
    // (which is not the case here), however it may also indicate that
    // the `resources` field is not an array.
    return BadRequest(
        "Failed to extract 'resources' from set quota request JSON '" +
        request.body + "': " + resourcesJSON.error());
  }

  if (resourcesJSON.isNone()) {
    return BadRequest(
        "Failed to extract 'resources' from set quota request JSON '" +
        request.body + "': Field is missing");
  }

  // Create protobuf representation of resources.
  Try<RepeatedPtrField<Resource>> resources =
    ::protobuf::parse<RepeatedPtrField<Resource>>(resourcesJSON.get());

  if (resources.isError()) {
    return BadRequest(
        "Failed to parse 'resources' from set quota request JSON '" +
        request.body + "': " + resources.error());
  }

  // Create the `QuotaInfo` protobuf message from the request JSON.
  Try<QuotaInfo> create = createQuotaInfo(resources.get());
  if (create.isError()) {
    return BadRequest(
        "Failed to create 'QuotaInfo' from set quota request JSON '" +
        request.body + "': " + create.error());
  }

  QuotaInfo quotaInfo = create.get();

  // Check that the `QuotaInfo` is a valid quota request.
  Try<Nothing> validate = quota::validation::quotaInfo(quotaInfo);
  if (validate.isError()) {
    return BadRequest(
        "Failed to validate set quota request JSON '" + request.body + "': " +
        validate.error());
  }

  // Check that the role is on the role whitelist, if it exists.
  if (!master->isWhitelistedRole(quotaInfo.role())) {
    return BadRequest(
        "Failed to validate set quota request JSON '" + request.body +
        "': Unknown role '" + quotaInfo.role() + "'");
  }

  // Check that we are not updating an existing quota.
  // TODO(joerg84): Update error message once quota update is in place.
  if (master->quotas.contains(quotaInfo.role())) {
    return BadRequest(
        "Failed to validate set quota request JSON '" + request.body +
        "': Can not set quota for a role that already has quota");
  }

  // The force flag can be used to overwrite the `capacityHeuristic` check.
  Result<JSON::Boolean> force = parse.get().find<JSON::Boolean>("force");
  if (force.isError()) {
    // An `Error` usually indicates that a search string is malformed
    // (which is not the case here), however it may also indicate that
    // the `force` field is not a boolean.
    return BadRequest(
        "Failed to extract 'force' from set quota request JSON '" +
        request.body + "': " + force.error());
  }

  // Extract principal from request credentials.
  Option<string> principal = None();
  if (credential.isSome()) {
    principal = credential.get().principal();
    quotaInfo.set_principal(principal.get());
  }

  const bool forced = force.isSome() ? force.get().value : false;

  return authorizeSetQuota(principal, quotaInfo.role())
    .then(defer(master->self(), [=](bool authorized) -> Future<http::Response> {
      if (!authorized) {
        return Unauthorized("Mesos master");
      }

      return _set(quotaInfo, forced);
    }));
}