You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2017/11/30 00:04:12 UTC

[01/13] mesos git commit: Storage local resource provider is built when gRPC is enabled on Linux.

Repository: mesos
Updated Branches:
  refs/heads/master 9d0cadc82 -> f9b076ce0


Storage local resource provider is built when gRPC is enabled on Linux.

Storage local resource provider will be using CSI, which is only
supported when enabling gRPC on Linux.

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


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

Branch: refs/heads/master
Commit: 459fb2ccfd359af5f15ef84b764d01f73bd6aafc
Parents: 9d0cadc
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:27 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:27 2017 -0800

----------------------------------------------------------------------
 configure.ac    |  2 ++
 src/Makefile.am | 10 ++++++++--
 2 files changed, 10 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/459fb2cc/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 382c929..5ffc33a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2017,6 +2017,8 @@ libssl is required for gRPC to build.
 
     with_bundled_grpc=yes
   fi
+
+  AC_DEFINE([ENABLE_GRPC])
 fi
 
 AM_CONDITIONAL([ENABLE_GRPC], [test "x$enable_grpc" = "xyes"])

http://git-wip-us.apache.org/repos/asf/mesos/blob/459fb2cc/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index b1ed4e5..1148453 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1008,7 +1008,6 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   resource_provider/manager.cpp						\
   resource_provider/registrar.cpp					\
   resource_provider/validation.cpp					\
-  resource_provider/storage/provider.cpp				\
   sched/sched.cpp							\
   scheduler/scheduler.cpp						\
   secret/resolver.cpp							\
@@ -1155,7 +1154,6 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   resource_provider/registry.hpp					\
   resource_provider/registry.proto					\
   resource_provider/validation.hpp					\
-  resource_provider/storage/provider.hpp				\
   sched/constants.hpp							\
   sched/flags.hpp							\
   scheduler/constants.hpp						\
@@ -1439,6 +1437,14 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   authentication/executor/jwt_secret_generator.hpp
 endif
 
+if ENABLE_GRPC
+if OS_LINUX
+libmesos_no_3rdparty_la_SOURCES +=					\
+  resource_provider/storage/provider.cpp				\
+  resource_provider/storage/provider.hpp
+endif
+endif
+
 libmesos_no_3rdparty_la_CPPFLAGS = $(MESOS_CPPFLAGS)
 
 libmesos_no_3rdparty_la_LIBADD = # Initialized to enable using +=.


[05/13] mesos git commit: Updated the top-level `CSIPluginInfo` proto.

Posted by ji...@apache.org.
Updated the top-level `CSIPluginInfo` proto.

The `type` and `name` fields are introduced to uniquely identify a CSI
service bundle. The purpose of the new proto and these two fields is to
decouple a CSI service bundle from a `ResoruceProviderInfo`, so we may
allow tasks to directly use non-resource volumes (such as NFS volumes).

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


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

Branch: refs/heads/master
Commit: 6216cdef7e61fa90bd0b08927ad4ba44ec757b07
Parents: 8b93fa3
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:49 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:49 2017 -0800

----------------------------------------------------------------------
 include/mesos/mesos.proto    | 54 ++++++++++++++++++++------
 include/mesos/type_utils.hpp | 20 +++++++---
 include/mesos/v1/mesos.hpp   | 20 +++++++---
 include/mesos/v1/mesos.proto | 54 ++++++++++++++++++++------
 src/common/type_utils.cpp    | 82 +++++++++++++++++++++++++++------------
 src/v1/mesos.cpp             | 82 +++++++++++++++++++++++++++------------
 6 files changed, 228 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6216cdef/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 524665a..25a9082 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -1002,28 +1002,56 @@ message SlaveInfo {
 
 
 /**
- * Describes a CSI plugin.
+ * Describes the container configuration to run a CSI plugin component.
  */
-message CSIPluginInfo {
-  required string name = 1;
-  required CommandInfo command = 2;
+message CSIPluginContainerInfo {
+  enum Service {
+    UNKNOWN = 0;
+    CONTROLLER_SERVICE = 1;
+    NODE_SERVICE = 2;
+  }
+
+  repeated Service services = 1;
+  optional CommandInfo command = 2;
   repeated Resource resources = 3;
   optional ContainerInfo container = 4;
 }
 
 
 /**
+ * Describes a CSI plugin.
+ */
+message CSIPluginInfo {
+  // The type of the CSI service. This uniquely identifies a CSI
+  // implementation. For instance:
+  //     org.apache.mesos.csi.test
+  //
+  // Please follow to Java package naming convention
+  // (https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions)
+  // to avoid conflicts on type names.
+  required string type = 1;
+
+  // The name of the CSI service. There could be mutliple instances of a
+  // type of CSI service. The name field is used to distinguish these
+  // instances. It should be a legal Java identifier
+  // (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html)
+  // to avoid conflicts on concatenation of type and name.
+  required string name = 2;
+
+  // A list of container configurations to run CSI plugin components.
+  // The controller service will be served by the first configuration
+  // that contains `CONTROLLER_SERVICE`, and the node service will be
+  // served by the first configuration that contains `NODE_SERVICE`.
+  repeated CSIPluginContainerInfo containers = 3;
+}
+
+
+/**
  * Describes a resource provider. Note that the 'id' field is only available
  * after a resource provider is registered with the master, and is made
  * available here to facilitate re-registration.
  */
 message ResourceProviderInfo {
-  message StorageInfo {
-    repeated CSIPluginInfo csi_plugins = 1;
-    required string controller_plugin = 2;
-    required string node_plugin = 3;
-  }
-
   optional ResourceProviderID id = 1;
   repeated Attribute attributes = 2;
 
@@ -1038,10 +1066,12 @@ message ResourceProviderInfo {
 
   // The name of the resource provider. There could be multiple
   // instances of a type of resource provider. The name field is used
-  // to distinguish these instances.
+  // to distinguish these instances. It should be a legal Java identifier
+  // (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html)
+  // to avoid conflicts on concatenation of type and name.
   required string name = 4;
 
-  optional StorageInfo storage = 5;
+  optional CSIPluginInfo storage = 5; // EXPERIMENTAL.
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/6216cdef/include/mesos/type_utils.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/type_utils.hpp b/include/mesos/type_utils.hpp
index 8de3f76..a348c7d 100644
--- a/include/mesos/type_utils.hpp
+++ b/include/mesos/type_utils.hpp
@@ -48,7 +48,14 @@ bool operator==(const CheckStatusInfo& left, const CheckStatusInfo& right);
 bool operator==(const CommandInfo& left, const CommandInfo& right);
 bool operator==(const CommandInfo::URI& left, const CommandInfo::URI& right);
 bool operator==(const ContainerID& left, const ContainerID& right);
+bool operator==(const ContainerInfo& left, const ContainerInfo& right);
 bool operator==(const Credential& left, const Credential& right);
+bool operator==(const CSIPluginInfo& left, const CSIPluginInfo& right);
+
+bool operator==(
+    const CSIPluginContainerInfo& left,
+    const CSIPluginContainerInfo& right);
+
 bool operator==(const DiscoveryInfo& left, const DiscoveryInfo& right);
 bool operator==(const Environment& left, const Environment& right);
 bool operator==(const ExecutorInfo& left, const ExecutorInfo& right);
@@ -77,11 +84,6 @@ bool operator!=(const ExecutorInfo& left, const ExecutorInfo& right);
 bool operator!=(const Labels& left, const Labels& right);
 bool operator!=(const TaskStatus& left, const TaskStatus& right);
 
-
-bool operator!=(
-    const ResourceProviderInfo& left,
-    const ResourceProviderInfo& right);
-
 inline bool operator==(const ExecutorID& left, const ExecutorID& right)
 {
   return left.value() == right.value();
@@ -233,6 +235,14 @@ inline bool operator!=(const ContainerID& left, const ContainerID& right)
 }
 
 
+inline bool operator!=(
+    const CSIPluginContainerInfo& left,
+    const CSIPluginContainerInfo& right)
+{
+  return !(left == right);
+}
+
+
 inline bool operator!=(const ExecutorID& left, const ExecutorID& right)
 {
   return left.value() != right.value();

http://git-wip-us.apache.org/repos/asf/mesos/blob/6216cdef/include/mesos/v1/mesos.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/v1/mesos.hpp b/include/mesos/v1/mesos.hpp
index 26b4596..d1401fb 100644
--- a/include/mesos/v1/mesos.hpp
+++ b/include/mesos/v1/mesos.hpp
@@ -45,7 +45,14 @@ namespace v1 {
 
 bool operator==(const CommandInfo& left, const CommandInfo& right);
 bool operator==(const CommandInfo::URI& left, const CommandInfo::URI& right);
+bool operator==(const ContainerInfo& left, const ContainerInfo& right);
 bool operator==(const Credential& left, const Credential& right);
+bool operator==(const CSIPluginInfo& left, const CSIPluginInfo& right);
+
+bool operator==(
+    const CSIPluginContainerInfo& left,
+    const CSIPluginContainerInfo& right);
+
 bool operator==(const DiscoveryInfo& left, const DiscoveryInfo& right);
 bool operator==(const Environment& left, const Environment& right);
 bool operator==(const ExecutorInfo& left, const ExecutorInfo& right);
@@ -68,11 +75,6 @@ bool operator==(const URL& left, const URL& right);
 bool operator==(const Volume& left, const Volume& right);
 
 bool operator!=(const Labels& left, const Labels& right);
-
-bool operator!=(
-    const ResourceProviderInfo& left,
-    const ResourceProviderInfo& right);
-
 bool operator!=(const TaskStatus& left, const TaskStatus& right);
 
 
@@ -233,6 +235,14 @@ inline bool operator!=(const ContainerID& left, const ContainerID& right)
 }
 
 
+inline bool operator!=(
+    const CSIPluginContainerInfo& left,
+    const CSIPluginContainerInfo& right)
+{
+  return !(left == right);
+}
+
+
 inline bool operator!=(const ExecutorID& left, const ExecutorID& right)
 {
   return left.value() != right.value();

http://git-wip-us.apache.org/repos/asf/mesos/blob/6216cdef/include/mesos/v1/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/v1/mesos.proto b/include/mesos/v1/mesos.proto
index 0ba6032..ddb5add 100644
--- a/include/mesos/v1/mesos.proto
+++ b/include/mesos/v1/mesos.proto
@@ -994,28 +994,56 @@ message AgentInfo {
 
 
 /**
- * Describes a CSI plugin.
+ * Describes the container configuration to run a CSI plugin component.
  */
-message CSIPluginInfo {
-  required string name = 1;
-  required CommandInfo command = 2;
+message CSIPluginContainerInfo {
+  enum Service {
+    UNKNOWN = 0;
+    CONTROLLER_SERVICE = 1;
+    NODE_SERVICE = 2;
+  }
+
+  repeated Service services = 1;
+  optional CommandInfo command = 2;
   repeated Resource resources = 3;
   optional ContainerInfo container = 4;
 }
 
 
 /**
+ * Describes a CSI plugin.
+ */
+message CSIPluginInfo {
+  // The type of the CSI service. This uniquely identifies a CSI
+  // implementation. For instance:
+  //     org.apache.mesos.csi.test
+  //
+  // Please follow to Java package naming convention
+  // (https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions)
+  // to avoid conflicts on type names.
+  required string type = 1;
+
+  // The name of the CSI service. There could be mutliple instances of a
+  // type of CSI service. The name field is used to distinguish these
+  // instances. It should be a legal Java identifier
+  // (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html)
+  // to avoid conflicts on concatenation of type and name.
+  required string name = 2;
+
+  // A list of container configurations to run CSI plugin components.
+  // The controller service will be served by the first configuration
+  // that containers `CONTROLLER_SERVICE`, and the node service will be
+  // served by the first configuration that contains `NODE_SERVICE`.
+  repeated CSIPluginContainerInfo containers = 3;
+}
+
+
+/**
  * Describes a resource provider. Note that the 'id' field is only available
  * after a resource provider is registered with the master, and is made
  * available here to facilitate re-registration.
  */
 message ResourceProviderInfo {
-  message StorageInfo {
-    repeated CSIPluginInfo csi_plugins = 1;
-    required string controller_plugin = 2;
-    required string node_plugin = 3;
-  }
-
   optional ResourceProviderID id = 1;
   repeated Attribute attributes = 2;
 
@@ -1030,10 +1058,12 @@ message ResourceProviderInfo {
 
   // The name of the resource provider. There could be multiple
   // instances of a type of resource provider. The name field is used
-  // to distinguish these instances.
+  // to distinguish these instances. It should be a legal Java identifier
+  // (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html)
+  // to avoid conflicts on concatenation of type and name.
   required string name = 4;
 
-  optional StorageInfo storage = 5;
+  optional CSIPluginInfo storage = 5; // EXPERIMENTAL.
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/6216cdef/src/common/type_utils.cpp
----------------------------------------------------------------------
diff --git a/src/common/type_utils.cpp b/src/common/type_utils.cpp
index c10f21f..3bff49e 100644
--- a/src/common/type_utils.cpp
+++ b/src/common/type_utils.cpp
@@ -99,6 +99,56 @@ bool operator==(const Credential& left, const Credential& right)
 }
 
 
+bool operator==(const CSIPluginInfo& left, const CSIPluginInfo& right)
+{
+  // Order of containers is important.
+  if (left.containers_size() != right.containers_size()) {
+    return false;
+  }
+
+  for (int i = 0; i < left.containers_size(); i++) {
+    if (left.containers(i) != right.containers(i)) {
+      return false;
+    }
+  }
+
+  return left.type() == right.type() &&
+    left.name() == right.name();
+}
+
+
+bool operator==(
+    const CSIPluginContainerInfo& left,
+    const CSIPluginContainerInfo& right)
+{
+  // Order of services is not important.
+  if (left.services_size() != right.services_size()) {
+    return false;
+  }
+
+  vector<bool> used(right.services_size(), false);
+
+  for (int i = 0; i < left.services_size(); i++) {
+    bool found = false;
+    for (int j = 0; j < right.services_size(); j++) {
+      if (left.services(i) == right.services(j) && !used[j]) {
+        found = used[j] = true;
+        break;
+      }
+    }
+    if (!found) {
+      return false;
+    }
+  }
+
+  return left.has_command() == right.has_command() &&
+    (!left.has_command() || left.command() == right.command()) &&
+    Resources(left.resources()) == Resources(right.resources()) &&
+    left.has_container() == right.has_container() &&
+    (!left.has_container() || left.container() == right.container());
+}
+
+
 bool operator==(
     const Environment::Variable& left,
     const Environment::Variable& right)
@@ -354,31 +404,13 @@ bool operator==(
     const ResourceProviderInfo& left,
     const ResourceProviderInfo& right)
 {
-  if (left.id() != right.id()) {
-    return false;
-  }
-
-  if (Attributes(left.attributes()) != Attributes(right.attributes())) {
-    return false;
-  }
-
-  if (left.type() != right.type()) {
-    return false;
-  }
-
-  if (left.name() != right.name()) {
-    return false;
-  }
-
-  return true;
-}
-
-
-bool operator!=(
-    const ResourceProviderInfo& left,
-    const ResourceProviderInfo& right)
-{
-  return !(left == right);
+  return left.has_id() == right.has_id() &&
+    (!left.has_id() || left.id() == right.id()) &&
+    Attributes(left.attributes()) == Attributes(right.attributes()) &&
+    left.type() == right.type() &&
+    left.name() == right.name() &&
+    left.has_storage() == right.has_storage() &&
+    (!left.has_storage() || left.storage() == right.storage());
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/6216cdef/src/v1/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/v1/mesos.cpp b/src/v1/mesos.cpp
index dcf323e..243b1cd 100644
--- a/src/v1/mesos.cpp
+++ b/src/v1/mesos.cpp
@@ -89,6 +89,56 @@ bool operator==(const Credential& left, const Credential& right)
 }
 
 
+bool operator==(const CSIPluginInfo& left, const CSIPluginInfo& right)
+{
+  // Order of containers is important.
+  if (left.containers_size() != right.containers_size()) {
+    return false;
+  }
+
+  for (int i = 0; i < left.containers_size(); i++) {
+    if (left.containers(i) != right.containers(i)) {
+      return false;
+    }
+  }
+
+  return left.type() == right.type() &&
+    left.name() == right.name();
+}
+
+
+bool operator==(
+    const CSIPluginContainerInfo& left,
+    const CSIPluginContainerInfo& right)
+{
+  // Order of services is not important.
+  if (left.services_size() != right.services_size()) {
+    return false;
+  }
+
+  vector<bool> used(right.services_size(), false);
+
+  for (int i = 0; i < left.services_size(); i++) {
+    bool found = false;
+    for (int j = 0; j < right.services_size(); j++) {
+      if (left.services(i) == right.services(j) && !used[j]) {
+        found = used[j] = true;
+        break;
+      }
+    }
+    if (!found) {
+      return false;
+    }
+  }
+
+  return left.has_command() == right.has_command() &&
+    (!left.has_command() || left.command() == right.command()) &&
+    Resources(left.resources()) == Resources(right.resources()) &&
+    left.has_container() == right.has_container() &&
+    (!left.has_container() || left.container() == right.container());
+}
+
+
 bool operator==(
     const Environment::Variable& left,
     const Environment::Variable& right)
@@ -348,31 +398,13 @@ bool operator==(
     const ResourceProviderInfo& left,
     const ResourceProviderInfo& right)
 {
-  if (left.id() != right.id()) {
-    return false;
-  }
-
-  if (Attributes(left.attributes()) != Attributes(right.attributes())) {
-    return false;
-  }
-
-  if (left.type() != right.type()) {
-    return false;
-  }
-
-  if (left.name() != right.name()) {
-    return false;
-  }
-
-  return true;
-}
-
-
-bool operator!=(
-    const ResourceProviderInfo& left,
-    const ResourceProviderInfo& right)
-{
-  return !(left == right);
+  return left.has_id() == right.has_id() &&
+    (!left.has_id() || left.id() == right.id()) &&
+    Attributes(left.attributes()) == Attributes(right.attributes()) &&
+    left.type() == right.type() &&
+    left.name() == right.name() &&
+    left.has_storage() == right.has_storage() &&
+    (!left.has_storage() || left.storage() == right.storage());
 }
 
 


[12/13] mesos git commit: Added a test for ContainerDaemon.

Posted by ji...@apache.org.
Added a test for ContainerDaemon.

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


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

Branch: refs/heads/master
Commit: e861c0e497bac65a6a7be8270f020f9b3e436d2b
Parents: 5c97963
Author: Jie Yu <yu...@gmail.com>
Authored: Wed Nov 29 13:35:21 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 16:03:25 2017 -0800

----------------------------------------------------------------------
 src/Makefile.am                      |   1 +
 src/tests/CMakeLists.txt             |   1 +
 src/tests/container_daemon_tests.cpp | 184 ++++++++++++++++++++++++++++++
 src/tests/mesos.cpp                  |  24 ++++
 src/tests/mesos.hpp                  |   7 ++
 5 files changed, 217 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e861c0e4/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 8dcc367..4a3b728 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2412,6 +2412,7 @@ mesos_tests_SOURCES =						\
   tests/common_validation_tests.cpp				\
   tests/container_logger_tests.cpp				\
   tests/containerizer.cpp					\
+  tests/container_daemon_tests.cpp				\
   tests/cram_md5_authentication_tests.cpp			\
   tests/credentials_tests.cpp					\
   tests/default_executor_tests.cpp				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/e861c0e4/src/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 8997cc0..65fca82 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -84,6 +84,7 @@ set(MESOS_TESTS_SRC
   check_tests.cpp
   command_executor_tests.cpp
   common_validation_tests.cpp
+  container_daemon_tests.cpp
   cram_md5_authentication_tests.cpp
   credentials_tests.cpp
   default_executor_tests.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/e861c0e4/src/tests/container_daemon_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/container_daemon_tests.cpp b/src/tests/container_daemon_tests.cpp
new file mode 100644
index 0000000..3d88390
--- /dev/null
+++ b/src/tests/container_daemon_tests.cpp
@@ -0,0 +1,184 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <process/future.hpp>
+#include <process/gmock.hpp>
+#include <process/http.hpp>
+
+#include <process/ssl/flags.hpp>
+
+#include <stout/option.hpp>
+#include <stout/try.hpp>
+#include <stout/uuid.hpp>
+
+#include <mesos/mesos.hpp>
+
+#include <mesos/authentication/secret_generator.hpp>
+
+#ifdef USE_SSL_SOCKET
+#include "authentication/executor/jwt_secret_generator.hpp"
+#endif // USE_SSL_SOCKET
+
+#include "common/validation.hpp"
+
+#include "slave/container_daemon.hpp"
+
+#include "tests/cluster.hpp"
+#include "tests/mesos.hpp"
+
+namespace http = process::http;
+
+#ifdef USE_SSL_SOCKET
+namespace openssl = process::network::openssl;
+#endif // USE_SSL_SOCKET
+
+using std::string;
+
+using process::Future;
+using process::Owned;
+using process::PID;
+using process::Promise;
+
+using process::http::authentication::Principal;
+
+#ifdef USE_SSL_SOCKET
+using mesos::authentication::executor::JWTSecretGenerator;
+#endif // USE_SSL_SOCKET
+
+using mesos::internal::common::validation::validateSecret;
+
+using mesos::internal::slave::ContainerDaemon;
+using mesos::internal::slave::Slave;
+
+using mesos::master::detector::MasterDetector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class ContainerDaemonTest : public MesosTest {};
+
+
+TEST_F(ContainerDaemonTest, RestartOnTermination)
+{
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+  Owned<SecretGenerator> secretGenerator;
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+
+#ifdef USE_SSL_SOCKET
+  ASSERT_SOME(slaveFlags.jwt_secret_key);
+
+  Try<string> jwtSecretKey = os::read(slaveFlags.jwt_secret_key.get());
+  ASSERT_SOME(jwtSecretKey);
+
+  secretGenerator.reset(new JWTSecretGenerator(jwtSecretKey.get()));
+#endif // USE_SSL_SOCKET
+
+  Future<Nothing> recover = FUTURE_DISPATCH(_, &Slave::__recover);
+
+  Try<Owned<cluster::Slave>> slave = StartSlave(
+      detector.get(),
+      secretGenerator.get(),
+      slaveFlags);
+
+  ASSERT_SOME(slave);
+
+  PID<Slave> slavePid = slave.get()->pid;
+
+  // Ensure slave has finished recovery.
+  AWAIT_READY(recover);
+
+  string scheme = "http";
+
+#ifdef USE_SSL_SOCKET
+  if (openssl::flags().enabled) {
+    scheme = "https";
+  }
+#endif // USE_SSL_SOCKET
+
+  http::URL url(
+      scheme,
+      slavePid.address.ip,
+      slavePid.address.port,
+      strings::join("/", slavePid.id, "api/v1"));
+
+  // NOTE: The current implicit authorization for creating standalone
+  // containers is to check if the container ID prefix in the claims
+  // of the principal is indeed a prefix of the container ID that is
+  // specified in the API call.
+  string containerIdPrefix = UUID::random().toString();
+
+  ContainerID containerId;
+  containerId.set_value(strings::join(
+        "-",
+        containerIdPrefix,
+        UUID::random().toString()));
+
+  Principal principal(
+      None(),
+      {{"cid_prefix", containerIdPrefix}});
+
+  Option<string> authToken;
+  if (secretGenerator.get() != nullptr) {
+    Future<Secret> secret = secretGenerator->generate(principal);
+    AWAIT_READY(secret);
+
+    ASSERT_NONE(validateSecret(secret.get()));
+    ASSERT_EQ(Secret::VALUE, secret->type());
+
+    authToken = secret->value().data();
+  }
+
+  int runs = 0;
+  Promise<Nothing> done;
+
+  auto postStartHook = [&]() -> Future<Nothing> {
+    runs++;
+    return Nothing();
+  };
+
+  auto postStopHook = [&]() -> Future<Nothing> {
+    if (runs >= 5) {
+      done.set(Nothing());
+    }
+    return Nothing();
+  };
+
+  Try<Owned<ContainerDaemon>> daemon = ContainerDaemon::create(
+      url,
+      authToken,
+      containerId,
+      createCommandInfo("exit 0"),
+      None(),
+      None(),
+      postStartHook,
+      postStopHook);
+
+  ASSERT_SOME(daemon);
+
+  AWAIT_READY(done.future());
+
+  Future<Nothing> wait = daemon.get()->wait();
+  EXPECT_TRUE(wait.isPending());
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e861c0e4/src/tests/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.cpp b/src/tests/mesos.cpp
index b6daf85..b76eb4d 100644
--- a/src/tests/mesos.cpp
+++ b/src/tests/mesos.cpp
@@ -596,6 +596,30 @@ Try<Owned<cluster::Slave>> MesosTest::StartSlave(
 }
 
 
+Try<Owned<cluster::Slave>> MesosTest::StartSlave(
+    mesos::master::detector::MasterDetector* detector,
+    mesos::SecretGenerator* secretGenerator,
+    const Option<slave::Flags>& flags)
+{
+  Try<Owned<cluster::Slave>> slave = cluster::Slave::create(
+      detector,
+      flags.isNone() ? CreateSlaveFlags() : flags.get(),
+      None(),
+      None(),
+      None(),
+      None(),
+      None(),
+      None(),
+      secretGenerator);
+
+  if (slave.isSome()) {
+    slave.get()->start();
+  }
+
+  return slave;
+}
+
+
 // Although the constructors and destructors for mock classes are
 // often trivial, defining them out-of-line (in a separate compilation
 // unit) improves compilation time: see MESOS-3827.

http://git-wip-us.apache.org/repos/asf/mesos/blob/e861c0e4/src/tests/mesos.hpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index 68f78d0..5fe5356 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -257,6 +257,13 @@ protected:
       const Option<slave::Flags>& flags = None(),
       bool mock = false);
 
+  // Starts a slave with the specified detector, secretGenerator,
+  // and flags.
+  virtual Try<process::Owned<cluster::Slave>> StartSlave(
+      mesos::master::detector::MasterDetector* detector,
+      mesos::SecretGenerator* secretGenerator,
+      const Option<slave::Flags>& flags = None());
+
   Option<zookeeper::URL> zookeeperUrl;
 
   const std::string defaultAgentResourcesString{


[04/13] mesos git commit: Added an object approver to authorize requests from resource providers.

Posted by ji...@apache.org.
Added an object approver to authorize requests from resource providers.

This patch adds `LocalImplicitResourceProviderObjectApprover`, which
authorize standalone container calls from a resource provider if the
container IDs are prefixed with the namespace string.

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


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

Branch: refs/heads/master
Commit: 8b93fa3e9081d6a9d8215d442c2a1073cea2a84d
Parents: 9bee2c3
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:38 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:38 2017 -0800

----------------------------------------------------------------------
 include/mesos/authorizer/authorizer.hpp | 13 ++++
 src/authorizer/local/authorizer.cpp     | 96 +++++++++++++++++++++++-----
 src/slave/http.cpp                      |  8 +--
 3 files changed, 98 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/8b93fa3e/include/mesos/authorizer/authorizer.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/authorizer.hpp b/include/mesos/authorizer/authorizer.hpp
index 40790f5..a86a6ee 100644
--- a/include/mesos/authorizer/authorizer.hpp
+++ b/include/mesos/authorizer/authorizer.hpp
@@ -71,6 +71,19 @@ public:
         container_id(nullptr),
         machine_id(nullptr) {}
 
+    Object(const ContainerID& _container_id)
+      : value(nullptr),
+        framework_info(nullptr),
+        task(nullptr),
+        task_info(nullptr),
+        executor_info(nullptr),
+        quota_info(nullptr),
+        weight_info(nullptr),
+        resource(nullptr),
+        command_info(nullptr),
+        container_id(&_container_id),
+        machine_id(nullptr) {}
+
     Object(const MachineID& _machine_id)
       : value(nullptr),
         framework_info(nullptr),

http://git-wip-us.apache.org/repos/asf/mesos/blob/8b93fa3e/src/authorizer/local/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.cpp b/src/authorizer/local/authorizer.cpp
index 35bf03c..e07f57e 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -34,6 +34,7 @@
 #include <stout/option.hpp>
 #include <stout/path.hpp>
 #include <stout/protobuf.hpp>
+#include <stout/strings.hpp>
 #include <stout/try.hpp>
 #include <stout/unreachable.hpp>
 
@@ -41,13 +42,14 @@
 #include "common/parse.hpp"
 #include "common/protobuf_utils.hpp"
 
-using process::dispatch;
+using std::string;
+using std::vector;
+
 using process::Failure;
 using process::Future;
 using process::Owned;
 
-using std::string;
-using std::vector;
+using process::dispatch;
 
 namespace mesos {
 namespace internal {
@@ -527,6 +529,28 @@ private:
 };
 
 
+class LocalImplicitResourceProviderObjectApprover : public ObjectApprover
+{
+public:
+  LocalImplicitResourceProviderObjectApprover(const string& subject)
+    : subject_(subject) {}
+
+  // Resource providers are permitted to perform an action when the
+  // ContainerID in the object is prefixed by the namespace extracted
+  // from the subject's claims.
+  virtual Try<bool> approved(
+      const Option<ObjectApprover::Object>& object) const noexcept override
+  {
+    return object.isSome() &&
+           object->container_id != nullptr &&
+           strings::startsWith(object->container_id->value(), subject_);
+  }
+
+private:
+  const string subject_;
+};
+
+
 // Implementation of the ObjectApprover interface denying all objects.
 class RejectingObjectApprover : public ObjectApprover
 {
@@ -1005,24 +1029,66 @@ public:
         subjectContainerId.get()));
   }
 
+  Future<Owned<ObjectApprover>> getImplicitResourceProviderObjectApprover(
+      const Option<authorization::Subject>& subject,
+      const authorization::Action& action)
+  {
+    CHECK(subject.isSome() &&
+          subject->has_claims() &&
+          !subject->has_value() &&
+          (action == authorization::LAUNCH_STANDALONE_CONTAINER ||
+           action == authorization::WAIT_STANDALONE_CONTAINER ||
+           action == authorization::KILL_STANDALONE_CONTAINER ||
+           action == authorization::REMOVE_STANDALONE_CONTAINER));
+
+    Option<string> subjectPrefix;
+    foreach (const Label& claim, subject->claims().labels()) {
+      if (claim.key() == "cid_prefix" && claim.has_value()) {
+        subjectPrefix = claim.value();
+      }
+    }
+
+    if (subjectPrefix.isNone()) {
+      // If the subject's claims do not include a namespace string,
+      // we deny all objects.
+      return Owned<ObjectApprover>(new RejectingObjectApprover());
+    }
+
+    return Owned<ObjectApprover>(
+        new LocalImplicitResourceProviderObjectApprover(
+            subjectPrefix.get()));
+  }
+
   Future<Owned<ObjectApprover>> getObjectApprover(
       const Option<authorization::Subject>& subject,
       const authorization::Action& action)
   {
-    // We return the `LocalImplicitExecutorObjectApprover` only for subjects and
-    // actions which it knows how to handle. This means the subject should have
-    // claims but no value, and the action should be one of the actions used by
-    // the default executor.
+    // We return implicit object approvers only for subjects and actions
+    // which comes from either the default executor or a local resource
+    // provider. This means the subject should have claims but no value,
+    // and the action should be one of the actions used by them.
     if (subject.isSome() &&
         subject->has_claims() &&
-        !subject->has_value() &&
-        (action == authorization::LAUNCH_NESTED_CONTAINER ||
-         action == authorization::WAIT_NESTED_CONTAINER ||
-         action == authorization::KILL_NESTED_CONTAINER ||
-         action == authorization::LAUNCH_NESTED_CONTAINER_SESSION ||
-         action == authorization::REMOVE_NESTED_CONTAINER ||
-         action == authorization::ATTACH_CONTAINER_OUTPUT)) {
-      return getImplicitExecutorObjectApprover(subject, action);
+        !subject->has_value()) {
+      // The `LocalImplicitExecutorObjectApprover` is used to authorize
+      // requests from the default executor.
+      if (action == authorization::LAUNCH_NESTED_CONTAINER ||
+          action == authorization::WAIT_NESTED_CONTAINER ||
+          action == authorization::KILL_NESTED_CONTAINER ||
+          action == authorization::LAUNCH_NESTED_CONTAINER_SESSION ||
+          action == authorization::REMOVE_NESTED_CONTAINER ||
+          action == authorization::ATTACH_CONTAINER_OUTPUT) {
+        return getImplicitExecutorObjectApprover(subject, action);
+      }
+
+      // The `LocalImplicitResourceProviderObjectApprover` is used to
+      // authorize requests from a local resource provider.
+      if (action == authorization::LAUNCH_STANDALONE_CONTAINER ||
+          action == authorization::WAIT_STANDALONE_CONTAINER ||
+          action == authorization::KILL_STANDALONE_CONTAINER ||
+          action == authorization::REMOVE_STANDALONE_CONTAINER) {
+        return getImplicitResourceProviderObjectApprover(subject, action);
+      }
     }
 
     // Currently, implicit executor authorization is the only case which handles

http://git-wip-us.apache.org/repos/asf/mesos/blob/8b93fa3e/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index 394e910..fd0e809 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -2439,7 +2439,7 @@ Future<Response> Http::_launchContainer(
   // launching a standalone container (possibly nested).
   Executor* executor = slave->getExecutor(containerId);
   if (executor == nullptr) {
-    if (!authorizer->accept()) {
+    if (!authorizer->accept(containerId)) {
       return Forbidden();
     }
   } else {
@@ -2650,7 +2650,7 @@ Future<Response> Http::_waitContainer(
   // waiting on a standalone container (possibly nested).
   Executor* executor = slave->getExecutor(containerId);
   if (executor == nullptr) {
-    if (!authorizer->accept()) {
+    if (!authorizer->accept(containerId)) {
       return Forbidden();
     }
   } else {
@@ -2820,7 +2820,7 @@ Future<Response> Http::_killContainer(
   // killing a standalone container (possibly nested).
   Executor* executor = slave->getExecutor(containerId);
   if (executor == nullptr) {
-    if (!authorizer->accept()) {
+    if (!authorizer->accept(containerId)) {
       return Forbidden();
     }
   } else {
@@ -2917,7 +2917,7 @@ Future<Response> Http::_removeContainer(
   // removing a standalone container (possibly nested).
   Executor* executor = slave->getExecutor(containerId);
   if (executor == nullptr) {
-    if (!authorizer->accept()) {
+    if (!authorizer->accept(containerId)) {
       return Forbidden();
     }
   } else {


[10/13] mesos git commit: Added utility functions for volume attributes and printing CSI messages.

Posted by ji...@apache.org.
Added utility functions for volume attributes and printing CSI messages.

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


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

Branch: refs/heads/master
Commit: eb7f16bff02eae6905729970c0569728c999af53
Parents: 8bca4be
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:31:11 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:31:11 2017 -0800

----------------------------------------------------------------------
 src/common/protobuf_utils.cpp      | 36 +++++++++++++++++++++++++++++++++
 src/common/protobuf_utils.hpp      | 10 +++++++++
 src/csi/utils.cpp                  |  5 +++++
 src/csi/utils.hpp                  | 24 ++++++++++++++++++++++
 src/tests/protobuf_utils_tests.cpp | 35 ++++++++++++++++++++++++++++++++
 5 files changed, 110 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/eb7f16bf/src/common/protobuf_utils.cpp
----------------------------------------------------------------------
diff --git a/src/common/protobuf_utils.cpp b/src/common/protobuf_utils.cpp
index d557342..c5504a0 100644
--- a/src/common/protobuf_utils.cpp
+++ b/src/common/protobuf_utils.cpp
@@ -60,6 +60,7 @@ using std::set;
 using std::string;
 using std::vector;
 
+using google::protobuf::Map;
 using google::protobuf::RepeatedPtrField;
 
 using mesos::slave::ContainerLimitation;
@@ -555,6 +556,40 @@ Label createLabel(const string& key, const Option<string>& value)
 }
 
 
+Labels convertStringMapToLabels(const Map<string, string>& map)
+{
+  Labels labels;
+
+  foreach (const auto& entry, map) {
+    Label* label = labels.mutable_labels()->Add();
+    label->set_key(entry.first);
+    label->set_value(entry.second);
+  }
+
+  return labels;
+}
+
+
+Try<Map<string, string>> convertLabelsToStringMap(const Labels& labels)
+{
+  Map<string, string> map;
+
+  foreach (const Label& label, labels.labels()) {
+    if (map.count(label.key())) {
+      return Error("Repeated key '" + label.key() + "' in labels");
+    }
+
+    if (!label.has_value()) {
+      return Error("Missing value for key '" + label.key() + "' in labels");
+    }
+
+    map[label.key()] = label.value();
+  }
+
+  return map;
+}
+
+
 void injectAllocationInfo(
     Offer::Operation* operation,
     const Resource::AllocationInfo& allocationInfo)
@@ -957,6 +992,7 @@ Try<Resources> getConsumedResources(const Offer::Operation& operation)
   UNREACHABLE();
 }
 
+
 namespace slave {
 
 bool operator==(const Capabilities& left, const Capabilities& right)

http://git-wip-us.apache.org/repos/asf/mesos/blob/eb7f16bf/src/common/protobuf_utils.hpp
----------------------------------------------------------------------
diff --git a/src/common/protobuf_utils.hpp b/src/common/protobuf_utils.hpp
index 31217bf..03b7caf 100644
--- a/src/common/protobuf_utils.hpp
+++ b/src/common/protobuf_utils.hpp
@@ -186,6 +186,16 @@ Label createLabel(
     const Option<std::string>& value = None());
 
 
+// Helper function to convert a protobuf string map to `Labels`.
+Labels convertStringMapToLabels(
+    const google::protobuf::Map<std::string, std::string>& map);
+
+
+// Helper function to convert a `Labels` to a protobuf string map.
+Try<google::protobuf::Map<std::string, std::string>> convertLabelsToStringMap(
+    const Labels& labels);
+
+
 // Previously, `Resource` did not contain `AllocationInfo`.
 // So for backwards compatibility with old schedulers and
 // tooling, we must allow operations to contain `Resource`s

http://git-wip-us.apache.org/repos/asf/mesos/blob/eb7f16bf/src/csi/utils.cpp
----------------------------------------------------------------------
diff --git a/src/csi/utils.cpp b/src/csi/utils.cpp
index ef1bf69..4dd21fb 100644
--- a/src/csi/utils.cpp
+++ b/src/csi/utils.cpp
@@ -16,9 +16,14 @@
 
 #include "csi/utils.hpp"
 
+#include <google/protobuf/util/json_util.h>
+
 #include <stout/strings.hpp>
 
 using std::ostream;
+using std::string;
+
+using google::protobuf::util::MessageToJsonString;
 
 namespace csi {
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/eb7f16bf/src/csi/utils.hpp
----------------------------------------------------------------------
diff --git a/src/csi/utils.hpp b/src/csi/utils.hpp
index 0a8622c..8ee97b2 100644
--- a/src/csi/utils.hpp
+++ b/src/csi/utils.hpp
@@ -18,8 +18,16 @@
 #define __CSI_UTILS_HPP__
 
 #include <ostream>
+#include <type_traits>
+
+#include <google/protobuf/map.h>
+
+#include <google/protobuf/util/json_util.h>
+
+#include <mesos/mesos.hpp>
 
 #include <stout/foreach.hpp>
+#include <stout/try.hpp>
 #include <stout/unreachable.hpp>
 
 #include "csi/spec.hpp"
@@ -31,6 +39,22 @@ bool operator==(const Version& left, const Version& right);
 
 std::ostream& operator<<(std::ostream& stream, const Version& version);
 
+
+// Default imprementation for output protobuf messages in namespace
+// `csi`. Note that any non-template overloading of the output operator
+// would take precedence over this function template.
+template <
+    typename Message,
+    typename std::enable_if<std::is_convertible<
+        Message*, google::protobuf::Message*>::value, int>::type = 0>
+std::ostream& operator<<(std::ostream& stream, const Message& message)
+{
+  // NOTE: We use Google's JSON utility functions for proto3.
+  std::string output;
+  google::protobuf::util::MessageToJsonString(message, &output);
+  return stream << output;
+}
+
 } // namespace csi {
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/eb7f16bf/src/tests/protobuf_utils_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/protobuf_utils_tests.cpp b/src/tests/protobuf_utils_tests.cpp
index 2be26a5..5fd4556 100644
--- a/src/tests/protobuf_utils_tests.cpp
+++ b/src/tests/protobuf_utils_tests.cpp
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include <mesos/mesos.hpp>
+#include <mesos/type_utils.hpp>
 
 #include "common/protobuf_utils.hpp"
 
@@ -30,6 +31,12 @@ using std::set;
 using std::string;
 using std::vector;
 
+using google::protobuf::Map;
+
+using mesos::internal::protobuf::convertLabelsToStringMap;
+using mesos::internal::protobuf::convertStringMapToLabels;
+using mesos::internal::protobuf::createLabel;
+
 namespace mesos {
 namespace internal {
 namespace tests {
@@ -194,6 +201,34 @@ TEST(ProtobufUtilTest, InjectAndStripAllocationInfoInOfferOperation)
 }
 
 
+// This tests that helper function `convertLabelsToStringMap` can
+// correctly convert a `Labels` to a protobuf string map and helper
+// function `convertStringMapToLabels` can convert it back.
+TEST(ProtobufUtilTest, ConvertBetweenLabelsAndStringMap)
+{
+  Labels labels1;
+  labels1.add_labels()->CopyFrom(createLabel("foo", "bar"));
+
+  Try<Map<string, string>> map1 = convertLabelsToStringMap(labels1);
+  ASSERT_SOME(map1);
+  ASSERT_NE(map1->end(), map1->find("foo"));
+  EXPECT_EQ("bar", map1->at("foo"));
+
+  EXPECT_EQ(labels1, convertStringMapToLabels(map1.get()));
+
+  Labels labels2;
+  labels2.add_labels()->CopyFrom(createLabel("foo", "bar"));
+  labels2.add_labels()->CopyFrom(createLabel("foo", "baz"));
+
+  EXPECT_ERROR(convertLabelsToStringMap(labels2));
+
+  Labels labels3;
+  labels3.add_labels()->CopyFrom(createLabel("foo", None()));
+
+  EXPECT_ERROR(convertLabelsToStringMap(labels3));
+}
+
+
 // This tests that Capabilities are correctly constructed
 // from given FrameworkInfo Capabilities.
 TEST(ProtobufUtilTest, FrameworkCapabilities)


[08/13] mesos git commit: Added filesystem layout for CSI plugins.

Posted by ji...@apache.org.
Added filesystem layout for CSI plugins.

A CSI plugin can now store the following persistent CSI
data in `<work_dir>/csi/<type>/<name>/`:

1. Mount points of CSI volumes: `volumes/<volume_id>`
2. States of CSI volumes: `states/<volume_id>/state`
3. Directory to place CSI endpoints: `/tmp/mesos-csi-XXXXXX`, which
   would be symlinked by `containers/<container_id>/endpoint`.

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


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

Branch: refs/heads/master
Commit: bec1cfd2186c7a17b6e7bd9235ffe1389f72afa8
Parents: 236fa59
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:31:02 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:31:02 2017 -0800

----------------------------------------------------------------------
 src/Makefile.am     |   4 +-
 src/csi/paths.cpp   | 255 +++++++++++++++++++++++++++++++++++++++++++++++
 src/csi/paths.hpp   | 134 +++++++++++++++++++++++++
 src/slave/paths.cpp |   7 ++
 src/slave/paths.hpp |   6 ++
 5 files changed, 405 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/bec1cfd2/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 1148453..a82ec7f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1492,9 +1492,11 @@ if ENABLE_GRPC
 # Convenience library for build the CSI client.
 noinst_LTLIBRARIES += libcsi.la
 libcsi_la_SOURCES =							\
-  csi/client.cpp
+  csi/client.cpp							\
+  csi/paths.cpp
 libcsi_la_SOURCES +=							\
   csi/client.hpp							\
+  csi/paths.hpp								\
   csi/spec.hpp
 nodist_libcsi_la_SOURCES = $(CXX_CSI_PROTOS)
 libcsi_la_CPPFLAGS = $(MESOS_CPPFLAGS)

http://git-wip-us.apache.org/repos/asf/mesos/blob/bec1cfd2/src/csi/paths.cpp
----------------------------------------------------------------------
diff --git a/src/csi/paths.cpp b/src/csi/paths.cpp
new file mode 100644
index 0000000..6caefc5
--- /dev/null
+++ b/src/csi/paths.cpp
@@ -0,0 +1,255 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "csi/paths.hpp"
+
+#include <mesos/type_utils.hpp>
+
+#include <process/address.hpp>
+#include <process/http.hpp>
+
+#include <stout/check.hpp>
+#include <stout/fs.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+
+namespace http = process::http;
+namespace unix = process::network::unix;
+
+using std::list;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace csi {
+namespace paths {
+
+// File names.
+const char CONTAINER_INFO_FILE[] = "container.info";
+const char ENDPOINT_SOCKET_FILE[] = "endpoint.sock";
+const char VOLUME_STATE_FILE[] = "volume.state";
+
+
+const char CONTAINERS_DIR[] = "containers";
+const char VOLUMES_DIR[] = "volumes";
+const char MOUNTS_DIR[] = "mounts";
+
+
+const char ENDPOINT_DIR_SYMLINK[] = "endpoint";
+const char ENDPOINT_DIR[] = "mesos-csi-XXXXXX";
+
+
+Try<list<string>> getContainerPaths(
+    const string& rootDir,
+    const string& type,
+    const string& name)
+{
+  return fs::list(path::join(rootDir, type, name, CONTAINERS_DIR, "*"));
+}
+
+
+string getContainerPath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const ContainerID& containerId)
+{
+  return path::join(
+      rootDir,
+      type,
+      name,
+      CONTAINERS_DIR,
+      stringify(containerId));
+}
+
+
+string getContainerInfoPath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const ContainerID& containerId)
+{
+  return path::join(
+      getContainerPath(rootDir, type, name, containerId),
+      CONTAINER_INFO_FILE);
+}
+
+
+string getEndpointDirSymlinkPath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const ContainerID& containerId)
+{
+  return path::join(
+      getContainerPath(rootDir, type, name, containerId),
+      ENDPOINT_DIR_SYMLINK);
+}
+
+
+Try<string> getEndpointSocketPath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const ContainerID& containerId)
+{
+  const string symlinkPath =
+    getEndpointDirSymlinkPath(rootDir, type, name, containerId);
+
+  Try<Nothing> mkdir = os::mkdir(Path(symlinkPath).dirname());
+  if(mkdir.isError()) {
+    return Error(
+        "Failed to create directory '" + Path(symlinkPath).dirname()  + "': " +
+        mkdir.error());
+  }
+
+  Result<string> endpointDir = os::realpath(symlinkPath);
+  if (endpointDir.isSome()) {
+    return path::join(endpointDir.get(), ENDPOINT_SOCKET_FILE);
+  }
+
+  if (os::exists(symlinkPath)) {
+    Try<Nothing> rm = os::rm(symlinkPath);
+    if (rm.isError()) {
+      return Error(
+          "Failed to remove endpoint symlink '" + symlinkPath + "': " +
+          rm.error());
+    }
+  }
+
+  Try<string> mkdtemp = os::mkdtemp(path::join(os::temp(), ENDPOINT_DIR));
+  if (mkdtemp.isError()) {
+    return Error(
+        "Failed to create endpoint directory in '" + os::temp() + "': " +
+        mkdtemp.error());
+  }
+
+  Try<Nothing> symlink = fs::symlink(mkdtemp.get(), symlinkPath);
+  if (symlink.isError()) {
+    return Error(
+        "Failed to symlink directory '" + mkdtemp.get() + "' to '" +
+        symlinkPath + "': " + symlink.error());
+  }
+
+  const string socketPath = path::join(mkdtemp.get(), ENDPOINT_SOCKET_FILE);
+
+  // Check if the socket path is too long.
+  Try<unix::Address> address = unix::Address::create(socketPath);
+  if (address.isError()) {
+    return Error(
+        "Failed to create address from '" + socketPath + "': " +
+        address.error());
+  }
+
+  return socketPath;
+}
+
+
+Try<list<string>> getVolumePaths(
+    const string& rootDir,
+    const string& type,
+    const string& name)
+{
+  return fs::list(path::join(rootDir, type, name, VOLUMES_DIR, "*"));
+}
+
+
+string getVolumePath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const string& volumeId)
+{
+  // Volume ID is percent-encoded to avoid invalid characters in the path.
+  return path::join(rootDir, type, name, VOLUMES_DIR, http::encode(volumeId));
+}
+
+
+Try<VolumePath> parseVolumePath(const string& rootDir, const string& dir)
+{
+  // TODO(chhsiao): Consider using `<regex>`, which requires GCC 4.9+.
+
+  // Make sure there's a separator at the end of the `rootdir` so that
+  // we don't accidentally slice off part of a directory.
+  const string prefix = path::join(rootDir, "");
+
+  if (!strings::startsWith(dir, prefix)) {
+    return Error(
+        "Directory '" + dir + "' does not fall under the root directory '" +
+        rootDir + "'");
+  }
+
+  vector<string> tokens = strings::tokenize(
+      dir.substr(prefix.size()),
+      stringify(os::PATH_SEPARATOR));
+
+  // A complete volume path consists of 4 tokens:
+  //   <type>/<name>/volumes/<volume_id>
+  if (tokens.size() != 4 || tokens[2] != VOLUMES_DIR) {
+    return Error(
+        "Path '" + path::join(tokens) + "' does not match the structure of a "
+        "volume path");
+  }
+
+  // Volume ID is percent-encoded to avoid invalid characters in the path.
+  Try<string> volumeId = http::decode(tokens[3]);
+  if (volumeId.isError()) {
+    return Error(
+        "Could not decode volume ID from string '" + tokens[3] + "': " +
+        volumeId.error());
+  }
+
+  return VolumePath{tokens[0], tokens[1], volumeId.get()};
+}
+
+
+string getVolumeStatePath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const string& volumeId)
+{
+  return path::join(
+      getVolumePath(rootDir, type, name, volumeId),
+      VOLUME_STATE_FILE);
+}
+
+
+string getMountRootDir(
+    const string& rootDir,
+    const string& type,
+    const string& name)
+{
+  return path::join(rootDir, type, name, MOUNTS_DIR);
+}
+
+
+string getMountPath(
+    const string& rootDir,
+    const string& type,
+    const string& name,
+    const string& volumeId)
+{
+  return path::join(
+      getMountRootDir(rootDir, type, name),
+      http::encode(volumeId));
+}
+
+} // namespace paths {
+} // namespace csi {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/bec1cfd2/src/csi/paths.hpp
----------------------------------------------------------------------
diff --git a/src/csi/paths.hpp b/src/csi/paths.hpp
new file mode 100644
index 0000000..37f65f8
--- /dev/null
+++ b/src/csi/paths.hpp
@@ -0,0 +1,134 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __CSI_PATHS_HPP__
+#define __CSI_PATHS_HPP__
+
+#include <list>
+#include <string>
+
+#include <mesos/mesos.hpp>
+
+#include <stout/try.hpp>
+
+namespace mesos {
+namespace csi {
+namespace paths {
+
+// The file system layout is as follows:
+//
+//   root (<work_dir>/csi/)
+//   |-- <type>
+//       |-- <name>
+//           |-- containers
+//           |   |-- <container_id>
+//           |       |-- container.info
+//           |       |-- endpoint (symlink to /tmp/mesos-csi-XXXXXX)
+//           |           |-- endpoint.sock
+//           |-- volumes
+//           |   |-- <volume_id>
+//           |        |-- volume.state
+//           |-- mounts
+//               |-- <volume_id> (mount point)
+
+
+struct VolumePath
+{
+  std::string type;
+  std::string name;
+  std::string volumeId;
+};
+
+
+Try<std::list<std::string>> getContainerPaths(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name);
+
+
+std::string getContainerPath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const ContainerID& containerId);
+
+
+std::string getContainerInfoPath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const ContainerID& containerId);
+
+
+std::string getEndpointDirSymlinkPath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const ContainerID& containerId);
+
+
+// Returns the resolved path to the endpoint socket, even if the socket
+// file itself does not exist. Creates and symlinks the endpoint
+// directory if necessary.
+Try<std::string> getEndpointSocketPath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const ContainerID& containerId);
+
+
+Try<std::list<std::string>> getVolumePaths(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name);
+
+
+std::string getVolumePath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const std::string& volumeId);
+
+
+Try<VolumePath> parseVolumePath(
+    const std::string& rootDir,
+    const std::string& dir);
+
+
+std::string getVolumeStatePath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const std::string& volumeId);
+
+
+std::string getMountRootDir(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name);
+
+
+std::string getMountPath(
+    const std::string& rootDir,
+    const std::string& type,
+    const std::string& name,
+    const std::string& volumeId);
+
+} // namespace paths {
+} // namespace csi {
+} // namespace mesos {
+
+#endif // __CSI_PATHS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/bec1cfd2/src/slave/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.cpp b/src/slave/paths.cpp
index f85e46c..fce18c5 100644
--- a/src/slave/paths.cpp
+++ b/src/slave/paths.cpp
@@ -68,6 +68,7 @@ const char RESOURCE_PROVIDER_STATE_FILE[] = "resource_provider.state";
 
 
 const char CONTAINERS_DIR[] = "containers";
+const char CSI_DIR[] = "csi";
 const char SLAVES_DIR[] = "slaves";
 const char FRAMEWORKS_DIR[] = "frameworks";
 const char EXECUTORS_DIR[] = "executors";
@@ -140,6 +141,12 @@ string getProvisionerDir(const string& rootDir)
 }
 
 
+string getCsiRootDir(const string& workDir)
+{
+  return path::join(workDir, CSI_DIR);
+}
+
+
 string getBootIdPath(const string& rootDir)
 {
   return path::join(rootDir, BOOT_ID_FILE);

http://git-wip-us.apache.org/repos/asf/mesos/blob/bec1cfd2/src/slave/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.hpp b/src/slave/paths.hpp
index 7944b7d..c73130fd 100644
--- a/src/slave/paths.hpp
+++ b/src/slave/paths.hpp
@@ -47,6 +47,8 @@ namespace paths {
 //
 //   (5) For provisioning root filesystems for containers.
 //
+//   (6) For CSI plugins to preserve data that persist across slaves.
+//
 // The file system layout is as follows:
 //
 //   root ('--work_dir' flag)
@@ -100,6 +102,7 @@ namespace paths {
 //   |       |-- <role>
 //   |           |-- <persistence_id> (persistent volume)
 //   |-- provisioner
+//   |-- csi
 
 
 struct ExecutorRunPath
@@ -138,6 +141,9 @@ std::string getSandboxRootDir(const std::string& rootDir);
 std::string getProvisionerDir(const std::string& rootDir);
 
 
+std::string getCsiRootDir(const std::string& workDir);
+
+
 std::string getLatestSlavePath(const std::string& rootDir);
 
 


[13/13] mesos git commit: Added a test in ContainerDaemon for testing failed authorization.

Posted by ji...@apache.org.
Added a test in ContainerDaemon for testing failed authorization.

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


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

Branch: refs/heads/master
Commit: f9b076ce0eded4b9f0676212dc90687181abffb0
Parents: e861c0e
Author: Jie Yu <yu...@gmail.com>
Authored: Wed Nov 29 14:02:38 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 16:03:29 2017 -0800

----------------------------------------------------------------------
 src/tests/container_daemon_tests.cpp | 84 +++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f9b076ce/src/tests/container_daemon_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/container_daemon_tests.cpp b/src/tests/container_daemon_tests.cpp
index 3d88390..9b1cd16 100644
--- a/src/tests/container_daemon_tests.cpp
+++ b/src/tests/container_daemon_tests.cpp
@@ -179,6 +179,90 @@ TEST_F(ContainerDaemonTest, RestartOnTermination)
   EXPECT_TRUE(wait.isPending());
 }
 
+
+#ifdef USE_SSL_SOCKET
+// This test verifies that the container daemon will terminate itself
+// if the agent operator API does not authorize the container launch.
+TEST_F(ContainerDaemonTest, FailedAuthorization)
+{
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+
+  ASSERT_SOME(slaveFlags.jwt_secret_key);
+
+  Try<string> jwtSecretKey = os::read(slaveFlags.jwt_secret_key.get());
+  ASSERT_SOME(jwtSecretKey);
+
+  Owned<SecretGenerator> secretGenerator(
+      new JWTSecretGenerator(jwtSecretKey.get()));
+
+  Future<Nothing> recover = FUTURE_DISPATCH(_, &Slave::__recover);
+
+  Try<Owned<cluster::Slave>> slave = StartSlave(
+      detector.get(),
+      secretGenerator.get(),
+      slaveFlags);
+
+  ASSERT_SOME(slave);
+
+  PID<Slave> slavePid = slave.get()->pid;
+
+  // Ensure slave has finished recovery.
+  AWAIT_READY(recover);
+
+  string scheme = "http";
+  if (openssl::flags().enabled) {
+    scheme = "https";
+  }
+
+  http::URL url(
+      scheme,
+      slavePid.address.ip,
+      slavePid.address.port,
+      strings::join("/", slavePid.id, "api/v1"));
+
+  // NOTE: The current implicit authorization for creating standalone
+  // containers is to check if the container ID prefix in the claims
+  // of the principal is indeed a prefix of the container ID that is
+  // specified in the API call.
+  //
+  // Using two random UUIDs here guarantees that one is not a prefix
+  // of another. Therefore, the authorization will fail.
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  Principal principal(
+      None(),
+      {{"cid_prefix", UUID::random().toString()}});
+
+  Future<Secret> secret = secretGenerator->generate(principal);
+  AWAIT_READY(secret);
+
+  ASSERT_NONE(validateSecret(secret.get()));
+  ASSERT_EQ(Secret::VALUE, secret->type());
+
+  string authToken = secret->value().data();
+
+  Try<Owned<ContainerDaemon>> daemon = ContainerDaemon::create(
+      url,
+      authToken,
+      containerId,
+      createCommandInfo("sleep 1000"),
+      None(),
+      None(),
+      []() -> Future<Nothing> { return Nothing(); },
+      []() -> Future<Nothing> { return Nothing(); });
+
+  ASSERT_SOME(daemon);
+
+  AWAIT_FAILED(daemon.get()->wait());
+}
+#endif // USE_SSL_SOCKET
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {


[07/13] mesos git commit: Added filesystem layout for local resource providers.

Posted by ji...@apache.org.
Added filesystem layout for local resource providers.

A local resource provide can store checkpoints, whose lifecycles should
be tied to the agent, under
`<work_dir>/meta/slaves/<slave_id>/resource_providers/<type>/<name>/
<id>`.

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


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

Branch: refs/heads/master
Commit: 236fa59c37709db6235a237f7b665ccecbf765eb
Parents: c0a4988
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:58 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:58 2017 -0800

----------------------------------------------------------------------
 src/resource_provider/daemon.cpp           |   3 +-
 src/resource_provider/local.cpp            |   4 +-
 src/resource_provider/local.hpp            |   6 +-
 src/resource_provider/storage/provider.cpp |  21 +++--
 src/resource_provider/storage/provider.hpp |   4 +
 src/slave/paths.cpp                        | 106 ++++++++++++++++++++++++
 src/slave/paths.hpp                        |  53 +++++++++++-
 7 files changed, 187 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/resource_provider/daemon.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/daemon.cpp b/src/resource_provider/daemon.cpp
index 339216b..354054e 100644
--- a/src/resource_provider/daemon.cpp
+++ b/src/resource_provider/daemon.cpp
@@ -220,7 +220,8 @@ Future<Nothing> LocalResourceProviderDaemonProcess::launch(
       ProviderData& data = providers[type].at(name);
 
       Try<Owned<LocalResourceProvider>> provider =
-        LocalResourceProvider::create(url, data.info, authToken);
+        LocalResourceProvider::create(
+            url, workDir, data.info, slaveId.get(), authToken);
 
       if (provider.isError()) {
         return Failure(

http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/resource_provider/local.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/local.cpp b/src/resource_provider/local.cpp
index 42b6b62..d1d6835 100644
--- a/src/resource_provider/local.cpp
+++ b/src/resource_provider/local.cpp
@@ -34,7 +34,9 @@ namespace internal {
 
 Try<Owned<LocalResourceProvider>> LocalResourceProvider::create(
     const http::URL& url,
+    const string& workDir,
     const ResourceProviderInfo& info,
+    const SlaveID& slaveId,
     const Option<string>& authToken)
 {
   // TODO(jieyu): Document the built-in local resource providers.
@@ -45,7 +47,7 @@ Try<Owned<LocalResourceProvider>> LocalResourceProvider::create(
   };
 
   if (creators.contains(info.type())) {
-    return creators.at(info.type())(url, info, authToken);
+    return creators.at(info.type())(url, workDir, info, slaveId, authToken);
   }
 
   return Error("Unknown local resource provider type '" + info.type() + "'");

http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/resource_provider/local.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/local.hpp b/src/resource_provider/local.hpp
index d1dfc90..46111d9 100644
--- a/src/resource_provider/local.hpp
+++ b/src/resource_provider/local.hpp
@@ -17,14 +17,14 @@
 #ifndef __RESOURCE_PROVIDER_LOCAL_HPP__
 #define __RESOURCE_PROVIDER_LOCAL_HPP__
 
+#include <mesos/mesos.hpp>
+
 #include <process/authenticator.hpp>
 #include <process/http.hpp>
 #include <process/owned.hpp>
 
 #include <stout/try.hpp>
 
-#include <mesos/mesos.hpp>
-
 namespace mesos {
 namespace internal {
 
@@ -33,7 +33,9 @@ class LocalResourceProvider
 public:
   static Try<process::Owned<LocalResourceProvider>> create(
       const process::http::URL& url,
+      const std::string& workDir,
       const ResourceProviderInfo& info,
+      const SlaveID& slaveId,
       const Option<std::string>& authToken);
 
   static Try<process::http::authentication::Principal> principal(

http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/resource_provider/storage/provider.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/storage/provider.cpp b/src/resource_provider/storage/provider.cpp
index 14d824c..f586afc 100644
--- a/src/resource_provider/storage/provider.cpp
+++ b/src/resource_provider/storage/provider.cpp
@@ -95,12 +95,16 @@ class StorageLocalResourceProviderProcess
 public:
   explicit StorageLocalResourceProviderProcess(
       const http::URL& _url,
+      const string& _workDir,
       const ResourceProviderInfo& _info,
+      const SlaveID& _slaveId,
       const Option<string>& _authToken)
     : ProcessBase(process::ID::generate("storage-local-resource-provider")),
       url(_url),
+      workDir(_workDir),
       contentType(ContentType::PROTOBUF),
       info(_info),
+      slaveId(_slaveId),
       authToken(_authToken) {}
 
   StorageLocalResourceProviderProcess(
@@ -116,9 +120,11 @@ public:
 private:
   void initialize() override;
 
-  const process::http::URL url;
+  const http::URL url;
+  const string workDir;
   const ContentType contentType;
   ResourceProviderInfo info;
+  const SlaveID slaveId;
   Owned<v1::resource_provider::Driver> driver;
   Option<string> authToken;
 };
@@ -178,8 +184,10 @@ void StorageLocalResourceProviderProcess::initialize()
 
 
 Try<Owned<LocalResourceProvider>> StorageLocalResourceProvider::create(
-    const process::http::URL& url,
+    const http::URL& url,
+    const string& workDir,
     const ResourceProviderInfo& info,
+    const SlaveID& slaveId,
     const Option<string>& authToken)
 {
   // Verify that the name follows Java package naming convention.
@@ -192,7 +200,7 @@ Try<Owned<LocalResourceProvider>> StorageLocalResourceProvider::create(
   }
 
   return Owned<LocalResourceProvider>(
-      new StorageLocalResourceProvider(url, info, authToken));
+      new StorageLocalResourceProvider(url, workDir, info, slaveId, authToken));
 }
 
 
@@ -206,10 +214,13 @@ Try<Principal> StorageLocalResourceProvider::principal(
 
 
 StorageLocalResourceProvider::StorageLocalResourceProvider(
-    const process::http::URL& url,
+    const http::URL& url,
+    const string& workDir,
     const ResourceProviderInfo& info,
+    const SlaveID& slaveId,
     const Option<string>& authToken)
-  : process(new StorageLocalResourceProviderProcess(url, info, authToken))
+  : process(new StorageLocalResourceProviderProcess(
+        url, workDir, info, slaveId, authToken))
 {
   spawn(CHECK_NOTNULL(process.get()));
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/resource_provider/storage/provider.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/storage/provider.hpp b/src/resource_provider/storage/provider.hpp
index 720b061..374f837 100644
--- a/src/resource_provider/storage/provider.hpp
+++ b/src/resource_provider/storage/provider.hpp
@@ -38,7 +38,9 @@ class StorageLocalResourceProvider : public LocalResourceProvider
 public:
   static Try<process::Owned<LocalResourceProvider>> create(
       const process::http::URL& url,
+      const std::string& workDir,
       const mesos::ResourceProviderInfo& info,
+      const SlaveID& slaveId,
       const Option<std::string>& authToken);
 
   static Try<process::http::authentication::Principal> principal(
@@ -55,7 +57,9 @@ public:
 private:
   explicit StorageLocalResourceProvider(
       const process::http::URL& url,
+      const std::string& workDir,
       const mesos::ResourceProviderInfo& info,
+      const SlaveID& slaveId,
       const Option<std::string>& authToken);
 
   process::Owned<StorageLocalResourceProviderProcess> process;

http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/slave/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.cpp b/src/slave/paths.cpp
index b03ffee..f85e46c 100644
--- a/src/slave/paths.cpp
+++ b/src/slave/paths.cpp
@@ -64,6 +64,7 @@ const char TASK_INFO_FILE[] = "task.info";
 const char TASK_UPDATES_FILE[] = "task.updates";
 const char RESOURCES_INFO_FILE[] = "resources.info";
 const char RESOURCES_TARGET_FILE[] = "resources.target";
+const char RESOURCE_PROVIDER_STATE_FILE[] = "resource_provider.state";
 
 
 const char CONTAINERS_DIR[] = "containers";
@@ -72,6 +73,7 @@ const char FRAMEWORKS_DIR[] = "frameworks";
 const char EXECUTORS_DIR[] = "executors";
 const char EXECUTOR_RUNS_DIR[] = "runs";
 const char RESOURCE_PROVIDER_REGISTRY[] = "resource_provider_registry";
+const char RESOURCE_PROVIDERS_DIR[] = "resource_providers";
 
 
 Try<ExecutorRunPath> parseExecutorRunPath(
@@ -474,6 +476,68 @@ string getResourceProviderRegistryPath(
 }
 
 
+Try<list<string>> getResourceProviderPaths(
+    const string& metaDir,
+    const SlaveID& slaveId)
+{
+  return fs::list(path::join(
+      getSlavePath(metaDir, slaveId),
+      RESOURCE_PROVIDERS_DIR,
+      "*", // Resource provider type.
+      "*", // Resource provider name.
+      "*"));
+}
+
+
+string getResourceProviderPath(
+    const string& metaDir,
+    const SlaveID& slaveId,
+    const string& resourceProviderType,
+    const string& resourceProviderName,
+    const ResourceProviderID& resourceProviderId)
+{
+  return path::join(
+      getSlavePath(metaDir, slaveId),
+      RESOURCE_PROVIDERS_DIR,
+      resourceProviderType,
+      resourceProviderName,
+      stringify(resourceProviderId));
+}
+
+
+string getResourceProviderStatePath(
+    const string& metaDir,
+    const SlaveID& slaveId,
+    const string& resourceProviderType,
+    const string& resourceProviderName,
+    const ResourceProviderID& resourceProviderId)
+{
+  return path::join(
+      getResourceProviderPath(
+          metaDir,
+          slaveId,
+          resourceProviderType,
+          resourceProviderName,
+          resourceProviderId),
+      RESOURCE_PROVIDER_STATE_FILE);
+}
+
+
+string getLatestResourceProviderPath(
+    const string& metaDir,
+    const SlaveID& slaveId,
+    const string& resourceProviderType,
+    const string& resourceProviderName)
+{
+  return path::join(
+      getSlavePath(metaDir, slaveId),
+      RESOURCE_PROVIDERS_DIR,
+      resourceProviderType,
+      resourceProviderName,
+      LATEST_SYMLINK);
+}
+
+
 string getResourcesInfoPath(
     const string& rootDir)
 {
@@ -672,6 +736,48 @@ string createSlaveDirectory(
   return directory;
 }
 
+
+string createResourceProviderDirectory(
+    const string& rootDir,
+    const SlaveID& slaveId,
+    const string& resourceProviderType,
+    const string& resourceProviderName,
+    const ResourceProviderID& resourceProviderId)
+{
+  const string directory = getResourceProviderPath(
+      rootDir,
+      slaveId,
+      resourceProviderType,
+      resourceProviderName,
+      resourceProviderId);
+
+  Try<Nothing> mkdir = os::mkdir(directory);
+
+  CHECK_SOME(mkdir)
+    << "Failed to create resource provider directory '" << directory << "'";
+
+  // Remove the previous "latest" symlink.
+  const string latest = getLatestResourceProviderPath(
+      rootDir,
+      slaveId,
+      resourceProviderType,
+      resourceProviderName);
+
+  if (os::exists(latest)) {
+    CHECK_SOME(os::rm(latest))
+      << "Failed to remove latest symlink '" << latest << "'";
+  }
+
+  // Symlink the new resource provider directory to "latest".
+  Try<Nothing> symlink = ::fs::symlink(directory, latest);
+
+  CHECK_SOME(symlink)
+    << "Failed to symlink directory '" << directory
+    << "' to '" << latest << "'";
+
+  return directory;
+}
+
 } // namespace paths {
 } // namespace slave {
 } // namespace internal {

http://git-wip-us.apache.org/repos/asf/mesos/blob/236fa59c/src/slave/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.hpp b/src/slave/paths.hpp
index 66dfa45..7944b7d 100644
--- a/src/slave/paths.hpp
+++ b/src/slave/paths.hpp
@@ -71,6 +71,12 @@ namespace paths {
 //   |       |-- latest (symlink)
 //   |       |-- <slave_id>
 //   |           |-- slave.info
+//   |           |-- resource_providers
+//   |           |   |-- <type>
+//   |           |       |-- <name>
+//   |           |           |-- latest (symlink)
+//   |           |           |-- <resource_provider_id>
+//   |           |               |-- resource_provider.state
 //   |           |-- frameworks
 //   |               |-- <framework_id>
 //   |                   |-- framework.info
@@ -112,7 +118,16 @@ Try<ExecutorRunPath> parseExecutorRunPath(
 
 const char LATEST_SYMLINK[] = "latest";
 
-// Helpers for obtaining paths in the layout:
+// Helpers for obtaining paths in the layout.
+// NOTE: The parameter names should adhere to the following convention:
+//
+//   (1) Use `workDir` if the helper expects the `--work_dir` flag.
+//
+//   (2) Use `metaDir` if the helper expects the meta directory.
+//
+//   (3) Use `rootDir` only if the helper is to be reused.
+//
+// TODO(chhsiao): Clean up the parameter names to follow the convention.
 
 std::string getMetaRootDir(const std::string& rootDir);
 
@@ -300,6 +315,34 @@ std::string getResourceProviderRegistryPath(
     const SlaveID& slaveId);
 
 
+Try<std::list<std::string>> getResourceProviderPaths(
+    const std::string& metaDir,
+    const SlaveID& slaveId);
+
+
+std::string getResourceProviderPath(
+    const std::string& metaDir,
+    const SlaveID& slaveId,
+    const std::string& resourceProviderType,
+    const std::string& resourceProviderName,
+    const ResourceProviderID& resourceProviderId);
+
+
+std::string getResourceProviderStatePath(
+    const std::string& metaDir,
+    const SlaveID& slaveId,
+    const std::string& resourceProviderType,
+    const std::string& resourceProviderName,
+    const ResourceProviderID& resourceProviderId);
+
+
+std::string getLatestResourceProviderPath(
+    const std::string& metaDir,
+    const SlaveID& slaveId,
+    const std::string& resourceProviderType,
+    const std::string& resourceProviderName);
+
+
 std::string getResourcesInfoPath(
     const std::string& rootDir);
 
@@ -333,6 +376,14 @@ std::string createSlaveDirectory(
     const SlaveID& slaveId);
 
 
+std::string createResourceProviderDirectory(
+    const std::string& rootDir,
+    const SlaveID& slaveId,
+    const std::string& resourceProviderType,
+    const std::string& resourceProviderName,
+    const ResourceProviderID& resourceProviderId);
+
+
 extern const char LIBPROCESS_PID_FILE[];
 extern const char HTTP_MARKER_FILE[];
 


[03/13] mesos git commit: Generated authentication tokens for local resource providers.

Posted by ji...@apache.org.
Generated authentication tokens for local resource providers.

`LocalResourceProviderDaemon` now uses `Slave::secretGenerater` to
generate an authentication token for each local resource provider. The
authentication token can then be used to call the V1 agent API. In order
to generate the tokens, `LocalResourceProviderDaemon::load()` is now an
asynchronous function.

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


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

Branch: refs/heads/master
Commit: 9bee2c300e2443e875a26858453563cedf438234
Parents: 957b0c0
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:33 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:33 2017 -0800

----------------------------------------------------------------------
 src/resource_provider/daemon.cpp           | 91 +++++++++++++++++++++----
 src/resource_provider/daemon.hpp           |  8 ++-
 src/resource_provider/local.cpp            |  5 +-
 src/resource_provider/local.hpp            |  3 +-
 src/resource_provider/storage/provider.cpp | 21 +++---
 src/resource_provider/storage/provider.hpp |  6 +-
 src/slave/slave.cpp                        | 15 ++--
 7 files changed, 115 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/resource_provider/daemon.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/daemon.cpp b/src/resource_provider/daemon.cpp
index 80b5c71..339216b 100644
--- a/src/resource_provider/daemon.cpp
+++ b/src/resource_provider/daemon.cpp
@@ -22,6 +22,7 @@
 
 #include <mesos/type_utils.hpp>
 
+#include <process/defer.hpp>
 #include <process/dispatch.hpp>
 #include <process/id.hpp>
 #include <process/process.hpp>
@@ -36,6 +37,8 @@
 #include <stout/protobuf.hpp>
 #include <stout/try.hpp>
 
+#include "common/validation.hpp"
+
 #include "resource_provider/local.hpp"
 
 namespace http = process::http;
@@ -49,11 +52,14 @@ using process::Owned;
 using process::Process;
 using process::ProcessBase;
 
+using process::defer;
 using process::dispatch;
 using process::spawn;
 using process::terminate;
 using process::wait;
 
+using process::http::authentication::Principal;
+
 namespace mesos {
 namespace internal {
 
@@ -64,11 +70,13 @@ public:
   LocalResourceProviderDaemonProcess(
       const http::URL& _url,
       const string& _workDir,
-      const Option<string>& _configDir)
+      const Option<string>& _configDir,
+      SecretGenerator* _secretGenerator)
     : ProcessBase(process::ID::generate("local-resource-provider-daemon")),
       url(_url),
       workDir(_workDir),
-      configDir(_configDir) {}
+      configDir(_configDir),
+      secretGenerator(_secretGenerator) {}
 
   LocalResourceProviderDaemonProcess(
       const LocalResourceProviderDaemonProcess& other) = delete;
@@ -95,9 +103,12 @@ private:
 
   Future<Nothing> launch(const string& type, const string& name);
 
+  Future<Option<string>> generateAuthToken(const ResourceProviderInfo& info);
+
   const http::URL url;
   const string workDir;
   const Option<string> configDir;
+  SecretGenerator* const secretGenerator;
 
   Option<SlaveID> slaveId;
   hashmap<string, hashmap<string, ProviderData>> providers;
@@ -203,26 +214,69 @@ Future<Nothing> LocalResourceProviderDaemonProcess::launch(
   CHECK_SOME(slaveId);
   CHECK(providers[type].contains(name));
 
-  ProviderData& data = providers[type].at(name);
+  return generateAuthToken(providers[type].at(name).info)
+    .then(defer(self(), [=](
+        const Option<string>& authToken) -> Future<Nothing> {
+      ProviderData& data = providers[type].at(name);
+
+      Try<Owned<LocalResourceProvider>> provider =
+        LocalResourceProvider::create(url, data.info, authToken);
+
+      if (provider.isError()) {
+        return Failure(
+            "Failed to create resource provider with type '" + type +
+            "' and name '" + name + "': " + provider.error());
+      }
+
+      data.provider = provider.get();
+
+      return Nothing();
+    }));
+}
 
-  Try<Owned<LocalResourceProvider>> provider =
-    LocalResourceProvider::create(url, data.info);
 
-  if (provider.isError()) {
+// Generates a secret for local resource provider authentication if needed.
+Future<Option<string>> LocalResourceProviderDaemonProcess::generateAuthToken(
+    const ResourceProviderInfo& info)
+{
+  if (secretGenerator == nullptr) {
+    return None();
+  }
+
+  Try<Principal> principal = LocalResourceProvider::principal(info);
+
+  if (principal.isError()) {
     return Failure(
-        "Failed to create resource provider with type '" + type +
-        "' and name '" + name + "': " + provider.error());
+        "Failed to generate resource provider principal with type '" +
+        info.type() + "' and name '" + info.name() + "': " +
+        principal.error());
   }
 
-  data.provider = provider.get();
+  return secretGenerator->generate(principal.get())
+    .then(defer(self(), [](const Secret& secret) -> Future<Option<string>> {
+      Option<Error> error = common::validation::validateSecret(secret);
 
-  return Nothing();
+      if (error.isSome()) {
+        return Failure(
+            "Failed to validate generated secret: " + error->message);
+      } else if (secret.type() != Secret::VALUE) {
+        return Failure(
+            "Expecting generated secret to be of VALUE type instead of " +
+            stringify(secret.type()) + " type; " +
+            "only VALUE type secrets are supported at this time");
+      }
+
+      CHECK(secret.has_value());
+
+      return secret.value().data();
+    }));
 }
 
 
 Try<Owned<LocalResourceProviderDaemon>> LocalResourceProviderDaemon::create(
     const http::URL& url,
-    const slave::Flags& flags)
+    const slave::Flags& flags,
+    SecretGenerator* secretGenerator)
 {
   // We require that the config directory exists to create a daemon.
   Option<string> configDir = flags.resource_provider_config_dir;
@@ -230,15 +284,24 @@ Try<Owned<LocalResourceProviderDaemon>> LocalResourceProviderDaemon::create(
     return Error("Config directory '" + configDir.get() + "' does not exist");
   }
 
-  return new LocalResourceProviderDaemon(url, flags.work_dir, configDir);
+  return new LocalResourceProviderDaemon(
+      url,
+      flags.work_dir,
+      configDir,
+      secretGenerator);
 }
 
 
 LocalResourceProviderDaemon::LocalResourceProviderDaemon(
     const http::URL& url,
     const string& workDir,
-    const Option<string>& configDir)
-  : process(new LocalResourceProviderDaemonProcess(url, workDir, configDir))
+    const Option<string>& configDir,
+    SecretGenerator* secretGenerator)
+  : process(new LocalResourceProviderDaemonProcess(
+        url,
+        workDir,
+        configDir,
+        secretGenerator))
 {
   spawn(CHECK_NOTNULL(process.get()));
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/resource_provider/daemon.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/daemon.hpp b/src/resource_provider/daemon.hpp
index 8668cd1..14bc41f 100644
--- a/src/resource_provider/daemon.hpp
+++ b/src/resource_provider/daemon.hpp
@@ -21,6 +21,8 @@
 
 #include <mesos/mesos.hpp>
 
+#include <mesos/authentication/secret_generator.hpp>
+
 #include <process/http.hpp>
 #include <process/owned.hpp>
 
@@ -44,7 +46,8 @@ class LocalResourceProviderDaemon
 public:
   static Try<process::Owned<LocalResourceProviderDaemon>> create(
       const process::http::URL& url,
-      const slave::Flags& flags);
+      const slave::Flags& flags,
+      SecretGenerator* secretGenerator);
 
   ~LocalResourceProviderDaemon();
 
@@ -60,7 +63,8 @@ private:
   LocalResourceProviderDaemon(
       const process::http::URL& url,
       const std::string& workDir,
-      const Option<std::string>& configDir);
+      const Option<std::string>& configDir,
+      SecretGenerator* secretGenerator);
 
   process::Owned<LocalResourceProviderDaemonProcess> process;
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/resource_provider/local.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/local.cpp b/src/resource_provider/local.cpp
index ff9819e..42b6b62 100644
--- a/src/resource_provider/local.cpp
+++ b/src/resource_provider/local.cpp
@@ -34,7 +34,8 @@ namespace internal {
 
 Try<Owned<LocalResourceProvider>> LocalResourceProvider::create(
     const http::URL& url,
-    const ResourceProviderInfo& info)
+    const ResourceProviderInfo& info,
+    const Option<string>& authToken)
 {
   // TODO(jieyu): Document the built-in local resource providers.
   const hashmap<string, lambda::function<decltype(create)>> creators = {
@@ -44,7 +45,7 @@ Try<Owned<LocalResourceProvider>> LocalResourceProvider::create(
   };
 
   if (creators.contains(info.type())) {
-    return creators.at(info.type())(url, info);
+    return creators.at(info.type())(url, info, authToken);
   }
 
   return Error("Unknown local resource provider type '" + info.type() + "'");

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/resource_provider/local.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/local.hpp b/src/resource_provider/local.hpp
index 40c8f98..d1dfc90 100644
--- a/src/resource_provider/local.hpp
+++ b/src/resource_provider/local.hpp
@@ -33,7 +33,8 @@ class LocalResourceProvider
 public:
   static Try<process::Owned<LocalResourceProvider>> create(
       const process::http::URL& url,
-      const ResourceProviderInfo& info);
+      const ResourceProviderInfo& info,
+      const Option<std::string>& authToken);
 
   static Try<process::http::authentication::Principal> principal(
       const ResourceProviderInfo& info);

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/resource_provider/storage/provider.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/storage/provider.cpp b/src/resource_provider/storage/provider.cpp
index 6817df5..14d824c 100644
--- a/src/resource_provider/storage/provider.cpp
+++ b/src/resource_provider/storage/provider.cpp
@@ -94,12 +94,14 @@ class StorageLocalResourceProviderProcess
 {
 public:
   explicit StorageLocalResourceProviderProcess(
-      const process::http::URL& _url,
-      const ResourceProviderInfo& _info)
+      const http::URL& _url,
+      const ResourceProviderInfo& _info,
+      const Option<string>& _authToken)
     : ProcessBase(process::ID::generate("storage-local-resource-provider")),
       url(_url),
       contentType(ContentType::PROTOBUF),
-      info(_info) {}
+      info(_info),
+      authToken(_authToken) {}
 
   StorageLocalResourceProviderProcess(
       const StorageLocalResourceProviderProcess& other) = delete;
@@ -118,6 +120,7 @@ private:
   const ContentType contentType;
   ResourceProviderInfo info;
   Owned<v1::resource_provider::Driver> driver;
+  Option<string> authToken;
 };
 
 
@@ -165,7 +168,7 @@ void StorageLocalResourceProviderProcess::initialize()
       defer(self(), &Self::disconnected),
       defer(self(), [this](queue<v1::resource_provider::Event> events) {
         while(!events.empty()) {
-        const v1::resource_provider::Event& event = events.front();
+          const v1::resource_provider::Event& event = events.front();
           received(devolve(event));
           events.pop();
         }
@@ -176,7 +179,8 @@ void StorageLocalResourceProviderProcess::initialize()
 
 Try<Owned<LocalResourceProvider>> StorageLocalResourceProvider::create(
     const process::http::URL& url,
-    const ResourceProviderInfo& info)
+    const ResourceProviderInfo& info,
+    const Option<string>& authToken)
 {
   // Verify that the name follows Java package naming convention.
   // TODO(chhsiao): We should move this check to a validation function
@@ -188,7 +192,7 @@ Try<Owned<LocalResourceProvider>> StorageLocalResourceProvider::create(
   }
 
   return Owned<LocalResourceProvider>(
-      new StorageLocalResourceProvider(url, info));
+      new StorageLocalResourceProvider(url, info, authToken));
 }
 
 
@@ -203,8 +207,9 @@ Try<Principal> StorageLocalResourceProvider::principal(
 
 StorageLocalResourceProvider::StorageLocalResourceProvider(
     const process::http::URL& url,
-    const ResourceProviderInfo& info)
-  : process(new StorageLocalResourceProviderProcess(url, info))
+    const ResourceProviderInfo& info,
+    const Option<string>& authToken)
+  : process(new StorageLocalResourceProviderProcess(url, info, authToken))
 {
   spawn(CHECK_NOTNULL(process.get()));
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/resource_provider/storage/provider.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/storage/provider.hpp b/src/resource_provider/storage/provider.hpp
index b61b41b..720b061 100644
--- a/src/resource_provider/storage/provider.hpp
+++ b/src/resource_provider/storage/provider.hpp
@@ -38,7 +38,8 @@ class StorageLocalResourceProvider : public LocalResourceProvider
 public:
   static Try<process::Owned<LocalResourceProvider>> create(
       const process::http::URL& url,
-      const mesos::ResourceProviderInfo& info);
+      const mesos::ResourceProviderInfo& info,
+      const Option<std::string>& authToken);
 
   static Try<process::http::authentication::Principal> principal(
       const mesos::ResourceProviderInfo& info);
@@ -54,7 +55,8 @@ public:
 private:
   explicit StorageLocalResourceProvider(
       const process::http::URL& url,
-      const mesos::ResourceProviderInfo& info);
+      const mesos::ResourceProviderInfo& info,
+      const Option<std::string>& authToken);
 
   process::Owned<StorageLocalResourceProviderProcess> process;
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bee2c30/src/slave/slave.cpp
----------------------------------------------------------------------
diff --git a/src/slave/slave.cpp b/src/slave/slave.cpp
index e156683..554a2da 100644
--- a/src/slave/slave.cpp
+++ b/src/slave/slave.cpp
@@ -429,7 +429,10 @@ void Slave::initialize()
       self().id + "/api/v1/resource_provider");
 
   Try<Owned<LocalResourceProviderDaemon>> _localResourceProviderDaemon =
-    LocalResourceProviderDaemon::create(localResourceProviderURL, flags);
+    LocalResourceProviderDaemon::create(
+        localResourceProviderURL,
+        flags,
+        secretGenerator);
 
   if (_localResourceProviderDaemon.isError()) {
     EXIT(EXIT_FAILURE)
@@ -1227,7 +1230,8 @@ void Slave::registered(
         CHECK_SOME(state::checkpoint(path, info_));
       }
 
-      // Start the local resource providers daemon once we have the slave id.
+      // We start the local resource providers daemon once the agent is
+      // running, so the resource providers can use the agent API.
       localResourceProviderDaemon->start(info.id());
 
       // Setup a timer so that the agent attempts to re-register if it
@@ -1333,6 +1337,10 @@ void Slave::reregistered(
       state = RUNNING;
       taskStatusUpdateManager->resume(); // Resume status updates.
 
+      // We start the local resource providers daemon once the agent is
+      // running, so the resource providers can use the agent API.
+      localResourceProviderDaemon->start(info.id());
+
       // Setup a timer so that the agent attempts to re-register if it
       // doesn't receive a ping from the master for an extended period
       // of time. This needs to be done once re-registered, in case we
@@ -6328,9 +6336,6 @@ Future<Nothing> Slave::recover(const Try<state::State>& state)
     } else {
       info = slaveState->info.get(); // Recover the slave info.
 
-      // Start the local resource providers daemon once we have the slave id.
-      localResourceProviderDaemon->start(info.id());
-
       // Recover the frameworks.
       foreachvalue (const FrameworkState& frameworkState,
                     slaveState->frameworks) {


[09/13] mesos git commit: Added utility functions and structures for CSI version and capabilities.

Posted by ji...@apache.org.
Added utility functions and structures for CSI version and capabilities.

This patch adds some helper structures and functions for CSI protobuf.
The comparison and output operators for `csi::Version` are declared in
the `::csi` namespace so they can find it through ADL. Also, it exposes
`::csi` in `mesos::csi` in `spec.hpp` instead of `client.hpp`.

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


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

Branch: refs/heads/master
Commit: 8bca4bedb8e492a3e6c037a3174f2aacb4dcd20a
Parents: bec1cfd
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:31:07 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:31:07 2017 -0800

----------------------------------------------------------------------
 src/Makefile.am    |  6 ++--
 src/csi/client.hpp |  2 --
 src/csi/spec.hpp   |  8 +++++
 src/csi/utils.cpp  | 42 +++++++++++++++++++++++++
 src/csi/utils.hpp  | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 137 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/8bca4bed/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index a82ec7f..9438a7e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1493,11 +1493,13 @@ if ENABLE_GRPC
 noinst_LTLIBRARIES += libcsi.la
 libcsi_la_SOURCES =							\
   csi/client.cpp							\
-  csi/paths.cpp
+  csi/paths.cpp								\
+  csi/utils.cpp
 libcsi_la_SOURCES +=							\
   csi/client.hpp							\
   csi/paths.hpp								\
-  csi/spec.hpp
+  csi/spec.hpp								\
+  csi/utils.hpp
 nodist_libcsi_la_SOURCES = $(CXX_CSI_PROTOS)
 libcsi_la_CPPFLAGS = $(MESOS_CPPFLAGS)
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/8bca4bed/src/csi/client.hpp
----------------------------------------------------------------------
diff --git a/src/csi/client.hpp b/src/csi/client.hpp
index a5571f4..91dda0d 100644
--- a/src/csi/client.hpp
+++ b/src/csi/client.hpp
@@ -26,8 +26,6 @@
 namespace mesos {
 namespace csi {
 
-using namespace ::csi;
-
 class Client
 {
 public:

http://git-wip-us.apache.org/repos/asf/mesos/blob/8bca4bed/src/csi/spec.hpp
----------------------------------------------------------------------
diff --git a/src/csi/spec.hpp b/src/csi/spec.hpp
index 60e40e0..c819be3 100644
--- a/src/csi/spec.hpp
+++ b/src/csi/spec.hpp
@@ -20,4 +20,12 @@
 // ONLY USEFUL AFTER RUNNING PROTOC WITH GRPC CPP PLUGIN.
 #include "csi/csi.grpc.pb.h"
 
+namespace mesos {
+namespace csi {
+
+using namespace ::csi;
+
+} // namespace csi {
+} // namespace mesos {
+
 #endif // __CSI_SPEC_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/8bca4bed/src/csi/utils.cpp
----------------------------------------------------------------------
diff --git a/src/csi/utils.cpp b/src/csi/utils.cpp
new file mode 100644
index 0000000..ef1bf69
--- /dev/null
+++ b/src/csi/utils.cpp
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "csi/utils.hpp"
+
+#include <stout/strings.hpp>
+
+using std::ostream;
+
+namespace csi {
+
+bool operator==(const Version& left, const Version& right)
+{
+  return left.major() == right.major() &&
+    left.minor() == right.minor() &&
+    left.patch() == right.patch();
+}
+
+
+ostream& operator<<(ostream& stream, const Version& version)
+{
+  return stream << strings::join(
+      ".",
+      version.major(),
+      version.minor(),
+      version.patch());
+}
+
+} // namespace csi {

http://git-wip-us.apache.org/repos/asf/mesos/blob/8bca4bed/src/csi/utils.hpp
----------------------------------------------------------------------
diff --git a/src/csi/utils.hpp b/src/csi/utils.hpp
new file mode 100644
index 0000000..0a8622c
--- /dev/null
+++ b/src/csi/utils.hpp
@@ -0,0 +1,83 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __CSI_UTILS_HPP__
+#define __CSI_UTILS_HPP__
+
+#include <ostream>
+
+#include <stout/foreach.hpp>
+#include <stout/unreachable.hpp>
+
+#include "csi/spec.hpp"
+
+namespace csi {
+
+bool operator==(const Version& left, const Version& right);
+
+
+std::ostream& operator<<(std::ostream& stream, const Version& version);
+
+} // namespace csi {
+
+
+namespace mesos {
+namespace csi {
+
+struct ControllerCapabilities
+{
+  ControllerCapabilities() = default;
+
+  template <typename Iterable>
+  ControllerCapabilities(const Iterable& capabilities)
+  {
+    foreach (const auto& capability, capabilities) {
+      if (capability.has_rpc() &&
+          ControllerServiceCapability::RPC::Type_IsValid(
+              capability.rpc().type())) {
+        switch(capability.rpc().type()) {
+          case ControllerServiceCapability::RPC::UNKNOWN:
+            break;
+          case ControllerServiceCapability::RPC::CREATE_DELETE_VOLUME:
+            createDeleteVolume = true;
+            break;
+          case ControllerServiceCapability::RPC::PUBLISH_UNPUBLISH_VOLUME:
+            publishUnpublishVolume = true;
+            break;
+          case ControllerServiceCapability::RPC::LIST_VOLUMES:
+            listVolumes = true;
+            break;
+          case ControllerServiceCapability::RPC::GET_CAPACITY:
+            getCapacity = true;
+            break;
+          case google::protobuf::kint32min:
+          case google::protobuf::kint32max:
+            UNREACHABLE();
+        }
+      }
+    }
+  }
+
+  bool createDeleteVolume = false;
+  bool publishUnpublishVolume = false;
+  bool listVolumes = false;
+  bool getCapacity = false;
+};
+
+} // namespace csi {
+} // namespace mesos {
+
+#endif // __CSI_UTILS_HPP__


[06/13] mesos git commit: Added default reservations in `ResourceProviderInfo`.

Posted by ji...@apache.org.
Added default reservations in `ResourceProviderInfo`.

The `default_reservations` field is used to set up the default
reservation stack for new resources from the resource provider.

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


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

Branch: refs/heads/master
Commit: c0a4988c676014999ff027401d8d8bcf396ace90
Parents: 6216cde
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:54 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:54 2017 -0800

----------------------------------------------------------------------
 include/mesos/mesos.proto      | 13 ++++++++++++-
 include/mesos/resources.hpp    | 11 +++++++++++
 include/mesos/v1/mesos.proto   | 13 ++++++++++++-
 include/mesos/v1/resources.hpp | 11 +++++++++++
 src/common/type_utils.cpp      | 11 +++++++++++
 src/v1/mesos.cpp               | 11 +++++++++++
 6 files changed, 68 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c0a4988c/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 25a9082..11089b7 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -1071,7 +1071,18 @@ message ResourceProviderInfo {
   // to avoid conflicts on concatenation of type and name.
   required string name = 4;
 
-  optional CSIPluginInfo storage = 5; // EXPERIMENTAL.
+  // The stack of default reservations. If this field is not empty, it
+  // indicates that resources from this resource provider are reserved
+  // by default, except for the resources that have been reserved or
+  // unreserved through offer operations. The first `ReservationInfo`
+  // may have type `STATIC` or `DYNAMIC`, but the rest must have
+  // `DYNAMIC`. One can create a new reservation on top of an existing
+  // one by pushing a new `ReservationInfo` to the back. The last
+  // `ReservationInfo` in this stack is the "current" reservation. The
+  // new reservation's role must be a child of the current one.
+  repeated Resource.ReservationInfo default_reservations = 5; // EXPERIMENTAL.
+
+  optional CSIPluginInfo storage = 6; // EXPERIMENTAL.
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/c0a4988c/include/mesos/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp
index 08c544d..eefe9ea 100644
--- a/include/mesos/resources.hpp
+++ b/include/mesos/resources.hpp
@@ -58,6 +58,17 @@ namespace mesos {
 class ResourceConversion;
 
 
+// Helper functions.
+bool operator==(
+    const Resource::ReservationInfo& left,
+    const Resource::ReservationInfo& right);
+
+
+bool operator!=(
+    const Resource::ReservationInfo& left,
+    const Resource::ReservationInfo& right);
+
+
 // NOTE: Resource objects stored in the class are always valid, are in
 // the "post-reservation-refinement" format, and kept combined if possible.
 // It is the caller's responsibility to validate any Resource object or

http://git-wip-us.apache.org/repos/asf/mesos/blob/c0a4988c/include/mesos/v1/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/v1/mesos.proto b/include/mesos/v1/mesos.proto
index ddb5add..c496da9 100644
--- a/include/mesos/v1/mesos.proto
+++ b/include/mesos/v1/mesos.proto
@@ -1063,7 +1063,18 @@ message ResourceProviderInfo {
   // to avoid conflicts on concatenation of type and name.
   required string name = 4;
 
-  optional CSIPluginInfo storage = 5; // EXPERIMENTAL.
+  // The stack of default reservations. If this field is not empty, it
+  // indicates that resources from this resource provider are reserved
+  // by default, except for the resources that have been reserved or
+  // unreserved through offer operations. The first `ReservationInfo`
+  // may have type `STATIC` or `DYNAMIC`, but the rest must have
+  // `DYNAMIC`. One can create a new reservation on top of an existing
+  // one by pushing a new `ReservationInfo` to the back. The last
+  // `ReservationInfo` in this stack is the "current" reservation. The
+  // new reservation's role must be a child of the current one.
+  repeated Resource.ReservationInfo default_reservations = 5; // EXPERIMENTAL.
+
+  optional CSIPluginInfo storage = 6; // EXPERIMENTAL.
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/c0a4988c/include/mesos/v1/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/v1/resources.hpp b/include/mesos/v1/resources.hpp
index d59fa35..7a5b0e3 100644
--- a/include/mesos/v1/resources.hpp
+++ b/include/mesos/v1/resources.hpp
@@ -58,6 +58,17 @@ namespace v1 {
 class ResourceConversion;
 
 
+// Helper functions.
+bool operator==(
+    const Resource::ReservationInfo& left,
+    const Resource::ReservationInfo& right);
+
+
+bool operator!=(
+    const Resource::ReservationInfo& left,
+    const Resource::ReservationInfo& right);
+
+
 // NOTE: Resource objects stored in the class are always valid, are in
 // the "post-reservation-refinement" format, and kept combined if possible.
 // It is the caller's responsibility to validate any Resource object or

http://git-wip-us.apache.org/repos/asf/mesos/blob/c0a4988c/src/common/type_utils.cpp
----------------------------------------------------------------------
diff --git a/src/common/type_utils.cpp b/src/common/type_utils.cpp
index 3bff49e..3657d55 100644
--- a/src/common/type_utils.cpp
+++ b/src/common/type_utils.cpp
@@ -404,6 +404,17 @@ bool operator==(
     const ResourceProviderInfo& left,
     const ResourceProviderInfo& right)
 {
+  // Order of reservations is important.
+  if (left.default_reservations_size() != right.default_reservations_size()) {
+    return false;
+  }
+
+  for (int i = 0; i < left.default_reservations_size(); i++) {
+    if (left.default_reservations(i) != right.default_reservations(i)) {
+      return false;
+    }
+  }
+
   return left.has_id() == right.has_id() &&
     (!left.has_id() || left.id() == right.id()) &&
     Attributes(left.attributes()) == Attributes(right.attributes()) &&

http://git-wip-us.apache.org/repos/asf/mesos/blob/c0a4988c/src/v1/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/v1/mesos.cpp b/src/v1/mesos.cpp
index 243b1cd..15eb848 100644
--- a/src/v1/mesos.cpp
+++ b/src/v1/mesos.cpp
@@ -398,6 +398,17 @@ bool operator==(
     const ResourceProviderInfo& left,
     const ResourceProviderInfo& right)
 {
+  // Order of reservations is important.
+  if (left.default_reservations_size() != right.default_reservations_size()) {
+    return false;
+  }
+
+  for (int i = 0; i < left.default_reservations_size(); i++) {
+    if (left.default_reservations(i) != right.default_reservations(i)) {
+      return false;
+    }
+  }
+
   return left.has_id() == right.has_id() &&
     (!left.has_id() || left.id() == right.id()) &&
     Attributes(left.attributes()) == Attributes(right.attributes()) &&


[02/13] mesos git commit: Added `LocalResourceProvider::principal()` for authentication.

Posted by ji...@apache.org.
Added `LocalResourceProvider::principal()` for authentication.

The `LocalResourceProvider::principal()` function takes a
`ResourceProviderInfo` and generates a principal with the following
claim:

  {"cid_prefix", <type-with-dots-replaced-by-dashes>-<name>--}

For example, for resource provider with type
'org.apache.mesos.rp.local.storage' and name 'foo', the claim would be:

  {"cid_prefix", "org-apache-mesos-rp-local-storage-foo--"}

In the future, we could add more claims for authorizing other
operations, such as authorization for Resource Provider API.

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


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

Branch: refs/heads/master
Commit: 957b0c0bbf8c03f94148ff8339718645474ada5e
Parents: 459fb2c
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:30:30 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:30:30 2017 -0800

----------------------------------------------------------------------
 src/resource_provider/local.cpp            | 49 ++++++++++++++++-----
 src/resource_provider/local.hpp            |  4 ++
 src/resource_provider/storage/provider.cpp | 58 +++++++++++++++++++++++++
 src/resource_provider/storage/provider.hpp |  3 ++
 4 files changed, 103 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/957b0c0b/src/resource_provider/local.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/local.cpp b/src/resource_provider/local.cpp
index ad98f33..ff9819e 100644
--- a/src/resource_provider/local.cpp
+++ b/src/resource_provider/local.cpp
@@ -14,34 +14,61 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <stout/hashmap.hpp>
+#include <stout/lambda.hpp>
+
 #include "resource_provider/local.hpp"
 
 #include "resource_provider/storage/provider.hpp"
 
+namespace http = process::http;
+
+using std::string;
+
 using process::Owned;
 
+using process::http::authentication::Principal;
+
 namespace mesos {
 namespace internal {
 
 Try<Owned<LocalResourceProvider>> LocalResourceProvider::create(
-    const process::http::URL& url,
+    const http::URL& url,
     const ResourceProviderInfo& info)
 {
   // TODO(jieyu): Document the built-in local resource providers.
-  if (info.type() == "org.apache.mesos.rp.local.storage") {
-    Try<Owned<LocalResourceProvider>> provider =
-      StorageLocalResourceProvider::create(url, info);
+  const hashmap<string, lambda::function<decltype(create)>> creators = {
+#if defined(ENABLE_GRPC) && defined(__linux__)
+    {"org.apache.mesos.rp.local.storage", &StorageLocalResourceProvider::create}
+#endif
+  };
+
+  if (creators.contains(info.type())) {
+    return creators.at(info.type())(url, info);
+  }
 
-    if (provider.isError()) {
-      return Error(
-          "Failed to create storage local resource provider: " +
-          provider.error());
-    }
+  return Error("Unknown local resource provider type '" + info.type() + "'");
+}
+
+
+Try<Principal> LocalResourceProvider::principal(
+    const ResourceProviderInfo& info)
+{
+  // TODO(chhsiao): Document the principals for built-in local resource
+  // providers.
+  const hashmap<string, lambda::function<decltype(principal)>>
+    principalGenerators = {
+#if defined(ENABLE_GRPC) && defined(__linux__)
+      {"org.apache.mesos.rp.local.storage",
+        &StorageLocalResourceProvider::principal}
+#endif
+    };
 
-    return provider.get();
+  if (principalGenerators.contains(info.type())) {
+    return principalGenerators.at(info.type())(info);
   }
 
-  return Error("Unknown resource provider type '" + info.type() + "'");
+  return Error("Unknown local resource provider type '" + info.type() + "'");
 }
 
 } // namespace internal {

http://git-wip-us.apache.org/repos/asf/mesos/blob/957b0c0b/src/resource_provider/local.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/local.hpp b/src/resource_provider/local.hpp
index ebaa07d..40c8f98 100644
--- a/src/resource_provider/local.hpp
+++ b/src/resource_provider/local.hpp
@@ -17,6 +17,7 @@
 #ifndef __RESOURCE_PROVIDER_LOCAL_HPP__
 #define __RESOURCE_PROVIDER_LOCAL_HPP__
 
+#include <process/authenticator.hpp>
 #include <process/http.hpp>
 #include <process/owned.hpp>
 
@@ -34,6 +35,9 @@ public:
       const process::http::URL& url,
       const ResourceProviderInfo& info);
 
+  static Try<process::http::authentication::Principal> principal(
+      const ResourceProviderInfo& info);
+
   virtual ~LocalResourceProvider() = default;
 };
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/957b0c0b/src/resource_provider/storage/provider.cpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/storage/provider.cpp b/src/resource_provider/storage/provider.cpp
index 49c042c..6817df5 100644
--- a/src/resource_provider/storage/provider.cpp
+++ b/src/resource_provider/storage/provider.cpp
@@ -16,6 +16,8 @@
 
 #include "resource_provider/storage/provider.hpp"
 
+#include <cctype>
+
 #include <glog/logging.h>
 
 #include <process/defer.hpp>
@@ -30,7 +32,10 @@
 
 #include "resource_provider/detector.hpp"
 
+namespace http = process::http;
+
 using std::queue;
+using std::string;
 
 using process::Owned;
 using process::Process;
@@ -40,6 +45,8 @@ using process::spawn;
 using process::terminate;
 using process::wait;
 
+using process::http::authentication::Principal;
+
 using mesos::ResourceProviderInfo;
 
 using mesos::resource_provider::Event;
@@ -49,6 +56,39 @@ using mesos::v1::resource_provider::Driver;
 namespace mesos {
 namespace internal {
 
+// Returns true if the string is a valid Java identifier.
+static bool isValidName(const string& s)
+{
+  if (s.empty()) {
+    return false;
+  }
+
+  foreach (const char c, s) {
+    if (!isalnum(c) && c != '_') {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+
+// Returns a prefix for naming standalone containers to run CSI plugins
+// for the resource provider. The prefix is of the following format:
+//     <rp_type>-<rp_name>--
+// where <rp_type> and <rp_name> are the type and name of the resource
+// provider, with dots replaced by dashes. We use a double-dash at the
+// end to explicitly mark the end of the prefix.
+static inline string getContainerIdPrefix(const ResourceProviderInfo& info)
+{
+  return strings::join(
+      "-",
+      strings::replace(info.type(), ".", "-"),
+      info.name(),
+      "-");
+}
+
+
 class StorageLocalResourceProviderProcess
   : public Process<StorageLocalResourceProviderProcess>
 {
@@ -138,11 +178,29 @@ Try<Owned<LocalResourceProvider>> StorageLocalResourceProvider::create(
     const process::http::URL& url,
     const ResourceProviderInfo& info)
 {
+  // Verify that the name follows Java package naming convention.
+  // TODO(chhsiao): We should move this check to a validation function
+  // for `ResourceProviderInfo`.
+  if (!isValidName(info.name())) {
+    return Error(
+        "Resource provider name '" + info.name() +
+        "' does not follow Java package naming convention");
+  }
+
   return Owned<LocalResourceProvider>(
       new StorageLocalResourceProvider(url, info));
 }
 
 
+Try<Principal> StorageLocalResourceProvider::principal(
+    const ResourceProviderInfo& info)
+{
+  return Principal(
+      Option<string>::none(),
+      {{"cid_prefix", getContainerIdPrefix(info)}});
+}
+
+
 StorageLocalResourceProvider::StorageLocalResourceProvider(
     const process::http::URL& url,
     const ResourceProviderInfo& info)

http://git-wip-us.apache.org/repos/asf/mesos/blob/957b0c0b/src/resource_provider/storage/provider.hpp
----------------------------------------------------------------------
diff --git a/src/resource_provider/storage/provider.hpp b/src/resource_provider/storage/provider.hpp
index 6de88c2..b61b41b 100644
--- a/src/resource_provider/storage/provider.hpp
+++ b/src/resource_provider/storage/provider.hpp
@@ -40,6 +40,9 @@ public:
       const process::http::URL& url,
       const mesos::ResourceProviderInfo& info);
 
+  static Try<process::http::authentication::Principal> principal(
+      const mesos::ResourceProviderInfo& info);
+
   ~StorageLocalResourceProvider() override;
 
   StorageLocalResourceProvider(


[11/13] mesos git commit: Added a container daemon to monitor a long-running standalone container.

Posted by ji...@apache.org.
Added a container daemon to monitor a long-running standalone container.

The `ContanierDaemon` class is responsible to monitor if a long-running
service running in a standalone container, and restart the service
container after it exits. It takes the following hook functions:

* `postStartHook`: called after the container is launched every time.
* `postStopHook`: called after the container exits every time.

`ContainerDaemon` does not manage the lifecycle of the contanier it
monitors, so the container persists across `ContainerDaemon`s.

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


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

Branch: refs/heads/master
Commit: 5c979635dfc5c20ffb48672396eb5ca4aeb3a2c7
Parents: eb7f16b
Author: Chun-Hung Hsiao <ch...@mesosphere.io>
Authored: Wed Nov 29 15:31:15 2017 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Nov 29 15:31:15 2017 -0800

----------------------------------------------------------------------
 src/CMakeLists.txt             |   1 +
 src/Makefile.am                |   2 +
 src/slave/container_daemon.cpp | 278 ++++++++++++++++++++++++++++++++++++
 src/slave/container_daemon.hpp |  92 ++++++++++++
 4 files changed, 373 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5c979635/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3f5d946..76ef6ca 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -246,6 +246,7 @@ configure_file(
 #####################################
 set(AGENT_SRC
   slave/constants.cpp
+  slave/container_daemon.cpp
   slave/container_logger.cpp
   slave/flags.cpp
   slave/gc.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/5c979635/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 9438a7e..8dcc367 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1012,6 +1012,7 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   scheduler/scheduler.cpp						\
   secret/resolver.cpp							\
   slave/constants.cpp							\
+  slave/container_daemon.cpp						\
   slave/container_logger.cpp						\
   slave/flags.cpp							\
   slave/gc.cpp								\
@@ -1159,6 +1160,7 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   scheduler/constants.hpp						\
   scheduler/flags.hpp							\
   slave/constants.hpp							\
+  slave/container_daemon.hpp						\
   slave/flags.hpp							\
   slave/gc.hpp								\
   slave/gc_process.hpp							\

http://git-wip-us.apache.org/repos/asf/mesos/blob/5c979635/src/slave/container_daemon.cpp
----------------------------------------------------------------------
diff --git a/src/slave/container_daemon.cpp b/src/slave/container_daemon.cpp
new file mode 100644
index 0000000..2e6c748
--- /dev/null
+++ b/src/slave/container_daemon.cpp
@@ -0,0 +1,278 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "slave/container_daemon.hpp"
+
+#include <mesos/agent/agent.hpp>
+
+#include <process/defer.hpp>
+#include <process/id.hpp>
+#include <process/process.hpp>
+
+#include <stout/lambda.hpp>
+#include <stout/stringify.hpp>
+#include <stout/unreachable.hpp>
+
+#include "common/http.hpp"
+
+#include "internal/evolve.hpp"
+
+namespace http = process::http;
+
+using std::string;
+
+using mesos::agent::Call;
+
+using process::Failure;
+using process::Future;
+using process::Owned;
+using process::Process;
+using process::Promise;
+
+using process::defer;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// Returns the 'Bearer' credential as a header for calling the V1 agent
+// API if the `authToken` is presented, or empty otherwise.
+// TODO(chhsiao): Currently we assume the JWT authenticator is used for
+// the agent operator API.
+static inline http::Headers getAuthHeader(const Option<string>& authToken)
+{
+  http::Headers headers;
+
+  if (authToken.isSome()) {
+    headers["Authorization"] = "Bearer " + authToken.get();
+  }
+
+  return headers;
+}
+
+
+class ContainerDaemonProcess : public Process<ContainerDaemonProcess>
+{
+public:
+  explicit ContainerDaemonProcess(
+      const http::URL& _agentUrl,
+      const Option<string>& _authToken,
+      const ContainerID& containerId,
+      const Option<CommandInfo>& commandInfo,
+      const Option<Resources>& resources,
+      const Option<ContainerInfo>& containerInfo,
+      const Option<std::function<Future<Nothing>()>>& _postStartHook,
+      const Option<std::function<Future<Nothing>()>>& _postStopHook);
+
+  ContainerDaemonProcess(const ContainerDaemonProcess& other) = delete;
+
+  ContainerDaemonProcess& operator=(
+      const ContainerDaemonProcess& other) = delete;
+
+  Future<Nothing> wait();
+
+protected:
+  void initialize() override;
+
+private:
+  void launchContainer();
+  void waitContainer();
+
+  const http::URL agentUrl;
+  const Option<string> authToken;
+  const ContentType contentType;
+  const Option<std::function<Future<Nothing>()>> postStartHook;
+  const Option<std::function<Future<Nothing>()>> postStopHook;
+
+  Call launchCall;
+  Call waitCall;
+
+  Promise<Nothing> terminated;
+};
+
+
+ContainerDaemonProcess::ContainerDaemonProcess(
+    const http::URL& _agentUrl,
+    const Option<string>& _authToken,
+    const ContainerID& containerId,
+    const Option<CommandInfo>& commandInfo,
+    const Option<Resources>& resources,
+    const Option<ContainerInfo>& containerInfo,
+    const Option<std::function<Future<Nothing>()>>& _postStartHook,
+    const Option<std::function<Future<Nothing>()>>& _postStopHook)
+  : ProcessBase(process::ID::generate("container-daemon")),
+    agentUrl(_agentUrl),
+    authToken(_authToken),
+    contentType(ContentType::PROTOBUF),
+    postStartHook(_postStartHook),
+    postStopHook(_postStopHook)
+{
+  launchCall.set_type(Call::LAUNCH_CONTAINER);
+  launchCall.mutable_launch_container()
+    ->mutable_container_id()->CopyFrom(containerId);
+
+  if (commandInfo.isSome()) {
+    launchCall.mutable_launch_container()
+      ->mutable_command()->CopyFrom(commandInfo.get());
+  }
+
+  if (resources.isSome()) {
+    launchCall.mutable_launch_container()
+      ->mutable_resources()->CopyFrom(resources.get());
+  }
+
+  if (containerInfo.isSome()) {
+    launchCall.mutable_launch_container()
+      ->mutable_container()->CopyFrom(containerInfo.get());
+  }
+
+  waitCall.set_type(Call::WAIT_CONTAINER);
+  waitCall.mutable_wait_container()->mutable_container_id()->CopyFrom(
+      containerId);
+}
+
+
+Future<Nothing> ContainerDaemonProcess::wait()
+{
+  return terminated.future();
+}
+
+
+void ContainerDaemonProcess::initialize()
+{
+  launchContainer();
+}
+
+
+void ContainerDaemonProcess::launchContainer()
+{
+  http::post(
+      agentUrl,
+      getAuthHeader(authToken),
+      serialize(contentType, evolve(launchCall)),
+      stringify(contentType))
+    .then(defer(self(), [=](
+        const http::Response& response) -> Future<Nothing> {
+      if (response.status != http::OK().status &&
+          response.status != http::Accepted().status) {
+        return Failure(
+            "Failed to launch container '" +
+            stringify(launchCall.launch_container().container_id()) +
+            "': Unexpected response '" + response.status + "' (" +
+            response.body + ")");
+      }
+
+      return postStartHook.isSome() ? postStartHook.get()() : Nothing();
+    }))
+    .onReady(defer(self(), &Self::waitContainer))
+    .onFailed(defer(self(), [=](const string& failure) {
+      terminated.fail(failure);
+    }))
+    .onDiscarded(defer(self(), [=] {
+      terminated.discard();
+    }));
+}
+
+
+void ContainerDaemonProcess::waitContainer()
+{
+  http::post(
+      agentUrl,
+      getAuthHeader(authToken),
+      serialize(contentType, evolve(waitCall)),
+      stringify(contentType))
+    .then(defer(self(), [=](const http::Response& response) -> Future<Nothing> {
+      if (response.status != http::OK().status &&
+          response.status != http::NotFound().status) {
+        return Failure(
+            "Failed to wait for container '" +
+            stringify(waitCall.wait_container().container_id()) +
+            "': Unexpected response '" + response.status + "' (" +
+            response.body + ")");
+      }
+
+      return postStopHook.isSome() ? postStopHook.get()() : Nothing();
+    }))
+    .onReady(defer(self(), &Self::launchContainer))
+    .onFailed(defer(self(), [=](const string& failure) {
+      terminated.fail(failure);
+    }))
+    .onDiscarded(defer(self(), [=] {
+      terminated.discard();
+    }));
+}
+
+
+Try<Owned<ContainerDaemon>> ContainerDaemon::create(
+    const http::URL& agentUrl,
+    const Option<string>& authToken,
+    const ContainerID& containerId,
+    const Option<CommandInfo>& commandInfo,
+    const Option<Resources>& resources,
+    const Option<ContainerInfo>& containerInfo,
+    const Option<std::function<Future<Nothing>()>>& postStartHook,
+    const Option<std::function<Future<Nothing>()>>& postStopHook)
+{
+  return Owned<ContainerDaemon>(new ContainerDaemon(
+      agentUrl,
+      authToken,
+      containerId,
+      commandInfo,
+      resources,
+      containerInfo,
+      postStartHook,
+      postStopHook));
+}
+
+
+ContainerDaemon::ContainerDaemon(
+    const http::URL& agentUrl,
+    const Option<string>& authToken,
+    const ContainerID& containerId,
+    const Option<CommandInfo>& commandInfo,
+    const Option<Resources>& resources,
+    const Option<ContainerInfo>& containerInfo,
+    const Option<std::function<Future<Nothing>()>>& postStartHook,
+    const Option<std::function<Future<Nothing>()>>& postStopHook)
+  : process(new ContainerDaemonProcess(
+        agentUrl,
+        authToken,
+        containerId,
+        commandInfo,
+        resources,
+        containerInfo,
+        postStartHook,
+        postStopHook))
+{
+  spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+ContainerDaemon::~ContainerDaemon()
+{
+  process::terminate(process.get());
+  process::wait(process.get());
+}
+
+
+Future<Nothing> ContainerDaemon::wait()
+{
+  return process->wait();
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/5c979635/src/slave/container_daemon.hpp
----------------------------------------------------------------------
diff --git a/src/slave/container_daemon.hpp b/src/slave/container_daemon.hpp
new file mode 100644
index 0000000..a58140d
--- /dev/null
+++ b/src/slave/container_daemon.hpp
@@ -0,0 +1,92 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __SLAVE_CONTAINER_DAEMON_HPP__
+#define __SLAVE_CONTAINER_DAEMON_HPP__
+
+#include <functional>
+
+#include <mesos/mesos.hpp>
+#include <mesos/resources.hpp>
+
+#include <process/future.hpp>
+#include <process/http.hpp>
+#include <process/owned.hpp>
+
+#include <stout/duration.hpp>
+#include <stout/option.hpp>
+#include <stout/try.hpp>
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// Forward declarations.
+class ContainerDaemonProcess;
+
+
+// A daemon that launches and monitors a service running in a standalone
+// container, and automatically restarts the container when it exits.
+//
+// NOTE: The `ContainerDaemon` itself is not responsible to manage the
+// lifecycle of the service container it monitors.
+class ContainerDaemon
+{
+public:
+  // Creates a container daemon that idempotently launches the container
+  // and then run the `postStartHook` function. Upon container exits, it
+  // executes the `postStopHook` function, then restarts the launch
+  // cycle again. Any failed or discarded future returned by the hook
+  // functions will be reflected by the `wait()` method.
+  static Try<process::Owned<ContainerDaemon>> create(
+      const process::http::URL& agentUrl,
+      const Option<std::string>& authToken,
+      const ContainerID& containerId,
+      const Option<CommandInfo>& commandInfo,
+      const Option<Resources>& resources,
+      const Option<ContainerInfo>& containerInfo,
+      const Option<std::function<process::Future<Nothing>()>>& postStartHook =
+        None(),
+      const Option<std::function<process::Future<Nothing>()>>& postStopHook =
+        None());
+
+  ~ContainerDaemon();
+
+  // Returns a future that only reaches a terminal state when a failure
+  // or a discarded future occurs during the launch cycle. This is
+  // intended to capture any loop-breaking error, and the caller should
+  // reconstruct a new daemon instance if they want to retry.
+  process::Future<Nothing> wait();
+
+private:
+  explicit ContainerDaemon(
+      const process::http::URL& agentUrl,
+      const Option<std::string>& authToken,
+      const ContainerID& containerId,
+      const Option<CommandInfo>& commandInfo,
+      const Option<Resources>& resources,
+      const Option<ContainerInfo>& containerInfo,
+      const Option<std::function<process::Future<Nothing>()>>& postStartHook,
+      const Option<std::function<process::Future<Nothing>()>>& postStopHook);
+
+  process::Owned<ContainerDaemonProcess> process;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __SLAVE_CONTAINER_DAEMON_HPP__