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 2016/04/24 19:27:33 UTC

mesos git commit: Implemented prepare() for volume isolator.

Repository: mesos
Updated Branches:
  refs/heads/master 3d2d3edb9 -> e8e65535c


Implemented prepare() for volume isolator.

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


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

Branch: refs/heads/master
Commit: e8e65535c8ee105e262662fde95aef2d93b289a5
Parents: 3d2d3ed
Author: Guangya Liu <gy...@gmail.com>
Authored: Sat Apr 23 22:30:23 2016 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Sun Apr 24 10:17:33 2016 -0700

----------------------------------------------------------------------
 .../mesos/isolators/docker/volume/isolator.cpp  | 192 ++++++++++++++++++-
 .../mesos/isolators/docker/volume/isolator.hpp  |  24 +++
 .../mesos/isolators/docker/volume/state.hpp     |  36 ++++
 3 files changed, 250 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e8e65535/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp b/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
index 915e5ae..7c147cf 100644
--- a/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
+++ b/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
@@ -14,11 +14,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <list>
+#include <process/collect.hpp>
 
 #include <stout/os.hpp>
 
 #include "slave/flags.hpp"
+#include "slave/state.hpp"
 
 #include "slave/containerizer/mesos/isolators/docker/volume/isolator.hpp"
 
@@ -26,6 +27,7 @@ namespace paths = mesos::internal::slave::docker::volume::paths;
 
 using std::list;
 using std::string;
+using std::vector;
 
 using process::Failure;
 using process::Future;
@@ -121,7 +123,193 @@ Future<Option<ContainerLaunchInfo>> DockerVolumeIsolatorProcess::prepare(
     const ContainerID& containerId,
     const ContainerConfig& containerConfig)
 {
-  return None();
+  const ExecutorInfo& executorInfo = containerConfig.executor_info();
+
+  if (!executorInfo.has_container()) {
+    return None();
+  }
+
+  if (executorInfo.container().type() != ContainerInfo::MESOS) {
+    return Failure(
+        "Can only prepare docker volume driver for a MESOS container");
+  }
+
+  // The hashset is used to check if there are duplicated docker
+  // volume for the same container.
+  hashset<DockerVolume> volumes;
+
+  // Represents mounts that will be sent to the driver client.
+  struct Mount
+  {
+    DockerVolume volume;
+    hashmap<string, string> options;
+  };
+
+  vector<Mount> mounts;
+
+  // The mount points in the container.
+  vector<string> targets;
+
+  foreach (const Volume& _volume, executorInfo.container().volumes()) {
+    if (!_volume.has_source()) {
+      continue;
+    }
+
+    if (_volume.source().type() != Volume::Source::DOCKER_VOLUME) {
+      VLOG(1) << "Ignored volume type '" << _volume.source().type()
+              << "' for container " << containerId << " as only "
+              << "'DOCKER_VOLUME' was supported by the docker "
+              << "volume isolator";
+      continue;
+    }
+
+    const string driver = _volume.source().docker_volume().driver();
+    const string name = _volume.source().docker_volume().name();
+
+    DockerVolume volume;
+    volume.set_driver(driver);
+    volume.set_name(name);
+
+    if (volumes.contains(volume)) {
+      return Failure(
+          "Found duplicate docker volume with driver '" +
+          driver + "' and name '" + name + "'");
+    }
+
+    // Determine driver options.
+    hashmap<string, string> options;
+    if (_volume.source().docker_volume().has_driver_options()) {
+      foreach (const Parameter& parameter,
+               _volume.source().docker_volume().driver_options().parameter()) {
+        options[parameter.key()] = parameter.value();
+      }
+    }
+
+    // Determine the mount point.
+    string target;
+
+    if (containerConfig.has_rootfs()) {
+      target = path::join(
+          containerConfig.rootfs(),
+          _volume.container_path());
+    } else {
+      target = path::join(
+          containerConfig.directory(),
+          _volume.container_path());
+    }
+
+    Mount mount;
+    mount.volume = volume;
+    mount.options = options;
+
+    volumes.insert(volume);
+    mounts.push_back(mount);
+    targets.push_back(target);
+  }
+
+  // Create the container directory.
+  const string containerDir =
+    paths::getContainerDir(rootDir, containerId.value());
+
+  Try<Nothing> mkdir = os::mkdir(containerDir);
+  if (mkdir.isError()) {
+    return Failure(
+        "Failed to create the container directory at '" +
+        containerDir + "': " + mkdir.error());
+  }
+
+  // Create DockerVolumes protobuf message to checkpoint.
+  DockerVolumes state;
+  foreach (const DockerVolume& volume, volumes) {
+    state.add_volumes()->CopyFrom(volume);
+  }
+
+  const string volumesPath =
+    paths::getVolumesPath(rootDir, containerId.value());
+
+  Try<Nothing> checkpoint = state::checkpoint(
+      volumesPath,
+      stringify(JSON::protobuf(state)));
+
+  if (checkpoint.isError()) {
+    return Failure(
+        "Failed to checkpoint docker volumes at '" +
+        volumesPath + "': " + checkpoint.error());
+  }
+
+  VLOG(1) << "Successfully created checkpoint at '" << volumesPath << "'";
+
+  infos.put(containerId, Owned<Info>(new Info(volumes)));
+
+  // Invoke driver client to create the mount.
+  list<Future<string>> futures;
+  foreach (const Mount& mount, mounts) {
+    futures.push_back(client->mount(
+        mount.volume.driver(),
+        mount.volume.name(),
+        mount.options));
+  }
+
+  // NOTE: Wait for all `mount()` to finish before returning to make
+  // sure `unmount()` is not called (via 'cleanup()') if some mount on
+  // is still pending.
+  return await(futures)
+    .then(defer(
+        PID<DockerVolumeIsolatorProcess>(this),
+        &DockerVolumeIsolatorProcess::_prepare,
+        containerId,
+        targets,
+        lambda::_1));
+}
+
+
+Future<Option<ContainerLaunchInfo>> DockerVolumeIsolatorProcess::_prepare(
+    const ContainerID& containerId,
+    const vector<string>& targets,
+    const list<Future<string>>& futures)
+{
+  ContainerLaunchInfo launchInfo;
+  launchInfo.set_namespaces(CLONE_NEWNS);
+
+  vector<string> messages;
+  vector<string> sources;
+  foreach (const Future<string>& future, futures) {
+    if (!future.isReady()) {
+      messages.push_back(future.isFailed() ? future.failure() : "discarded");
+      continue;
+    }
+
+    sources.push_back(strings::trim(future.get()));
+  }
+
+  if (!messages.empty()) {
+    return Failure(strings::join("\n", messages));
+  }
+
+  CHECK_EQ(sources.size(), targets.size());
+
+  for (size_t i = 0; i < sources.size(); i++) {
+    const string source = sources[i];
+    const string target = targets[i];
+
+    VLOG(1) << "Mounting docker volume mount point '" << source
+            << "' to '" << target  << "' for container '"
+            << containerId << "'";
+
+    // Create the mount point if it does not exist.
+    Try<Nothing> mkdir = os::mkdir(target);
+    if (mkdir.isError()) {
+      return Failure(
+          "Failed to create mount point at '" +
+          target + "': " + mkdir.error());
+    }
+
+    const string command = "mount -n --rbind " + source + " " + target;
+
+    launchInfo.add_commands()->set_value(command);
+  }
+
+  return launchInfo;
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/e8e65535/src/slave/containerizer/mesos/isolators/docker/volume/isolator.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/docker/volume/isolator.hpp b/src/slave/containerizer/mesos/isolators/docker/volume/isolator.hpp
index bedc687..02e1cff 100644
--- a/src/slave/containerizer/mesos/isolators/docker/volume/isolator.hpp
+++ b/src/slave/containerizer/mesos/isolators/docker/volume/isolator.hpp
@@ -17,10 +17,19 @@
 #ifndef __DOCKER_VOLUME_ISOLATOR_HPP__
 #define __DOCKER_VOLUME_ISOLATOR_HPP__
 
+#include <list>
+#include <string>
+#include <vector>
+
+#include <process/owned.hpp>
+
+#include <stout/hashmap.hpp>
+
 #include "slave/containerizer/mesos/isolator.hpp"
 
 #include "slave/containerizer/mesos/isolators/docker/volume/driver.hpp"
 #include "slave/containerizer/mesos/isolators/docker/volume/paths.hpp"
+#include "slave/containerizer/mesos/isolators/docker/volume/state.hpp"
 
 namespace mesos {
 namespace internal {
@@ -61,14 +70,29 @@ public:
       const ContainerID& containerId);
 
 private:
+  struct Info
+  {
+    Info (const hashset<DockerVolume>& _volumes)
+      : volumes(_volumes) {}
+
+    hashset<DockerVolume> volumes;
+  };
+
   DockerVolumeIsolatorProcess(
       const Flags& flags,
       const std::string& rootDir,
       const process::Owned<docker::volume::DriverClient>& client);
 
+  process::Future<Option<mesos::slave::ContainerLaunchInfo>> _prepare(
+      const ContainerID& containerId,
+      const std::vector<std::string>& targets,
+      const std::list<process::Future<std::string>>& futures);
+
   const Flags flags;
   const std::string rootDir;
   const process::Owned<docker::volume::DriverClient> client;
+
+  hashmap<ContainerID, process::Owned<Info>> infos;
 };
 
 } // namespace slave {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e8e65535/src/slave/containerizer/mesos/isolators/docker/volume/state.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/docker/volume/state.hpp b/src/slave/containerizer/mesos/isolators/docker/volume/state.hpp
index bc0c329..8e5f1aa 100644
--- a/src/slave/containerizer/mesos/isolators/docker/volume/state.hpp
+++ b/src/slave/containerizer/mesos/isolators/docker/volume/state.hpp
@@ -17,7 +17,43 @@
 #ifndef __ISOLATOR_DOCKER_VOLUME_STATE_HPP__
 #define __ISOLATOR_DOCKER_VOLUME_STATE_HPP__
 
+#include <boost/functional/hash.hpp>
+
 // ONLY USEFUL AFTER RUNNING PROTOC.
 #include "slave/containerizer/mesos/isolators/docker/volume/state.pb.h"
 
+namespace mesos {
+namespace internal {
+namespace slave {
+
+inline bool operator==(const DockerVolume& left, const DockerVolume& right)
+{
+  return (left.driver() == right.driver()) && (left.name() == right.name());
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+
+namespace std {
+
+template <>
+struct hash<mesos::internal::slave::DockerVolume>
+{
+  typedef size_t result_type;
+
+  typedef mesos::internal::slave::DockerVolume argument_type;
+
+  result_type operator()(const argument_type& volume) const
+  {
+    size_t seed = 0;
+    boost::hash_combine(seed, std::hash<std::string>()(volume.driver()));
+    boost::hash_combine(seed, std::hash<std::string>()(volume.name()));
+    return seed;
+  }
+};
+
+} // namespace std {
+
 #endif // __ISOLATOR_DOCKER_VOLUME_STATE_HPP__