You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/08/09 20:13:44 UTC
[1/2] mesos git commit: Removed the external containerizer.
Repository: mesos
Updated Branches:
refs/heads/master 751a48155 -> f6a0e72c5
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/containerizer/external_containerizer.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/external_containerizer.hpp b/src/slave/containerizer/external_containerizer.hpp
deleted file mode 100644
index feeb027..0000000
--- a/src/slave/containerizer/external_containerizer.hpp
+++ /dev/null
@@ -1,299 +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 __EXTERNAL_CONTAINERIZER_HPP__
-#define __EXTERNAL_CONTAINERIZER_HPP__
-
-#include <list>
-#include <string>
-#include <tuple>
-
-#include <process/owned.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/hashmap.hpp>
-#include <stout/protobuf.hpp>
-#include <stout/try.hpp>
-
-#include "slave/state.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-
-#include "slave/containerizer/mesos/launcher.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-// The scheme an external containerizer programs have to adhere to is;
-//
-// COMMAND < INPUT-PROTO > RESULT-PROTO
-//
-// launch < containerizer::Launch
-// update < containerizer::Update
-// usage < containerizer::Usage > mesos::ResourceStatistics
-// wait < containerizer::Wait > containerizer::Termination
-// destroy < containerizer::Destroy
-// containers > containerizer::Containers
-// recover
-//
-// 'wait' on the external containerizer side is expected to block
-// until the task command/executor has terminated.
-//
-// Additionally, we have the following environment variable setup
-// for external containerizer programs:
-// MESOS_LIBEXEC_DIRECTORY = path to mesos-executor, mesos-usage, ...
-// MESOS_WORK_DIRECTORY = slave work directory. This should be used
-// for distiguishing slave instances.
-// MESOS_DEFAULT_CONTAINER_IMAGE = default image as provided via
-// slave flags (default_container_image). This variable is provided
-// only in calls to 'launch'.
-
-// Check src/examples/python/test_containerizer.py for a rough
-// implementation template of this protocol.
-
-// For debugging purposes of an external containerizer program, it
-// might be helpful to enable verbose logging on the slave (GLOG_v=2).
-
-class ExternalContainerizerProcess;
-
-class ExternalContainerizer : public Containerizer
-{
-public:
- static Try<ExternalContainerizer*> create(const Flags& flags);
-
- ExternalContainerizer(const Flags& flags);
-
- virtual ~ExternalContainerizer();
-
- virtual process::Future<Nothing> recover(
- const Option<state::SlaveState>& state);
-
- virtual process::Future<bool> launch(
- const ContainerID& containerId,
- const ExecutorInfo& executorInfo,
- const std::string& directory,
- const Option<std::string>& user,
- const SlaveID& slaveId,
- const process::PID<Slave>& slavePid,
- bool checkpoint);
-
- virtual process::Future<bool> launch(
- const ContainerID& containerId,
- const TaskInfo& task,
- const ExecutorInfo& executorInfo,
- const std::string& directory,
- const Option<std::string>& user,
- const SlaveID& slaveId,
- const process::PID<Slave>& slavePid,
- bool checkpoint);
-
- virtual process::Future<Nothing> update(
- const ContainerID& containerId,
- const Resources& resources);
-
- virtual process::Future<ResourceStatistics> usage(
- const ContainerID& containerId);
-
- virtual process::Future<containerizer::Termination> wait(
- const ContainerID& containerId);
-
- virtual void destroy(const ContainerID& containerId);
-
- virtual process::Future<hashset<ContainerID>> containers();
-
-private:
- process::Owned<ExternalContainerizerProcess> process;
-};
-
-
-class ExternalContainerizerProcess
- : public process::Process<ExternalContainerizerProcess>
-{
-public:
- ExternalContainerizerProcess(const Flags& flags);
-
- // Recover containerized executors as specified by state. See
- // containerizer.hpp:recover for more.
- process::Future<Nothing> recover(const Option<state::SlaveState>& state);
-
- // Start the containerized executor.
- process::Future<bool> launch(
- const ContainerID& containerId,
- const Option<TaskInfo>& taskInfo,
- const ExecutorInfo& executorInfo,
- const std::string& directory,
- const Option<std::string>& user,
- const SlaveID& slaveId,
- const process::PID<Slave>& slavePid,
- bool checkpoint);
-
- // Update the container's resources.
- process::Future<Nothing> update(
- const ContainerID& containerId,
- const Resources& resources);
-
- // Gather resource usage statistics for the containerized executor.
- process::Future<ResourceStatistics> usage(const ContainerID& containerId);
-
- // Get a future on the containerized executor's Termination.
- process::Future<containerizer::Termination> wait(
- const ContainerID& containerId);
-
- // Terminate the containerized executor.
- void destroy(const ContainerID& containerId);
-
- // Get all active container-id's.
- process::Future<hashset<ContainerID>> containers();
-
-private:
- // Startup flags.
- const Flags flags;
-
- // Information describing a container environment. A sandbox has to
- // be prepared before the external containerizer can be invoked.
- struct Sandbox
- {
- Sandbox(const std::string& directory, const Option<std::string>& user)
- : directory(directory), user(user) {}
-
- const std::string directory;
- const Option<std::string> user;
- };
-
- // Information describing a running container.
- struct Container
- {
- Container(const Option<Sandbox>& sandbox)
- : sandbox(sandbox), pid(None()), destroying(false) {}
-
- // Keep sandbox information available for subsequent containerizer
- // invocations.
- Option<Sandbox> sandbox;
-
- // External containerizer pid as per wait-invocation.
- // Wait should block on the external containerizer side, hence we
- // need to keep its pid for terminating if needed.
- Option<pid_t> pid;
-
- process::Promise<containerizer::Termination> termination;
-
- // Is set when container is being destroyed.
- bool destroying;
-
- // As described in MESOS-1251, we need to make sure that events
- // that are triggered before launch has completed, are in fact
- // queued until then to reduce complexity within external
- // containerizer program implementations. To achieve that, we
- // simply queue all events onto this promise.
- // TODO(tillt): Consider adding a timeout when queuing onto this
- // promise to account for external containerizer launch
- // invocations that got stuck.
- process::Promise<Nothing> launched;
-
- Resources resources;
- };
-
- // Stores all active containers.
- hashmap<ContainerID, process::Owned<Container>> actives;
-
- process::Future<Nothing> _recover(
- const Option<state::SlaveState>& state,
- const process::Future<Option<int>>& future);
-
- process::Future<Nothing> __recover(
- const Option<state::SlaveState>& state,
- const hashset<ContainerID>& containers);
-
- process::Future<Nothing> ___recover();
-
- process::Future<bool> _launch(
- const ContainerID& containerId,
- const process::Future<Option<int>>& future);
-
- void __launch(
- const ContainerID& containerId,
- const process::Future<bool>& future);
-
- process::Future<containerizer::Termination> _wait(
- const ContainerID& containerId);
-
- void __wait(
- const ContainerID& containerId,
- const process::Future<std::tuple<
- process::Future<Result<containerizer::Termination>>,
- process::Future<Option<int>>>>& future);
-
- process::Future<Nothing> _update(
- const ContainerID& containerId,
- const Resources& resources);
-
- process::Future<Nothing> __update(
- const ContainerID& containerId,
- const process::Future<Option<int>>& future);
-
- process::Future<ResourceStatistics> _usage(
- const ContainerID& containerId);
-
- process::Future<ResourceStatistics> __usage(
- const ContainerID& containerId,
- const process::Future<std::tuple<
- process::Future<Result<ResourceStatistics>>,
- process::Future<Option<int>>>>& future);
-
- void _destroy(const ContainerID& containerId);
-
- void __destroy(
- const ContainerID& containerId,
- const process::Future<Option<int>>& future);
-
- process::Future<hashset<ContainerID>> _containers(
- const process::Future<std::tuple<
- process::Future<Result<containerizer::Containers>>,
- process::Future<Option<int>>>>& future);
-
- // Abort a possibly pending "wait" in the external containerizer
- // process.
- void unwait(const ContainerID& containerId);
-
- // Call back for when the containerizer has terminated all processes
- // in the container.
- void cleanup(const ContainerID& containerId);
-
- // Invoke the external containerizer with the given command.
- Try<process::Subprocess> invoke(
- const std::string& command,
- const Option<Sandbox>& sandbox = None(),
- const Option<std::map<std::string, std::string>>& environment = None());
-
- // Invoke the external containerizer with the given command and
- // a protobuf message to be piped into its stdin.
- // There can not be an Option<google::protobuf::Message> due to the
- // pure virtual members of that class, hence this override is
- // needed.
- Try<process::Subprocess> invoke(
- const std::string& command,
- const google::protobuf::Message& message,
- const Option<Sandbox>& sandbox = None(),
- const Option<std::map<std::string, std::string>>& environment = None());
-};
-
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __EXTERNAL_CONTAINERIZER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/flags.cpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp
index b8ecc98..c07d6e5 100644
--- a/src/slave/flags.cpp
+++ b/src/slave/flags.cpp
@@ -94,9 +94,9 @@ mesos::internal::slave::Flags::Flags()
"`cgroups/cpu,cgroups/mem`, or network/port_mapping\n"
"(configure with flag: `--with-network-isolator` to enable),\n"
"or `gpu/nvidia` for nvidia specific gpu isolation,\n"
- "or `external`, or load an alternate isolator module using\n"
- "the `--modules` flag. Note that this flag is only relevant\n"
- "for the Mesos Containerizer.",
+ "or load an alternate isolator module using the `--modules`\n"
+ "flag. Note that this flag is only relevant for the Mesos\n"
+ "Containerizer.",
"posix/cpu,posix/mem");
add(&Flags::launcher,
@@ -500,25 +500,15 @@ mesos::internal::slave::Flags::Flags()
" ]\n"
"}");
- add(&Flags::containerizer_path,
- "containerizer_path",
- "The path to the external containerizer executable used when\n"
- "external isolation is activated (`--isolation=external`).");
-
add(&Flags::containerizers,
"containerizers",
"Comma-separated list of containerizer implementations\n"
"to compose in order to provide containerization.\n"
- "Available options are `mesos`, `external`, and\n"
- "`docker` (on Linux). The order the containerizers\n"
- "are specified is the order they are tried.\n",
+ "Available options are `mesos` and `docker` (on Linux).\n"
+ "The order the containerizers are specified is the order\n"
+ "they are tried.\n",
"mesos");
- add(&Flags::default_container_image,
- "default_container_image",
- "The default container image to use if not specified by a task,\n"
- "when using external containerizer.");
-
// Docker containerizer flags.
add(&Flags::docker,
"docker",
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index 58fba4a..ef2394c 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -103,9 +103,7 @@ public:
Option<Firewall> firewall_rules;
Option<Path> credential;
Option<ACLs> acls;
- Option<std::string> containerizer_path;
std::string containerizers;
- Option<std::string> default_container_image;
std::string docker;
Option<std::string> docker_mesos_image;
Duration docker_remove_delay;
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/tests/containerizer/external_containerizer_test.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/external_containerizer_test.cpp b/src/tests/containerizer/external_containerizer_test.cpp
deleted file mode 100644
index 226eac5..0000000
--- a/src/tests/containerizer/external_containerizer_test.cpp
+++ /dev/null
@@ -1,267 +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 <unistd.h>
-
-#include <string>
-#include <vector>
-#include <map>
-
-#include <gmock/gmock.h>
-
-#include <mesos/resources.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#include "master/master.hpp"
-
-#include "slave/flags.hpp"
-#include "slave/slave.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-#include "slave/containerizer/external_containerizer.hpp"
-
-#include "tests/mesos.hpp"
-#include "tests/flags.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-using mesos::internal::slave::Containerizer;
-using mesos::internal::slave::Slave;
-
-using mesos::master::detector::MasterDetector;
-
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::Invoke;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-// The external containerizer tests currently rely on a Python script
-// which needs the Mesos Python egg being built.
-// TODO(tillt): Consider providing tests that do not rely on Python.
-#ifdef MESOS_HAS_PYTHON
-
-// TODO(tillt): Update and enhance the ExternalContainerizer tests,
-// possibly following some of the patterns used within the
-// IsolatorTests or even entirely reusing the Containerizer tests.
-class ExternalContainerizerTest : public MesosTest {};
-
-
-class MockExternalContainerizer : public slave::ExternalContainerizer
-{
-public:
- MOCK_METHOD8(
- launch,
- process::Future<bool>(
- const ContainerID&,
- const TaskInfo&,
- const ExecutorInfo&,
- const string&,
- const Option<string>&,
- const SlaveID&,
- const process::PID<slave::Slave>&,
- bool checkpoint));
-
- MockExternalContainerizer(const slave::Flags& flags)
- : ExternalContainerizer(flags)
- {
- // Set up defaults for mocked methods.
- // NOTE: See TestContainerizer::setup for why we use
- // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
- // 'ON_CALL' and 'WillByDefault'.
- EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
- .WillRepeatedly(Invoke(this, &MockExternalContainerizer::_launch));
- }
-
- process::Future<bool> _launch(
- const ContainerID& containerId,
- const TaskInfo& taskInfo,
- const ExecutorInfo& executorInfo,
- const string& directory,
- const Option<string>& user,
- const SlaveID& slaveId,
- const PID<Slave>& slavePid,
- bool checkpoint)
- {
- return slave::ExternalContainerizer::launch(
- containerId,
- taskInfo,
- executorInfo,
- directory,
- user,
- slaveId,
- slavePid,
- checkpoint);
- }
-};
-
-
-// This test has been temporarily disabled due to MESOS-1257.
-TEST_F(ExternalContainerizerTest, DISABLED_Launch)
-{
- Try<Owned<cluster::Master>> master = this->StartMaster();
- ASSERT_SOME(master);
-
- Flags testFlags;
-
- slave::Flags flags = this->CreateSlaveFlags();
-
- flags.isolation = "external";
- flags.containerizer_path =
- testFlags.build_dir + "/src/examples/python/test-containerizer";
-
- MockExternalContainerizer containerizer(flags);
-
- Owned<MasterDetector> detector = master.get()->createDetector();
-
- Try<Owned<cluster::Slave>> slave =
- this->StartSlave(detector.get(), &containerizer, flags);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
-
- Future<FrameworkID> frameworkId;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureArg<1>(&frameworkId));
-
- Future<vector<Offer> > offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(frameworkId);
- AWAIT_READY(offers);
-
- EXPECT_NE(0u, offers.get().size());
-
- TaskInfo task;
- task.set_name("isolator_test");
- task.mutable_task_id()->set_value("1");
- task.mutable_slave_id()->CopyFrom(offers.get()[0].slave_id());
- task.mutable_resources()->CopyFrom(offers.get()[0].resources());
-
- Resources resources(offers.get()[0].resources());
- Option<Bytes> mem = resources.mem();
- ASSERT_SOME(mem);
- Option<double> cpus = resources.cpus();
- ASSERT_SOME(cpus);
-
- const string& file = path::join(flags.work_dir, "ready");
-
- // This task induces user/system load in a child process by
- // running top in a child process for ten seconds.
- task.mutable_command()->set_value(
-#ifdef __APPLE__
- // Use logging mode with 30,000 samples with no interval.
- "top -l 30000 -s 0 2>&1 > /dev/null & "
-#else
- // Batch mode, with 30,000 samples with no interval.
- "top -b -d 0 -n 30000 2>&1 > /dev/null & "
-#endif
- "touch " + file + "; " // Signals that the top command is running.
- "sleep 60");
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status))
- .WillRepeatedly(Return()); // Ignore rest for now.
-
- Future<ContainerID> containerId;
- EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _, _))
- .WillOnce(DoAll(FutureArg<0>(&containerId),
- Invoke(&containerizer,
- &MockExternalContainerizer::_launch)));
-
- driver.launchTasks(offers.get()[0].id(), {task});
-
- AWAIT_READY(containerId);
-
- AWAIT_READY(status);
-
- EXPECT_EQ(TASK_RUNNING, status.get().state());
-
- // Wait for the task to begin inducing cpu time.
- while (!os::exists(file));
-
- ExecutorID executorId;
- executorId.set_value(task.task_id().value());
-
- // We'll wait up to 10 seconds for the child process to induce
- // 1/8 of a second of user and system cpu time in total.
- // TODO(bmahler): Also induce rss memory consumption, by re-using
- // the balloon framework.
- ResourceStatistics statistics;
- Duration waited = Duration::zero();
- do {
- Future<ResourceStatistics> usage = containerizer.usage(containerId.get());
- AWAIT_READY(usage);
-
- statistics = usage.get();
-
- // If we meet our usage expectations, we're done!
- // NOTE: We are currently getting dummy-data from the test-
- // containerizer python script matching these expectations.
- // TODO(tillt): Consider working with real data.
- if (statistics.cpus_user_time_secs() >= 0.120 &&
- statistics.cpus_system_time_secs() >= 0.05 &&
- statistics.mem_rss_bytes() >= 1024u) {
- break;
- }
-
- os::sleep(Milliseconds(100));
- waited += Milliseconds(100);
- } while (waited < Seconds(10));
-
- EXPECT_GE(statistics.cpus_user_time_secs(), 0.120);
- EXPECT_GE(statistics.cpus_system_time_secs(), 0.05);
- EXPECT_EQ(statistics.cpus_limit(), cpus.get());
- EXPECT_GE(statistics.mem_rss_bytes(), 1024u);
- EXPECT_EQ(statistics.mem_limit_bytes(), mem.get().bytes());
-
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.killTask(task.task_id());
-
- AWAIT_READY(status);
-
- EXPECT_EQ(TASK_KILLED, status.get().state());
-
- driver.stop();
- driver.join();
-}
-
-#endif // MESOS_HAS_PYTHON
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {
[2/2] mesos git commit: Removed the external containerizer.
Posted by ji...@apache.org.
Removed the external containerizer.
Review: https://reviews.apache.org/r/50914/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f6a0e72c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f6a0e72c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f6a0e72c
Branch: refs/heads/master
Commit: f6a0e72c5d142d247dfe5d5c5b6cfb1c3aa1b8cd
Parents: 751a481
Author: Gilbert Song <so...@gmail.com>
Authored: Tue Aug 9 13:13:32 2016 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Tue Aug 9 13:13:32 2016 -0700
----------------------------------------------------------------------
configure.ac | 2 -
docs/committers.md | 4 -
docs/configuration.md | 28 +-
docs/containerizer-internals.md | 1 -
docs/external-containerizer.md | 501 -------
docs/fetcher.md | 4 +-
docs/home.md | 1 -
docs/sandbox.md | 1 -
include/mesos/containerizer/containerizer.proto | 62 -
src/CMakeLists.txt | 1 -
src/Makefile.am | 5 -
src/examples/python/test-containerizer.in | 37 -
src/examples/python/test_containerizer.py | 361 ------
src/slave/containerizer/containerizer.cpp | 42 +-
.../containerizer/external_containerizer.cpp | 1217 ------------------
.../containerizer/external_containerizer.hpp | 299 -----
src/slave/flags.cpp | 22 +-
src/slave/flags.hpp | 2 -
.../external_containerizer_test.cpp | 267 ----
19 files changed, 13 insertions(+), 2844 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index f82c631..57482d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1656,8 +1656,6 @@ There are two possible workarounds for this issue:
[chmod +x src/examples/python/test-executor])
AC_CONFIG_FILES([src/examples/python/test-framework],
[chmod +x src/examples/python/test-framework])
- AC_CONFIG_FILES([src/examples/python/test-containerizer],
- [chmod +x src/examples/python/test-containerizer])
AC_CONFIG_FILES([src/python/setup.py])
AC_CONFIG_FILES([src/python/cli/setup.py])
AC_CONFIG_FILES([src/python/interface/setup.py])
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/committers.md
----------------------------------------------------------------------
diff --git a/docs/committers.md b/docs/committers.md
index a4cf837..9c2cf6b 100644
--- a/docs/committers.md
+++ b/docs/committers.md
@@ -371,10 +371,6 @@ committers to learn about areas of the code that they are unfamiliar with.
<td>Docker Containerizer</td>
<td>Tim Chen, Benjamin Hindman</td>
</tr>
- <tr>
- <td>External Containerizer</td>
- <td>Till Toenshoff, Benjamin Hindman</td>
- </tr>
</tbody>
</thead>
</table>
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/configuration.md
----------------------------------------------------------------------
diff --git a/docs/configuration.md b/docs/configuration.md
index d6a7eb0..2090672 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1146,21 +1146,12 @@ in the sandbox directory.
</tr>
<tr>
<td>
- --containerizer_path=VALUE
- </td>
- <td>
-The path to the external containerizer executable used when
-external isolation is activated (<code>--isolation=external</code>).
- </td>
-</tr>
-<tr>
- <td>
--containerizers=VALUE
</td>
<td>
Comma-separated list of containerizer implementations
to compose in order to provide containerization.
-Available options are <code>mesos</code>, <code>external</code>, and
+Available options are <code>mesos</code> and
<code>docker</code> (on Linux). The order the containerizers
are specified is the order they are tried.
(default: mesos)
@@ -1183,15 +1174,6 @@ Example:
</tr>
<tr>
<td>
- --default_container_image=VALUE
- </td>
- <td>
-The default container image to use if not specified by a task,
-when using external containerizer.
- </td>
-</tr>
-<tr>
- <td>
--default_container_info=VALUE
</td>
<td>
@@ -1521,10 +1503,10 @@ Strategy for provisioning container rootfs from images, e.g., <code>aufs</code>,
Isolation mechanisms to use, e.g., <code>posix/cpu,posix/mem</code>, or
<code>cgroups/cpu,cgroups/mem</code>, or network/port_mapping
(configure with flag: <code>--with-network-isolator</code> to enable),
-or `gpu/nvidia` for nvidia specific gpu isolation,
-or <code>external</code>, or load an alternate isolator module using
-the <code>--modules</code> flag. Note that this flag is only relevant
-for the Mesos Containerizer. (default: posix/cpu,posix/mem)
+or `gpu/nvidia` for nvidia specific gpu isolation, or load an alternate
+isolator module using the <code>--modules</code> flag. Note that this
+flag is only relevant for the Mesos Containerizer.
+(default: posix/cpu,posix/mem)
</td>
</tr>
<tr>
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/containerizer-internals.md
----------------------------------------------------------------------
diff --git a/docs/containerizer-internals.md b/docs/containerizer-internals.md
index 1839c72..97776a6 100644
--- a/docs/containerizer-internals.md
+++ b/docs/containerizer-internals.md
@@ -33,7 +33,6 @@ Mesos currently supports the following containerizers:
* Composing
* [Docker](docker-containerizer.md)
* [Mesos](containerizer.md)
-* [External](external-containerizer.md) (deprecated)
#### Composing Containerizer
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/external-containerizer.md
----------------------------------------------------------------------
diff --git a/docs/external-containerizer.md b/docs/external-containerizer.md
deleted file mode 100644
index eece9e7..0000000
--- a/docs/external-containerizer.md
+++ /dev/null
@@ -1,501 +0,0 @@
----
-title: Apache Mesos - External Containerizer
-layout: documentation
----
-
-# External Containerizer
-
-**NOTE:** The external containerizer is deprecated. See
-[MESOS-3370](https://issues.apache.org/jira/browse/MESOS-3370) for details.
-
-* EC = external containerizer. A part of the mesos agent that provides
-an API for containerizing via external plugin executables.
-* ECP = external containerizer program. An external plugin executable
-implementing the actual containerizing by interfacing with a
-containerizing system (e.g. Docker).
-
-# Containerizing
-
-
-# General Overview
-
-EC invokes ECP as a shell process, passing the command as a parameter
-to the ECP executable. Additional data is exhanged via stdin and
-stdout.
-
-The ECP is expected to return a zero exit code for all commands it was
-able to process. A non-zero status code signals an error. Below you
-will find an overview of the commands that have to be implemented by
-an ECP, as well as their invocation scheme.
-
-The ECP is expected to be using stderr for state info and displaying
-additional debug information. That information is getting logged to
-a file, see [Enviroment: **Sandbox**](#sandbox).
-
-
-### Call and communication scheme
-
-Interface describing the functions an ECP has to implement via
-command calls. Many invocations on the ECP will also pass a
-protobuf message along via stdin. Some invocations on the ECP also
-expect to deliver a result protobuf message back via stdout.
-All protobuf messages are prefixed by their original length -
-this is sometimes referred to as "Record-IO"-format. See
-[Record-IO De/Serializing Example](#record-io-deserializing-example).
-
-**COMMAND < INPUT-PROTO > RESULT-PROTO**
-
-* `launch < containerizer::Launch`
-* `update < containerizer::Update`
-* `usage < containerizer::Usage > mesos::ResourceStatistics`
-* `wait < containerizer::Wait > containerizer::Termination`
-* `destroy < containerizer::Destroy`
-* `containers > containerizer::Containers`
-* `recover`
-
-
-# Command Ordering
-
-## Make no assumptions
-Commands may pretty much come in any order. There is only one
-exception to this rule; when launching a task, the EC will make sure
-that the ECP first receives a `launch` on that specific container, all
-other commands are queued until `launch` returns from the ECP.
-
-
-# Use Cases
-
-## Task Launching EC / ECP Overview
-
-* EC invokes `launch` on the ECP.
- * Along with that call, the ECP will receive a containerizer::Launch
- protobuf message via stdin.
- * ECP now makes sure the executor gets started.
-**Note** that `launch` is not supposed to block. It should return
-immediately after triggering the executor/command - that could be done
-via fork-exec within the ECP.
-* EC invokes `wait` on the ECP.
- * Along with that call, the ECP will receive a containerizer::Wait
- protobuf message via stdin.
- * ECP now blocks until the launched command is reaped - that could be
- implemented via waitpid within the ECP.
- * Once the command is reaped, the ECP should deliver a
- containerizer::Termination protobuf message via stdout, back to the
- EC.
-
-
-## Container Lifecycle Sequence Diagrams
-
-
-### Container Launching
-
-A container is in a staging state and now gets started and observed
-until it gets into a final state.
-
-![Container Launching Scheme](images/ec_launch_seqdiag.png?raw=true)
-
-### Container Running
-
-A container has gotten launched at some point and now is considered
-being in a non terminal state by the agent. The following commands
-will get triggered multiple times at the ECP over the lifetime of a
-container. Their order however is not determined.
-
-![Container Running Scheme](images/ec_lifecycle_seqdiag.png?raw=true)
-
-### Resource Limitation
-
-While a container is active, a resource limitation was identified
-(e.g. out of memory) by the ECP isolation mechanism of choice.
-
-![Resource Limitation Scheme](images/ec_kill_seqdiag.png?raw=true)
-
-<a name="agent-recovery-overview"></a>
-## Agent Recovery Overview
-
-* Agent recovers via check pointed state.
-* EC invokes `recover` on the ECP - there is no protobuf message sent
-or expected as a result from this command.
- * The ECP may try to recover internal states via its own failover
-mechanisms, if needed.
-* After `recover` returns, the EC will invoke `containers` on the ECP.
- * The ECP should return Containers which is a list of currently
- active containers.
-**Note** these containers are known to the ECP but might in fact
-partially be unknown to the agent (e.g. agent failed after launch but
-before or within wait) - those containers are considered to be
-orphans.
-* The EC now compares the list of agent known containers to those
-listed within `Containers`. For each orphan it identifies, the agent
-will invoke a `wait` followed by a `destroy` on the ECP for those
-containers.
-* Agent will now call `wait` on the ECP (via EC) for all recovered
-containers. This does once again put `wait` into the position of the
-ultimate command reaper.
-
-
-## Agent Recovery Sequence Diagram
-
-### Recovery
-
-While containers are active, the agent fails over.
-
-![Recovery Scheme](images/ec_recover_seqdiag.png?raw=true)
-
-### Orphan Destruction
-
-Containers identified by the ECP as being active but not agent state
-recoverable are getting terminated.
-
-![Orphan Destruction Scheme](images/ec_orphan_seqdiag.png?raw=true)
-
-
-# Command Details
-
-## launch
-### Start the containerized executor
-
-Hands over all information the ECP needs for launching a task
-via an executor.
-This call should not wait for the executor/command to return. The
-actual reaping of the containerized command is done via the `wait`
-call.
-
- launch < containerizer::Launch
-
-This call receives the containerizer::Launch protobuf via stdin.
-
- /**
- * Encodes the launch command sent to the external containerizer
- * program.
- */
- message Launch {
- required ContainerID container_id = 1;
- optional TaskInfo task_info = 2;
- optional ExecutorInfo executor_info = 3;
- optional string directory = 4;
- optional string user = 5;
- optional SlaveID agent_id = 6;
- optional string agent_pid = 7;
- optional bool checkpoint = 8;
- }
-
-This call does not return any data via stdout.
-
-## wait
-### Gets information on the containerized executor's Termination
-
-Is expected to reap the executor/command. This call should block
-until the executor/command has terminated.
-
- wait < containerizer::Wait > containerizer::Termination
-
-This call receives the containerizer::Wait protobuf via stdin.
-
- /**
- * Encodes the wait command sent to the external containerizer
- * program.
- */
- message Wait {
- required ContainerID container_id = 1;
- }
-
-This call is expected to return containerizer::Termination via stdout.
-
- /**
- * Information about a container termination, returned by the
- * containerizer to the agent.
- */
- message Termination {
- // A container may be killed if it exceeds its resources; this will
- // be indicated by killed=true and described by the message string.
- required bool killed = 1;
- required string message = 2;
-
- // Exit status of the process.
- optional int32 status = 3;
- }
-
-The Termination attribute `killed` is to be set only when the
-containerizer or the underlying isolation had to enforce a limitation
-by killing the task (e.g. task exceeded suggested memory limit).
-
-## update
-### Updates the container's resource limits
-
-Is sending (new) resource constraints for the given container.
-Resource constraints onto a container may vary over the lifetime of
-the containerized task.
-
- update < containerizer::Update
-
-This call receives the containerizer::Update protobuf via stdin.
-
- /**
- * Encodes the update command sent to the external containerizer
- * program.
- */
- message Update {
- required ContainerID container_id = 1;
- repeated Resource resources = 2;
- }
-
-This call does not return any data via stdout.
-
-## usage
-### Gathers resource usage statistics for a containerized task
-Is used for polling the current resource uses for the given container.
-
- usage < containerizer::Usage > mesos::ResourceStatistics
-
-This call received the containerizer::Usage protobuf via stdin.
-
- /**
- * Encodes the usage command sent to the external containerizer
- * program.
- */
- message Usage {
- required ContainerID container_id = 1;
- }
-
-This call is expected to return mesos::ResourceStatistics via stdout.
-
- /*
- * A snapshot of resource usage statistics.
- */
- message ResourceStatistics {
- required double timestamp = 1; // Snapshot time, in seconds since the Epoch.
-
- // CPU Usage Information:
- // Total CPU time spent in user mode, and kernel mode.
- optional double cpus_user_time_secs = 2;
- optional double cpus_system_time_secs = 3;
-
- // Number of CPUs allocated.
- optional double cpus_limit = 4;
-
- // cpu.stat on process throttling (for contention issues).
- optional uint32 cpus_nr_periods = 7;
- optional uint32 cpus_nr_throttled = 8;
- optional double cpus_throttled_time_secs = 9;
-
- // Memory Usage Information:
- optional uint64 mem_rss_bytes = 5; // Resident Set Size.
-
- // Amount of memory resources allocated.
- optional uint64 mem_limit_bytes = 6;
-
- // Broken out memory usage information (files, anonymous, and mmaped files)
- optional uint64 mem_file_bytes = 10;
- optional uint64 mem_anon_bytes = 11;
- optional uint64 mem_mapped_file_bytes = 12;
- }
-
-## destroy
-### Terminates the containerized executor
-
-Is used in rare situations, like for graceful agent shutdown
-but also in agent fail over scenarios - see Agent Recovery for more.
-
- destroy < containerizer::Destroy
-
-This call receives the containerizer::Destroy protobuf via stdin.
-
- /**
- * Encodes the destroy command sent to the external containerizer
- * program.
- */
- message Destroy {
- required ContainerID container_id = 1;
- }
-
-This call does not return any data via stdout.
-
-## containers
-### Gets all active container-id's
-
-Returns all container identifiers known to be currently active.
-
- containers > containerizer::Containers
-
-This call does not receive any additional data via stdin.
-
-This call is expected to pass containerizer::Containers back via
-stdout.
-
- /**
- * Information on all active containers returned by the containerizer
- * to the agent.
- */
- message Containers {
- repeated ContainerID containers = 1;
- }
-
-
-## recover
-### Internal ECP state recovery
-
-Allows the ECP to do a state recovery on its own. If the ECP
-uses state check-pointing e.g. via file system, then this call would
-be a good moment to de-serialize that state information. Make sure you
-also see [Agent Recovery Overview](#agent-recovery-overview) for more.
-
- recover
-
-This call does not receive any additional data via stdin.
-No returned data via stdout.
-
-
-
-### Protobuf Message Definitions
-
-For possibly more up-to-date versions of the above mentioned protobufs
-as well as protobuf messages referenced by them, please check:
-
-* containerizer::XXX are defined within
- include/mesos/containerizer/containerizer.proto.
-
-* mesos::XXX are defined within include/mesos/mesos.proto.
-
-
-
-# Environment
-
-<a name="sandbox"></a>
-## **Sandbox**
-
-A sandbox environment is formed by `cd` into the work-directory of the
-executor as well as a stderr redirect into the executor's "stderr"
-log-file.
-**Note** not **all** invocations have a complete sandbox environment.
-
-
-## Addional Environment Variables
-
-Additionally, there are a few new environment variables set when
-invoking the ECP.
-
-
-* MESOS_LIBEXEC_DIRECTORY = path to mesos-executor, mesos-usage, ...
-This information is always present.
-
-* MESOS_WORK_DIRECTORY = agent work directory. This should be used for
-distinguishing agent instances.
-This information is always present.
-
-**Note** that this is specifically helpful for being able to tie a set
-of containers to a specific agent instance, thus allowing proper
-recovery when needed.
-
-* MESOS_DEFAULT_CONTAINER_IMAGE = default image as provided via agent
-flags (default_container_image). This variable is provided only in
-calls to `launch`.
-
-
-
-# Debugging
-
-<a name="enhanced-verbosity-logging"></a>
-## Enhanced Verbosity Logging
-
-For receiving an increased level of status information from the EC
-use the GLOG verbosity level. Prefix your mesos startup call by
-setting the level to a value higher than or equal to two.
-
-`GLOG_v=2 ./bin/mesos-agent --master=[...]`
-
-
-## ECP stderr Logging
-
-All output to stderr of your ECP will get logged to the executor's
-'stderr' log file.
-The specific location can be extracted from the [Enhanced Verbosity
-Logging](#enhanced-verbosity-logging) of the EC.
-
-Example Log Output:
-
- I0603 02:12:34.165662 174215168 external_containerizer.cpp:1083] Invoking external containerizer for method 'launch'
- I0603 02:12:34.165675 174215168 external_containerizer.cpp:1100] calling: [/Users/till/Development/mesos-till/build/src/test-containerizer launch]
- I0603 02:12:34.165678 175824896 agent.cpp:497] Successfully attached file '/tmp/ExternalContainerizerTest_Launch_lP22ci/agents/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345'
- I0603 02:12:34.165686 174215168 external_containerizer.cpp:1101] directory: /tmp/ExternalContainerizerTest_Launch_lP22ci/agents/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345
-
-The stderr output of the ECP for this call is found within the stderr file located in the directory displayed in the last quoted line.
-
- cat /tmp/ExternalContainerizerTest_Launch_lP22ci/agents/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345/stderr
-
-
-# Appendix
-
-## Record-IO Proto Example: Launch
-
-This is what a properly record-io formatted protobuf looks like.
-
-**name: offset**
-
-* length: 00 - 03 = record length in byte
-
-* payload: 04 - (length + 4) = protobuf payload
-
-Example length: 00000240h = 576 byte total protobuf size
-
-Example Hexdump:
-
- 00000000: 4002 0000 0a26 0a24 3433 3532 3533 6162 2d64 3234 362d 3437 :@....&.$435253ab-d246-47
- 00000018: 6265 2d61 3335 302d 3335 3432 3034 3635 6438 3638 1a81 020a :be-a350-35420465d868....
- 00000030: 030a 0131 2a16 0a04 6370 7573 1000 1a09 0900 0000 0000 0000 :...1*...cpus............
- 00000048: 4032 012a 2a15 0a03 6d65 6d10 001a 0909 0000 0000 0000 9040 :@2.**...mem............@
- 00000060: 3201 2a2a 160a 0464 6973 6b10 001a 0909 0000 0000 0000 9040 :2.**...disk............@
- 00000078: 3201 2a2a 180a 0570 6f72 7473 1001 220a 0a08 0898 f201 1080 :2.**...ports..".........
- 00000090: fa01 3201 2a3a 2a1a 2865 6368 6f20 274e 6f20 7375 6368 2066 :..2.*:*.(echo 'No such f
- 000000a8: 696c 6520 6f72 2064 6972 6563 746f 7279 273b 2065 7869 7420 :ile or directory'; exit
- 000000c0: 3142 2b0a 2932 3031 3430 3532 362d 3031 3530 3036 2d31 3637 :1B+.)20140526-015006-167
- 000000d8: 3737 3334 332d 3535 3430 332d 3632 3536 372d 3030 3030 4a3d :77343-55403-62567-0000J=
- 000000f0: 436f 6d6d 616e 6420 4578 6563 7574 6f72 2028 5461 736b 3a20 :Command Executor (Task:
- 00000108: 3129 2028 436f 6d6d 616e 643a 2073 6820 2d63 2027 7768 696c :1) (Command: sh -c 'whil
- 00000120: 6520 7472 7565 203b 2e2e 2e27 2952 0131 22c5 012f 746d 702f :e true ;...')R.1"../tmp/
- 00000138: 4578 7465 726e 616c 436f 6e74 6169 6e65 7269 7a65 7254 6573 :ExternalContainerizerTes
- 00000150: 745f 4c61 756e 6368 5f6c 5855 6839 662f 736c 6176 6573 2f32 :t_Launch_lXUh9f/agents/2
- 00000168: 3031 3430 3532 362d 3031 3530 3036 2d31 3637 3737 3334 332d :0140526-015006-16777343-
- 00000180: 3535 3430 332d 3632 3536 372d 302f 6672 616d 6577 6f72 6b73 :55403-62567-0/frameworks
- 00000198: 2f32 3031 3430 3532 362d 3031 3530 3036 2d31 3637 3737 3334 :/20140526-015006-1677734
- 000001b0: 332d 3535 3430 332d 3632 3536 372d 3030 3030 2f65 7865 6375 :3-55403-62567-0000/execu
- 000001c8: 746f 7273 2f31 2f72 756e 732f 3433 3532 3533 6162 2d64 3234 :tors/1/runs/435253ab-d24
- 000001e0: 362d 3437 6265 2d61 3335 302d 3335 3432 3034 3635 6438 3638 :6-47be-a350-35420465d868
- 000001f8: 2a04 7469 6c6c 3228 0a26 3230 3134 3035 3236 2d30 3135 3030 :*.till2(.&20140526-01500
- 00000210: 362d 3136 3737 3733 3433 2d35 3534 3033 2d36 3235 3637 2d30 :6-16777343-55403-62567-0
- 00000228: 3a18 736c 6176 6528 3129 4031 3237 2e30 2e30 2e31 3a35 3534 ::.slave(1)@127.0.0.1:554
- 00000240: 3033 4000
-
-<a name="record-io-deserializing-example"></a>
-## Record-IO De/Serializing Example
-How to send and receive such record-io formatted message
-using Python
-
-*taken from src/examples/python/test_containerizer.py*
-
- # Read a data chunk prefixed by its total size from stdin.
- def receive():
- # Read size (uint32 => 4 bytes).
- size = struct.unpack('I', sys.stdin.read(4))
- if size[0] <= 0:
- print >> sys.stderr, "Expected protobuf size over stdin. " \
- "Received 0 bytes."
- return ""
-
- # Read payload.
- data = sys.stdin.read(size[0])
- if len(data) != size[0]:
- print >> sys.stderr, "Expected %d bytes protobuf over stdin. " \
- "Received %d bytes." % (size[0], len(data))
- return ""
-
- return data
-
- # Write a protobuf message prefixed by its total size (aka recordio)
- # to stdout.
- def send(data):
- # Write size (uint32 => 4 bytes).
- sys.stdout.write(struct.pack('I', len(data)))
-
- # Write payload.
- sys.stdout.write(data)
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/fetcher.md
----------------------------------------------------------------------
diff --git a/docs/fetcher.md b/docs/fetcher.md
index 6889845..d4ffd52 100644
--- a/docs/fetcher.md
+++ b/docs/fetcher.md
@@ -34,9 +34,7 @@ The Mesos fetcher mechanism comprises of these two parts:
1. The agent-internal Fetcher Process (in terms of libprocess) that controls and
coordinates all fetch actions. Every agent instance has exactly one internal
-fetcher instance that is used by every kind of containerizer (except the
-external containerizer variant, which is responsible for its own approach to
-fetching).
+fetcher instance that is used by every kind of containerizer.
2. The external program `mesos-fetcher` that is invoked by the former. It
performs all network and disk operations except file deletions and file size
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/home.md
----------------------------------------------------------------------
diff --git a/docs/home.md b/docs/home.md
index 80451f4..c8aeaef 100644
--- a/docs/home.md
+++ b/docs/home.md
@@ -22,7 +22,6 @@ layout: documentation
* [Docker Volume Support](docker-volume.md)
* [CNI support](cni.md)
* [Docker Containerizer](docker-containerizer.md) for launching a Docker image as a Task, or as an Executor.
- * [External Containerizer](external-containerizer.md) for custom containerization implementations (deprecated).
* [Roles](roles.md)
* [Weights](weights.md)
* [Authentication](authentication.md)
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/sandbox.md
----------------------------------------------------------------------
diff --git a/docs/sandbox.md b/docs/sandbox.md
index db973e3..af0036d 100644
--- a/docs/sandbox.md
+++ b/docs/sandbox.md
@@ -150,7 +150,6 @@ executor and isolators:
* Docker containerizer - As of Docker `1.9.1`, the Docker containerizer
does not enforce nor support a disk quota. See the
[Docker issue](https://github.com/docker/docker/issues/3804).
-* [External containerizer](external-containerizer.md).
## Sandbox lifecycle
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/include/mesos/containerizer/containerizer.proto
----------------------------------------------------------------------
diff --git a/include/mesos/containerizer/containerizer.proto b/include/mesos/containerizer/containerizer.proto
index 4d8e827..7c35ef3 100644
--- a/include/mesos/containerizer/containerizer.proto
+++ b/include/mesos/containerizer/containerizer.proto
@@ -23,59 +23,6 @@ option java_outer_classname = "Protos";
/**
- * Encodes the launch command sent to the external containerizer
- * program.
- */
-message Launch {
- required ContainerID container_id = 1;
- optional TaskInfo task_info = 2;
- optional ExecutorInfo executor_info = 3;
- optional string directory = 4;
- optional string user = 5;
- optional SlaveID slave_id = 6;
- optional string slave_pid = 7;
- optional bool checkpoint = 8;
-}
-
-
-/**
- * Encodes the update command sent to the external containerizer
- * program.
- */
-message Update {
- required ContainerID container_id = 1;
- repeated Resource resources = 2;
-}
-
-
-/**
- * Encodes the wait command sent to the external containerizer
- * program.
- */
-message Wait {
- required ContainerID container_id = 1;
-}
-
-
-/**
- * Encodes the destroy command sent to the external containerizer
- * program.
- */
-message Destroy {
- required ContainerID container_id = 1;
-}
-
-
-/**
- * Encodes the usage command sent to the external containerizer
- * program.
- */
-message Usage {
- required ContainerID container_id = 1;
-}
-
-
-/**
* Information about a container termination, returned by the
* containerizer to the slave.
*/
@@ -89,12 +36,3 @@ message Termination {
repeated TaskStatus.Reason reasons = 5;
optional string message = 2;
}
-
-
-/**
- * Information on all active containers returned by the containerizer
- * to the slave.
- */
-message Containers {
- repeated ContainerID containers = 1;
-}
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1286ee0..6088c26 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -315,7 +315,6 @@ set(AGENT_SRC
slave/containerizer/mesos/launch.cpp
slave/containerizer/fetcher.cpp
slave/containerizer/containerizer.cpp
- slave/containerizer/external_containerizer.cpp
slave/containerizer/composing.cpp
slave/containerizer/mesos/containerizer.cpp
slave/container_logger.cpp
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 39e3199..cf76a69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -843,7 +843,6 @@ libmesos_no_3rdparty_la_SOURCES += \
slave/containerizer/composing.cpp \
slave/containerizer/containerizer.cpp \
slave/containerizer/docker.cpp \
- slave/containerizer/external_containerizer.cpp \
slave/containerizer/fetcher.cpp \
slave/containerizer/mesos/containerizer.cpp \
slave/containerizer/mesos/isolator.cpp \
@@ -967,7 +966,6 @@ libmesos_no_3rdparty_la_SOURCES += \
slave/containerizer/composing.hpp \
slave/containerizer/containerizer.hpp \
slave/containerizer/docker.hpp \
- slave/containerizer/external_containerizer.hpp \
slave/containerizer/fetcher.hpp \
slave/containerizer/mesos/constants.hpp \
slave/containerizer/mesos/containerizer.hpp \
@@ -2156,7 +2154,6 @@ mesos_tests_SOURCES = \
tests/containerizer/docker_containerizer_tests.cpp \
tests/containerizer/docker_spec_tests.cpp \
tests/containerizer/docker_tests.cpp \
- tests/containerizer/external_containerizer_test.cpp \
tests/containerizer/isolator_tests.cpp \
tests/containerizer/launcher.cpp \
tests/containerizer/memory_test_helper.cpp \
@@ -2248,8 +2245,6 @@ if HAS_PYTHON
mesos_tests_DEPENDENCIES += $(MESOS_EGG)
EXAMPLESCRIPTSPYTHON = \
- examples/python/test_containerizer.py \
- examples/python/test-containerizer \
examples/python/test_executor.py \
examples/python/test-executor \
examples/python/test_framework.py \
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/examples/python/test-containerizer.in
----------------------------------------------------------------------
diff --git a/src/examples/python/test-containerizer.in b/src/examples/python/test-containerizer.in
deleted file mode 100644
index 5066b45..0000000
--- a/src/examples/python/test-containerizer.in
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-
-# This script uses MESOS_SOURCE_DIR and MESOS_BUILD_DIR which come
-# from configuration substitutions.
-MESOS_SOURCE_DIR=@abs_top_srcdir@
-MESOS_BUILD_DIR=@abs_top_builddir@
-
-# Use colors for errors.
-. ${MESOS_SOURCE_DIR}/support/colors.sh
-
-# Force the use of the Python interpreter configured during building.
-test ! -z "${PYTHON}" && \
- echo "${RED}Ignoring PYTHON environment variable (using @PYTHON@)${NORMAL}"
-
-PYTHON=@PYTHON@
-
-SETUPTOOLS=`echo ${MESOS_BUILD_DIR}/3rdparty/setuptools-*/`
-
-# Just warn in the case when build with --disable-bundled.
-test ! -e ${SETUPTOOLS} && \
- echo "${RED}Failed to find ${SETUPTOOLS}${NORMAL}"
-
-PROTOBUF=`echo ${MESOS_BUILD_DIR}/3rdparty/protobuf-*/python/`
-
-test ! -e ${PROTOBUF} && \
- echo "${RED}Failed to find ${PROTOBUF}${NORMAL}"
-
-MESOS_EGGS=$(find ${MESOS_BUILD_DIR}/src/python/dist -name "*.egg" | tr "\\n" ":")
-
-SCRIPT=${MESOS_SOURCE_DIR}/src/examples/python/test_containerizer.py
-
-test ! -e ${SCRIPT} && \
- echo "${RED}Failed to find ${SCRIPT}${NORMAL}" && \
- exit 1
-
-PYTHONPATH="${SETUPTOOLS}:${PROTOBUF}:${MESOS_EGGS}" \
- exec ${PYTHON} ${SCRIPT} "${@}"
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/examples/python/test_containerizer.py
----------------------------------------------------------------------
diff --git a/src/examples/python/test_containerizer.py b/src/examples/python/test_containerizer.py
deleted file mode 100644
index 8e154b0..0000000
--- a/src/examples/python/test_containerizer.py
+++ /dev/null
@@ -1,361 +0,0 @@
-#!/usr/bin/env python
-
-# 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.
-
-# The scheme an external containerizer has to adhere to is;
-#
-# COMMAND < INPUT-PROTO > RESULT-PROTO
-#
-# launch < Launch
-# update < Update
-# usage < Usage > ResourceStatistics
-# wait < Wait > Termination
-# destroy < Destroy
-# containers > Containers
-# recover
-#
-# 'wait' is expected to block until the task command/executor has
-# terminated.
-
-import fcntl
-import multiprocessing
-import os
-import signal
-import subprocess
-import sys
-import struct
-import time
-import google
-
-from mesos.interface import containerizer_pb2
-from mesos.interface import mesos_pb2
-
-# Render a string describing how to use this script.
-def use(argv0, methods):
- out = "Usage: %s <command>\n" % argv0
- out += "Valid commands: " + ', '.join(methods)
-
- return out
-
-
-# Read a data chunk prefixed by its total size from stdin.
-def receive():
- # Read size (uint32 => 4 bytes).
- size = struct.unpack('I', sys.stdin.read(4))
- if size[0] <= 0:
- print >> sys.stderr, "Expected protobuf size over stdin. " \
- "Received 0 bytes."
- return ""
-
- # Read payload.
- data = sys.stdin.read(size[0])
- if len(data) != size[0]:
- print >> sys.stderr, "Expected %d bytes protobuf over stdin. " \
- "Received %d bytes." % (size[0], len(data))
- return ""
-
- return data
-
-
-# Write a protobuf message prefixed by its total size (aka recordio)
-# to stdout.
-def send(data):
- # Write size (uint32 => 4 bytes).
- sys.stdout.write(struct.pack('I', len(data)))
-
- # Write payload.
- sys.stdout.write(data)
-
-
-# Start a containerized executor. Expects to receive an Launch
-# protobuf via stdin.
-def launch():
- try:
- data = receive()
- if len(data) == 0:
- return 1
-
- launch = containerizer_pb2.Launch()
- launch.ParseFromString(data)
-
- if launch.task_info.HasField("executor"):
- command = ["sh",
- "-c",
- launch.task_info.executor.command.value]
- else:
- print >> sys.stderr, "No executor passed; using mesos-executor!"
- executor = os.path.join(os.environ['MESOS_LIBEXEC_DIRECTORY'],
- "mesos-executor")
- command = ["sh",
- "-c",
- executor]
- print >> sys.stderr, "command " + str(command)
-
- lock_dir = os.path.join("/tmp/mesos-test-containerizer",
- launch.container_id.value)
- subprocess.check_call(["mkdir", "-p", lock_dir])
-
- # Fork a child process for allowing a blocking wait.
- pid = os.fork()
- if pid == 0:
- # We are in the child.
- proc = subprocess.Popen(command, env=os.environ.copy())
-
- # Wait and serialize the process status when done.
- lock = os.path.join(lock_dir, "wait")
- with open(lock, "w+") as lk:
- fcntl.flock(lk, fcntl.LOCK_EX)
-
- status = proc.wait()
-
- lk.write(str(status) + "\n")
-
- sys.exit(status)
- else:
- # We are in the parent.
-
- # Serialize the subprocess pid.
- lock = os.path.join(lock_dir, "pid")
- with open(lock, "w+") as lk:
- fcntl.flock(lk, fcntl.LOCK_EX)
-
- lk.write(str(pid) + "\n")
-
- except google.protobuf.message.DecodeError:
- print >> sys.stderr, "Could not deserialise Launch protobuf"
- return 1
-
- except OSError as e:
- print >> sys.stderr, e.strerror
- return 1
-
- except ValueError:
- print >> sys.stderr, "Value is invalid"
- return 1
-
- return 0
-
-
-# Update the container's resources.
-# Expects to receive a Update protobuf via stdin.
-def update():
- try:
- data = receive()
- if len(data) == 0:
- return 1
-
- update = containerizer_pb2.Update()
- update.ParseFromString(data)
-
- print >> sys.stderr, "Received " \
- + str(len(update.resources)) \
- + " resource elements."
-
- except google.protobuf.message.DecodeError:
- print >> sys.stderr, "Could not deserialise Update protobuf."
- return 1
-
- except OSError as e:
- print >> sys.stderr, e.strerror
- return 1
-
- except ValueError:
- print >> sys.stderr, "Value is invalid"
- return 1
-
- return 0
-
-
-# Gather resource usage statistics for the containerized executor.
-# Delivers an ResourceStatistics protobut via stdout when
-# successful.
-def usage():
- try:
- data = receive()
- if len(data) == 0:
- return 1
- usage = containerizer_pb2.Usage()
- usage.ParseFromString(data)
-
- statistics = mesos_pb2.ResourceStatistics()
-
- statistics.timestamp = time.time()
-
- # Return hardcoded dummy statistics.
- # TODO(tillt): Make use of mesos-usage here for capturing real
- # statistics.
- statistics.mem_rss_bytes = 1073741824
- statistics.mem_limit_bytes = 1073741824
- statistics.cpus_limit = 2
- statistics.cpus_user_time_secs = 0.12
- statistics.cpus_system_time_secs = 0.5
-
- send(statistics.SerializeToString())
-
- except google.protobuf.message.DecodeError:
- print >> sys.stderr, "Could not deserialise Usage protobuf."
- return 1
-
- except google.protobuf.message.EncodeError:
- print >> sys.stderr, "Could not serialise ResourceStatistics protobuf."
- return 1
-
- except OSError as e:
- print >> sys.stderr, e.strerror
- return 1
-
- return 0
-
-
-# Terminate the containerized executor.
-def destroy():
- try:
- data = receive()
- if len(data) == 0:
- return 1
- destroy = containerizer_pb2.Destroy()
- destroy.ParseFromString(data)
-
- lock_dir = os.path.join("/tmp/mesos-test-containerizer",
- destroy.container_id.value)
- lock = os.path.join(lock_dir, "pid")
-
- # Obtain our shared lock once it becomes available, read
- # the pid and kill that process.
- with open(lock, "r") as lk:
- fcntl.flock(lk, fcntl.LOCK_SH)
-
- pid = int(lk.read())
-
- os.kill(pid, signal.SIGKILL)
-
- except google.protobuf.message.DecodeError:
- print >> sys.stderr, "Could not deserialise Destroy protobuf."
- return 1
-
- except OSError as e:
- print >> sys.stderr, e.strerror
- return 1
-
- return 0
-
-
-# Recover all containerized executors states.
-def recover():
-
- # This currently does not try to recover any internal state and
- # therefore is to be regarded as being not complete.
- # A complete implementation would attempt to recover all active
- # containers by deserializing all previously checkpointed
- # ContainerIDs.
-
- return 0
-
-
-# Get the containerized executor's Termination.
-# Delivers a Termination protobuf filled with the information
-# gathered from launch's wait via stdout.
-def wait():
- try:
- data = receive()
- if len(data) == 0:
- return 1
- wait = containerizer_pb2.Wait()
- wait.ParseFromString(data)
-
- lock_dir = os.path.join("/tmp/mesos-test-containerizer",
- wait.container_id.value)
- lock = os.path.join(lock_dir, "wait")
-
- # Obtain our shared lock once it becomes available and read
- # the status code.
- with open(lock, "r") as lk:
- fcntl.flock(lk, fcntl.LOCK_SH)
- status = int(lk.read())
-
- # Deliver the termination protobuf back to the slave.
- termination = containerizer_pb2.Termination()
- termination.killed = false
- termination.status = status
- termination.message = ""
-
- send(termination.SerializeToString())
-
- except google.protobuf.message.DecodeError:
- print >> sys.stderr, "Could not deserialise Termination protobuf."
- return 1
-
- except google.protobuf.message.EncodeError:
- print >> sys.stderr, "Could not serialise Termination protobuf."
- return 1
-
- except OSError as e:
- print >> sys.stderr, e.strerror
- return 1
-
- return 0
-
-
-def containers():
- try:
- containers = containerizer_pb2.Containers()
-
- # This currently does not fill in any active containers and
- # therefore is to be regarded as being not complete.
- # A complete implementation would fill the containers message
- # with all active ContainerIDs.
-
- send(containers.SerializeToString())
-
- except google.protobuf.message.EncodeError:
- print >> sys.stderr, "Could not serialise Containers protobuf."
- return 1
-
- except OSError as e:
- print >> sys.stderr, e.strerror
- return 1
-
- return 0
-
-
-if __name__ == "__main__":
- methods = { "launch": launch,
- "update": update,
- "destroy": destroy,
- "containers": containers,
- "recover": recover,
- "usage": usage,
- "wait": wait }
-
- if sys.argv[1:2] == ["--help"] or sys.argv[1:2] == ["-h"]:
- print use(sys.argv[0], methods.keys())
- sys.exit(0)
-
- if len(sys.argv) < 2:
- print >> sys.stderr, "Please pass a command"
- print >> sys.stderr, use(sys.argv[0], methods.keys())
- sys.exit(1)
-
- command = sys.argv[1]
- if command not in methods:
- print >> sys.stderr, "Invalid command passed"
- print >> sys.stderr, use(sys.argv[0], methods.keys())
- sys.exit(2)
-
- method = methods.get(command)
-
- sys.exit(method())
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/containerizer/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp
index ba3b3f6..2d59caf 100644
--- a/src/slave/containerizer/containerizer.cpp
+++ b/src/slave/containerizer/containerizer.cpp
@@ -37,7 +37,6 @@
#include "slave/containerizer/composing.hpp"
#include "slave/containerizer/containerizer.hpp"
#include "slave/containerizer/docker.hpp"
-#include "slave/containerizer/external_containerizer.hpp"
#include "slave/containerizer/mesos/containerizer.hpp"
#include "slave/containerizer/mesos/launcher.hpp"
@@ -59,8 +58,7 @@ namespace internal {
namespace slave {
// TODO(idownes): Move this to the Containerizer interface to complete
-// the delegation of containerization, i.e., external containerizers should be
-// able to report the resources they can isolate.
+// the delegation of containerization.
Try<Resources> Containerizer::resources(const Flags& flags)
{
Try<Resources> parsed = Resources::parse(
@@ -189,28 +187,6 @@ Try<Containerizer*> Containerizer::create(
bool local,
Fetcher* fetcher)
{
- if (flags.isolation == "external") {
- LOG(WARNING) << "The 'external' isolation flag is deprecated, "
- << "please update your flags to"
- << " '--containerizers=external'.";
-
- if (flags.container_logger.isSome()) {
- return Error(
- "The external containerizer does not support custom container "
- "logger modules. The '--isolation=external' flag cannot be "
- " set along with '--container_logger=...'");
- }
-
- Try<ExternalContainerizer*> containerizer =
- ExternalContainerizer::create(flags);
- if (containerizer.isError()) {
- return Error("Could not create ExternalContainerizer: " +
- containerizer.error());
- }
-
- return containerizer.get();
- }
-
// Get the set of containerizer types.
const vector<string> _types = strings::split(flags.containerizers, ",");
const set<string> containerizerTypes(_types.begin(), _types.end());
@@ -295,22 +271,6 @@ Try<Containerizer*> Containerizer::create(
} else {
containerizers.push_back(containerizer.get());
}
- } else if (type == "external") {
- if (flags.container_logger.isSome()) {
- return Error(
- "The external containerizer does not support custom container "
- "logger modules. The '--containerizers=external' flag cannot be "
- "set along with '--container_logger=...'");
- }
-
- Try<ExternalContainerizer*> containerizer =
- ExternalContainerizer::create(flags);
- if (containerizer.isError()) {
- return Error("Could not create ExternalContainerizer: " +
- containerizer.error());
- } else {
- containerizers.push_back(containerizer.get());
- }
} else {
return Error("Unknown or unsupported containerizer: " + type);
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/containerizer/external_containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/external_containerizer.cpp b/src/slave/containerizer/external_containerizer.cpp
deleted file mode 100644
index 9ee137a..0000000
--- a/src/slave/containerizer/external_containerizer.cpp
+++ /dev/null
@@ -1,1217 +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>
-
-#ifndef __WINDOWS__
-#include <poll.h>
-#endif // __WINDOWS__
-
-#include <signal.h>
-#include <stdio.h>
-
-#include <list>
-#include <tuple>
-
-#include <mesos/type_utils.hpp>
-
-#include <process/async.hpp>
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/delay.hpp>
-#include <process/id.hpp>
-#include <process/io.hpp>
-#include <process/reap.hpp>
-
-#include <stout/check.hpp>
-#include <stout/foreach.hpp>
-#include <stout/lambda.hpp>
-#include <stout/nothing.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/strings.hpp>
-#include <stout/uuid.hpp>
-
-#include <stout/os/killtree.hpp>
-
-#include "common/status_utils.hpp"
-
-#include "slave/paths.hpp"
-
-#include "slave/containerizer/external_containerizer.hpp"
-
-using lambda::bind;
-
-using std::list;
-using std::map;
-using std::set;
-using std::string;
-using std::tuple;
-using std::vector;
-
-using namespace process;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-using state::ExecutorState;
-using state::FrameworkState;
-using state::RunState;
-using state::SlaveState;
-
-Try<ExternalContainerizer*> ExternalContainerizer::create(const Flags& flags)
-{
- return new ExternalContainerizer(flags);
-}
-
-
-// Validate the invocation result.
-static Option<Error> validate(
- const Future<Option<int>>& future)
-{
- if (!future.isReady()) {
- return Error("Status not ready");
- }
-
- Option<int> status = future.get();
- if (status.isNone()) {
- return Error("External containerizer has no status available");
- }
-
- // The status is a waitpid-result which has to be checked for SIGNAL
- // based termination before masking out the exit-code.
- if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
- return Error("Externel containerizer " + WSTRINGIFY(status.get()));
- }
-
- return None();
-}
-
-
-// Validate the invocation results and extract a piped protobuf
-// message.
-template <typename T>
-static Try<T> result(
- const Future<tuple<Future<Result<T>>, Future<Option<int>>>>& future)
-{
- if (!future.isReady()) {
- return Error("Could not receive any result");
- }
-
- Option<Error> error = validate(std::get<1>(future.get()));
- if (error.isSome()) {
- return error.get();
- }
-
- Future<Result<T>> result = std::get<0>(future.get());
- if (result.isFailed()) {
- return Error("Could not receive any result: " + result.failure());
- }
-
- if (result.get().isError()) {
- return Error("Could not receive any result: " + result.get().error());
- }
-
- if (result.get().isNone()) {
- return Error("Could not receive any result");
- }
-
- return result.get().get();
-}
-
-
-ExternalContainerizer::ExternalContainerizer(const Flags& flags)
- : process(new ExternalContainerizerProcess(flags))
-{
- spawn(process.get());
-}
-
-
-ExternalContainerizer::~ExternalContainerizer()
-{
- terminate(process.get());
- process::wait(process.get());
-}
-
-
-Future<Nothing> ExternalContainerizer::recover(
- const Option<state::SlaveState>& state)
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::recover,
- state);
-}
-
-
-Future<bool> ExternalContainerizer::launch(
- const ContainerID& containerId,
- const ExecutorInfo& executorInfo,
- const string& directory,
- const Option<string>& user,
- const SlaveID& slaveId,
- const PID<Slave>& slavePid,
- bool checkpoint)
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::launch,
- containerId,
- None(),
- executorInfo,
- directory,
- user,
- slaveId,
- slavePid,
- checkpoint);
-}
-
-
-Future<bool> ExternalContainerizer::launch(
- const ContainerID& containerId,
- const TaskInfo& taskInfo,
- const ExecutorInfo& executorInfo,
- const string& directory,
- const Option<string>& user,
- const SlaveID& slaveId,
- const PID<Slave>& slavePid,
- bool checkpoint)
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::launch,
- containerId,
- taskInfo,
- executorInfo,
- directory,
- user,
- slaveId,
- slavePid,
- checkpoint);
-}
-
-
-Future<Nothing> ExternalContainerizer::update(
- const ContainerID& containerId,
- const Resources& resources)
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::update,
- containerId,
- resources);
-}
-
-
-Future<ResourceStatistics> ExternalContainerizer::usage(
- const ContainerID& containerId)
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::usage,
- containerId);
-}
-
-
-Future<containerizer::Termination> ExternalContainerizer::wait(
- const ContainerID& containerId)
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::wait,
- containerId);
-}
-
-
-void ExternalContainerizer::destroy(const ContainerID& containerId)
-{
- dispatch(process.get(),
- &ExternalContainerizerProcess::destroy,
- containerId);
-}
-
-
-Future<hashset<ContainerID>> ExternalContainerizer::containers()
-{
- return dispatch(process.get(),
- &ExternalContainerizerProcess::containers);
-}
-
-
-ExternalContainerizerProcess::ExternalContainerizerProcess(
- const Flags& _flags) : flags(_flags) {}
-
-
-Future<Nothing> ExternalContainerizerProcess::recover(
- const Option<state::SlaveState>& state)
-{
- LOG(INFO) << "Recovering containerizer";
-
- // Ask the external containerizer to recover its internal state.
- Try<Subprocess> invoked = invoke("recover");
-
- if (invoked.isError()) {
- return Failure("Recover failed: " + invoked.error());
- }
-
- return invoked.get().status()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_recover,
- state,
- lambda::_1));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::_recover(
- const Option<state::SlaveState>& state,
- const Future<Option<int>>& future)
-{
- VLOG(1) << "Recover validation callback triggered";
-
- Option<Error> error = validate(future);
-
- if (error.isSome()) {
- return Failure("Recover failed: " + error.get().message);
- }
-
- // Gather the active containers from the external containerizer.
- return containers()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::__recover,
- state,
- lambda::_1));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::__recover(
- const Option<state::SlaveState>& state,
- const hashset<ContainerID>& containers)
-{
- VLOG(1) << "Recover continuation triggered";
-
- // An orphaned container is known to the external containerizer but
- // not to the slave, thus not recoverable but pending.
- hashset<ContainerID> orphaned = containers;
-
- if (state.isSome()) {
- foreachvalue (const FrameworkState& framework, state.get().frameworks) {
- foreachvalue (const ExecutorState& executor, framework.executors) {
- if (executor.info.isNone()) {
- LOG(WARNING) << "Skipping recovery of executor '" << executor.id
- << "' of framework " << framework.id
- << " because its info could not be recovered";
- continue;
- }
-
- if (executor.latest.isNone()) {
- LOG(WARNING) << "Skipping recovery of executor '" << executor.id
- << "' of framework " << framework.id
- << " because its latest run could not be recovered";
- continue;
- }
-
- // We are only interested in the latest run of the executor!
- const ContainerID& containerId = executor.latest.get();
- Option<RunState> run = executor.runs.get(containerId);
- CHECK_SOME(run);
-
- if (run.get().completed) {
- VLOG(1) << "Skipping recovery of executor '" << executor.id
- << "' of framework " << framework.id
- << " because its latest run "
- << containerId << " is completed";
- continue;
- }
-
- // Containers the external containerizer does not have
- // information on, should be skipped as their state is not
- // recoverable.
- if (!containers.contains(containerId)) {
- LOG(WARNING) << "Skipping recovery of executor '" << executor.id
- << "' of framework " << framework.id
- << " because the external containerizer has not "
- << " identified " << containerId << " as active";
- continue;
- }
-
- LOG(INFO) << "Recovering container '" << containerId
- << "' for executor '" << executor.id
- << "' of framework " << framework.id;
-
- Option<string> user = None();
-
- // NOTE: `chown` has no meaningful interpretation on Windows. This is
- // safe to `#ifdef` out because we don't compile the user flag on
- // Windows, so this should always be `None`.
-#ifndef __WINDOWS__
- if (flags.switch_user) {
- // The command (either in form of task or executor command)
- // can define a specific user to run as. If present, this
- // precedes the framework user value.
- if (executor.info.isSome() &&
- executor.info.get().command().has_user()) {
- user = executor.info.get().command().user();
- } else if (framework.info.isSome()) {
- user = framework.info.get().user();
- }
- }
-#endif // __WINDOWS__
-
- // Re-create the sandbox for this container.
- const string directory = paths::createExecutorDirectory(
- flags.work_dir,
- state.get().id,
- framework.id,
- executor.id,
- containerId,
- user);
-
- Sandbox sandbox(directory, user);
-
- // Collect this container as being active.
- actives.put(containerId, Owned<Container>(new Container(sandbox)));
-
- // Assume that this container had been launched, if this proves
- // to be wrong, the containerizer::Termination delivered by the
- // subsequent wait invocation will tell us.
- actives[containerId]->launched.set(Nothing());
-
- // Remove this container from the orphan collection as it is not
- // orphaned.
- orphaned.erase(containerId);
- }
- }
- }
-
- // Done when we got no orphans to take care of.
- if (orphaned.empty()) {
- VLOG(1) << "Recovery done";
- return Nothing();
- }
-
- list<Future<containerizer::Termination>> futures;
-
- // Enforce a 'destroy' on all orphaned containers.
- foreach (const ContainerID& containerId, orphaned) {
- LOG(INFO) << "Destroying container '" << containerId << "' as it "
- << "is in an orphaned state.";
- // For being able to wait on an orphan, we need to create an
- // internal Container state - we just can not have a sandbox for
- // it.
- actives.put(containerId, Owned<Container>(new Container(None())));
- actives[containerId]->launched.set(Nothing());
-
- // Wrap the orphan destruction by a wait so we know when it is
- // finally gone.
- futures.push_back(_wait(containerId));
-
- destroy(containerId);
- }
-
- VLOG(1) << "Awaiting all orphans to get destructed";
-
- // Orphan destruction needs to complete before we satisfy the
- // returned future.
- return collect(futures)
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::___recover));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::___recover()
-{
- VLOG(1) << "Recovery done";
- return Nothing();
-}
-
-
-Future<bool> ExternalContainerizerProcess::launch(
- const ContainerID& containerId,
- const Option<TaskInfo>& taskInfo,
- const ExecutorInfo& executor,
- const string& directory,
- const Option<string>& user,
- const SlaveID& slaveId,
- const PID<Slave>& slavePid,
- bool checkpoint)
-{
- LOG(INFO) << "Launching container '" << containerId << "'";
-
- if (actives.contains(containerId)) {
- return Failure("Cannot start already running container '" +
- containerId.value() + "'");
- }
-
- map<string, string> environment = executorEnvironment(
- executor,
- directory,
- slaveId,
- slavePid,
- checkpoint,
- flags);
-
- // TODO(tillt): Consider moving this into
- // Containerizer::executorEnvironment.
- if (!flags.hadoop_home.empty()) {
- environment["HADOOP_HOME"] = flags.hadoop_home;
- }
-
- if (flags.default_container_image.isSome()) {
- environment["MESOS_DEFAULT_CONTAINER_IMAGE"] =
- flags.default_container_image.get();
- }
-
- containerizer::Launch launch;
- launch.mutable_container_id()->CopyFrom(containerId);
- if (taskInfo.isSome()) {
- launch.mutable_task_info()->CopyFrom(taskInfo.get());
- }
- launch.mutable_executor_info()->CopyFrom(executor);
- launch.set_directory(directory);
- if (user.isSome()) {
- launch.set_user(user.get());
- }
- launch.mutable_slave_id()->CopyFrom(slaveId);
- launch.set_slave_pid(slavePid);
- launch.set_checkpoint(checkpoint);
-
- Sandbox sandbox(directory, user);
-
- Try<Subprocess> invoked = invoke(
- "launch",
- launch,
- sandbox,
- environment);
-
- if (invoked.isError()) {
- return Failure("Launch of container '" + containerId.value() +
- "' failed: " + invoked.error());
- }
-
- // Checkpoint the executor's pid if requested.
- // NOTE: Containerizer(s) currently rely on their state being
- // persisted in the slave. However, that responsibility should have
- // been delegated to the containerizer.
- // To work around the mandatory forked pid recovery, we need to
- // checkpoint one. See MESOS-1328 and MESOS-923.
- // TODO(tillt): Remove this entirely as soon as MESOS-923 is fixed.
- if (checkpoint) {
- const string& path = slave::paths::getForkedPidPath(
- slave::paths::getMetaRootDir(flags.work_dir),
- slaveId,
- executor.framework_id(),
- executor.executor_id(),
- containerId);
-
- LOG(INFO) << "Checkpointing executor's forked pid " << invoked.get().pid()
- << " to '" << path << "'";
-
- Try<Nothing> checkpointed =
- slave::state::checkpoint(path, stringify(invoked.get().pid()));
-
- if (checkpointed.isError()) {
- LOG(ERROR) << "Failed to checkpoint executor's forked pid to '"
- << path << "': " << checkpointed.error();
-
- return Failure("Could not checkpoint executor's pid");
- }
- }
-
- // Record the container launch intend.
- actives.put(containerId, Owned<Container>(new Container(sandbox)));
-
- return invoked.get().status()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_launch,
- containerId,
- lambda::_1))
- .onAny(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::__launch,
- containerId,
- lambda::_1));
-}
-
-
-Future<bool> ExternalContainerizerProcess::_launch(
- const ContainerID& containerId,
- const Future<Option<int>>& future)
-{
- VLOG(1) << "Launch validation callback triggered on container '"
- << containerId << "'";
-
- Option<Error> error = validate(future);
- if (error.isSome()) {
- return Failure("Could not launch container '" +
- containerId.value() + "': " + error.get().message);
- }
-
- VLOG(1) << "Launch finishing up for container '" << containerId << "'";
-
- // Launch is done, we can now process all other commands that might
- // have gotten chained up.
- actives[containerId]->launched.set(Nothing());
-
- return true;
-}
-
-
-void ExternalContainerizerProcess::__launch(
- const ContainerID& containerId,
- const Future<bool>& future)
-{
- VLOG(1) << "Launch confirmation callback triggered on container '"
- << containerId << "'";
-
- // We need to cleanup whenever this callback was invoked due to a
- // failure or discarded future.
- if (!future.isReady()) {
- cleanup(containerId);
- }
-}
-
-
-Future<containerizer::Termination> ExternalContainerizerProcess::wait(
- const ContainerID& containerId)
-{
- VLOG(1) << "Wait triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "' not running");
- }
-
- // Defer wait until launch is done.
- return actives[containerId]->launched.future()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_wait,
- containerId));
-}
-
-
-Future<containerizer::Termination> ExternalContainerizerProcess::_wait(
- const ContainerID& containerId)
-{
- VLOG(1) << "Wait continuation triggered on container '"
- << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "' not running");
- }
-
- // We must not run multiple 'wait' invocations concurrently on the
- // same container.
- if (actives[containerId]->pid.isSome()) {
- VLOG(2) << "Already waiting for " << containerId;
- return actives[containerId]->termination.future();
- }
-
- containerizer::Wait wait;
- wait.mutable_container_id()->CopyFrom(containerId);
-
- Try<Subprocess> invoked = invoke(
- "wait",
- wait,
- actives[containerId]->sandbox);
-
- if (invoked.isError()) {
- // 'wait' has failed, we need to tear down everything now.
- unwait(containerId);
- return Failure("Wait on container '" + containerId.value() +
- "' failed: " + invoked.error());
- }
-
- actives[containerId]->pid = invoked.get().pid();
-
- // Invoke the protobuf::read asynchronously.
- // TODO(tillt): Consider moving protobuf::read into libprocess and
- // making it work fully asynchronously.
-#ifndef __WINDOWS__
- Result<containerizer::Termination>(*read)(int, bool, bool) =
-#else
- Result<containerizer::Termination>(*read)(HANDLE, bool, bool) =
-#endif // __WINDOWS__
- &::protobuf::read<containerizer::Termination>;
-
- Future<Result<containerizer::Termination>> future = async(
- read, invoked.get().out().get(), false, false);
-
- // Await both, a protobuf Message from the subprocess as well as
- // its exit.
- await(future, invoked.get().status())
- .onAny(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::__wait,
- containerId,
- lambda::_1));
-
- return actives[containerId]->termination.future();
-}
-
-
-void ExternalContainerizerProcess::__wait(
- const ContainerID& containerId,
- const Future<tuple<
- Future<Result<containerizer::Termination>>,
- Future<Option<int>>>>& future)
-{
- VLOG(1) << "Wait callback triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- LOG(ERROR) << "Container '" << containerId << "' not running";
- return;
- }
-
- // When 'wait' was terminated by 'destroy', it is getting SIGKILLed
- // (see unwait). We need to test for that specific case as otherwise
- // the result validation below will return an error due to a non 0
- // exit status.
- if (actives[containerId]->destroying && future.isReady()) {
- Future<Option<int>> statusFuture = std::get<1>(future.get());
- if (statusFuture.isReady()) {
- Option<int> status = statusFuture.get();
- if (status.isSome()) {
- VLOG(2) << "Wait got destroyed on '" << containerId << "'";
- containerizer::Termination termination;
- termination.set_status(status.get());
- actives[containerId]->termination.set(termination);
- cleanup(containerId);
- return;
- }
- }
- }
-
- Try<containerizer::Termination> termination =
- result<containerizer::Termination>(future);
-
- if (termination.isError()) {
- VLOG(2) << "Wait termination failed on '" << containerId << "'";
- // 'wait' has failed, we need to tear down everything now.
- actives[containerId]->termination.fail(termination.error());
- unwait(containerId);
- } else {
- VLOG(2) << "Wait Termination: " << termination.get().DebugString();
- // Set the promise to alert others waiting on this container.
- actives[containerId]->termination.set(termination.get());
- }
-
- // The container has been waited on, we can safely cleanup now.
- cleanup(containerId);
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::update(
- const ContainerID& containerId,
- const Resources& resources)
-{
- VLOG(1) << "Update triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "'' not running");
- }
-
- // Defer update until launch is done.
- return actives[containerId]->launched.future()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_update,
- containerId,
- resources));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::_update(
- const ContainerID& containerId,
- const Resources& resources)
-{
- VLOG(1) << "Update continuation triggered on container '"
- << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "'' not running");
- }
-
- actives[containerId]->resources = resources;
-
- containerizer::Update update;
- update.mutable_container_id()->CopyFrom(containerId);
- update.mutable_resources()->CopyFrom(resources);
-
- Try<Subprocess> invoked = invoke(
- "update",
- update,
- actives[containerId]->sandbox);
-
- if (invoked.isError()) {
- return Failure("Update of container '" + containerId.value() +
- "' failed: " + invoked.error());
- }
-
- return invoked.get().status()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::__update,
- containerId,
- lambda::_1));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::__update(
- const ContainerID& containerId,
- const Future<Option<int>>& future)
-{
- VLOG(1) << "Update callback triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "' not running");
- }
-
- Option<Error> error = validate(future);
- if (error.isSome()) {
- return Failure(error.get());
- }
-
- return Nothing();
-}
-
-
-Future<ResourceStatistics> ExternalContainerizerProcess::usage(
- const ContainerID& containerId)
-{
- VLOG(1) << "Usage triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "'' not running");
- }
-
- // Defer usage until launch is done.
- return actives[containerId]->launched.future()
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_usage,
- containerId));
-}
-
-
-Future<ResourceStatistics> ExternalContainerizerProcess::_usage(
- const ContainerID& containerId)
-{
- VLOG(1) << "Usage continuation on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "'' not running");
- }
-
- containerizer::Usage usage;
- usage.mutable_container_id()->CopyFrom(containerId);
-
- Try<Subprocess> invoked = invoke(
- "usage",
- usage,
- actives[containerId]->sandbox);
-
- if (invoked.isError()) {
- // 'usage' has failed but we keep the container alive for now.
- return Failure("Usage on container '" + containerId.value() +
- "' failed: " + invoked.error());
- }
-
-#ifndef __WINDOWS__
- Result<ResourceStatistics>(*read)(int, bool, bool) =
-#else
- Result<ResourceStatistics>(*read)(HANDLE, bool, bool) =
-#endif // __WINDOWS__
- &::protobuf::read<ResourceStatistics>;
-
- Future<Result<ResourceStatistics>> future = async(
- read, invoked.get().out().get(), false, false);
-
- // Await both, a protobuf Message from the subprocess as well as
- // its exit.
- return await(future, invoked.get().status())
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::__usage,
- containerId,
- lambda::_1));
-}
-
-
-Future<ResourceStatistics> ExternalContainerizerProcess::__usage(
- const ContainerID& containerId,
- const Future<tuple<
- Future<Result<ResourceStatistics>>,
- Future<Option<int>>>>& future)
-{
- VLOG(1) << "Usage callback triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- return Failure("Container '" + containerId.value() + "' not running");
- }
-
- Try<ResourceStatistics> statistics = result<ResourceStatistics>(future);
-
- if (statistics.isError()) {
- return Failure(statistics.error());
- }
-
- VLOG(2) << "Container '" << containerId << "' "
- << "total mem usage "
- << statistics.get().mem_rss_bytes() << " "
- << "total CPU user usage "
- << statistics.get().cpus_user_time_secs() << " "
- << "total CPU system usage "
- << statistics.get().cpus_system_time_secs();
-
- return statistics.get();
-}
-
-
-void ExternalContainerizerProcess::destroy(const ContainerID& containerId)
-{
- VLOG(1) << "Destroy triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- LOG(ERROR) << "Container '" << containerId << "' not running";
- return;
- }
-
- // Defer destroy until launch is done.
- actives[containerId]->launched.future()
- .onAny(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_destroy,
- containerId));
-}
-
-
-void ExternalContainerizerProcess::_destroy(const ContainerID& containerId)
-{
- VLOG(1) << "Destroy continuation on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- LOG(ERROR) << "Container '" << containerId << "' not running";
- return;
- }
-
- if (actives[containerId]->destroying) {
- LOG(WARNING) << "Container '" << containerId
- << "' is already being destroyed";
- return;
- }
- actives[containerId]->destroying = true;
-
- containerizer::Destroy destroy;
- destroy.mutable_container_id()->CopyFrom(containerId);
-
- Try<Subprocess> invoked = invoke(
- "destroy",
- destroy,
- actives[containerId]->sandbox);
-
- if (invoked.isError()) {
- LOG(ERROR) << "Destroy of container '" << containerId
- << "' failed: " << invoked.error();
- unwait(containerId);
- return;
- }
-
- invoked.get().status()
- .onAny(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::__destroy,
- containerId,
- lambda::_1));
-}
-
-
-void ExternalContainerizerProcess::__destroy(
- const ContainerID& containerId,
- const Future<Option<int>>& future)
-{
- VLOG(1) << "Destroy callback triggered on container '" << containerId << "'";
-
- if (!actives.contains(containerId)) {
- LOG(ERROR) << "Container '" << containerId << "' not running ";
- return;
- }
-
- Option<Error> error = validate(future);
- if (error.isSome()) {
- LOG(ERROR) << "Destroy of container '" << containerId
- << "' failed: " << error.get().message;
- }
-
- // Additionally to the optional external destroy-command, we need to
- // terminate the external containerizer's "wait" process.
- unwait(containerId);
-}
-
-
-Future<hashset<ContainerID>> ExternalContainerizerProcess::containers()
-{
- VLOG(1) << "Containers triggered";
-
- Try<Subprocess> invoked = invoke("containers");
-
- if (invoked.isError()) {
- return Failure("Containers failed: " + invoked.error());
- }
-
-#ifndef __WINDOWS__
- Result<containerizer::Containers>(*read)(int, bool, bool) =
-#else
- Result<containerizer::Containers>(*read)(HANDLE, bool, bool) =
-#endif // __WINDOWS__
- &::protobuf::read<containerizer::Containers>;
-
- Future<Result<containerizer::Containers>> future = async(
- read, invoked.get().out().get(), false, false);
-
- // Await both, a protobuf Message from the subprocess as well as
- // its exit.
- return await(future, invoked.get().status())
- .then(defer(
- PID<ExternalContainerizerProcess>(this),
- &ExternalContainerizerProcess::_containers,
- lambda::_1));
-}
-
-
-Future<hashset<ContainerID>> ExternalContainerizerProcess::_containers(
- const Future<tuple<
- Future<Result<containerizer::Containers>>,
- Future<Option<int>>>>& future)
-{
- VLOG(1) << "Containers callback triggered";
-
- Try<containerizer::Containers> containers =
- result<containerizer::Containers>(future);
-
- if (containers.isError()) {
- return Failure(containers.error());
- }
-
- hashset<ContainerID> result;
- foreach (const ContainerID& containerId, containers.get().containers()) {
- result.insert(containerId);
- }
-
- return result;
-}
-
-
-void ExternalContainerizerProcess::cleanup(const ContainerID& containerId)
-{
- VLOG(1) << "Callback performing final cleanup of running state";
-
- if (actives.contains(containerId)) {
- actives.erase(containerId);
- } else {
- LOG(WARNING) << "Container '" << containerId << "' not running anymore";
- }
-}
-
-
-void ExternalContainerizerProcess::unwait(const ContainerID& containerId)
-{
- if (!actives.contains(containerId)) {
- LOG(WARNING) << "Container '" << containerId << "' not running";
- return;
- }
-
- Option<pid_t> pid = actives[containerId]->pid;
-
- // Containers that are being waited on have the "wait" command's
- // pid assigned.
- if (pid.isNone()) {
- // If we reached this, launch most likely failed due to some error
- // on the external containerizer's side (e.g. returned non zero on
- // launch).
- LOG(WARNING) << "Container '" << containerId << "' not being waited on";
- cleanup(containerId);
- return;
- }
-
- // Terminate the containerizer.
- VLOG(2) << "About to send a SIGKILL to containerizer pid: " << pid.get();
-
- // TODO(tillt): Add graceful termination as soon as we have an
- // accepted way to do that in place.
- Try<list<os::ProcessTree>> trees =
- os::killtree(pid.get(), SIGKILL, true, true);
-
- if (trees.isError()) {
- LOG(WARNING) << "Failed to kill the process tree rooted at pid "
- << pid.get() << ": " << trees.error();
- cleanup(containerId);
- return;
- }
-
- LOG(INFO) << "Killed the following process tree/s:\n"
- << stringify(trees.get());
-
- // The cleanup function will get invoked via __wait which triggers
- // once the external containerizer's "wait" subprocess gets
- // terminated.
-}
-
-
-Try<Subprocess> ExternalContainerizerProcess::invoke(
- const string& command,
- const Option<Sandbox>& sandbox,
- const Option<map<string, string>>& commandEnvironment)
-{
- CHECK_SOME(flags.containerizer_path) << "containerizer_path not set";
-
- VLOG(1) << "Invoking external containerizer for method '" << command << "'";
-
- // Prepare a default environment.
- map<string, string> environment = os::environment();
-
- environment["MESOS_LIBEXEC_DIRECTORY"] = flags.launcher_dir;
- environment["MESOS_WORK_DIRECTORY"] = flags.work_dir;
-
- // Update default environment with command specific one.
- if (commandEnvironment.isSome()) {
- environment.insert(
- commandEnvironment.get().begin(),
- commandEnvironment.get().end());
- }
-
- // Construct the command to execute.
- string execute = flags.containerizer_path.get() + " " + command;
-
- VLOG(2) << "calling: [" << execute << "]";
- VLOG_IF(2, sandbox.isSome()) << "directory: " << sandbox.get().directory;
- VLOG_IF(2, sandbox.isSome() &&
- sandbox.get().user.isSome()) << "user: " << sandbox.get().user.get();
-
- // NOTE: `chown` has no meaningful interpretation on Windows. This is safe to
- // `#ifdef` out because we don't compile the user flag on Windows, so this
- // should always be `None`.
-#ifndef __WINDOWS__
- // Re/establish the sandbox conditions for the containerizer.
- if (sandbox.isSome() && sandbox.get().user.isSome()) {
- Try<Nothing> chown = os::chown(
- sandbox.get().user.get(),
- sandbox.get().directory);
- if (chown.isError()) {
- return Error("Failed to chown work directory: " + chown.error());
- }
- }
-#endif // __WINDOWS__
-
- // Fork exec of external process. Run a chdir and a setsid within
- // the child-context.
- // TODO(tillt): Consider having the kernel notify us when our parent
- // process dies e.g. by invoking prctl(PR_SET_PDEATHSIG, ..) on linux.
- Try<Subprocess> external = process::subprocess(
- execute,
- Subprocess::PIPE(),
- Subprocess::PIPE(),
- Subprocess::PIPE(),
- process::SETSID,
- environment,
- None(),
- Subprocess::Hook::None(),
- sandbox.isSome() ? Option<string>::some(sandbox->directory) : None());
-
- if (external.isError()) {
- return Error("Failed to execute external containerizer: " +
- external.error());
- }
-
- // Set stderr into non-blocking mode.
- Try<Nothing> nonblock = os::nonblock(external.get().err().get());
- if (nonblock.isError()) {
- return Error("Failed to accept nonblock: " + nonblock.error());
- }
-
- // We are not setting stdin or stdout into non-blocking mode as
- // protobuf::read / write do currently not support it.
-
- // Redirect output (stderr) from the external containerizer to log
- // file in the executor work directory, chown'ing it if a user is
- // specified. When no sandbox is given, redirect to /dev/null to
- // prevent blocking on the subprocess side.
- // TODO(tillt): Consider switching to atomic close-on-exec instead.
- Try<int> err = os::open(
- sandbox.isSome() ? path::join(sandbox.get().directory, "stderr")
- : "/dev/null",
- O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (err.isError()) {
- return Error(
- "Failed to redirect stderr: Failed to open: " +
- err.error());
- }
-
- // NOTE: `chown` has no meaningful interpretation on Windows. This is safe to
- // `#ifdef` out because we don't compile the user flag on Windows, so this
- // should always be `None`.
-#ifndef __WINDOWS__
- if (sandbox.isSome() && sandbox.get().user.isSome()) {
- Try<Nothing> chown = os::chown(
- sandbox.get().user.get(),
- path::join(sandbox.get().directory, "stderr"));
- if (chown.isError()) {
- os::close(err.get());
- return Error(
- "Failed to redirect stderr: Failed to chown: " +
- chown.error());
- }
- }
-#endif // __WINDOWS__
-
- // TODO(tillt): Consider adding an overload to io::redirect
- // that accepts a file path as 'to' for further reducing code.
- io::redirect(external.get().err().get(), err.get());
-
- // Redirect does 'dup' the file descriptor, hence we can close the
- // original now.
- os::close(err.get());
-
- VLOG(2) << "Subprocess pid: " << external.get().pid() << ", "
- << "output pipe: " << external.get().out().get();
-
- return external;
-}
-
-
-Try<Subprocess> ExternalContainerizerProcess::invoke(
- const string& command,
- const google::protobuf::Message& message,
- const Option<Sandbox>& sandbox,
- const Option<map<string, string>>& commandEnvironment)
-{
- Try<Subprocess> external = invoke(command, sandbox, commandEnvironment);
- if (external.isError()) {
- return external;
- }
-
- // Transmit protobuf data via stdout towards the external
- // containerizer. Each message is prefixed by its total size.
- Try<Nothing> write = ::protobuf::write(external.get().in().get(), message);
- if (write.isError()) {
- return Error("Failed to write protobuf to pipe: " + write.error());
- }
-
- return external;
-}
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {