You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gr...@apache.org on 2019/07/26 02:12:46 UTC

[mesos] 01/03: Moved the Docker executor declaration into a header.

This is an automated email from the ASF dual-hosted git repository.

grag pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit a1e4a9aa1d6f2dee9fd56432122c2fa6a35edb77
Author: Greg Mann <gr...@mesosphere.io>
AuthorDate: Thu Jul 25 12:17:41 2019 -0700

    Moved the Docker executor declaration into a header.
    
    This moves the declaration of the Docker executor into the
    Docker executor header file and moves the code for the Docker
    executor binary into a new launcher implementation file.
    
    This change will enable the Mesos executor driver
    implementation to make use of the `DockerExecutor` symbol.
    
    Review: https://reviews.apache.org/r/71033/
---
 src/CMakeLists.txt               |   4 +-
 src/Makefile.am                  |   3 +-
 src/docker/CMakeLists.txt        |  20 ---
 src/docker/executor.cpp          | 348 +++++++++------------------------------
 src/docker/executor.hpp          |  53 ++++++
 src/launcher/CMakeLists.txt      |   5 +
 src/launcher/docker_executor.cpp | 266 ++++++++++++++++++++++++++++++
 7 files changed, 409 insertions(+), 290 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c455ed6..218a75e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -263,7 +263,8 @@ set(CSI_SRC
   csi/volume_manager.cpp)
 
 set(DOCKER_SRC
-  docker/docker.cpp)
+  docker/docker.cpp
+  docker/executor.cpp)
 
 if (NOT WIN32)
   list(APPEND DOCKER_SRC
@@ -644,7 +645,6 @@ endif ()
 ##############################
 add_subdirectory(checks)
 add_subdirectory(cli)
-add_subdirectory(docker)
 add_subdirectory(examples)
 add_subdirectory(launcher)
 add_subdirectory(local)
diff --git a/src/Makefile.am b/src/Makefile.am
index 46c66f1..697ab10 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1092,6 +1092,7 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   docker/docker.cpp							\
   docker/docker.hpp							\
   docker/executor.hpp							\
+  docker/executor.cpp							\
   docker/spec.cpp							\
   examples/flags.hpp							\
   examples/test_anonymous_module.hpp					\
@@ -1818,7 +1819,7 @@ mesos_usage_CPPFLAGS = $(MESOS_CPPFLAGS)
 mesos_usage_LDADD = libmesos.la $(LDADD)
 
 pkglibexec_PROGRAMS += mesos-docker-executor
-mesos_docker_executor_SOURCES = docker/executor.cpp
+mesos_docker_executor_SOURCES = launcher/docker_executor.cpp
 mesos_docker_executor_CPPFLAGS = $(MESOS_CPPFLAGS)
 mesos_docker_executor_LDADD = libmesos.la $(LDADD)
 
diff --git a/src/docker/CMakeLists.txt b/src/docker/CMakeLists.txt
deleted file mode 100644
index 1196664..0000000
--- a/src/docker/CMakeLists.txt
+++ /dev/null
@@ -1,20 +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.
-
-# THE DOCKER EXECUTOR EXECUTABLE.
-#################################
-add_executable(mesos-docker-executor executor.cpp)
-target_link_libraries(mesos-docker-executor PRIVATE mesos)
diff --git a/src/docker/executor.cpp b/src/docker/executor.cpp
index f638e4b..de8216f 100644
--- a/src/docker/executor.cpp
+++ b/src/docker/executor.cpp
@@ -856,291 +856,105 @@ private:
 };
 
 
-class DockerExecutor : public Executor
+DockerExecutor::DockerExecutor(
+    const Owned<Docker>& docker,
+    const string& container,
+    const string& sandboxDirectory,
+    const string& mappedDirectory,
+    const Duration& shutdownGracePeriod,
+    const string& launcherDir,
+    const map<string, string>& taskEnvironment,
+    const Option<ContainerDNSInfo>& defaultContainerDNS,
+    bool cgroupsEnableCfs)
 {
-public:
-  DockerExecutor(
-      const Owned<Docker>& docker,
-      const string& container,
-      const string& sandboxDirectory,
-      const string& mappedDirectory,
-      const Duration& shutdownGracePeriod,
-      const string& launcherDir,
-      const map<string, string>& taskEnvironment,
-      const Option<ContainerDNSInfo>& defaultContainerDNS,
-      bool cgroupsEnableCfs)
-  {
-    process = Owned<DockerExecutorProcess>(new DockerExecutorProcess(
-        docker,
-        container,
-        sandboxDirectory,
-        mappedDirectory,
-        shutdownGracePeriod,
-        launcherDir,
-        taskEnvironment,
-        defaultContainerDNS,
-        cgroupsEnableCfs));
-
-    spawn(process.get());
-  }
-
-  ~DockerExecutor() override
-  {
-    terminate(process.get());
-    wait(process.get());
-  }
-
-  void registered(
-      ExecutorDriver* driver,
-      const ExecutorInfo& executorInfo,
-      const FrameworkInfo& frameworkInfo,
-      const SlaveInfo& slaveInfo) override
-  {
-    dispatch(process.get(),
-             &DockerExecutorProcess::registered,
-             driver,
-             executorInfo,
-             frameworkInfo,
-             slaveInfo);
-  }
-
-  void reregistered(
-      ExecutorDriver* driver,
-      const SlaveInfo& slaveInfo) override
-  {
-    dispatch(process.get(),
-             &DockerExecutorProcess::reregistered,
-             driver,
-             slaveInfo);
-  }
-
-  void disconnected(ExecutorDriver* driver) override
-  {
-    dispatch(process.get(), &DockerExecutorProcess::disconnected, driver);
-  }
-
-  void launchTask(ExecutorDriver* driver, const TaskInfo& task) override
-  {
-    dispatch(process.get(), &DockerExecutorProcess::launchTask, driver, task);
-  }
-
-  void killTask(ExecutorDriver* driver, const TaskID& taskId) override
-  {
-    dispatch(process.get(), &DockerExecutorProcess::killTask, driver, taskId);
-  }
-
-  void frameworkMessage(ExecutorDriver* driver, const string& data) override
-  {
-    dispatch(process.get(),
-             &DockerExecutorProcess::frameworkMessage,
-             driver,
-             data);
-  }
-
-  void shutdown(ExecutorDriver* driver) override
-  {
-    dispatch(process.get(), &DockerExecutorProcess::shutdown, driver);
-  }
-
-  void error(ExecutorDriver* driver, const string& data) override
-  {
-    dispatch(process.get(), &DockerExecutorProcess::error, driver, data);
-  }
-
-private:
-  Owned<DockerExecutorProcess> process;
-};
-
-
-} // namespace docker {
-} // namespace internal {
-} // namespace mesos {
+  process = Owned<DockerExecutorProcess>(new DockerExecutorProcess(
+      docker,
+      container,
+      sandboxDirectory,
+      mappedDirectory,
+      shutdownGracePeriod,
+      launcherDir,
+      taskEnvironment,
+      defaultContainerDNS,
+      cgroupsEnableCfs));
+
+  spawn(process.get());
+}
 
 
-int main(int argc, char** argv)
+DockerExecutor::~DockerExecutor()
 {
-  GOOGLE_PROTOBUF_VERIFY_VERSION;
-
-#ifdef __WINDOWS__
-  // We need a handle to the job object which this container is associated with.
-  // Without this handle, the job object would be destroyed by the OS when the
-  // agent exits (or crashes), making recovery impossible. By holding a handle,
-  // we tie the lifetime of the job object to the container itself. In this way,
-  // a recovering agent can reattach to the container by opening a new handle to
-  // the job object.
-  const pid_t pid = ::GetCurrentProcessId();
-  const Try<std::wstring> name = os::name_job(pid);
-  if (name.isError()) {
-    cerr << "Failed to create job object name from pid: " << name.error()
-         << endl;
-    return EXIT_FAILURE;
-  }
-
-  // NOTE: This handle will not be destructed, even though it is a
-  // `SharedHandle`, because it will (purposefully) never go out of scope.
-  Try<SharedHandle> handle = os::open_job(JOB_OBJECT_QUERY, false, name.get());
-  if (handle.isError()) {
-    cerr << "Failed to open job object '" << stringify(name.get())
-         << "' for the current container: " << handle.error() << endl;
-    return EXIT_FAILURE;
-  }
-#endif // __WINDOWS__
-
-  mesos::internal::docker::Flags flags;
-
-  // Load flags from environment and command line.
-  Try<flags::Warnings> load = flags.load(None(), &argc, &argv);
-
-  if (flags.help) {
-    cout << flags.usage() << endl;
-    return EXIT_SUCCESS;
-  }
-
-  if (load.isError()) {
-    cerr << flags.usage(load.error()) << endl;
-    return EXIT_FAILURE;
-  }
-
-  mesos::internal::logging::initialize(argv[0], true, flags); // Catch signals.
-
-  // Log any flag warnings (after logging is initialized).
-  foreach (const flags::Warning& warning, load->warnings) {
-    LOG(WARNING) << warning.message;
-  }
-
-  VLOG(1) << stringify(flags);
-
-  if (flags.docker.isNone()) {
-    EXIT(EXIT_FAILURE) << flags.usage("Missing required option --docker");
-  }
-
-  if (flags.container.isNone()) {
-    EXIT(EXIT_FAILURE) << flags.usage("Missing required option --container");
-  }
+  terminate(process.get());
+  wait(process.get());
+}
 
-  if (flags.sandbox_directory.isNone()) {
-    EXIT(EXIT_FAILURE)
-      << flags.usage("Missing required option --sandbox_directory");
-  }
 
-  if (flags.mapped_directory.isNone()) {
-    EXIT(EXIT_FAILURE)
-      << flags.usage("Missing required option --mapped_directory");
-  }
+void DockerExecutor::registered(
+    ExecutorDriver* driver,
+    const ExecutorInfo& executorInfo,
+    const FrameworkInfo& frameworkInfo,
+    const SlaveInfo& slaveInfo)
+{
+  dispatch(process.get(),
+           &DockerExecutorProcess::registered,
+           driver,
+           executorInfo,
+           frameworkInfo,
+           slaveInfo);
+}
 
-  map<string, string> taskEnvironment;
-  if (flags.task_environment.isSome()) {
-    // Parse the string as JSON.
-    Try<JSON::Object> json =
-      JSON::parse<JSON::Object>(flags.task_environment.get());
-
-    if (json.isError()) {
-      EXIT(EXIT_FAILURE)
-        << flags.usage("Failed to parse --task_environment: " + json.error());
-    }
 
-    // Convert from JSON to map.
-    foreachpair (
-        const string& key,
-        const JSON::Value& value,
-        json->values) {
-      if (!value.is<JSON::String>()) {
-        EXIT(EXIT_FAILURE) << flags.usage(
-            "Value of key '" + key + "' in --task_environment is not a string");
-      }
+void DockerExecutor::reregistered(
+    ExecutorDriver* driver,
+    const SlaveInfo& slaveInfo)
+{
+  dispatch(process.get(),
+           &DockerExecutorProcess::reregistered,
+           driver,
+           slaveInfo);
+}
 
-      // Save the parsed and validated key/value.
-      taskEnvironment[key] = value.as<JSON::String>().value;
-    }
-  }
 
-  Option<mesos::internal::ContainerDNSInfo> defaultContainerDNS;
-  if (flags.default_container_dns.isSome()) {
-    Try<mesos::internal::ContainerDNSInfo> parse =
-      flags::parse<mesos::internal::ContainerDNSInfo>(
-          flags.default_container_dns.get());
+void DockerExecutor::disconnected(ExecutorDriver* driver)
+{
+  dispatch(process.get(), &DockerExecutorProcess::disconnected, driver);
+}
 
-    if (parse.isError()) {
-      EXIT(EXIT_FAILURE) << flags.usage(
-          "Failed to parse --default_container_dns: " + parse.error());
-    }
 
-    defaultContainerDNS = parse.get();
-  }
+void DockerExecutor::launchTask(ExecutorDriver* driver, const TaskInfo& task)
+{
+  dispatch(process.get(), &DockerExecutorProcess::launchTask, driver, task);
+}
 
-  // Get executor shutdown grace period from the environment.
-  //
-  // NOTE: We avoided introducing a docker executor flag for this
-  // because the docker executor exits if it sees an unknown flag.
-  // This makes it difficult to add or remove docker executor flags
-  // that are unconditionally set by the agent.
-  Duration shutdownGracePeriod =
-    mesos::internal::slave::DEFAULT_EXECUTOR_SHUTDOWN_GRACE_PERIOD;
-  Option<string> value = os::getenv("MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD");
-  if (value.isSome()) {
-    Try<Duration> parse = Duration::parse(value.get());
-    if (parse.isError()) {
-      EXIT(EXIT_FAILURE)
-        << "Failed to parse value '" << value.get() << "'"
-        << " of 'MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD': " << parse.error();
-    }
 
-    shutdownGracePeriod = parse.get();
-  }
+void DockerExecutor::killTask(ExecutorDriver* driver, const TaskID& taskId)
+{
+  dispatch(process.get(), &DockerExecutorProcess::killTask, driver, taskId);
+}
 
-  // If the deprecated flag is set, respect it and choose the bigger value.
-  //
-  // TODO(alexr): Remove this after the deprecation cycle (started in 1.0).
-  if (flags.stop_timeout.isSome() &&
-      flags.stop_timeout.get() > shutdownGracePeriod) {
-    shutdownGracePeriod = flags.stop_timeout.get();
-  }
 
-  if (flags.launcher_dir.isNone()) {
-    EXIT(EXIT_FAILURE) << flags.usage("Missing required option --launcher_dir");
-  }
+void DockerExecutor::frameworkMessage(
+    ExecutorDriver* driver,
+    const string& data)
+{
+  dispatch(process.get(),
+           &DockerExecutorProcess::frameworkMessage,
+           driver,
+           data);
+}
 
-  process::initialize();
 
-  // The 2nd argument for docker create is set to false so we skip
-  // validation when creating a docker abstraction, as the slave
-  // should have already validated docker.
-  Try<Owned<Docker>> docker = Docker::create(
-      flags.docker.get(),
-      flags.docker_socket.get(),
-      false);
+void DockerExecutor::shutdown(ExecutorDriver* driver)
+{
+  dispatch(process.get(), &DockerExecutorProcess::shutdown, driver);
+}
 
-  if (docker.isError()) {
-    EXIT(EXIT_FAILURE)
-      << "Unable to create docker abstraction: " << docker.error();
-  }
 
-  Owned<mesos::internal::docker::DockerExecutor> executor(
-      new mesos::internal::docker::DockerExecutor(
-          docker.get(),
-          flags.container.get(),
-          flags.sandbox_directory.get(),
-          flags.mapped_directory.get(),
-          shutdownGracePeriod,
-          flags.launcher_dir.get(),
-          taskEnvironment,
-          defaultContainerDNS,
-          flags.cgroups_enable_cfs));
-
-  Owned<mesos::MesosExecutorDriver> driver(
-      new mesos::MesosExecutorDriver(executor.get()));
-
-  bool success = driver->run() == mesos::DRIVER_STOPPED;
-
-  // NOTE: We need to delete the executor and driver before we call
-  // `process::finalize` because the executor/driver will try to terminate
-  // and wait on a libprocess actor in their destructor.
-  driver.reset();
-  executor.reset();
-
-  // NOTE: We need to finalize libprocess, on Windows especially,
-  // as any binary that uses the networking stack on Windows must
-  // also clean up the networking stack before exiting.
-  process::finalize(true);
-  return success ? EXIT_SUCCESS : EXIT_FAILURE;
+void DockerExecutor::error(ExecutorDriver* driver, const string& data)
+{
+  dispatch(process.get(), &DockerExecutorProcess::error, driver, data);
 }
+
+} // namespace docker {
+} // namespace internal {
+} // namespace mesos {
diff --git a/src/docker/executor.hpp b/src/docker/executor.hpp
index f21e84c..dfb8ad0 100644
--- a/src/docker/executor.hpp
+++ b/src/docker/executor.hpp
@@ -22,10 +22,15 @@
 #include <map>
 #include <string>
 
+#include <mesos/executor.hpp>
+
+#include <process/owned.hpp>
 #include <process/process.hpp>
 
 #include <stout/option.hpp>
 
+#include "docker/docker.hpp"
+
 #include "logging/flags.hpp"
 
 namespace mesos {
@@ -102,6 +107,54 @@ struct Flags : public virtual mesos::internal::logging::Flags
   Option<Duration> stop_timeout;
 };
 
+
+class DockerExecutorProcess;
+
+
+class DockerExecutor : public Executor
+{
+public:
+  DockerExecutor(
+      const process::Owned<Docker>& docker,
+      const std::string& container,
+      const std::string& sandboxDirectory,
+      const std::string& mappedDirectory,
+      const Duration& shutdownGracePeriod,
+      const std::string& launcherDir,
+      const std::map<std::string, std::string>& taskEnvironment,
+      const Option<ContainerDNSInfo>& defaultContainerDNS,
+      bool cgroupsEnableCfs);
+
+  ~DockerExecutor() override;
+
+  void registered(
+      ExecutorDriver* driver,
+      const ExecutorInfo& executorInfo,
+      const FrameworkInfo& frameworkInfo,
+      const SlaveInfo& slaveInfo) override;
+
+  void reregistered(
+      ExecutorDriver* driver,
+      const SlaveInfo& slaveInfo) override;
+
+  void disconnected(ExecutorDriver* driver) override;
+
+  void launchTask(ExecutorDriver* driver, const TaskInfo& task) override;
+
+  void killTask(ExecutorDriver* driver, const TaskID& taskId) override;
+
+  void frameworkMessage(
+      ExecutorDriver* driver,
+      const std::string& data) override;
+
+  void shutdown(ExecutorDriver* driver) override;
+
+  void error(ExecutorDriver* driver, const std::string& data) override;
+
+private:
+  process::Owned<DockerExecutorProcess> process;
+};
+
 } // namespace docker {
 } // namespace internal {
 } // namespace mesos {
diff --git a/src/launcher/CMakeLists.txt b/src/launcher/CMakeLists.txt
index 2ffa946..73587f4 100644
--- a/src/launcher/CMakeLists.txt
+++ b/src/launcher/CMakeLists.txt
@@ -24,3 +24,8 @@ target_link_libraries(mesos-executor PRIVATE mesos)
 
 add_executable(mesos-fetcher fetcher.cpp)
 target_link_libraries(mesos-fetcher PRIVATE mesos)
+
+# THE DOCKER EXECUTOR EXECUTABLE.
+#################################
+add_executable(mesos-docker-executor docker_executor.cpp)
+target_link_libraries(mesos-docker-executor PRIVATE mesos)
diff --git a/src/launcher/docker_executor.cpp b/src/launcher/docker_executor.cpp
new file mode 100644
index 0000000..3f12c78
--- /dev/null
+++ b/src/launcher/docker_executor.cpp
@@ -0,0 +1,266 @@
+// 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 <stdio.h>
+
+#include <map>
+#include <string>
+
+#include <mesos/executor.hpp>
+#include <mesos/mesos.hpp>
+
+#include <process/collect.hpp>
+#include <process/delay.hpp>
+#include <process/id.hpp>
+#include <process/loop.hpp>
+#include <process/owned.hpp>
+#include <process/process.hpp>
+#include <process/protobuf.hpp>
+#include <process/reap.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/error.hpp>
+#include <stout/flags.hpp>
+#include <stout/json.hpp>
+#include <stout/lambda.hpp>
+#include <stout/os.hpp>
+#include <stout/protobuf.hpp>
+#include <stout/try.hpp>
+#ifdef __WINDOWS__
+#include <stout/windows.hpp>
+#endif // __WINDOWS__
+
+#include <stout/os/killtree.hpp>
+
+#ifdef __WINDOWS__
+#include <stout/os/windows/jobobject.hpp>
+#endif // __WINDOWS__
+
+#include "checks/checks_runtime.hpp"
+#include "checks/health_checker.hpp"
+
+#include "common/protobuf_utils.hpp"
+#include "common/status_utils.hpp"
+
+#include "docker/docker.hpp"
+#include "docker/executor.hpp"
+
+#include "logging/flags.hpp"
+#include "logging/logging.hpp"
+
+#include "messages/flags.hpp"
+#include "messages/messages.hpp"
+
+#include "slave/constants.hpp"
+
+using namespace mesos;
+using namespace process;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::map;
+using std::string;
+using std::vector;
+
+
+int main(int argc, char** argv)
+{
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#ifdef __WINDOWS__
+  // We need a handle to the job object which this container is associated with.
+  // Without this handle, the job object would be destroyed by the OS when the
+  // agent exits (or crashes), making recovery impossible. By holding a handle,
+  // we tie the lifetime of the job object to the container itself. In this way,
+  // a recovering agent can reattach to the container by opening a new handle to
+  // the job object.
+  const pid_t pid = ::GetCurrentProcessId();
+  const Try<std::wstring> name = os::name_job(pid);
+  if (name.isError()) {
+    cerr << "Failed to create job object name from pid: " << name.error()
+         << endl;
+    return EXIT_FAILURE;
+  }
+
+  // NOTE: This handle will not be destructed, even though it is a
+  // `SharedHandle`, because it will (purposefully) never go out of scope.
+  Try<SharedHandle> handle = os::open_job(JOB_OBJECT_QUERY, false, name.get());
+  if (handle.isError()) {
+    cerr << "Failed to open job object '" << stringify(name.get())
+         << "' for the current container: " << handle.error() << endl;
+    return EXIT_FAILURE;
+  }
+#endif // __WINDOWS__
+
+  mesos::internal::docker::Flags flags;
+
+  // Load flags from environment and command line.
+  Try<flags::Warnings> load = flags.load(None(), &argc, &argv);
+
+  if (flags.help) {
+    cout << flags.usage() << endl;
+    return EXIT_SUCCESS;
+  }
+
+  if (load.isError()) {
+    cerr << flags.usage(load.error()) << endl;
+    return EXIT_FAILURE;
+  }
+
+  mesos::internal::logging::initialize(argv[0], true, flags); // Catch signals.
+
+  // Log any flag warnings (after logging is initialized).
+  foreach (const flags::Warning& warning, load->warnings) {
+    LOG(WARNING) << warning.message;
+  }
+
+  VLOG(1) << stringify(flags);
+
+  if (flags.docker.isNone()) {
+    EXIT(EXIT_FAILURE) << flags.usage("Missing required option --docker");
+  }
+
+  if (flags.container.isNone()) {
+    EXIT(EXIT_FAILURE) << flags.usage("Missing required option --container");
+  }
+
+  if (flags.sandbox_directory.isNone()) {
+    EXIT(EXIT_FAILURE)
+      << flags.usage("Missing required option --sandbox_directory");
+  }
+
+  if (flags.mapped_directory.isNone()) {
+    EXIT(EXIT_FAILURE)
+      << flags.usage("Missing required option --mapped_directory");
+  }
+
+  map<string, string> taskEnvironment;
+  if (flags.task_environment.isSome()) {
+    // Parse the string as JSON.
+    Try<JSON::Object> json =
+      JSON::parse<JSON::Object>(flags.task_environment.get());
+
+    if (json.isError()) {
+      EXIT(EXIT_FAILURE)
+        << flags.usage("Failed to parse --task_environment: " + json.error());
+    }
+
+    // Convert from JSON to map.
+    foreachpair (
+        const string& key,
+        const JSON::Value& value,
+        json->values) {
+      if (!value.is<JSON::String>()) {
+        EXIT(EXIT_FAILURE) << flags.usage(
+            "Value of key '" + key + "' in --task_environment is not a string");
+      }
+
+      // Save the parsed and validated key/value.
+      taskEnvironment[key] = value.as<JSON::String>().value;
+    }
+  }
+
+  Option<mesos::internal::ContainerDNSInfo> defaultContainerDNS;
+  if (flags.default_container_dns.isSome()) {
+    Try<mesos::internal::ContainerDNSInfo> parse =
+      flags::parse<mesos::internal::ContainerDNSInfo>(
+          flags.default_container_dns.get());
+
+    if (parse.isError()) {
+      EXIT(EXIT_FAILURE) << flags.usage(
+          "Failed to parse --default_container_dns: " + parse.error());
+    }
+
+    defaultContainerDNS = parse.get();
+  }
+
+  // Get executor shutdown grace period from the environment.
+  //
+  // NOTE: We avoided introducing a docker executor flag for this
+  // because the docker executor exits if it sees an unknown flag.
+  // This makes it difficult to add or remove docker executor flags
+  // that are unconditionally set by the agent.
+  Duration shutdownGracePeriod =
+    mesos::internal::slave::DEFAULT_EXECUTOR_SHUTDOWN_GRACE_PERIOD;
+  Option<string> value = os::getenv("MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD");
+  if (value.isSome()) {
+    Try<Duration> parse = Duration::parse(value.get());
+    if (parse.isError()) {
+      EXIT(EXIT_FAILURE)
+        << "Failed to parse value '" << value.get() << "'"
+        << " of 'MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD': " << parse.error();
+    }
+
+    shutdownGracePeriod = parse.get();
+  }
+
+  // If the deprecated flag is set, respect it and choose the bigger value.
+  //
+  // TODO(alexr): Remove this after the deprecation cycle (started in 1.0).
+  if (flags.stop_timeout.isSome() &&
+      flags.stop_timeout.get() > shutdownGracePeriod) {
+    shutdownGracePeriod = flags.stop_timeout.get();
+  }
+
+  if (flags.launcher_dir.isNone()) {
+    EXIT(EXIT_FAILURE) << flags.usage("Missing required option --launcher_dir");
+  }
+
+  process::initialize();
+
+  // The 2nd argument for docker create is set to false so we skip
+  // validation when creating a docker abstraction, as the slave
+  // should have already validated docker.
+  Try<Owned<Docker>> docker = Docker::create(
+      flags.docker.get(),
+      flags.docker_socket.get(),
+      false);
+
+  if (docker.isError()) {
+    EXIT(EXIT_FAILURE)
+      << "Unable to create docker abstraction: " << docker.error();
+  }
+
+  Owned<mesos::internal::docker::DockerExecutor> executor(
+      new mesos::internal::docker::DockerExecutor(
+          docker.get(),
+          flags.container.get(),
+          flags.sandbox_directory.get(),
+          flags.mapped_directory.get(),
+          shutdownGracePeriod,
+          flags.launcher_dir.get(),
+          taskEnvironment,
+          defaultContainerDNS,
+          flags.cgroups_enable_cfs));
+
+  Owned<mesos::MesosExecutorDriver> driver(
+      new mesos::MesosExecutorDriver(executor.get()));
+
+  bool success = driver->run() == mesos::DRIVER_STOPPED;
+
+  // NOTE: We need to delete the executor and driver before we call
+  // `process::finalize` because the executor/driver will try to terminate
+  // and wait on a libprocess actor in their destructor.
+  driver.reset();
+  executor.reset();
+
+  // NOTE: We need to finalize libprocess, on Windows especially,
+  // as any binary that uses the networking stack on Windows must
+  // also clean up the networking stack before exiting.
+  process::finalize(true);
+  return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}