You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gi...@apache.org on 2017/12/31 12:17:56 UTC

[1/3] mesos git commit: Added authorization for prune images API call.

Repository: mesos
Updated Branches:
  refs/heads/master 0da7b6cc3 -> 5e88bc076


Added authorization for prune images API call.

Review: https://reviews.apache.org/r/64864/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/250a9a53
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/250a9a53
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/250a9a53

Branch: refs/heads/master
Commit: 250a9a53ccd937733ef0a507b555aecbb44af356
Parents: 0da7b6c
Author: Zhitao Li <zh...@gmail.com>
Authored: Sun Dec 31 18:27:44 2017 +0800
Committer: Gilbert Song <so...@gmail.com>
Committed: Sun Dec 31 19:39:57 2017 +0800

----------------------------------------------------------------------
 include/mesos/authorizer/acls.proto       | 14 ++++++
 include/mesos/authorizer/authorizer.proto |  4 ++
 src/authorizer/local/authorizer.cpp       | 20 +++++++++
 src/slave/http.cpp                        | 60 +++++++++++++++++---------
 4 files changed, 78 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/250a9a53/include/mesos/authorizer/acls.proto
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/acls.proto b/include/mesos/authorizer/acls.proto
index 5c7ed34..152410f 100644
--- a/include/mesos/authorizer/acls.proto
+++ b/include/mesos/authorizer/acls.proto
@@ -495,6 +495,19 @@ message ACL {
     // SOME resource provider types and names.
     required Entity resource_providers = 2;
   }
+
+  // Which principals are authorized to prune unused container images.
+  message PruneImages {
+    // Subjects: HTTP Username.
+    required Entity principals = 1;
+
+    // Objects: Given implicitly.
+    // Use Entity type ANY or NONE to allow or deny access.
+    //
+    // TODO(zhitao): Consider allowing granular permission to act upon
+    // SOME image reference.
+    required Entity images = 2;
+  }
 }
 
 
@@ -572,4 +585,5 @@ message ACLs {
   repeated ACL.RemoveStandaloneContainer remove_standalone_container = 44;
   repeated ACL.ViewStandaloneContainer view_standalone_containers = 46;
   repeated ACL.ModifyResourceProviderConfig modify_resource_provider_configs = 45;
+  repeated ACL.PruneImages prune_images = 47;
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/250a9a53/include/mesos/authorizer/authorizer.proto
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/authorizer.proto b/include/mesos/authorizer/authorizer.proto
index 90ffdfa..1508c01 100644
--- a/include/mesos/authorizer/authorizer.proto
+++ b/include/mesos/authorizer/authorizer.proto
@@ -250,6 +250,10 @@ enum Action {
   // allowed to add, update and remove resource provider config files or is
   // unauthorized.
   MODIFY_RESOURCE_PROVIDER_CONFIG = 39;
+
+  // This action will not fill in any object fields. A principal is either
+  // allowed to prune unused container images or is unauthorized.
+  PRUNE_IMAGES = 41;
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/250a9a53/src/authorizer/local/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.cpp b/src/authorizer/local/authorizer.cpp
index 0722ac9..b3ec601 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -412,6 +412,7 @@ public:
         case authorization::STOP_MAINTENANCE:
         case authorization::UPDATE_MAINTENANCE_SCHEDULE:
         case authorization::MODIFY_RESOURCE_PROVIDER_CONFIG:
+        case authorization::PRUNE_IMAGES:
           aclObject.set_type(ACL::Entity::ANY);
 
           break;
@@ -700,6 +701,7 @@ public:
         case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
         case authorization::LAUNCH_STANDALONE_CONTAINER:
         case authorization::MARK_AGENT_GONE:
+        case authorization::PRUNE_IMAGES:
         case authorization::REGISTER_AGENT:
         case authorization::REMOVE_NESTED_CONTAINER:
         case authorization::REMOVE_STANDALONE_CONTAINER:
@@ -917,6 +919,7 @@ public:
       case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
       case authorization::LAUNCH_STANDALONE_CONTAINER:
       case authorization::MARK_AGENT_GONE:
+      case authorization::PRUNE_IMAGES:
       case authorization::REGISTER_AGENT:
       case authorization::REMOVE_NESTED_CONTAINER:
       case authorization::REMOVE_STANDALONE_CONTAINER:
@@ -1130,6 +1133,7 @@ public:
       case authorization::KILL_STANDALONE_CONTAINER:
       case authorization::LAUNCH_STANDALONE_CONTAINER:
       case authorization::MARK_AGENT_GONE:
+      case authorization::PRUNE_IMAGES:
       case authorization::REGISTER_AGENT:
       case authorization::REMOVE_NESTED_CONTAINER:
       case authorization::REMOVE_STANDALONE_CONTAINER:
@@ -1504,6 +1508,16 @@ private:
         }
 
         return acls_;
+      case authorization::PRUNE_IMAGES:
+        foreach (const ACL::PruneImages& acl, acls.prune_images()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.images();
+
+          acls_.push_back(acl_);
+        }
+
+        return acls_;
       case authorization::REGISTER_FRAMEWORK:
       case authorization::CREATE_VOLUME:
       case authorization::RESERVE_RESOURCES:
@@ -1684,6 +1698,12 @@ Option<Error> LocalAuthorizer::validate(const ACLs& acls)
     }
   }
 
+  foreach (const ACL::PruneImages& acl, acls.prune_images()) {
+    if (acl.images().type() == ACL::Entity::SOME) {
+      return Error("acls.prune_images type must be either NONE or ANY");
+    }
+  }
+
   // TODO(alexr): Consider validating not only protobuf, but also the original
   // JSON in order to spot misspelled names. A misspelled action may affect
   // authorization result and hence lead to a security issue (e.g. when there

http://git-wip-us.apache.org/repos/asf/mesos/blob/250a9a53/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index 446be55..71e0bbb 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -2444,8 +2444,6 @@ Future<Response> Http::pruneImages(
 {
   CHECK_EQ(agent::Call::PRUNE_IMAGES, call.type());
 
-  // TODO(zhitao): Add AuthN/AuthZ.
-
   LOG(INFO) << "Processing PRUNE_IMAGES call";
   vector<Image> excludedImages(call.prune_images().excluded_images().begin(),
                                call.prune_images().excluded_images().end());
@@ -2458,25 +2456,47 @@ Future<Response> Http::pruneImages(
               std::back_inserter(excludedImages));
   }
 
-  return slave->containerizer->pruneImages(excludedImages)
-      .then([acceptType](const Future<Nothing>& result)
-      ->Future<Response> {
-        if (!result.isReady()) {
-          // TODO(zhitao): Because `containerizer::pruneImages` returns
-          // a `Nothing` now, we cannot distinguish between actual
-          // failure or the case that operator should drain the agent.
-          // Consider returning more information.
-          LOG(WARNING)
-            << "Failed to prune images: "
-            << (result.isFailed() ? result.failure() : "discarded");
-
-          return result.isFailed()
-            ? InternalServerError(result.failure())
-            : InternalServerError();
-        }
+  Future<Owned<ObjectApprover>> approver;
 
-        return OK();
-      });
+  if (slave->authorizer.isSome()) {
+    Option<authorization::Subject> subject = createSubject(principal);
+
+    approver = slave->authorizer.get()->getObjectApprover(
+        subject,
+        authorization::PRUNE_IMAGES);
+  } else {
+    approver = Owned<ObjectApprover>(new AcceptingObjectApprover());
+  }
+
+  return approver
+    .then(defer(slave->self(), [this, excludedImages](
+        const Owned<ObjectApprover>& approver) -> Future<Response> {
+      Try<bool> approved = approver->approved(ObjectApprover::Object());
+      if (approved.isError()) {
+        return InternalServerError("Authorization error: " + approved.error());
+      } else if (!approved.get()) {
+        return Forbidden();
+      }
+
+      return slave->containerizer->pruneImages(excludedImages)
+          .then([](const Future<Nothing>& result) -> Future<Response> {
+            if (!result.isReady()) {
+              // TODO(zhitao): Because `containerizer::pruneImages` returns
+              // a `Nothing` now, we cannot distinguish between actual
+              // failure or the case that operator should drain the agent.
+              // Consider returning more information.
+              LOG(WARNING)
+                << "Failed to prune images: "
+                << (result.isFailed() ? result.failure() : "discarded");
+
+              return result.isFailed()
+                ? InternalServerError(result.failure())
+                : InternalServerError();
+            }
+
+            return OK();
+          });
+    }));
 }
 
 


[2/3] mesos git commit: Added test for `prune_images` acl validation.

Posted by gi...@apache.org.
Added test for `prune_images` acl validation.

Review: https://reviews.apache.org/r/64865/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/310ba44a
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/310ba44a
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/310ba44a

Branch: refs/heads/master
Commit: 310ba44a4d48d65e5f28db050fed72e343cde441
Parents: 250a9a5
Author: Zhitao Li <zh...@gmail.com>
Authored: Sun Dec 31 18:27:55 2017 +0800
Committer: Gilbert Song <so...@gmail.com>
Committed: Sun Dec 31 19:40:03 2017 +0800

----------------------------------------------------------------------
 src/tests/authorization_tests.cpp | 55 ++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/310ba44a/src/tests/authorization_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/authorization_tests.cpp b/src/tests/authorization_tests.cpp
index 4f3da08..a76ad18 100644
--- a/src/tests/authorization_tests.cpp
+++ b/src/tests/authorization_tests.cpp
@@ -5495,6 +5495,61 @@ TYPED_TEST(AuthorizationTest, ModifyResourceProviderConfig)
   }
 }
 
+
+// This tests the authorization of requests to prune images.
+TYPED_TEST(AuthorizationTest, PruneImages)
+{
+  ACLs acls;
+
+  {
+    // "foo" principal can prune any images.
+    mesos::ACL::PruneImages* acl = acls.add_prune_images();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_images()->set_type(mesos::ACL::Entity::ANY);
+  }
+
+  {
+    // Nobody else can prune images.
+    mesos::ACL::PruneImages* acl = acls.add_prune_images();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_images()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  {
+    // "foo" is allowed to prune images. This request should succeed.
+    authorization::Request request;
+    request.set_action(authorization::PRUNE_IMAGES);
+    request.mutable_subject()->set_value("foo");
+
+    AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+  }
+
+  {
+    // "bar" is not allowed to prune images. The request should fail.
+    authorization::Request request;
+    request.set_action(authorization::PRUNE_IMAGES);
+    request.mutable_subject()->set_value("bar");
+
+    AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+  }
+
+  {
+    // Test that no authorizer is created with invalid ACLs.
+    ACLs invalid;
+
+    mesos::ACL::PruneImages* acl = invalid.add_prune_images();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_images()->add_values("yoda");
+
+    Try<Authorizer*> create = TypeParam::create(parameterize(invalid));
+    EXPECT_ERROR(create);
+  }
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {


[3/3] mesos git commit: Documented new image gc support in Mesos containerizer.

Posted by gi...@apache.org.
Documented new image gc support in Mesos containerizer.

Review: https://reviews.apache.org/r/64813/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/5e88bc07
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/5e88bc07
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/5e88bc07

Branch: refs/heads/master
Commit: 5e88bc076f9c04293057ed1e4b2f941d1238368c
Parents: 310ba44
Author: Zhitao Li <zh...@gmail.com>
Authored: Sun Dec 31 18:28:04 2017 +0800
Committer: Gilbert Song <so...@gmail.com>
Committed: Sun Dec 31 20:08:12 2017 +0800

----------------------------------------------------------------------
 docs/container-image.md   | 40 ++++++++++++++++++++++++++++++++++++++++
 docs/operator-http-api.md | 30 ++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5e88bc07/docs/container-image.md
----------------------------------------------------------------------
diff --git a/docs/container-image.md b/docs/container-image.md
index 99f4f5c..1112bd7 100644
--- a/docs/container-image.md
+++ b/docs/container-image.md
@@ -390,6 +390,46 @@ and mount it under the sandbox directory. The executor can perform
 `pivot_root` or `chroot` itself to enter the container root
 filesystem.
 
+## Garbage Collect Unused Container Images
+
+Experimental support of garbage-collecting unused container images was added at
+Mesos 1.5. This can be either configured automatically via a new agent flag
+`--image_gc_config`, or manually invoked through agent's
+[v1 Operator HTTP API](operator-http-api.md#prune_images). This can be used
+to avoid unbounded disk space usage of image stores.
+
+This is implemented with a simple mark-and-sweep logic. When image GC happens,
+we check all layers and images referenced by active running containers and avoid
+removing them from the image store. As a pre-requisite, if there are active
+containers launched before Mesos 1.5.0, we cannot determine what images can be
+safely garbage collected, so agent will refuse to invoke image GC. To garbage
+collect container images, users are expected to drain all containers launched
+before Mesos 1.5.0.
+
+**NOTE**: currently, the image GC is only supported for docker store in Mesos
+Containerizer.
+
+### Automatic Image GC through Agent Flag
+
+To enable automatic image GC, use the new agent flag `--image_gc_config`:
+
+    --image_gc_config=file:///home/vagrant/image-gc-config.json
+
+or as a JSON object,
+
+    --image_gc_config="{ \
+      \"image_disk_headroom\": 0.1, \
+      \"image_disk_watch_interval\": { \
+        \"nano_seconds\": 3600 \
+        }, \
+      \"excluded_images\": \[ \] \
+    }"
+
+
+### Manual Image GC through HTTP API
+See `PRUNE_IMAGES` section in
+[v1 Operator HTTP API](operator-http-api.md#prune_images) for manual image GC
+through the agent HTTP API.
 
 ## References
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/5e88bc07/docs/operator-http-api.md
----------------------------------------------------------------------
diff --git a/docs/operator-http-api.md b/docs/operator-http-api.md
index f81e5bc..af6a3a6 100644
--- a/docs/operator-http-api.md
+++ b/docs/operator-http-api.md
@@ -3848,3 +3848,33 @@ REMOVE_NESTED_CONTAINER HTTP Response (JSON):
 
 HTTP/1.1 200 OK
 ```
+
+### PRUNE_IMAGES
+
+This call triggers garbage collection for container images. This call can
+only be made when all running containers are launched with Mesos version 1.5
+or newer. An optional list of excluded images from GC can be speficied via
+`prune_images.excluded_images` field.
+
+```
+PRUNE_IMAGES HTTP Request (JSON):
+
+POST /api/v1  HTTP/1.1
+
+Host: agenthost:5051
+Content-Type: application/json
+Accept: application/json
+
+{
+  "type": "PRUNE_IMAGES",
+  "prune_images": {
+    "excluded_images": [
+      {"type":"DOCKER","docker":{"name":"mysql:latest"}}
+    ]
+  }
+}
+
+PRUNE_IMAGES HTTP Response (JSON):
+
+HTTP/1.1 200 OK
+```