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__