You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by vi...@apache.org on 2014/02/12 02:31:14 UTC
[08/11] git commit: Containerizer (part 1)
Containerizer (part 1)
The proposed Containerizer interface is to replace the existing
Isolator.
One ContainerizerProcess has been written:
MesosContainerizerProcess - implements containerizeration internally
using a Launcher and one or more Isolators (following review)
The intent is to also support a generic ExternalContainerizerProcess
that can delegate containerizeration by making external calls. Other
Containerizers could interface with specific external containerization
techniques such as Docker or LXC.
Review: https://reviews.apache.org/r/16147
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f90fe764
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f90fe764
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f90fe764
Branch: refs/heads/master
Commit: f90fe7641ea8f7066a6a1171a24ddaa8dc30e789
Parents: c578fe5
Author: Ian Downes <ia...@gmail.com>
Authored: Tue Feb 11 16:24:59 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 16:59:50 2014 -0800
----------------------------------------------------------------------
include/mesos/mesos.proto | 25 +-
src/Makefile.am | 26 +-
src/common/type_utils.hpp | 40 +
src/launcher/fetcher.cpp | 242 +++
src/launcher/launcher.cpp | 489 ------
src/launcher/launcher.hpp | 125 --
src/launcher/main.cpp | 100 --
src/local/local.cpp | 23 +-
src/slave/cgroups_isolator.cpp | 1412 ------------------
src/slave/cgroups_isolator.hpp | 320 ----
src/slave/containerizer/containerizer.cpp | 269 ++++
src/slave/containerizer/containerizer.hpp | 137 ++
src/slave/containerizer/mesos_containerizer.cpp | 907 +++++++++++
src/slave/containerizer/mesos_containerizer.hpp | 208 +++
src/slave/flags.hpp | 15 +-
src/slave/http.cpp | 2 +-
src/slave/isolator.cpp | 53 -
src/slave/isolator.hpp | 110 --
src/slave/main.cpp | 20 +-
src/slave/monitor.cpp | 132 +-
src/slave/monitor.hpp | 54 +-
src/slave/paths.hpp | 32 +-
src/slave/process_isolator.cpp | 516 -------
src/slave/process_isolator.hpp | 117 --
src/slave/slave.cpp | 487 +++---
src/slave/slave.hpp | 32 +-
src/slave/state.cpp | 45 +-
src/slave/state.hpp | 10 +-
src/slave/status_update_manager.cpp | 33 +-
src/slave/status_update_manager.hpp | 8 +-
30 files changed, 2243 insertions(+), 3746 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 7079e03..69a4a60 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -83,6 +83,17 @@ message ExecutorID {
/**
+ * A slave generated ID to distinguish a container. The ID must be unique
+ * between any active or completed containers on the slave. In particular,
+ * containers for different runs of the same (framework, executor) pair must be
+ * unique.
+ */
+message ContainerID {
+ required string value = 1;
+}
+
+
+/**
* Describes a framework. If the user field is set to an empty string
* Mesos will automagically set it to the current user. Note that the
* ID is only available after a framework has registered, however, it
@@ -109,13 +120,13 @@ message FrameworkInfo {
/**
- * Describes a command, executed via: '/bin/sh -c value'. Any uri's
- * specified are fetched before executing the command.
- * If executable field for an uri is set, executable file permission
- * is set on the downloaded file. Also, if the downloaded file has ".tgz"
- * extension it is extracted into the executor's working directory.
- * In addition, any environment variables are set before executing
- * the command (so they can be used to "parameterize" your command).
+ * Describes a command, executed via: '/bin/sh -c value'. Any URIs specified
+ * are fetched before executing the command. If the executable field for an
+ * uri is set, executable file permission is set on the downloaded file.
+ * Otherwise, if the downloaded file has a recognized archive extension
+ * (currently [compressed] tar and zip) it is extracted into the executor's
+ * working directory. In addition, any environment variables are set before
+ * executing the command (so they can be used to "parameterize" your command).
*/
message CommandInfo {
message URI {
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index c307068..9d39666 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -176,10 +176,9 @@ libmesos_no_3rdparty_la_SOURCES = \
slave/state.cpp \
slave/slave.cpp \
slave/http.cpp \
- slave/isolator.cpp \
- slave/process_isolator.cpp \
+ slave/containerizer/containerizer.cpp \
+ slave/containerizer/mesos_containerizer.cpp \
slave/status_update_manager.cpp \
- launcher/launcher.cpp \
exec/exec.cpp \
common/lock.cpp \
common/date_utils.cpp \
@@ -204,11 +203,9 @@ pkginclude_HEADERS = $(top_srcdir)/include/mesos/executor.hpp \
nodist_pkginclude_HEADERS = ../include/mesos/mesos.hpp mesos.pb.h
if OS_LINUX
- libmesos_no_3rdparty_la_SOURCES += slave/cgroups_isolator.cpp
libmesos_no_3rdparty_la_SOURCES += linux/cgroups.cpp
libmesos_no_3rdparty_la_SOURCES += linux/fs.cpp
else
- EXTRA_DIST += slave/cgroups_isolator.cpp
EXTRA_DIST += linux/cgroups.cpp
EXTRA_DIST += linux/fs.cpp
endif
@@ -220,7 +217,7 @@ libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp \
common/type_utils.hpp common/thread.hpp \
examples/utils.hpp files/files.hpp \
hdfs/hdfs.hpp \
- launcher/launcher.hpp linux/cgroups.hpp \
+ linux/cgroups.hpp \
linux/fs.hpp local/flags.hpp local/local.hpp \
logging/flags.hpp logging/logging.hpp \
master/allocator.hpp \
@@ -232,17 +229,16 @@ libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp \
master/registrar.hpp \
master/master.hpp master/sorter.hpp \
messages/messages.hpp slave/constants.hpp \
+ slave/containerizer/containerizer.hpp \
+ slave/containerizer/mesos_containerizer.hpp \
slave/flags.hpp slave/gc.hpp slave/monitor.hpp \
- slave/isolator.hpp \
- slave/cgroups_isolator.hpp \
slave/paths.hpp slave/state.hpp \
slave/status_update_manager.hpp \
- slave/process_isolator.hpp \
slave/slave.hpp \
tests/environment.hpp tests/script.hpp \
tests/zookeeper.hpp tests/flags.hpp tests/utils.hpp \
tests/cluster.hpp \
- tests/isolator.hpp \
+ tests/containerizer.hpp \
tests/mesos.hpp \
tests/zookeeper_test_server.hpp zookeeper/authentication.hpp \
zookeeper/contender.hpp \
@@ -391,10 +387,10 @@ mesos_local_SOURCES = local/main.cpp
mesos_local_CPPFLAGS = $(MESOS_CPPFLAGS)
mesos_local_LDADD = libmesos.la
-pkglibexec_PROGRAMS += mesos-launcher
-mesos_launcher_SOURCES = launcher/main.cpp
-mesos_launcher_CPPFLAGS = $(MESOS_CPPFLAGS)
-mesos_launcher_LDADD = libmesos.la
+pkglibexec_PROGRAMS += mesos-fetcher
+mesos_fetcher_SOURCES = launcher/fetcher.cpp
+mesos_fetcher_CPPFLAGS = $(MESOS_CPPFLAGS)
+mesos_fetcher_LDADD = libmesos.la
pkglibexec_PROGRAMS += mesos-executor
mesos_executor_SOURCES = launcher/executor.cpp
@@ -838,6 +834,7 @@ mesos_tests_SOURCES = \
tests/allocator_tests.cpp \
tests/attributes_tests.cpp \
tests/authentication_tests.cpp \
+ tests/containerizer.cpp \
tests/environment.cpp \
tests/examples_tests.cpp \
tests/exception_tests.cpp \
@@ -878,7 +875,6 @@ mesos_tests_LDADD = ../$(LIBPROCESS)/3rdparty/libgmock.la libmesos.la
mesos_tests_DEPENDENCIES = # Initialized to allow += below.
if OS_LINUX
- mesos_tests_SOURCES += tests/cgroups_isolator_tests.cpp
mesos_tests_SOURCES += tests/cgroups_tests.cpp
mesos_tests_SOURCES += tests/fs_tests.cpp
endif
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/common/type_utils.hpp
----------------------------------------------------------------------
diff --git a/src/common/type_utils.hpp b/src/common/type_utils.hpp
index b8fc573..784a808 100644
--- a/src/common/type_utils.hpp
+++ b/src/common/type_utils.hpp
@@ -72,6 +72,14 @@ inline std::ostream& operator << (
}
+inline std::ostream& operator << (
+ std::ostream& stream,
+ const ContainerID& containerId)
+{
+ return stream << containerId.value();
+}
+
+
inline std::ostream& operator << (std::ostream& stream, const TaskState& state)
{
return stream << TaskState_descriptor()->FindValueByNumber(state)->name();
@@ -150,6 +158,18 @@ inline bool operator == (const ExecutorID& left, const ExecutorID& right)
}
+inline bool operator == (const ContainerID& left, const ContainerID& right)
+{
+ return left.value() == right.value();
+}
+
+
+inline bool operator != (const ContainerID& left, const ContainerID& right)
+{
+ return left.value() != right.value();
+}
+
+
inline bool operator == (const FrameworkID& left, const std::string& right)
{
return left.value() == right;
@@ -180,6 +200,12 @@ inline bool operator == (const ExecutorID& left, const std::string& right)
}
+inline bool operator == (const ContainerID& left, const std::string& right)
+{
+ return left.value() == right;
+}
+
+
inline bool operator < (const FrameworkID& left, const FrameworkID& right)
{
return left.value() < right.value();
@@ -210,6 +236,12 @@ inline bool operator < (const ExecutorID& left, const ExecutorID& right)
}
+inline bool operator < (const ContainerID& left, const ContainerID& right)
+{
+ return left.value() < right.value();
+}
+
+
inline bool operator == (const Environment& left, const Environment& right)
{
if (left.variables().size() != right.variables().size()) {
@@ -355,6 +387,14 @@ inline std::size_t hash_value(const ExecutorID& executorId)
}
+inline std::size_t hash_value(const ContainerID& containerId)
+{
+ size_t seed = 0;
+ boost::hash_combine(seed, containerId.value());
+ return seed;
+}
+
+
namespace internal {
inline bool operator == (const Task& left, const Task& right)
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/fetcher.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/fetcher.cpp b/src/launcher/fetcher.cpp
new file mode 100644
index 0000000..9c9f07d
--- /dev/null
+++ b/src/launcher/fetcher.cpp
@@ -0,0 +1,242 @@
+/**
+ * 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/mesos.hpp>
+
+#include <stout/net.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/strings.hpp>
+
+#include "hdfs/hdfs.hpp"
+
+using namespace mesos;
+
+using std::string;
+
+// Try to extract filename into directory. If filename is recognized as an
+// archive it will be extracted and true returned; if not recognized then false
+// will be returned. An Error is returned if the extraction command fails.
+Try<Nothing> extract(const string& filename, const string& directory)
+{
+ string command;
+ // Extract any .tgz, tar.gz, tar.bz2 or zip files.
+ if (strings::endsWith(filename, ".tgz") ||
+ strings::endsWith(filename, ".tar.gz") ||
+ strings::endsWith(filename, ".tbz2") ||
+ strings::endsWith(filename, ".tar.bz2") ||
+ strings::endsWith(filename, ".txz") ||
+ strings::endsWith(filename, ".tar.xz")) {
+ command = "tar -C '" + directory + "' xJf";
+ } else if (strings::endsWith(filename, ".zip")) {
+ command = "unzip -d '" + directory + "'";
+ } else {
+ return Error("Could not extract file with unrecognized extension");
+ }
+
+ command += " '" + filename + "'";
+ int status = os::system(command);
+ if (status != 0) {
+ return Error("Failed to extract: command " + command +
+ " exited with status: " + stringify(status));
+ }
+
+ LOG(INFO) << "Extracted resource '" << filename
+ << "' into '" << directory << "'";
+
+ return Nothing();
+}
+
+
+// Fetch URI into directory.
+Try<string> fetch(
+ const string& uri,
+ const string& directory)
+{
+ LOG(INFO) << "Fetching URI '" << uri << "'";
+
+ // Some checks to make sure using the URI value in shell commands
+ // is safe. TODO(benh): These should be pushed into the scheduler
+ // driver and reported to the user.
+ if (uri.find_first_of('\\') != string::npos ||
+ uri.find_first_of('\'') != string::npos ||
+ uri.find_first_of('\0') != string::npos) {
+ LOG(ERROR) << "URI contains illegal characters, refusing to fetch";
+ return Error("Illegal characters in URI");
+ }
+
+ // Grab the resource from HDFS if its path begins with hdfs:// or
+ // hftp:
+ // TODO(matei): Enforce some size limits on files we get from HDFS
+ if (strings::startsWith(uri, "hdfs://") ||
+ strings::startsWith(uri, "hftp://")) {
+ Try<string> base = os::basename(uri);
+ if (base.isError()) {
+ LOG(ERROR) << "Invalid basename for URI: " << base.error();
+ return Error("Invalid basename for URI");
+ }
+ string path = path::join(directory, base.get());
+
+ HDFS hdfs;
+
+ LOG(INFO) << "Downloading resource from '" << uri
+ << "' to '" << path << "'";
+ Try<Nothing> result = hdfs.copyToLocal(uri, path);
+ if (result.isError()) {
+ LOG(ERROR) << "HDFS copyToLocal failed: " << result.error();
+ return Error(result.error());
+ }
+
+ return path;
+ } else if (strings::startsWith(uri, "http://") ||
+ strings::startsWith(uri, "https://") ||
+ strings::startsWith(uri, "ftp://") ||
+ strings::startsWith(uri, "ftps://")) {
+ string path = uri.substr(uri.find("://") + 3);
+ if (path.find("/") == string::npos ||
+ path.size() <= path.find("/") + 1) {
+ LOG(ERROR) << "Malformed URL (missing path)";
+ return Error("Malformed URI");
+ }
+
+ path = path::join(directory, path.substr(path.find_last_of("/") + 1));
+ LOG(INFO) << "Downloading '" << uri << "' to '" << path << "'";
+ Try<int> code = net::download(uri, path);
+ if (code.isError()) {
+ LOG(ERROR) << "Error downloading resource: " << code.error().c_str();
+ return Error("Fetch of URI failed (" + code.error() + ")");
+ } else if (code.get() != 200) {
+ LOG(ERROR) << "Error downloading resource, received HTTP/FTP return code "
+ << code.get();
+ return Error("HTTP/FTP error (" + stringify(code.get()) + ")");
+ }
+
+ return path;
+ } else { // Copy the local resource.
+ string local = uri;
+ if (local.find_first_of("/") != 0) {
+ // We got a non-Hadoop and non-absolute path.
+ if (os::hasenv("MESOS_FRAMEWORKS_HOME")) {
+ local = path::join(os::getenv("MESOS_FRAMEWORKS_HOME"), local);
+ LOG(INFO) << "Prepended environment variable "
+ << "MESOS_FRAMEWORKS_HOME to relative path, "
+ << "making it: '" << local << "'";
+ } else {
+ LOG(ERROR) << "A relative path was passed for the resource but the "
+ << "environment variable MESOS_FRAMEWORKS_HOME is not set. "
+ << "Please either specify this config option "
+ << "or avoid using a relative path";
+ return Error("Could not resolve relative URI");
+ }
+ }
+
+ Try<string> base = os::basename(local);
+ if (base.isError()) {
+ LOG(ERROR) << base.error();
+ return Error("Fetch of URI failed");
+ }
+
+ // Copy the resource to the directory.
+ string path = path::join(directory, base.get());
+ std::ostringstream command;
+ command << "cp '" << local << "' '" << path << "'";
+ LOG(INFO) << "Copying resource from '" << local
+ << "' to '" << directory << "'";
+
+ int status = os::system(command.str());
+ if (status != 0) {
+ LOG(ERROR) << "Failed to copy '" << local
+ << "' : Exit status " << status;
+ return Error("Local copy failed");
+ }
+
+ return path;
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ CommandInfo commandInfo;
+ // Construct URIs from the encoded environment string.
+ const std::string& uris = os::getenv("MESOS_EXECUTOR_URIS");
+ foreach (const std::string& token, strings::tokenize(uris, " ")) {
+ // Delimiter between URI and execute permission.
+ size_t pos = token.rfind("+");
+ CHECK(pos != std::string::npos)
+ << "Invalid executor uri token in env " << token;
+
+ CommandInfo::URI uri;
+ uri.set_value(token.substr(0, pos));
+ uri.set_executable(token.substr(pos + 1) == "1");
+
+ commandInfo.add_uris()->MergeFrom(uri);
+ }
+
+ CHECK(os::hasenv("MESOS_WORK_DIRECTORY"))
+ << "Missing MESOS_WORK_DIRECTORY environment variable";
+ std::string directory = os::getenv("MESOS_WORK_DIRECTORY");
+
+ // We cannot use Some in the ternary expression because the compiler needs to
+ // be able to infer the type, thus the explicit Option<string>.
+ // TODO(idownes): Add an os::hasenv that returns an Option<string>.
+ Option<std::string> user = os::hasenv("MESOS_USER")
+ ? Option<std::string>(os::getenv("MESOS_USER")) // Explicit so it compiles.
+ : None();
+
+ // Fetch each URI to a local file, chmod, then chown if a user is provided.
+ foreach (const CommandInfo::URI& uri, commandInfo.uris()) {
+ // Fetch the URI to a local file.
+ Try<string> fetched = fetch(uri.value(), directory);
+ if (fetched.isError()) {
+ EXIT(1) << "Failed to fetch: " << uri.value();
+ }
+
+ // Chmod the fetched URI if it's executable, else assume it's an archive
+ // that should be extracted.
+ if (uri.executable()) {
+ bool chmodded = os::chmod(
+ fetched.get(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ if (!chmodded) {
+ EXIT(1) << "Failed to chmod: " << fetched.get();
+ }
+ } else {
+ //TODO(idownes): Consider removing the archive once extracted.
+ // Try to extract the file if it's recognized as an archive.
+ Try<Nothing> extracted = extract(fetched.get(), directory);
+ if (extracted.isError()) {
+ EXIT(1) << "Failed to extract "
+ << fetched.get() << ":" << extracted.error();
+ }
+ }
+
+ // Recursively chown the directory if a user is provided.
+ if (user.isSome()) {
+ Try<Nothing> chowned = os::chown(user.get(), directory);
+ if (chowned.isError()) {
+ EXIT(1) << "Failed to chown " << directory << ": " << chowned.error();
+ }
+ }
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/launcher.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/launcher.cpp b/src/launcher/launcher.cpp
deleted file mode 100644
index d5ab667..0000000
--- a/src/launcher/launcher.cpp
+++ /dev/null
@@ -1,489 +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 <dirent.h>
-#include <errno.h>
-#include <libgen.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <map>
-#include <sstream>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <stout/fatal.hpp>
-#include <stout/foreach.hpp>
-#include <stout/net.hpp>
-#include <stout/nothing.hpp>
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#include "hdfs/hdfs.hpp"
-
-#include "launcher/launcher.hpp"
-
-#include "slave/flags.hpp"
-#include "slave/paths.hpp"
-#include "slave/state.hpp"
-
-using std::cerr;
-using std::cout;
-using std::endl;
-using std::map;
-using std::ostringstream;
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace launcher {
-
-ExecutorLauncher::ExecutorLauncher(
- const SlaveID& _slaveId,
- const FrameworkID& _frameworkId,
- const ExecutorID& _executorId,
- const UUID& _uuid,
- const CommandInfo& _commandInfo,
- const string& _user,
- const string& _workDirectory,
- const string& _slaveDirectory,
- const string& _slavePid,
- const string& _frameworksHome,
- const string& _hadoopHome,
- bool _redirectIO,
- bool _shouldSwitchUser,
- bool _checkpoint,
- Duration _recoveryTimeout)
- : slaveId(_slaveId),
- frameworkId(_frameworkId),
- executorId(_executorId),
- uuid(_uuid),
- commandInfo(_commandInfo),
- user(_user),
- workDirectory(_workDirectory),
- slaveDirectory(_slaveDirectory),
- slavePid(_slavePid),
- frameworksHome(_frameworksHome),
- hadoopHome(_hadoopHome),
- redirectIO(_redirectIO),
- shouldSwitchUser(_shouldSwitchUser),
- checkpoint(_checkpoint),
- recoveryTimeout(_recoveryTimeout) {}
-
-
-ExecutorLauncher::~ExecutorLauncher() {}
-
-
-// NOTE: We avoid fatalerror()s in this function because, we don't
-// want to kill the slave (in the case of cgroups isolator).
-int ExecutorLauncher::setup()
-{
- // Checkpoint the forked pid, if necessary. The checkpointing must
- // be done in the forked process (cgroups isolator) or execed
- // launcher process (process isolator), because the slave process
- // can die immediately after the isolator forks but before it would
- // have a chance to write the pid to disk. That would result in an
- // orphaned executor process unknown to the recovering slave.
- if (checkpoint) {
- const string& path = slave::paths::getForkedPidPath(
- slave::paths::getMetaRootDir(slaveDirectory),
- slaveId,
- frameworkId,
- executorId,
- uuid);
- cout << "Checkpointing executor's forked pid " << getpid()
- << " to '" << path << "'" << endl;
-
- Try<Nothing> checkpoint =
- slave::state::checkpoint(path, stringify(getpid()));
-
- if (checkpoint.isError()) {
- cerr << "Failed to checkpoint executor's forked pid to '"
- << path << "': " << checkpoint.error();
- return -1;
- }
- }
-
- const string& cwd = os::getcwd();
-
- // TODO(benh): Do this in the slave?
- if (shouldSwitchUser) {
- Try<Nothing> chown = os::chown(user, workDirectory);
-
- if (chown.isError()) {
- cerr << "Failed to change ownership of the executor work directory "
- << workDirectory << " to user " << user << ": " << chown.error()
- << endl;
- return -1;
- }
- }
-
- // Enter working directory.
- if (!os::chdir(workDirectory)) {
- cerr << "Failed to chdir into executor work directory" << endl;
- return -1;
- }
-
- // Redirect output to files in working dir if required.
- // TODO(bmahler): It would be best if instead of closing stderr /
- // stdout and redirecting, we instead always output to stderr /
- // stdout. Also tee'ing their output into the work directory files
- // when redirection is desired.
- if (redirectIO) {
- if (freopen("stdout", "w", stdout) == NULL) {
- fatalerror("freopen failed");
- }
- if (freopen("stderr", "w", stderr) == NULL) {
- fatalerror("freopen failed");
- }
- }
-
- if (fetchExecutors() < 0) {
- cerr << "Failed to fetch executors" << endl;
- return -1;
- }
-
- // Go back to previous directory.
- if (!os::chdir(cwd)) {
- cerr << "Failed to chdir (back) into slave directory" << endl;
- return -1;
- }
-
- return 0;
-}
-
-
-int ExecutorLauncher::launch()
-{
- // Enter working directory.
- if (os::chdir(workDirectory) < 0) {
- fatalerror("Failed to chdir into the executor work directory");
- }
-
- if (shouldSwitchUser) {
- switchUser();
- }
-
- setupEnvironment();
-
- const string& command = commandInfo.value();
-
- // Execute the command (via '/bin/sh -c command').
- execl("/bin/sh", "sh", "-c", command.c_str(), (char*) NULL);
-
- // If we get here, the execv call failed.
- fatalerror("Could not execute '/bin/sh -c %s'", command.c_str());
-
- return -1; // Silence end of non-void function warning.
-}
-
-
-int ExecutorLauncher::run()
-{
- int ret = setup();
- if (ret < 0) {
- return ret;
- }
- return launch();
-}
-
-
-// Download the executor's files and optionally set executable permissions
-// if requested.
-int ExecutorLauncher::fetchExecutors()
-{
- cout << "Fetching resources into '" << workDirectory << "'" << endl;
-
- foreach(const CommandInfo::URI& uri, commandInfo.uris()) {
- string resource = uri.value();
- bool executable = uri.has_executable() && uri.executable();
-
- cout << "Fetching resource '" << resource << "'" << endl;
-
- // Some checks to make sure using the URI value in shell commands
- // is safe. TODO(benh): These should be pushed into the scheduler
- // driver and reported to the user.
- if (resource.find_first_of('\\') != string::npos ||
- resource.find_first_of('\'') != string::npos ||
- resource.find_first_of('\0') != string::npos) {
- cerr << "Illegal characters in URI" << endl;
- return -1;
- }
-
- // Grab the resource from HDFS if its path begins with hdfs:// or
- // htfp://. TODO(matei): Enforce some size limits on files we get
- // from HDFS
- if (resource.find("hdfs://") == 0 || resource.find("hftp://") == 0) {
- HDFS hdfs(path::join(hadoopHome, "bin/hadoop"));
-
- Try<std::string> basename = os::basename(resource);
- if (basename.isError()) {
- cerr << basename.error() << endl;
- return -1;
- }
-
- string localFile = path::join(".", basename.get());
-
- Try<Nothing> copy = hdfs.copyToLocal(resource, localFile);
-
- if (copy.isError()) {
- cerr << "Failed to copy from HDFS: " << copy.error() << endl;
- return -1;
- }
-
- resource = localFile;
- } else if (resource.find("http://") == 0
- || resource.find("https://") == 0
- || resource.find("ftp://") == 0
- || resource.find("ftps://") == 0) {
- string path = resource.substr(resource.find("://") + 3);
- if (path.find("/") == string::npos) {
- cerr << "Malformed URL (missing path)" << endl;
- return -1;
- }
-
- if (path.size() <= path.find("/") + 1) {
- cerr << "Malformed URL (missing path)" << endl;
- return -1;
- }
-
- path = path::join(".", path.substr(path.find_last_of("/") + 1));
- cout << "Downloading '" << resource << "' to '" << path << "'" << endl;
- Try<int> code = net::download(resource, path);
- if (code.isError()) {
- cerr << "Error downloading resource: " << code.error().c_str() << endl;
- return -1;
- } else if (code.get() != 200) {
- cerr << "Error downloading resource, received HTTP/FTP return code "
- << code.get() << endl;
- return -1;
- }
- resource = path;
- } else { // Copy the local resource.
- if (resource.find_first_of("/") != 0) {
- // We got a non-Hadoop and non-absolute path.
- if (frameworksHome != "") {
- resource = path::join(frameworksHome, resource);
- cout << "Prepended configuration option frameworks_home to resource "
- << "path, making it: '" << resource << "'" << endl;
- } else {
- cerr << "A relative path was passed for the resource, but "
- << "the configuration option frameworks_home is not set. "
- << "Please either specify this config option "
- << "or avoid using a relative path" << endl;
- return -1;
- }
- }
-
- // Copy the resource to the current working directory.
- ostringstream command;
- command << "cp '" << resource << "' .";
- cout << "Copying resource from '" << resource << "' to ." << endl;
-
- int status = os::system(command.str());
- if (status != 0) {
- cerr << "Failed to copy '" << resource
- << "' : Exit status " << status << endl;
- return -1;
- }
-
- Try<std::string> base = os::basename(resource);
- if (base.isError()) {
- cerr << base.error() << endl;
- return -1;
- }
-
- resource = path::join(".", base.get());
- }
-
- if (shouldSwitchUser) {
- Try<Nothing> chown = os::chown(user, resource);
-
- if (chown.isError()) {
- cerr << "Failed to chown '" << resource << "' to user " << user << ": "
- << chown.error() << endl;
- return -1;
- }
- }
-
- if (executable &&
- !os::chmod(resource, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
- cerr << "Failed to chmod '" << resource << "'" << endl;
- return -1;
- }
-
- // Extract any .tgz, tar.gz, tar.bz2 or zip files.
- if (strings::endsWith(resource, ".tgz") ||
- strings::endsWith(resource, ".tar.gz")) {
- string command = "tar xzf '" + resource + "'";
- cout << "Extracting resource: " << command << endl;
- int code = os::system(command);
- if (code != 0) {
- cerr << "Failed to extract resource: tar exit code " << code << endl;
- return -1;
- }
- } else if (strings::endsWith(resource, ".tbz2") ||
- strings::endsWith(resource, ".tar.bz2")) {
- string command = "tar xjf '" + resource + "'";
- cout << "Extracting resource: " << command << endl;
- int code = os::system(command);
- if (code != 0) {
- cerr << "Failed to extract resource: tar exit code " << code << endl;
- return -1;
- }
- } else if (strings::endsWith(resource, ".txz") ||
- strings::endsWith(resource, ".tar.xz")) {
- // If you want to use XZ on Mac OS, you can try the packages here:
- // http://macpkg.sourceforge.net/
- string command = "tar xJf '" + resource + "'";
- cout << "Extracting resource: " << command << endl;
- int code = os::system(command);
- if (code != 0) {
- cerr << "Failed to extract resource: tar exit code " << code << endl;
- return -1;
- }
- } else if (strings::endsWith(resource, ".zip")) {
- string command = "unzip '" + resource + "'";
- cout << "Extracting resource: " << command << endl;
- int code = os::system(command);
- if (code != 0) {
- cerr << "Failed to extract resource: unzip exit code " << code << endl;
- return -1;
- }
- }
- }
-
- // Recursively chown the work directory, since extraction may have occurred.
- if (shouldSwitchUser) {
- Try<Nothing> chown = os::chown(user, ".");
-
- if (chown.isError()) {
- cerr << "Failed to recursively chown the work directory "
- << workDirectory << " to user " << user << ": " << chown.error()
- << endl;
- return -1;
- }
- }
-
- return 0;
-}
-
-
-void ExecutorLauncher::switchUser()
-{
- if (!os::su(user)) {
- fatal("Failed to switch to user %s for executor %s of framework %s",
- user.c_str(), executorId.value().c_str(), frameworkId.value().c_str());
- }
-}
-
-
-// Set up environment variables for launching a framework's executor.
-void ExecutorLauncher::setupEnvironment()
-{
- foreachpair (const string& key, const string& value, getEnvironment()) {
- os::setenv(key, value);
- }
-}
-
-
-map<string, string> ExecutorLauncher::getEnvironment()
-{
- map<string, string> env;
-
- // Set LIBPROCESS_PORT so that we bind to a random free port (since
- // this might have been set via --port option). We do this before
- // the environment variables below in case it is included.
- env["LIBPROCESS_PORT"] = "0";
-
- // Also add MESOS_NATIVE_LIBRARY if it's not already present (and
- // like above, we do this before the environment variables below in
- // case the framework wants to override).
- if (!os::hasenv("MESOS_NATIVE_LIBRARY")) {
- string path =
-#ifdef __APPLE__
- LIBDIR "/libmesos-" VERSION ".dylib";
-#else
- LIBDIR "/libmesos-" VERSION ".so";
-#endif
- if (os::exists(path)) {
- env["MESOS_NATIVE_LIBRARY"] = path;
- }
- }
-
- // Set up the environment as specified in the ExecutorInfo.
- if (commandInfo.has_environment()) {
- foreach (const Environment::Variable& variable,
- commandInfo.environment().variables()) {
- env[variable.name()] = variable.value();
- }
- }
-
- // Set Mesos environment variables for slave ID, framework ID, etc.
- env["MESOS_DIRECTORY"] = workDirectory;
- env["MESOS_SLAVE_PID"] = slavePid;
- env["MESOS_SLAVE_ID"] = slaveId.value();
- env["MESOS_FRAMEWORK_ID"] = frameworkId.value();
- env["MESOS_EXECUTOR_ID"] = executorId.value();
- env["MESOS_EXECUTOR_UUID"] = uuid.toString();
- env["MESOS_CHECKPOINT"] = checkpoint ? "1" : "0";
-
- if (checkpoint) {
- env["MESOS_RECOVERY_TIMEOUT"] = stringify(recoveryTimeout);
- }
-
- return env;
-}
-
-
-// Get Mesos environment variables that launcher/main.cpp will
-// pass as arguments to an ExecutorLauncher there.
-map<string, string> ExecutorLauncher::getLauncherEnvironment()
-{
- map<string, string> env = getEnvironment();
-
- string uris = "";
- foreach (const CommandInfo::URI& uri, commandInfo.uris()) {
- uris += uri.value() + "+" +
- (uri.has_executable() && uri.executable() ? "1" : "0");
- uris += " ";
- }
-
- // Remove extra space at the end.
- if (uris.size() > 0) {
- uris = strings::trim(uris);
- }
-
- env["MESOS_EXECUTOR_URIS"] = uris;
- env["MESOS_COMMAND"] = commandInfo.value();
- env["MESOS_USER"] = user;
- env["MESOS_SLAVE_DIRECTORY"] = slaveDirectory;
- env["MESOS_HADOOP_HOME"] = hadoopHome;
- env["MESOS_REDIRECT_IO"] = redirectIO ? "1" : "0";
- env["MESOS_SWITCH_USER"] = shouldSwitchUser ? "1" : "0";
-
- return env;
-}
-
-} // namespace launcher {
-} // namespace internal {
-} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/launcher/launcher.hpp b/src/launcher/launcher.hpp
deleted file mode 100644
index 104fe81..0000000
--- a/src/launcher/launcher.hpp
+++ /dev/null
@@ -1,125 +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 __LAUNCHER_HPP__
-#define __LAUNCHER_HPP__
-
-#include <map>
-#include <string>
-
-#include <mesos/mesos.hpp>
-
-#include <stout/duration.hpp>
-#include <stout/uuid.hpp>
-
-#include "slave/flags.hpp"
-
-namespace mesos {
-namespace internal {
-namespace launcher {
-
-// This class sets up the environment for an executor and then exec()'s it.
-// It can either be used after a fork() in the slave process, or run as a
-// standalone program (with the main function in launcher_main.cpp).
-//
-// The environment is initialized through for steps:
-// 1) A work directory for the framework is created by createWorkingDirectory().
-// 2) The executor is fetched off HDFS if necessary by fetchExecutor().
-// 3) Environment variables are set by setupEnvironment().
-// 4) We switch to the framework's user in switchUser().
-//
-// Isolators that wish to override the default behaviour can subclass
-// Launcher and override some of the methods to perform extra actions.
-class ExecutorLauncher {
-public:
- ExecutorLauncher(
- const SlaveID& slaveId,
- const FrameworkID& frameworkId,
- const ExecutorID& executorId,
- const UUID& uuid,
- const CommandInfo& commandInfo,
- const std::string& user,
- const std::string& workDirectory,
- const std::string& slaveWorkDirectory,
- const std::string& slavePid,
- const std::string& frameworksHome,
- const std::string& hadoopHome,
- bool redirectIO,
- bool shouldSwitchUser,
- bool checkpoint,
- Duration recoveryTimeout);
-
- virtual ~ExecutorLauncher();
-
- // Initialize the working directory and fetch the executor.
- virtual int setup();
-
- // Launches the downloaded executor.
- virtual int launch();
-
- // Convenience function that calls setup() and then launch().
- virtual int run();
-
- // Return a map of environment variables for exec'ing a
- // launch_main.cpp (mesos-launcher binary) process. This is used
- // by isolators that cannot exec the user's executor directly
- // (e.g., due to potential deadlocks in forked process).
- virtual std::map<std::string, std::string> getLauncherEnvironment();
-
-protected:
- // Download the required files for the executor from the given set of URIs.
- // Optionally, it will set the executable file permissions for the files.
- // This method is expected to place files in the workDirectory.
- virtual int fetchExecutors();
-
- // Return a map of environment variables for launching a
- // framework's executor.
- virtual std::map<std::string, std::string> getEnvironment();
-
- // Set up environment variables for launching a
- // framework's executor.
- virtual void setupEnvironment();
-
- // Switch to a framework's user in preparation for exec()'ing its executor.
- virtual void switchUser();
-
-protected:
- const SlaveID slaveId;
- const FrameworkID frameworkId;
- const ExecutorID executorId;
- const UUID uuid;
- const CommandInfo commandInfo;
- const std::string user;
- const std::string workDirectory;
- const std::string slaveDirectory;
- const std::string slavePid;
- const std::string frameworksHome;
- const std::string hadoopHome;
- const bool redirectIO; // Whether to redirect stdout and stderr to files.
- const bool shouldSwitchUser; // Whether to setuid to framework's user.
- const bool checkpoint; // Whether the framework enabled checkpointing.
-
- // Executor suicide timeout for slave recovery.
- const Duration recoveryTimeout;
-};
-
-} // namespace launcher {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __LAUNCHER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/main.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp
deleted file mode 100644
index de64609..0000000
--- a/src/launcher/main.cpp
+++ /dev/null
@@ -1,100 +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 <string>
-
-#include <mesos/mesos.hpp>
-
-#include <stout/duration.hpp>
-#include <stout/strings.hpp>
-#include <stout/os.hpp>
-
-#include "launcher/launcher.hpp"
-
-using namespace mesos;
-using namespace mesos::internal; // For 'utils'.
-
-using std::string;
-
-
-int main(int argc, char** argv)
-{
- GOOGLE_PROTOBUF_VERIFY_VERSION;
-
- SlaveID slaveId;
- slaveId.set_value(os::getenv("MESOS_SLAVE_ID"));
-
- FrameworkID frameworkId;
- frameworkId.set_value(os::getenv("MESOS_FRAMEWORK_ID"));
-
- ExecutorID executorId;
- executorId.set_value(os::getenv("MESOS_EXECUTOR_ID"));
-
- CommandInfo commandInfo;
- commandInfo.set_value(os::getenv("MESOS_COMMAND"));
-
- // Construct URIs from the encoded environment string.
- const std::string& uris = os::getenv("MESOS_EXECUTOR_URIS");
- foreach (const std::string& token, strings::tokenize(uris, " ")) {
- size_t pos = token.rfind("+"); // Delim between uri and exec permission.
- CHECK(pos != std::string::npos) << "Invalid executor uri token in env "
- << token;
-
- CommandInfo::URI uri;
- uri.set_value(token.substr(0, pos));
- uri.set_executable(token.substr(pos + 1) == "1");
-
- commandInfo.add_uris()->MergeFrom(uri);
- }
-
- bool checkpoint = os::getenv("MESOS_CHECKPOINT", false) == "1";
-
- Duration recoveryTimeout = slave::RECOVERY_TIMEOUT;
-
- // Get the recovery timeout if checkpointing is enabled.
- if (checkpoint) {
- string value = os::getenv("MESOS_RECOVERY_TIMEOUT", false);
-
- if (!value.empty()) {
- Try<Duration> _recoveryTimeout = Duration::parse(value);
-
- CHECK_SOME(_recoveryTimeout)
- << "Cannot parse MESOS_RECOVERY_TIMEOUT '" + value + "'";
-
- recoveryTimeout = _recoveryTimeout.get();
- }
- }
-
- return mesos::internal::launcher::ExecutorLauncher(
- slaveId,
- frameworkId,
- executorId,
- UUID::fromString(os::getenv("MESOS_EXECUTOR_UUID")),
- commandInfo,
- os::getenv("MESOS_USER"),
- os::getenv("MESOS_DIRECTORY"),
- os::getenv("MESOS_SLAVE_DIRECTORY"),
- os::getenv("MESOS_SLAVE_PID"),
- os::getenv("MESOS_FRAMEWORKS_HOME", false),
- os::getenv("MESOS_HADOOP_HOME"),
- os::getenv("MESOS_REDIRECT_IO") == "1",
- os::getenv("MESOS_SWITCH_USER") == "1",
- checkpoint,
- recoveryTimeout)
- .run();
-}
http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/local/local.cpp
----------------------------------------------------------------------
diff --git a/src/local/local.cpp b/src/local/local.cpp
index e650de9..5112391 100644
--- a/src/local/local.cpp
+++ b/src/local/local.cpp
@@ -40,7 +40,7 @@
#include "master/master.hpp"
#include "master/registrar.hpp"
-#include "slave/process_isolator.hpp"
+#include "slave/containerizer/containerizer.hpp"
#include "slave/slave.hpp"
#include "state/leveldb.hpp"
@@ -57,9 +57,8 @@ using mesos::internal::master::allocator::HierarchicalDRFAllocatorProcess;
using mesos::internal::master::Master;
using mesos::internal::master::Registrar;
+using mesos::internal::slave::Containerizer;
using mesos::internal::slave::Slave;
-using mesos::internal::slave::Isolator;
-using mesos::internal::slave::ProcessIsolator;
using process::PID;
using process::UPID;
@@ -80,7 +79,7 @@ static state::Storage* storage = NULL;
static state::protobuf::State* state = NULL;
static Registrar* registrar = NULL;
static Master* master = NULL;
-static map<Isolator*, Slave*> slaves;
+static map<Containerizer*, Slave*> slaves;
static StandaloneMasterDetector* detector = NULL;
static MasterContender* contender = NULL;
static Files* files = NULL;
@@ -142,9 +141,6 @@ PID<Master> launch(const Flags& flags, Allocator* _allocator)
vector<UPID> pids;
for (int i = 0; i < flags.num_slaves; i++) {
- // TODO(benh): Create a local isolator?
- ProcessIsolator* isolator = new ProcessIsolator();
-
slave::Flags flags;
Try<Nothing> load = flags.load("MESOS_");
if (load.isError()) {
@@ -152,13 +148,18 @@ PID<Master> launch(const Flags& flags, Allocator* _allocator)
<< "slave flags from the environment: " << load.error();
}
+ Try<Containerizer*> containerizer = Containerizer::create(flags, true);
+ if (containerizer.isError()) {
+ EXIT(1) << "Failed to create a containerizer: " << containerizer.error();
+ }
+
// Use a different work directory for each slave.
flags.work_dir = path::join(flags.work_dir, stringify(i));
// NOTE: At this point detector is already initialized by the
// Master.
- Slave* slave = new Slave(flags, true, detector, isolator, files);
- slaves[isolator] = slave;
+ Slave* slave = new Slave(flags, detector, containerizer.get(), files);
+ slaves[containerizer.get()] = slave;
pids.push_back(process::spawn(slave));
}
@@ -182,10 +183,10 @@ void shutdown()
// isolator, we can't delete the isolator until we have stopped
// the slave.
- foreachpair (Isolator* isolator, Slave* slave, slaves) {
+ foreachpair (Containerizer* containerizer, Slave* slave, slaves) {
process::terminate(slave->self());
process::wait(slave->self());
- delete isolator;
+ delete containerizer;
delete slave;
}