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 2015/10/26 19:40:27 UTC

[03/12] mesos git commit: Relocated MesosContainerizer specific files to the correct location.

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/docker/token_manager.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/docker/token_manager.cpp b/src/slave/containerizer/mesos/provisioner/docker/token_manager.cpp
new file mode 100644
index 0000000..ad5b63f
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/docker/token_manager.cpp
@@ -0,0 +1,362 @@
+/**
+ * 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/defer.hpp>
+#include <process/dispatch.hpp>
+
+#include "slave/containerizer/mesos/provisioner/docker/token_manager.hpp"
+
+using std::hash;
+using std::string;
+using std::vector;
+
+using process::Clock;
+using process::Failure;
+using process::Future;
+using process::Owned;
+using process::Process;
+using process::Time;
+
+using process::http::Request;
+using process::http::Response;
+using process::http::URL;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+namespace registry {
+
+class TokenManagerProcess : public Process<TokenManagerProcess>
+{
+public:
+  static Try<Owned<TokenManagerProcess>> create(const URL& realm);
+
+  Future<Token> getToken(
+      const string& service,
+      const string& scope,
+      const Option<string>& account);
+
+private:
+  static const string TOKEN_PATH_PREFIX;
+  static const Duration RESPONSE_TIMEOUT;
+
+  TokenManagerProcess(const URL& realm)
+    : realm_(realm) {}
+
+  Try<Token> getTokenFromResponse(const Response& response) const;
+
+  /**
+   * Key for the token cache.
+   */
+  struct TokenCacheKey
+  {
+    string service;
+    string scope;
+  };
+
+  struct TokenCacheKeyHash
+  {
+    size_t operator()(const TokenCacheKey& key) const
+    {
+      hash<string> hashFn;
+
+      return (hashFn(key.service) ^
+          (hashFn(key.scope) << 1));
+    }
+  };
+
+  struct TokenCacheKeyEqual
+  {
+    bool operator()(
+        const TokenCacheKey& left,
+        const TokenCacheKey& right) const
+    {
+      return ((left.service == right.service) &&
+          (left.scope == right.scope));
+    }
+  };
+
+  typedef hashmap<
+    const TokenCacheKey,
+    Token,
+    TokenCacheKeyHash,
+    TokenCacheKeyEqual> TokenCacheType;
+
+  const URL realm_;
+  TokenCacheType tokenCache_;
+
+  TokenManagerProcess(const TokenManagerProcess&) = delete;
+  TokenManagerProcess& operator=(const TokenManagerProcess&) = delete;
+};
+
+const Duration TokenManagerProcess::RESPONSE_TIMEOUT = Seconds(10);
+const string TokenManagerProcess::TOKEN_PATH_PREFIX = "/v2/token/";
+
+
+Token::Token(
+    const string& _raw,
+    const JSON::Object& _header,
+    const JSON::Object& _claims,
+    const Option<Time>& _expiration,
+    const Option<Time>& _notBefore)
+  : raw(_raw),
+    header(_header),
+    claims(_claims),
+    expiration(_expiration),
+    notBefore(_notBefore) {}
+
+
+// TODO(josephw): Parse this string with some protobufs.
+Try<Token> Token::create(const string& raw)
+{
+  auto decode = [](
+      const string& segment) -> Try<JSON::Object> {
+    const auto padding = segment.length() % 4;
+    string paddedSegment(segment);
+
+    if (padding) {
+      paddedSegment.append(padding, '=');
+    }
+
+    Try<string> decoded = base64::decode(paddedSegment);
+    if (decoded.isError()) {
+      return Error(decoded.error());
+    }
+
+    return JSON::parse<JSON::Object>(decoded.get());
+  };
+
+  const vector<string> tokens = strings::tokenize(raw, ".");
+
+  if (tokens.size() != 3) {
+    return Error("Invalid raw token string");
+  }
+
+  Try<JSON::Object> header = decode(tokens[0]);
+  if (header.isError()) {
+    return Error("Failed to decode 'header' segment: " + header.error());
+  }
+
+  Try<JSON::Object> claims = decode(tokens[1]);
+  if (claims.isError()) {
+    return Error("Failed to decode 'claims' segment: " + claims.error());
+  }
+
+  Result<Time> expirationTime = getTimeValue(claims.get(), "exp");
+  if (expirationTime.isError()) {
+    return Error("Failed to decode expiration time: " + expirationTime.error());
+  }
+
+  Option<Time> expiration;
+  if (expirationTime.isSome()) {
+    expiration = expirationTime.get();
+  }
+
+  Result<Time> notBeforeTime = getTimeValue(claims.get(), "nbf");
+  if (notBeforeTime.isError()) {
+    return Error("Failed to decode not-before time: " + notBeforeTime.error());
+  }
+
+  Option<Time> notBefore;
+  if (notBeforeTime.isSome()) {
+    notBefore = notBeforeTime.get();
+  }
+
+  Token token(raw, header.get(), claims.get(), expiration, notBefore);
+
+  if (token.isExpired()) {
+    return Error("Token has expired");
+  }
+
+  // TODO(jojy): Add signature validation.
+  return token;
+}
+
+
+Result<Time> Token::getTimeValue(const JSON::Object& object, const string& key)
+{
+  Result<JSON::Number> jsonValue = object.find<JSON::Number>(key);
+
+  Option<Time> timeValue;
+
+  // If expiration is provided, we will process it for future validations.
+  if (jsonValue.isSome()) {
+    Try<Time> time = Time::create(jsonValue.get().as<double>());
+    if (time.isError()) {
+      return Error("Failed to decode time: " + time.error());
+    }
+
+    timeValue = time.get();
+  }
+
+  return timeValue;
+}
+
+
+bool Token::isExpired() const
+{
+  if (expiration.isSome()) {
+    return (Clock::now() >= expiration.get());
+  }
+
+  return false;
+}
+
+
+bool Token::isValid() const
+{
+  if (!isExpired()) {
+    if (notBefore.isSome()) {
+      return (Clock::now() >= notBefore.get());
+    }
+
+    return true;
+  }
+
+  // TODO(jojy): Add signature validation.
+  return false;
+}
+
+
+Try<Owned<TokenManager>> TokenManager::create(
+    const URL& realm)
+{
+  Try<Owned<TokenManagerProcess>> process = TokenManagerProcess::create(realm);
+  if (process.isError()) {
+    return Error(process.error());
+  }
+
+  return Owned<TokenManager>(new TokenManager(process.get()));
+}
+
+
+TokenManager::TokenManager(Owned<TokenManagerProcess>& process)
+  : process_(process)
+{
+  spawn(CHECK_NOTNULL(process_.get()));
+}
+
+
+TokenManager::~TokenManager()
+{
+  terminate(process_.get());
+  process::wait(process_.get());
+}
+
+
+Future<Token> TokenManager::getToken(
+    const string& service,
+    const string& scope,
+    const Option<string>& account)
+{
+  return dispatch(
+      process_.get(),
+      &TokenManagerProcess::getToken,
+      service,
+      scope,
+      account);
+}
+
+
+Try<Owned<TokenManagerProcess>> TokenManagerProcess::create(const URL& realm)
+{
+  return Owned<TokenManagerProcess>(new TokenManagerProcess(realm));
+}
+
+
+Try<Token> TokenManagerProcess::getTokenFromResponse(
+    const Response& response) const
+{
+  Try<JSON::Object> tokenJSON = JSON::parse<JSON::Object>(response.body);
+  if (tokenJSON.isError()) {
+    return Error(tokenJSON.error());
+  }
+
+  Result<JSON::String> tokenString =
+    tokenJSON.get().find<JSON::String>("token");
+
+  if (tokenString.isError()) {
+    return Error(tokenString.error());
+  }
+
+  Try<Token> result = Token::create(tokenString.get().value);
+  if (result.isError()) {
+    return Error(result.error());
+  }
+
+  return result.get();;
+}
+
+
+Future<Token> TokenManagerProcess::getToken(
+    const string& service,
+    const string& scope,
+    const Option<string>& account)
+{
+  const TokenCacheKey tokenKey = {service, scope};
+
+  if (tokenCache_.contains(tokenKey)) {
+    Token token = tokenCache_.at(tokenKey);
+
+    if (token.isValid()) {
+      return token;
+    } else {
+      LOG(WARNING) << "Cached token was invalid. Will fetch once again";
+    }
+  }
+
+  URL tokenUrl = realm_;
+  tokenUrl.path = TOKEN_PATH_PREFIX;
+
+  tokenUrl.query = {
+    {"service", service},
+    {"scope", scope},
+  };
+
+  if (account.isSome()) {
+    tokenUrl.query.insert({"account", account.get()});
+  }
+
+  return process::http::get(tokenUrl, None())
+    .after(RESPONSE_TIMEOUT, [] (Future<Response> resp) -> Future<Response> {
+      resp.discard();
+      return Failure("Timeout waiting for response to token request");
+    })
+    .then(defer(self(), [this, tokenKey](
+        const Future<Response>& response) -> Future<Token> {
+      Try<Token> token = getTokenFromResponse(response.get());
+      if (token.isError()) {
+        return Failure(
+            "Failed to parse JSON Web Token object from response: " +
+            token.error());
+      }
+
+      tokenCache_.insert({tokenKey, token.get()});
+
+      return token.get();
+    }));
+}
+
+// TODO(jojy): Add implementation for basic authentication based getToken API.
+
+} // namespace registry {
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/docker/token_manager.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/docker/token_manager.hpp b/src/slave/containerizer/mesos/provisioner/docker/token_manager.hpp
new file mode 100644
index 0000000..2f4abff
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/docker/token_manager.hpp
@@ -0,0 +1,179 @@
+/**
+ * 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 __PROVISIONER_DOCKER_TOKEN_MANAGER_HPP__
+#define __PROVISIONER_DOCKER_TOKEN_MANAGER_HPP__
+
+#include <functional>
+#include <string>
+
+#include <stout/base64.hpp>
+#include <stout/duration.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/strings.hpp>
+
+#include <process/future.hpp>
+#include <process/http.hpp>
+#include <process/process.hpp>
+#include <process/time.hpp>
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+namespace registry {
+
+
+/**
+ * Encapsulates JSON Web Token.
+ *
+ * Reference: https://tools.ietf.org/html/rfc7519.
+ */
+struct Token
+{
+  /**
+   * Factory method for Token object.
+   *
+   * Parses the raw token string and validates for token's expiration.
+   *
+   * @returns Token if parsing and validation succeeds.
+   *          Error if parsing or validation fails.
+   */
+  static Try<Token> create(const std::string& rawString);
+
+  /**
+   * Compares token's expiration time(expressed in seconds) with current time.
+   *
+   * @returns True if token's expiration time is greater than current time.
+   *          False if token's expiration time is less than or equal to current
+   *          time.
+   */
+  bool isExpired() const;
+
+  /**
+   * Validates the token if its "exp" "nbf" values are in range.
+   *
+   * @returns True if current time is within token's "exp" and "nbf" values.
+   *          False if current time is not within token's "exp" and "nbf"
+   *          values.
+   */
+  bool isValid() const;
+
+  const std::string raw;
+  const JSON::Object header;
+  const JSON::Object claims;
+  // TODO(jojy): Add signature information.
+
+private:
+  Token(
+      const std::string& raw,
+      const JSON::Object& headerJson,
+      const JSON::Object& claimsJson,
+      const Option<process::Time>& expireTime,
+      const Option<process::Time>& notBeforeTime);
+
+  static Result<process::Time> getTimeValue(
+      const JSON::Object& object,
+      const std::string& key);
+
+  const Option<process::Time> expiration;
+  const Option<process::Time> notBefore;
+};
+
+
+// Forward declaration.
+class TokenManagerProcess;
+
+
+/**
+ *  Acquires and manages docker registry tokens. It keeps the tokens in its
+ *  cache to server any future request for the same token.
+ *  The cache grows unbounded.
+ *  TODO(jojy): The cache can be optimized to prune based on the expiry time of
+ *  the token and server's issue time.
+ */
+class TokenManager
+{
+public:
+  /**
+   * Factory method for creating TokenManager object.
+   *
+   * TokenManager and registry authorization realm has a 1:1 relationship.
+   *
+   * @param realm URL of the authorization server from where token will be
+   *     requested by this TokenManager.
+   * @returns Owned<TokenManager> if success.
+   *          Error on failure.
+   */
+  static Try<process::Owned<TokenManager>> create(
+      const process::http::URL& realm);
+
+  /**
+   * Returns JSON Web Token from cache or from remote server using "Basic
+   * authorization".
+   *
+   * @param service Name of the service that hosts the resource for which
+   *     token is being requested.
+   * @param scope unique scope returned by the 401 Unauthorized response
+   *     from the registry.
+   * @param account Name of the account which the client is acting as.
+   * @param user base64 encoded userid for basic authorization.
+   * @param password base64 encoded password for basic authorization.
+   * @returns Token struct that encapsulates JSON Web Token.
+   */
+  process::Future<Token> getToken(
+      const std::string& service,
+      const std::string& scope,
+      const Option<std::string>& account,
+      const std::string& user,
+      const Option<std::string>& password);
+
+  /**
+   * Returns JSON Web Token from cache or from remote server using "TLS/Cert"
+   * based authorization.
+   *
+   * @param service Name of the service that hosts the resource for which
+   *     token is being requested.
+   * @param scope unique scope returned by the 401 Unauthorized response
+   *     from the registry.
+   * @param account Name of the account which the client is acting as.
+   * @returns Token struct that encapsulates JSON Web Token.
+   */
+  process::Future<Token> getToken(
+      const std::string& service,
+      const std::string& scope,
+      const Option<std::string>& account);
+
+  ~TokenManager();
+
+private:
+  TokenManager(process::Owned<TokenManagerProcess>& process);
+
+  TokenManager(const TokenManager&) = delete;
+  TokenManager& operator=(const TokenManager&) = delete;
+
+  process::Owned<TokenManagerProcess> process_;
+};
+
+} // namespace registry {
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __PROVISIONER_DOCKER_TOKEN_MANAGER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/paths.cpp b/src/slave/containerizer/mesos/provisioner/paths.cpp
new file mode 100644
index 0000000..b019eb9
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/paths.cpp
@@ -0,0 +1,192 @@
+/**
+ * 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 <list>
+
+#include <glog/logging.h>
+
+#include <mesos/type_utils.hpp>
+
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+
+#include <stout/os/stat.hpp>
+
+#include "slave/paths.hpp"
+
+#include "slave/containerizer/mesos/provisioner/paths.hpp"
+
+using std::list;
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace provisioner {
+namespace paths {
+
+static string getContainersDir(const string& provisionerDir)
+{
+  return path::join(provisionerDir, "containers");
+}
+
+
+static string getBackendsDir(const string& containerDir)
+{
+  return path::join(containerDir, "backends");
+}
+
+
+static string getBackendDir(const string& backendsDir, const string& backend)
+{
+  return path::join(backendsDir, backend);
+}
+
+
+static string getRootfsesDir(const string& backendDir)
+{
+  return path::join(backendDir, "rootfses");
+}
+
+
+static string getRootfsDir(const string& rootfsesDir, const string& roofsId)
+{
+  return path::join(rootfsesDir, roofsId);
+}
+
+
+string getContainerDir(
+    const string& provisionerDir,
+    const ContainerID& containerId)
+{
+  return path::join(getContainersDir(provisionerDir), containerId.value());
+}
+
+
+string getContainerRootfsDir(
+    const string& provisionerDir,
+    const ContainerID& containerId,
+    const string& backend,
+    const string& rootfsId)
+{
+  return getRootfsDir(
+      getRootfsesDir(
+          getBackendDir(
+              getBackendsDir(
+                  getContainerDir(
+                      provisionerDir,
+                      containerId)),
+              backend)),
+      rootfsId);
+}
+
+
+Try<hashset<ContainerID>> listContainers(
+    const string& provisionerDir)
+{
+  hashset<ContainerID> results;
+
+  string containersDir = getContainersDir(provisionerDir);
+  if (!os::exists(containersDir)) {
+    // No container has been created yet.
+    return results;
+  }
+
+  Try<list<string>> containerIds = os::ls(containersDir);
+  if (containerIds.isError()) {
+    return Error("Unable to list the containers directory: " +
+                 containerIds.error());
+  }
+
+  foreach (const string& entry, containerIds.get()) {
+    string containerPath = path::join(containersDir, entry);
+
+    if (!os::stat::isdir(containerPath)) {
+      LOG(WARNING) << "Ignoring unexpected container entry at: "
+                   << containerPath;
+      continue;
+    }
+
+    ContainerID containerId;
+    containerId.set_value(entry);
+    results.insert(containerId);
+  }
+
+  return results;
+}
+
+
+Try<hashmap<string, hashset<string>>> listContainerRootfses(
+    const string& provisionerDir,
+    const ContainerID& containerId)
+{
+  hashmap<string, hashset<string>> results;
+
+  string backendsDir = getBackendsDir(
+      getContainerDir(
+          provisionerDir,
+          containerId));
+
+  Try<list<string>> backends = os::ls(backendsDir);
+  if (backends.isError()) {
+    return Error("Unable to list the container directory: " + backends.error());
+  }
+
+  foreach (const string& backend, backends.get()) {
+    string backendDir = getBackendDir(backendsDir, backend);
+    if (!os::stat::isdir(backendDir)) {
+      LOG(WARNING) << "Ignoring unexpected backend entry at: " << backendDir;
+      continue;
+    }
+
+    Try<list<string>> rootfses = os::ls(getRootfsesDir(backendDir));
+    if (rootfses.isError()) {
+      return Error("Unable to list the backend directory: " + rootfses.error());
+    }
+
+    hashset<string> backendResults;
+
+    foreach (const string& rootfsId, rootfses.get()) {
+      string rootfs = getRootfsDir(getRootfsesDir(backendDir), rootfsId);
+
+      if (!os::stat::isdir(rootfs)) {
+        LOG(WARNING) << "Ignoring unexpected rootfs entry at: " << backendDir;
+        continue;
+      }
+
+      backendResults.insert(rootfsId);
+    }
+
+    if (backendResults.empty()) {
+      LOG(WARNING) << "Ignoring a backend directory with no rootfs in it: "
+                   << backendDir;
+      continue;
+    }
+
+    // The rootfs directory has passed validation.
+    results.put(backend, backendResults);
+  }
+
+  return results;
+}
+
+} // namespace paths {
+} // namespace provisioner {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/paths.hpp b/src/slave/containerizer/mesos/provisioner/paths.hpp
new file mode 100644
index 0000000..7ebd36d
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/paths.hpp
@@ -0,0 +1,80 @@
+/**
+ * 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 __PROVISIONER_PATHS_HPP__
+#define __PROVISIONER_PATHS_HPP__
+
+#include <string>
+
+#include <mesos/mesos.hpp>
+
+#include <stout/hashmap.hpp>
+#include <stout/hashset.hpp>
+#include <stout/try.hpp>
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace provisioner {
+namespace paths {
+
+// The provisioner rootfs directory is as follows:
+// <work_dir> ('--work_dir' flag)
+// |-- provisioner
+//     |-- containers
+//         |-- <container_id>
+//             |-- backends
+//                 |-- <backend> (copy, bind, etc.)
+//                     |-- rootfses
+//                         |-- <rootfs_id> (the rootfs)
+//
+// There can be multiple backends due to the change of backend flags.
+// Under each backend a rootfs is identified by the 'rootfs_id' which
+// is a UUID.
+
+std::string getContainerDir(
+    const std::string& provisionerDir,
+    const ContainerID& containerId);
+
+
+std::string getContainerRootfsDir(
+    const std::string& provisionerDir,
+    const ContainerID& containerId,
+    const std::string& backend,
+    const std::string& rootfsId);
+
+
+// Recursively "ls" the container directory and return a map of
+// backend -> {rootfsId, ...}
+Try<hashmap<std::string, hashset<std::string>>>
+listContainerRootfses(
+    const std::string& provisionerDir,
+    const ContainerID& containerId);
+
+
+// Return a set of container IDs.
+Try<hashset<ContainerID>> listContainers(
+    const std::string& provisionerDir);
+
+} // namespace paths {
+} // namespace provisioner {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __PROVISIONER_PATHS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/provisioner.cpp b/src/slave/containerizer/mesos/provisioner/provisioner.cpp
new file mode 100644
index 0000000..e99c1c9
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/provisioner.cpp
@@ -0,0 +1,444 @@
+/**
+ * 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 <mesos/type_utils.hpp>
+
+#include <process/collect.hpp>
+#include <process/defer.hpp>
+#include <process/dispatch.hpp>
+#include <process/process.hpp>
+
+#include <process/metrics/counter.hpp>
+#include <process/metrics/metrics.hpp>
+
+#include <stout/foreach.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/hashset.hpp>
+#include <stout/os.hpp>
+#include <stout/stringify.hpp>
+#include <stout/uuid.hpp>
+
+#include "slave/paths.hpp"
+
+#include "slave/containerizer/mesos/provisioner/backend.hpp"
+#include "slave/containerizer/mesos/provisioner/paths.hpp"
+#include "slave/containerizer/mesos/provisioner/provisioner.hpp"
+#include "slave/containerizer/mesos/provisioner/store.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+using std::vector;
+
+using mesos::slave::ContainerState;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+class ProvisionerProcess : public Process<ProvisionerProcess>
+{
+public:
+  ProvisionerProcess(
+      const Flags& flags,
+      const string& rootDir,
+      const hashmap<Image::Type, Owned<Store>>& stores,
+      const hashmap<string, Owned<Backend>>& backends);
+
+  Future<Nothing> recover(
+      const list<ContainerState>& states,
+      const hashset<ContainerID>& orphans);
+
+  Future<string> provision(
+      const ContainerID& containerId,
+      const Image& image);
+
+  Future<bool> destroy(const ContainerID& containerId);
+
+private:
+  Future<string> _provision(
+      const ContainerID& containerId,
+      const vector<string>& layers);
+
+  Future<bool> _destroy(const ContainerID& containerId);
+
+  const Flags flags;
+
+  // Absolute path to the provisioner root directory. It can be
+  // derived from '--work_dir' but we keep a separate copy here
+  // because we converted it into an absolute path so managed rootfs
+  // paths match the ones in 'mountinfo' (important if mount-based
+  // backends are used).
+  const string rootDir;
+
+  const hashmap<Image::Type, Owned<Store>> stores;
+  const hashmap<string, Owned<Backend>> backends;
+
+  struct Info
+  {
+    // Mappings: backend -> {rootfsId, ...}
+    hashmap<string, hashset<string>> rootfses;
+  };
+
+  hashmap<ContainerID, Owned<Info>> infos;
+
+  struct Metrics
+  {
+    Metrics();
+    ~Metrics();
+
+    process::metrics::Counter remove_container_errors;
+  } metrics;
+};
+
+
+Try<Owned<Provisioner>> Provisioner::create(
+    const Flags& flags,
+    Fetcher* fetcher)
+{
+  string _rootDir = slave::paths::getProvisionerDir(flags.work_dir);
+
+  Try<Nothing> mkdir = os::mkdir(_rootDir);
+  if (mkdir.isError()) {
+    return Error(
+        "Failed to create provisioner root directory '" +
+        _rootDir + "': " + mkdir.error());
+  }
+
+  Result<string> rootDir = os::realpath(_rootDir);
+  if (rootDir.isError()) {
+    return Error(
+        "Failed to resolve the realpath of provisioner root directory '" +
+        _rootDir + "': " + rootDir.error());
+  }
+
+  CHECK_SOME(rootDir); // Can't be None since we just created it.
+
+  Try<hashmap<Image::Type, Owned<Store>>> stores = Store::create(flags);
+  if (stores.isError()) {
+    return Error("Failed to create image stores: " + stores.error());
+  }
+
+  hashmap<string, Owned<Backend>> backends = Backend::create(flags);
+  if (backends.empty()) {
+    return Error("No usable provisioner backend created");
+  }
+
+  if (!backends.contains(flags.image_provisioner_backend)) {
+    return Error(
+        "The specified provisioner backend '" +
+        flags.image_provisioner_backend + "' is unsupported");
+  }
+
+  return Owned<Provisioner>(new Provisioner(
+      Owned<ProvisionerProcess>(new ProvisionerProcess(
+          flags,
+          rootDir.get(),
+          stores.get(),
+          backends))));
+}
+
+
+Provisioner::Provisioner(Owned<ProvisionerProcess> _process)
+  : process(_process)
+{
+  spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+Provisioner::~Provisioner()
+{
+  if (process.get() != NULL) {
+    terminate(process.get());
+    wait(process.get());
+  }
+}
+
+
+Future<Nothing> Provisioner::recover(
+    const list<ContainerState>& states,
+    const hashset<ContainerID>& orphans)
+{
+  return dispatch(
+      CHECK_NOTNULL(process.get()),
+      &ProvisionerProcess::recover,
+      states,
+      orphans);
+}
+
+
+Future<string> Provisioner::provision(
+    const ContainerID& containerId,
+    const Image& image)
+{
+  return dispatch(
+      CHECK_NOTNULL(process.get()),
+      &ProvisionerProcess::provision,
+      containerId,
+      image);
+}
+
+
+Future<bool> Provisioner::destroy(const ContainerID& containerId)
+{
+  return dispatch(
+      CHECK_NOTNULL(process.get()),
+      &ProvisionerProcess::destroy,
+      containerId);
+}
+
+
+ProvisionerProcess::ProvisionerProcess(
+    const Flags& _flags,
+    const string& _rootDir,
+    const hashmap<Image::Type, Owned<Store>>& _stores,
+    const hashmap<string, Owned<Backend>>& _backends)
+  : flags(_flags),
+    rootDir(_rootDir),
+    stores(_stores),
+    backends(_backends) {}
+
+
+Future<Nothing> ProvisionerProcess::recover(
+    const list<ContainerState>& states,
+    const hashset<ContainerID>& orphans)
+{
+  // Register living containers, including the ones that do not
+  // provision images.
+  hashset<ContainerID> alive;
+  foreach (const ContainerState& state, states) {
+    alive.insert(state.container_id());
+  }
+
+  // List provisioned containers; recover living ones; destroy unknown
+  // orphans. Note that known orphan containers are recovered as well
+  // and they will be destroyed by the containerizer using the normal
+  // cleanup path. See MESOS-2367 for details.
+  Try<hashset<ContainerID>> containers =
+    provisioner::paths::listContainers(rootDir);
+
+  if (containers.isError()) {
+    return Failure(
+        "Failed to list the containers managed by the provisioner: " +
+        containers.error());
+  }
+
+  // Scan the list of containers, register all of them with 'infos'
+  // but mark unknown orphans for immediate cleanup.
+  hashset<ContainerID> unknownOrphans;
+
+  foreach (const ContainerID& containerId, containers.get()) {
+    Owned<Info> info = Owned<Info>(new Info());
+
+    Try<hashmap<string, hashset<string>>> rootfses =
+      provisioner::paths::listContainerRootfses(rootDir, containerId);
+
+    if (rootfses.isError()) {
+      return Failure(
+          "Unable to list rootfses belonged to container " +
+          stringify(containerId) + ": " + rootfses.error());
+    }
+
+    foreachkey (const string& backend, rootfses.get()) {
+      if (!backends.contains(backend)) {
+        return Failure(
+            "Found rootfses managed by an unrecognized backend: " + backend);
+      }
+
+      info->rootfses.put(backend, rootfses.get()[backend]);
+    }
+
+    infos.put(containerId, info);
+
+    if (alive.contains(containerId) || orphans.contains(containerId)) {
+      LOG(INFO) << "Recovered container " << containerId;
+      continue;
+    } else {
+      // For immediate cleanup below.
+      unknownOrphans.insert(containerId);
+    }
+  }
+
+  // Cleanup unknown orphan containers' rootfses.
+  list<Future<bool>> cleanups;
+  foreach (const ContainerID& containerId, unknownOrphans) {
+    LOG(INFO) << "Cleaning up unknown orphan container " << containerId;
+    cleanups.push_back(destroy(containerId));
+  }
+
+  Future<Nothing> cleanup = collect(cleanups)
+    .then([]() -> Future<Nothing> { return Nothing(); });
+
+  // Recover stores.
+  list<Future<Nothing>> recovers;
+  foreachvalue (const Owned<Store>& store, stores) {
+    recovers.push_back(store->recover());
+  }
+
+  Future<Nothing> recover = collect(recovers)
+    .then([]() -> Future<Nothing> { return Nothing(); });
+
+  // A successful provisioner recovery depends on:
+  // 1) Recovery of living containers and known orphans (done above).
+  // 2) Successful cleanup of unknown orphans.
+  // 3) Successful store recovery.
+  //
+  // TODO(jieyu): Do not recover 'store' before unknown orphans are
+  // cleaned up. In the future, we may want to cleanup unused rootfses
+  // in 'store', which might fail if there still exist unknown orphans
+  // holding references to them.
+  return collect(cleanup, recover)
+    .then([=]() -> Future<Nothing> {
+      LOG(INFO) << "Provisioner recovery complete";
+      return Nothing();
+    });
+}
+
+
+Future<string> ProvisionerProcess::provision(
+    const ContainerID& containerId,
+    const Image& image)
+{
+  if (!stores.contains(image.type())) {
+    return Failure(
+        "Unsupported container image type: " +
+        stringify(image.type()));
+  }
+
+  // Get and then provision image layers from the store.
+  return stores.get(image.type()).get()->get(image)
+    .then(defer(self(), &Self::_provision, containerId, lambda::_1));
+}
+
+
+Future<string> ProvisionerProcess::_provision(
+    const ContainerID& containerId,
+    const vector<string>& layers)
+{
+  // TODO(jieyu): Choose a backend smartly. For instance, if there is
+  // only one layer returned from the store. prefer to use bind
+  // backend because it's the simplest.
+  const string& backend = flags.image_provisioner_backend;
+  CHECK(backends.contains(backend));
+
+  string rootfsId = UUID::random().toString();
+
+  string rootfs = provisioner::paths::getContainerRootfsDir(
+      rootDir,
+      containerId,
+      backend,
+      rootfsId);
+
+  LOG(INFO) << "Provisioning image rootfs '" << rootfs
+            << "' for container " << containerId;
+
+  // NOTE: It's likely that the container ID already exists in 'infos'
+  // because one container might provision multiple images.
+  if (!infos.contains(containerId)) {
+    infos.put(containerId, Owned<Info>(new Info()));
+  }
+
+  infos[containerId]->rootfses[backend].insert(rootfsId);
+
+  return backends.get(backend).get()->provision(layers, rootfs)
+    .then([rootfs]() -> Future<string> { return rootfs; });
+}
+
+
+Future<bool> ProvisionerProcess::destroy(const ContainerID& containerId)
+{
+  if (!infos.contains(containerId)) {
+    LOG(INFO) << "Ignoring destroy request for unknown container "
+              << containerId;
+
+    return false;
+  }
+
+  // Unregister the container first. If destroy() fails, we can rely
+  // on recover() to retry it later.
+  Owned<Info> info = infos[containerId];
+  infos.erase(containerId);
+
+  list<Future<bool>> futures;
+  foreachkey (const string& backend, info->rootfses) {
+    if (!backends.contains(backend)) {
+      return Failure("Unknown backend '" + backend + "'");
+    }
+
+    foreach (const string& rootfsId, info->rootfses[backend]) {
+      string rootfs = provisioner::paths::getContainerRootfsDir(
+          rootDir,
+          containerId,
+          backend,
+          rootfsId);
+
+      LOG(INFO) << "Destroying container rootfs at '" << rootfs
+                << "' for container " << containerId;
+
+      futures.push_back(backends.get(backend).get()->destroy(rootfs));
+    }
+  }
+
+  // TODO(xujyan): Revisit the usefulness of this return value.
+  return collect(futures)
+    .then(defer(self(), &ProvisionerProcess::_destroy, containerId));
+}
+
+
+Future<bool> ProvisionerProcess::_destroy(const ContainerID& containerId)
+{
+  // This should be fairly cheap as the directory should only
+  // contain a few empty sub-directories at this point.
+  //
+  // TODO(jieyu): Currently, it's possible that some directories
+  // cannot be removed due to EBUSY. EBUSY is caused by the race
+  // between cleaning up this container and new containers copying
+  // the host mount table. It's OK to ignore them. The cleanup
+  // will be retried during slave recovery.
+  string containerDir =
+    provisioner::paths::getContainerDir(rootDir, containerId);
+
+  Try<Nothing> rmdir = os::rmdir(containerDir);
+  if (rmdir.isError()) {
+    LOG(ERROR) << "Failed to remove the provisioned container directory "
+               << "at '" << containerDir << "': " << rmdir.error();
+
+    ++metrics.remove_container_errors;
+  }
+
+  return true;
+}
+
+
+ProvisionerProcess::Metrics::Metrics()
+  : remove_container_errors(
+      "containerizer/mesos/provisioner/remove_container_errors")
+{
+  process::metrics::add(remove_container_errors);
+}
+
+
+ProvisionerProcess::Metrics::~Metrics()
+{
+  process::metrics::remove(remove_container_errors);
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/provisioner.hpp b/src/slave/containerizer/mesos/provisioner/provisioner.hpp
new file mode 100644
index 0000000..912fc5a
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/provisioner.hpp
@@ -0,0 +1,94 @@
+/**
+ * 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 __PROVISIONER_HPP__
+#define __PROVISIONER_HPP__
+
+#include <list>
+
+#include <mesos/resources.hpp>
+
+#include <mesos/slave/isolator.hpp> // For ContainerState.
+
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/fetcher.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// Forward declaration.
+class ProvisionerProcess;
+
+
+class Provisioner
+{
+public:
+  // Create the provisioner based on the specified flags.
+  static Try<process::Owned<Provisioner>> create(
+      const Flags& flags,
+      Fetcher* fetcher);
+
+  // NOTE: Made 'virtual' for mocking and testing.
+  virtual ~Provisioner();
+
+  // Recover root filesystems for containers from the run states and
+  // the orphan containers (known to the launcher but not known to the
+  // slave) detected by the launcher. This function is also
+  // responsible for cleaning up any intermediate artifacts (e.g.
+  // directories) to not leak anything.
+  virtual process::Future<Nothing> recover(
+      const std::list<mesos::slave::ContainerState>& states,
+      const hashset<ContainerID>& orphans);
+
+  // Provision a root filesystem for the container using the specified
+  // image and return the absolute path to the root filesystem.
+  virtual process::Future<std::string> provision(
+      const ContainerID& containerId,
+      const Image& image);
+
+  // Destroy a previously provisioned root filesystem. Assumes that
+  // all references (e.g., mounts, open files) to the provisioned
+  // filesystem have been removed. Return false if there is no
+  // provisioned root filesystem for the given container.
+  virtual process::Future<bool> destroy(const ContainerID& containerId);
+
+protected:
+  Provisioner() {} // For creating mock object.
+
+private:
+  explicit Provisioner(process::Owned<ProvisionerProcess> process);
+
+  Provisioner(const Provisioner&) = delete; // Not copyable.
+  Provisioner& operator=(const Provisioner&) = delete; // Not assignable.
+
+  process::Owned<ProvisionerProcess> process;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __PROVISIONER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/store.cpp b/src/slave/containerizer/mesos/provisioner/store.cpp
new file mode 100644
index 0000000..4b8abe6
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/store.cpp
@@ -0,0 +1,79 @@
+/**
+ * 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 <string>
+
+#include <mesos/type_utils.hpp>
+
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/strings.hpp>
+
+#include "slave/containerizer/mesos/provisioner/store.hpp"
+
+#include "slave/containerizer/mesos/provisioner/appc/store.hpp"
+
+#include "slave/containerizer/mesos/provisioner/docker/store.hpp"
+
+using namespace process;
+
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+Try<hashmap<Image::Type, Owned<Store>>> Store::create(const Flags& flags)
+{
+  if (flags.image_providers.isNone()) {
+    return hashmap<Image::Type, Owned<Store>>();
+  }
+
+  hashmap<Image::Type, Try<Owned<Store>>(*)(const Flags&)> creators;
+  creators.put(Image::APPC, &appc::Store::create);
+  creators.put(Image::DOCKER, &docker::Store::create);
+
+  hashmap<Image::Type, Owned<Store>> stores;
+
+  foreach (const string& type,
+           strings::tokenize(flags.image_providers.get(), ",")) {
+    Image::Type imageType;
+    if (!Image::Type_Parse(strings::upper(type), &imageType)) {
+      return Error("Unknown image type '" + type + "'");
+    }
+
+    if (!creators.contains(imageType)) {
+      return Error("Unsupported image type '" + type + "'");
+    }
+
+    Try<Owned<Store>> store = creators[imageType](flags);
+    if (store.isError()) {
+      return Error(
+          "Failed to create store for image type '" +
+          type + "': " + store.error());
+    }
+
+    stores.put(imageType, store.get());
+  }
+
+  return stores;
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/mesos/provisioner/store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/store.hpp b/src/slave/containerizer/mesos/provisioner/store.hpp
new file mode 100644
index 0000000..cf3e7d7
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/store.hpp
@@ -0,0 +1,75 @@
+/**
+ * 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 __PROVISIONER_STORE_HPP__
+#define __PROVISIONER_STORE_HPP__
+
+#include <string>
+#include <vector>
+
+#include <mesos/mesos.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include <stout/try.hpp>
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// An image store abstraction that "stores" images. It serves as a
+// read-through cache (cache misses are fetched remotely and
+// transparently) for images.
+class Store
+{
+public:
+  static Try<hashmap<Image::Type, process::Owned<Store>>> create(
+      const Flags& flags);
+
+  virtual ~Store() {}
+
+  virtual process::Future<Nothing> recover() = 0;
+
+  // Get the specified image (and all its recursive dependencies) as a
+  // list of rootfs layers in the topological order (dependencies go
+  // before dependents in the list). The images required to build this
+  // list are either retrieved from the local cache or fetched
+  // remotely.
+  //
+  // NOTE: The returned list should not have duplicates. e.g., in the
+  // following scenario the result should be [C, B, D, A] (B before D
+  // in this example is decided by the order in which A specifies its
+  // dependencies).
+  //
+  // A --> B --> C
+  // |           ^
+  // |---> D ----|
+  //
+  // The returned future fails if the requested image or any of its
+  // dependencies cannot be found or failed to be fetched.
+  virtual process::Future<std::vector<std::string>> get(const Image& image) = 0;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __PROVISIONER_STORE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/appc/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/appc/paths.cpp b/src/slave/containerizer/provisioner/appc/paths.cpp
deleted file mode 100644
index 8817c0f..0000000
--- a/src/slave/containerizer/provisioner/appc/paths.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * 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 <list>
-
-#include <glog/logging.h>
-
-#include <stout/path.hpp>
-
-#include "slave/containerizer/provisioner/appc/paths.hpp"
-
-using std::list;
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-namespace paths {
-
-string getStagingDir(const string& storeDir)
-{
-  return path::join(storeDir, "staging");
-}
-
-
-string getImagesDir(const string& storeDir)
-{
-  return path::join(storeDir, "images");
-}
-
-
-string getImagePath(const string& storeDir, const string& imageId)
-{
-  return path::join(getImagesDir(storeDir), imageId);
-}
-
-
-string getImageRootfsPath(
-    const string& storeDir,
-    const string& imageId)
-{
-  return path::join(getImagePath(storeDir, imageId), "rootfs");
-}
-
-
-string getImageRootfsPath(const string& imagePath)
-{
-  return path::join(imagePath, "rootfs");
-}
-
-
-string getImageManifestPath(
-    const string& storeDir,
-    const string& imageId)
-{
-  return path::join(getImagePath(storeDir, imageId), "manifest");
-}
-
-
-string getImageManifestPath(const string& imagePath)
-{
-  return path::join(imagePath, "manifest");
-}
-
-} // namespace paths {
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/appc/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/appc/paths.hpp b/src/slave/containerizer/provisioner/appc/paths.hpp
deleted file mode 100644
index 7c36d67..0000000
--- a/src/slave/containerizer/provisioner/appc/paths.hpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * 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 __PROVISIONER_APPC_PATHS_HPP__
-#define __PROVISIONER_APPC_PATHS_HPP__
-
-#include <string>
-
-#include <mesos/mesos.hpp>
-
-#include <stout/hashmap.hpp>
-#include <stout/try.hpp>
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-namespace paths {
-
-// The appc store file system layout is as follows:
-//
-// <store_dir> ('--appc_store_dir' flag)
-// |--staging (contains temp directories for staging downloads)
-// |
-// |--images (stores validated images)
-//    |--<image_id> (in the form of "sha512-<128_character_hash_sum>")
-//       |--manifest
-//       |--rootfs
-//          |--... (according to the ACI spec)
-//
-// TODO(xujyan): The staging directory is unused for now (it's
-// externally managed) but implemented to illustrate the need for a
-// separate 'images' directory. Complete the layout diagram when the
-// staging directory is utilized by the provisioner.
-
-std::string getStagingDir(const std::string& storeDir);
-
-
-std::string getImagesDir(const std::string& storeDir);
-
-
-std::string getImagePath(
-    const std::string& storeDir,
-    const std::string& imageId);
-
-
-std::string getImageRootfsPath(
-    const std::string& storeDir,
-    const std::string& imageId);
-
-
-std::string getImageRootfsPath(const std::string& imagePath);
-
-
-std::string getImageManifestPath(
-    const std::string& storeDir,
-    const std::string& imageId);
-
-
-std::string getImageManifestPath(const std::string& imagePath);
-
-} // namespace paths {
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __PROVISIONER_APPC_PATHS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/appc/spec.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/appc/spec.cpp b/src/slave/containerizer/provisioner/appc/spec.cpp
deleted file mode 100644
index bbe523d..0000000
--- a/src/slave/containerizer/provisioner/appc/spec.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * 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 <stout/os/stat.hpp>
-#include <stout/protobuf.hpp>
-#include <stout/strings.hpp>
-
-#include "slave/containerizer/provisioner/appc/paths.hpp"
-#include "slave/containerizer/provisioner/appc/spec.hpp"
-
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-namespace spec {
-
-Option<Error> validateManifest(const AppcImageManifest& manifest)
-{
-  // TODO(idownes): Validate that required fields are present when
-  // this cannot be expressed in the protobuf specification, e.g.,
-  // repeated fields with >= 1.
-  // TODO(xujyan): More thorough type validation:
-  // https://github.com/appc/spec/blob/master/spec/types.md
-  if (manifest.ackind() != "ImageManifest") {
-    return Error("Incorrect acKind field: " + manifest.ackind());
-  }
-
-  return None();
-}
-
-
-Option<Error> validateImageID(const string& imageId)
-{
-  if (!strings::startsWith(imageId, "sha512-")) {
-    return Error("Image ID needs to start with sha512-");
-  }
-
-  string hash = strings::remove(imageId, "sha512-", strings::PREFIX);
-  if (hash.length() != 128) {
-    return Error("Invalid hash length for: " + hash);
-  }
-
-  return None();
-}
-
-
-Option<Error> validateLayout(const string& imagePath)
-{
-  if (!os::stat::isdir(paths::getImageRootfsPath(imagePath))) {
-    return Error("No rootfs directory found in image layout");
-  }
-
-  if (!os::stat::isfile(paths::getImageManifestPath(imagePath))) {
-    return Error("No manifest found in image layout");
-  }
-
-  return None();
-}
-
-
-Try<AppcImageManifest> parse(const string& value)
-{
-  Try<JSON::Object> json = JSON::parse<JSON::Object>(value);
-  if (json.isError()) {
-    return Error("JSON parse failed: " + json.error());
-  }
-
-  Try<AppcImageManifest> manifest =
-    protobuf::parse<AppcImageManifest>(json.get());
-
-  if (manifest.isError()) {
-    return Error("Protobuf parse failed: " + manifest.error());
-  }
-
-  Option<Error> error = validateManifest(manifest.get());
-  if (error.isSome()) {
-    return Error("Schema validation failed: " + error.get().message);
-  }
-
-  return manifest.get();
-}
-
-} // namespace spec {
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/appc/spec.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/appc/spec.hpp b/src/slave/containerizer/provisioner/appc/spec.hpp
deleted file mode 100644
index 2bc8c6f..0000000
--- a/src/slave/containerizer/provisioner/appc/spec.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * 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 __PROVISIONER_APPC_SPEC_HPP__
-#define __PROVISIONER_APPC_SPEC_HPP__
-
-#include <string>
-
-#include <stout/error.hpp>
-#include <stout/option.hpp>
-
-#include <mesos/mesos.hpp>
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-namespace spec {
-
-// Validate if the specified image manifest conforms to the Appc spec.
-Option<Error> validateManifest(const AppcImageManifest& manifest);
-
-// Validate if the specified image ID conforms to the Appc spec.
-Option<Error> validateImageID(const std::string& imageId);
-
-// Validate if the specified image has the disk layout that conforms
-// to the Appc spec.
-Option<Error> validateLayout(const std::string& imagePath);
-
-// Parse the AppcImageManifest in the specified JSON string.
-Try<AppcImageManifest> parse(const std::string& value);
-
-} // namespace spec {
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __PROVISIONER_APPC_SPEC_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/appc/store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/appc/store.cpp b/src/slave/containerizer/provisioner/appc/store.cpp
deleted file mode 100644
index a5ef4ea..0000000
--- a/src/slave/containerizer/provisioner/appc/store.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-/**
- * 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 <list>
-
-#include <glog/logging.h>
-
-#include <process/defer.hpp>
-#include <process/dispatch.hpp>
-
-#include <stout/check.hpp>
-#include <stout/hashmap.hpp>
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#include "slave/containerizer/provisioner/appc/paths.hpp"
-#include "slave/containerizer/provisioner/appc/spec.hpp"
-#include "slave/containerizer/provisioner/appc/store.hpp"
-
-using namespace process;
-
-using std::list;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-
-// Defines a locally cached image (which has passed validation).
-struct CachedImage
-{
-  static Try<CachedImage> create(const string& imagePath);
-
-  CachedImage(
-      const AppcImageManifest& _manifest,
-      const string& _id,
-      const string& _path)
-    : manifest(_manifest), id(_id), path(_path) {}
-
-  string rootfs() const
-  {
-    return path::join(path, "rootfs");
-  }
-
-  const AppcImageManifest manifest;
-
-  // Image ID of the format "sha512-value" where "value" is the hex
-  // encoded string of the sha512 digest of the uncompressed tar file
-  // of the image.
-  const string id;
-
-  // Absolute path to the extracted image.
-  const string path;
-};
-
-
-Try<CachedImage> CachedImage::create(const string& imagePath)
-{
-  Option<Error> error = spec::validateLayout(imagePath);
-  if (error.isSome()) {
-    return Error("Invalid image layout: " + error.get().message);
-  }
-
-  string imageId = Path(imagePath).basename();
-
-  error = spec::validateImageID(imageId);
-  if (error.isSome()) {
-    return Error("Invalid image ID: " + error.get().message);
-  }
-
-  Try<string> read = os::read(paths::getImageManifestPath(imagePath));
-  if (read.isError()) {
-    return Error("Failed to read manifest: " + read.error());
-  }
-
-  Try<AppcImageManifest> manifest = spec::parse(read.get());
-  if (manifest.isError()) {
-    return Error("Failed to parse manifest: " + manifest.error());
-  }
-
-  return CachedImage(manifest.get(), imageId, imagePath);
-}
-
-
-// Helper that implements this:
-// https://github.com/appc/spec/blob/master/spec/aci.md#dependency-matching
-static bool matches(Image::Appc requirements, const CachedImage& candidate)
-{
-  // The name must match.
-  if (candidate.manifest.name() != requirements.name()) {
-    return false;
-  }
-
-  // If an id is specified the candidate must match.
-  if (requirements.has_id() && (candidate.id != requirements.id())) {
-    return false;
-  }
-
-  // Extract labels for easier comparison, this also weeds out duplicates.
-  // TODO(xujyan): Detect duplicate labels in image manifest validation
-  // and Image::Appc validation.
-  hashmap<string, string> requiredLabels;
-  foreach (const Label& label, requirements.labels().labels()) {
-    requiredLabels[label.key()] = label.value();
-  }
-
-  hashmap<string, string> candidateLabels;
-  foreach (const AppcImageManifest::Label& label,
-           candidate.manifest.labels()) {
-    candidateLabels[label.name()] = label.value();
-  }
-
-  // Any label specified must be present and match in the candidate.
-  foreachpair (const string& name,
-               const string& value,
-               requiredLabels) {
-    if (!candidateLabels.contains(name) ||
-        candidateLabels.get(name).get() != value) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-
-class StoreProcess : public Process<StoreProcess>
-{
-public:
-  StoreProcess(const string& rootDir);
-
-  ~StoreProcess() {}
-
-  Future<Nothing> recover();
-
-  Future<vector<string>> get(const Image& image);
-
-private:
-  // Absolute path to the root directory of the store as defined by
-  // --appc_store_dir.
-  const string rootDir;
-
-  // Mappings: name -> id -> image.
-  hashmap<string, hashmap<string, CachedImage>> images;
-};
-
-
-Try<Owned<slave::Store>> Store::create(const Flags& flags)
-{
-  Try<Nothing> mkdir = os::mkdir(paths::getImagesDir(flags.appc_store_dir));
-  if (mkdir.isError()) {
-    return Error("Failed to create the images directory: " + mkdir.error());
-  }
-
-  // Make sure the root path is canonical so all image paths derived
-  // from it are canonical too.
-  Result<string> rootDir = os::realpath(flags.appc_store_dir);
-  if (!rootDir.isSome()) {
-    // The above mkdir call recursively creates the store directory
-    // if necessary so it cannot be None here.
-    CHECK_ERROR(rootDir);
-
-    return Error(
-        "Failed to get the realpath of the store root directory: " +
-        rootDir.error());
-  }
-
-  return Owned<slave::Store>(new Store(
-      Owned<StoreProcess>(new StoreProcess(rootDir.get()))));
-}
-
-
-Store::Store(Owned<StoreProcess> _process)
-  : process(_process)
-{
-  spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-Store::~Store()
-{
-  terminate(process.get());
-  wait(process.get());
-}
-
-
-Future<Nothing> Store::recover()
-{
-  return dispatch(process.get(), &StoreProcess::recover);
-}
-
-
-Future<vector<string>> Store::get(const Image& image)
-{
-  return dispatch(process.get(), &StoreProcess::get, image);
-}
-
-
-StoreProcess::StoreProcess(const string& _rootDir) : rootDir(_rootDir) {}
-
-
-Future<Nothing> StoreProcess::recover()
-{
-  // Recover everything in the store.
-  Try<list<string>> imageIds = os::ls(paths::getImagesDir(rootDir));
-  if (imageIds.isError()) {
-    return Failure(
-        "Failed to list images under '" +
-        paths::getImagesDir(rootDir) + "': " +
-        imageIds.error());
-  }
-
-  foreach (const string& imageId, imageIds.get()) {
-    string path = paths::getImagePath(rootDir, imageId);
-    if (!os::stat::isdir(path)) {
-      LOG(WARNING) << "Unexpected entry in storage: " << imageId;
-      continue;
-    }
-
-    Try<CachedImage> image = CachedImage::create(path);
-    if (image.isError()) {
-      LOG(WARNING) << "Unexpected entry in storage: " << image.error();
-      continue;
-    }
-
-    LOG(INFO) << "Restored image '" << image.get().manifest.name() << "'";
-
-    images[image.get().manifest.name()].put(image.get().id, image.get());
-  }
-
-  return Nothing();
-}
-
-
-Future<vector<string>> StoreProcess::get(const Image& image)
-{
-  if (image.type() != Image::APPC) {
-    return Failure("Not an Appc image: " + stringify(image.type()));
-  }
-
-  const Image::Appc& appc = image.appc();
-
-  if (!images.contains(appc.name())) {
-    return Failure("No Appc image named '" + appc.name() + "' can be found");
-  }
-
-  // Get local candidates.
-  vector<CachedImage> candidates;
-  foreach (const CachedImage& candidate, images[appc.name()].values()) {
-    // The first match is returned.
-    // TODO(xujyan): Some tie-breaking rules are necessary.
-    if (matches(appc, candidate)) {
-      LOG(INFO) << "Found match for Appc image '" << appc.name()
-                << "' in the store";
-
-      // The Appc store current doesn't support dependencies and this
-      // is enforced by manifest validation: if the image's manifest
-      // contains dependencies it would fail the validation and
-      // wouldn't be stored in the store.
-      return vector<string>({candidate.rootfs()});
-    }
-  }
-
-  return Failure("No Appc image named '" + appc.name() +
-                 "' can match the requirements");
-}
-
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/appc/store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/appc/store.hpp b/src/slave/containerizer/provisioner/appc/store.hpp
deleted file mode 100644
index e845519..0000000
--- a/src/slave/containerizer/provisioner/appc/store.hpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * 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 __PROVISIONER_APPC_STORE_HPP__
-#define __PROVISIONER_APPC_STORE_HPP__
-
-#include "slave/containerizer/provisioner/store.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-
-// Forward declaration.
-class StoreProcess;
-
-
-class Store : public slave::Store
-{
-public:
-  static Try<process::Owned<slave::Store>> create(const Flags& flags);
-
-  ~Store();
-
-  virtual process::Future<Nothing> recover();
-
-  // TODO(xujyan): Fetching remotely is not implemented for now and
-  // until then the future fails directly if the image is not in the
-  // local cache.
-  // TODO(xujyan): The store currently doesn't support images that
-  // have dependencies and we should add it later.
-  virtual process::Future<std::vector<std::string>> get(const Image& image);
-
-private:
-  Store(process::Owned<StoreProcess> process);
-
-  Store(const Store&) = delete; // Not copyable.
-  Store& operator=(const Store&) = delete; // Not assignable.
-
-  process::Owned<StoreProcess> process;
-};
-
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __PROVISIONER_APPC_STORE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/backend.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/backend.cpp b/src/slave/containerizer/provisioner/backend.cpp
deleted file mode 100644
index b5d9670..0000000
--- a/src/slave/containerizer/provisioner/backend.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * 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 <glog/logging.h>
-
-#include <stout/os.hpp>
-
-#include "slave/containerizer/provisioner/backend.hpp"
-
-#include "slave/containerizer/provisioner/backends/bind.hpp"
-#include "slave/containerizer/provisioner/backends/copy.hpp"
-
-using namespace process;
-
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-hashmap<string, Owned<Backend>> Backend::create(const Flags& flags)
-{
-  hashmap<string, Try<Owned<Backend>>(*)(const Flags&)> creators;
-
-#ifdef __linux__
-  creators.put("bind", &BindBackend::create);
-#endif // __linux__
-  creators.put("copy", &CopyBackend::create);
-
-  hashmap<string, Owned<Backend>> backends;
-
-  foreachkey (const string& name, creators) {
-    Try<Owned<Backend>> backend = creators[name](flags);
-    if (backend.isError()) {
-      LOG(WARNING) << "Failed to create '" << name << "' backend: "
-                   << backend.error();
-      continue;
-    }
-    backends.put(name, backend.get());
-  }
-
-  return backends;
-}
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/backend.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/backend.hpp b/src/slave/containerizer/provisioner/backend.hpp
deleted file mode 100644
index 1c80b79..0000000
--- a/src/slave/containerizer/provisioner/backend.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * 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 __PROVISIONER_BACKEND_HPP__
-#define __PROVISIONER_BACKEND_HPP__
-
-#include <string>
-#include <vector>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/hashmap.hpp>
-#include <stout/try.hpp>
-
-#include "slave/flags.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-// Provision a root filesystem for a container.
-class Backend
-{
-public:
-  virtual ~Backend() {}
-
-  // Return a map of all supported backends keyed by their names. Note
-  // that Backends that failed to be created due to incorrect flags are
-  // simply not added to the result.
-  static hashmap<std::string, process::Owned<Backend>> create(
-      const Flags& flags);
-
-  // Provision a root filesystem for a container into the specified 'rootfs'
-  // directory by applying the specified list of root filesystem layers in
-  // the list order, i.e., files in a layer can overwrite/shadow those from
-  // another layer earlier in the list.
-  virtual process::Future<Nothing> provision(
-      const std::vector<std::string>& layers,
-      const std::string& rootfs) = 0;
-
-  // Destroy the root filesystem provisioned at the specified 'rootfs'
-  // directory. Return false if there is no provisioned root filesystem
-  // to destroy for the given directory.
-  virtual process::Future<bool> destroy(const std::string& rootfs) = 0;
-};
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __PROVISIONER_BACKEND_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/backends/bind.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/backends/bind.cpp b/src/slave/containerizer/provisioner/backends/bind.cpp
deleted file mode 100644
index 1fe1746..0000000
--- a/src/slave/containerizer/provisioner/backends/bind.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/**
- * 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 <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <process/dispatch.hpp>
-#include <process/process.hpp>
-
-#include <process/metrics/counter.hpp>
-#include <process/metrics/metrics.hpp>
-
-#include <stout/foreach.hpp>
-#include <stout/os.hpp>
-
-#include "linux/fs.hpp"
-
-#include "slave/containerizer/provisioner/backends/bind.hpp"
-
-using namespace process;
-
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-class BindBackendProcess : public Process<BindBackendProcess>
-{
-public:
-  Future<Nothing> provision(const vector<string>& layers, const string& rootfs);
-
-  Future<bool> destroy(const string& rootfs);
-
-  struct Metrics
-  {
-    Metrics();
-    ~Metrics();
-
-    process::metrics::Counter remove_rootfs_errors;
-  } metrics;
-};
-
-
-Try<Owned<Backend>> BindBackend::create(const Flags&)
-{
-  Result<string> user = os::user();
-  if (!user.isSome()) {
-    return Error("Failed to determine user: " +
-                 (user.isError() ? user.error() : "username not found"));
-  }
-
-  if (user.get() != "root") {
-    return Error("BindBackend requires root privileges");
-  }
-
-  return Owned<Backend>(new BindBackend(
-      Owned<BindBackendProcess>(new BindBackendProcess())));
-}
-
-
-BindBackend::~BindBackend()
-{
-  terminate(process.get());
-  wait(process.get());
-}
-
-
-BindBackend::BindBackend(Owned<BindBackendProcess> _process)
-  : process(_process)
-{
-  spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-Future<Nothing> BindBackend::provision(
-    const vector<string>& layers,
-    const string& rootfs)
-{
-  return dispatch(
-      process.get(), &BindBackendProcess::provision, layers, rootfs);
-}
-
-
-Future<bool> BindBackend::destroy(const string& rootfs)
-{
-  return dispatch(process.get(), &BindBackendProcess::destroy, rootfs);
-}
-
-
-Future<Nothing> BindBackendProcess::provision(
-    const vector<string>& layers,
-    const string& rootfs)
-{
-  if (layers.size() > 1) {
-    return Failure(
-        "Multiple layers are not supported by the bind backend");
-  }
-
-  if (layers.size() == 0) {
-    return Failure("No filesystem layer provided");
-  }
-
-  Try<Nothing> mkdir = os::mkdir(rootfs);
-  if (mkdir.isError()) {
-    return Failure("Failed to create container rootfs at " + rootfs);
-  }
-
-  // TODO(xujyan): Use MS_REC? Does any provisioner use mounts within
-  // its image store in a single layer?
-  Try<Nothing> mount = fs::mount(
-      layers.front(),
-      rootfs,
-      None(),
-      MS_BIND,
-      NULL);
-
-  if (mount.isError()) {
-    return Failure(
-        "Failed to bind mount rootfs '" + layers.front() +
-        "' to '" + rootfs + "': " + mount.error());
-  }
-
-  // And remount it read-only.
-  mount = fs::mount(
-      None(), // Ignored.
-      rootfs,
-      None(),
-      MS_BIND | MS_RDONLY | MS_REMOUNT,
-      NULL);
-
-  if (mount.isError()) {
-    return Failure(
-        "Failed to remount rootfs '" + rootfs + "' read-only: " +
-        mount.error());
-  }
-
-  // Mark the mount as shared+slave.
-  mount = fs::mount(
-      None(),
-      rootfs,
-      None(),
-      MS_SLAVE,
-      NULL);
-
-  if (mount.isError()) {
-    return Failure(
-        "Failed to mark mount '" + rootfs +
-        "' as a slave mount: " + mount.error());
-  }
-
-  mount = fs::mount(
-      None(),
-      rootfs,
-      None(),
-      MS_SHARED,
-      NULL);
-
-  if (mount.isError()) {
-    return Failure(
-        "Failed to mark mount '" + rootfs +
-        "' as a shared mount: " + mount.error());
-  }
-
-  return Nothing();
-}
-
-
-Future<bool> BindBackendProcess::destroy(const string& rootfs)
-{
-  Try<fs::MountInfoTable> mountTable = fs::MountInfoTable::read();
-
-  if (mountTable.isError()) {
-    return Failure("Failed to read mount table: " + mountTable.error());
-  }
-
-  foreach (const fs::MountInfoTable::Entry& entry, mountTable.get().entries) {
-    // TODO(xujyan): If MS_REC was used in 'provision()' we would need
-    // to check `strings::startsWith(entry.target, rootfs)` here to
-    // unmount all nested mounts.
-    if (entry.target == rootfs) {
-      // NOTE: This would fail if the rootfs is still in use.
-      Try<Nothing> unmount = fs::unmount(entry.target);
-      if (unmount.isError()) {
-        return Failure(
-            "Failed to destroy bind-mounted rootfs '" + rootfs + "': " +
-            unmount.error());
-      }
-
-      // TODO(jieyu): If 'rmdir' here returns EBUSY, we still returns
-      // a success. This is currently possible because the parent
-      // mount of 'rootfs' might not be a shared mount. Thus,
-      // containers in different mount namespaces might hold extra
-      // references to this mount. It is OK to ignore the EBUSY error
-      // because the provisioner will later try to delete all the
-      // rootfses for the terminated containers.
-      if (::rmdir(rootfs.c_str()) != 0) {
-        string message =
-          "Failed to remove rootfs mount point '" + rootfs +
-          "': " + strerror(errno);
-
-        if (errno == EBUSY) {
-          LOG(ERROR) << message;
-          ++metrics.remove_rootfs_errors;
-        } else {
-          return Failure(message);
-        }
-      }
-
-      return true;
-    }
-  }
-
-  return false;
-}
-
-
-BindBackendProcess::Metrics::Metrics()
-  : remove_rootfs_errors(
-      "containerizer/mesos/provisioner/bind/remove_rootfs_errors")
-{
-  process::metrics::add(remove_rootfs_errors);
-}
-
-
-BindBackendProcess::Metrics::~Metrics()
-{
-  process::metrics::remove(remove_rootfs_errors);
-}
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a722d74/src/slave/containerizer/provisioner/backends/bind.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/backends/bind.hpp b/src/slave/containerizer/provisioner/backends/bind.hpp
deleted file mode 100644
index 1685938..0000000
--- a/src/slave/containerizer/provisioner/backends/bind.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * 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 __PROVISIONER_BACKENDS_BIND_HPP__
-#define __PROVISIONER_BACKENDS_BIND_HPP__
-
-#include "slave/containerizer/provisioner/backend.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-// Forward declaration.
-class BindBackendProcess;
-
-
-// This is a specialized backend that may be useful for deployments
-// using large (multi-GB) single-layer images *and* where more recent
-// kernel features such as overlayfs are not available (overlayfs-based
-// backend tracked by MESOS-2971). For small images (10's to 100's of MB)
-// the copy backend may be sufficient. NOTE:
-// 1) BindBackend supports only a single layer. Multi-layer images will
-//    fail to provision and the container will fail to launch!
-// 2) The filesystem is read-only because all containers using this
-//    image share the source. Select writable areas can be achieved by
-//    mounting read-write volumes to places like /tmp, /var/tmp,
-//    /home, etc. using the ContainerInfo. These can be relative to
-//    the executor work directory.
-//    N.B. Since the filesystem is read-only, '--sandbox_directory' must
-//    already exist within the filesystem because the filesystem isolator
-//    is unable to create it!
-// 3) It's fast because the bind mount requires (nearly) zero IO.
-class BindBackend : public Backend
-{
-public:
-  virtual ~BindBackend();
-
-  // BindBackend doesn't use any flag.
-  static Try<process::Owned<Backend>> create(const Flags&);
-
-  virtual process::Future<Nothing> provision(
-      const std::vector<std::string>& layers,
-      const std::string& rootfs);
-
-  virtual process::Future<bool> destroy(const std::string& rootfs);
-
-private:
-  explicit BindBackend(process::Owned<BindBackendProcess> process);
-
-  BindBackend(const BindBackend&); // Not copyable.
-  BindBackend& operator=(const BindBackend&); // Not assignable.
-
-  process::Owned<BindBackendProcess> process;
-};
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __PROVISIONER_BACKENDS_BIND_HPP__