You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ya...@apache.org on 2015/09/10 20:47:06 UTC

[1/4] mesos git commit: Renamed appc_backend flag to appc_provisioner_backend.

Repository: mesos
Updated Branches:
  refs/heads/master 8949b1a10 -> 6ae0f80b3


Renamed appc_backend flag to appc_provisioner_backend.

Review: https://reviews.apache.org/r/38240


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/3b2d9290
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/3b2d9290
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/3b2d9290

Branch: refs/heads/master
Commit: 3b2d92906c969a7e7a47569a7cc00fbc8985c683
Parents: 0636f2b
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Wed Sep 9 16:29:32 2015 -0700
Committer: Jiang Yan Xu <ya...@jxu.me>
Committed: Thu Sep 10 10:42:06 2015 -0700

----------------------------------------------------------------------
 .../provisioners/appc/provisioner.cpp              | 17 ++++++++++-------
 src/slave/flags.cpp                                |  4 ++--
 src/slave/flags.hpp                                |  2 +-
 src/tests/containerizer/appc_provisioner_tests.cpp |  4 ++--
 4 files changed, 15 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/3b2d9290/src/slave/containerizer/provisioners/appc/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc/provisioner.cpp b/src/slave/containerizer/provisioners/appc/provisioner.cpp
index 5204cbc..aa4db48 100644
--- a/src/slave/containerizer/provisioners/appc/provisioner.cpp
+++ b/src/slave/containerizer/provisioners/appc/provisioner.cpp
@@ -129,9 +129,9 @@ Try<Owned<Provisioner>> AppcProvisioner::create(
     return Error("No usable provisioner backend created");
   }
 
-  if (!backends.contains(flags.appc_backend)) {
-    return Error("The specified provisioner backend '" + flags.appc_backend +
-                 "'is unsupported");
+  if (!backends.contains(flags.appc_provisioner_backend)) {
+    return Error("The specified provisioner backend '" +
+                 flags.appc_provisioner_backend + "'is unsupported");
   }
 
   return Owned<Provisioner>(new AppcProvisioner(
@@ -310,13 +310,14 @@ Future<string> AppcProvisionerProcess::provision(
 
   string rootfsId = UUID::random().toString();
   string rootfs = provisioners::paths::getContainerRootfsDir(
-      root, containerId, flags.appc_backend, rootfsId);
+      root, containerId, flags.appc_provisioner_backend, rootfsId);
 
   if (!infos.contains(containerId)) {
     infos.put(containerId, Owned<Info>(new Info()));
   }
 
-  infos[containerId]->rootfses[flags.appc_backend].put(rootfsId, rootfs);
+  infos[containerId]->rootfses[flags.appc_provisioner_backend].put(
+      rootfsId, rootfs);
 
   // Get and then provision image layers from the store.
   return store->get(image.appc())
@@ -330,8 +331,10 @@ Future<string> AppcProvisionerProcess::_provision(
 {
   LOG(INFO) << "Provisioning image layers to rootfs '" << rootfs << "'";
 
-  CHECK(backends.contains(flags.appc_backend));
-  return backends.get(flags.appc_backend).get()->provision(layers, rootfs)
+  CHECK(backends.contains(flags.appc_provisioner_backend));
+  return backends.get(flags.appc_provisioner_backend).get()->provision(
+      layers,
+      rootfs)
     .then([rootfs]() -> Future<string> { return rootfs; });
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/3b2d9290/src/slave/flags.cpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp
index 7539441..b676bac 100644
--- a/src/slave/flags.cpp
+++ b/src/slave/flags.cpp
@@ -69,8 +69,8 @@ mesos::internal::slave::Flags::Flags()
       "Directory the appc provisioner will store images in",
       "/tmp/mesos/store/appc");
 
-  add(&Flags::appc_backend,
-      "appc_backend",
+  add(&Flags::appc_provisioner_backend,
+      "appc_provisioner_backend",
       "Strategy for provisioning container rootfs from appc images",
       "copy");
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/3b2d9290/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index b8335aa..799c963 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -50,7 +50,7 @@ public:
   Option<std::string> provisioners;
 
   std::string appc_store_dir;
-  std::string appc_backend;
+  std::string appc_provisioner_backend;
 
   std::string default_role;
   Option<std::string> attributes;

http://git-wip-us.apache.org/repos/asf/mesos/blob/3b2d9290/src/tests/containerizer/appc_provisioner_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/appc_provisioner_tests.cpp b/src/tests/containerizer/appc_provisioner_tests.cpp
index f30e1a1..34748b1 100644
--- a/src/tests/containerizer/appc_provisioner_tests.cpp
+++ b/src/tests/containerizer/appc_provisioner_tests.cpp
@@ -196,7 +196,7 @@ TEST_F(AppcProvisionerTest, ROOT_Provision)
   // Create provisioner.
   slave::Flags flags;
   flags.appc_store_dir = path::join(os::getcwd(), "store");
-  flags.appc_backend = "bind";
+  flags.appc_provisioner_backend = "bind";
   flags.provisioners = "appc";
   flags.work_dir = "work_dir";
 
@@ -276,7 +276,7 @@ TEST_F(AppcProvisionerTest, ROOT_Provision)
       "containers",
       containerId.value(),
       "backends",
-      flags.appc_backend,
+      flags.appc_provisioner_backend,
       "rootfses"));
 
   ASSERT_SOME(rootfses);


[2/4] mesos git commit: Renamed appc.{hpp|cpp} to slave/containerizer/provisioners/appc/provisioner.{hpp|cpp}.

Posted by ya...@apache.org.
Renamed appc.{hpp|cpp} to slave/containerizer/provisioners/appc/provisioner.{hpp|cpp}.

Review: https://reviews.apache.org/r/38239


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/0636f2b3
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/0636f2b3
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/0636f2b3

Branch: refs/heads/master
Commit: 0636f2b3ba97af9a088fd6aa347be4d486d49fdd
Parents: 8949b1a
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Wed Sep 9 16:18:53 2015 -0700
Committer: Jiang Yan Xu <ya...@jxu.me>
Committed: Thu Sep 10 10:42:06 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |   8 +-
 src/slave/containerizer/provisioner.cpp         |   2 +-
 src/slave/containerizer/provisioners/appc.cpp   | 380 -------------------
 src/slave/containerizer/provisioners/appc.hpp   |  78 ----
 .../provisioners/appc/provisioner.cpp           | 379 ++++++++++++++++++
 .../provisioners/appc/provisioner.hpp           |  78 ++++
 6 files changed, 462 insertions(+), 463 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/0636f2b3/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index cea470e..8963cea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -482,9 +482,9 @@ libmesos_no_3rdparty_la_SOURCES =					\
 	slave/containerizer/mesos/containerizer.cpp			\
 	slave/containerizer/mesos/launch.cpp				\
 	slave/containerizer/provisioner.cpp				\
-	slave/containerizer/provisioners/appc.cpp			\
-	slave/containerizer/provisioners/paths.cpp                      \
+        slave/containerizer/provisioners/paths.cpp                      \
 	slave/containerizer/provisioners/appc/paths.cpp			\
+	slave/containerizer/provisioners/appc/provisioner.cpp		\
 	slave/containerizer/provisioners/appc/spec.cpp			\
 	slave/containerizer/provisioners/appc/store.cpp			\
 	slave/containerizer/provisioners/backend.cpp			\
@@ -763,9 +763,9 @@ libmesos_no_3rdparty_la_SOURCES +=					\
 	slave/containerizer/launcher.hpp				\
 	slave/containerizer/linux_launcher.hpp				\
 	slave/containerizer/provisioner.hpp				\
-	slave/containerizer/provisioners/appc.hpp			\
-	slave/containerizer/provisioners/paths.hpp                      \
+        slave/containerizer/provisioners/paths.hpp                      \
 	slave/containerizer/provisioners/appc/paths.hpp			\
+	slave/containerizer/provisioners/appc/provisioner.hpp		\
 	slave/containerizer/provisioners/appc/spec.hpp			\
 	slave/containerizer/provisioners/appc/store.hpp			\
 	slave/containerizer/provisioners/backend.hpp			\

http://git-wip-us.apache.org/repos/asf/mesos/blob/0636f2b3/src/slave/containerizer/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner.cpp b/src/slave/containerizer/provisioner.cpp
index 95894c0..2ac9008 100644
--- a/src/slave/containerizer/provisioner.cpp
+++ b/src/slave/containerizer/provisioner.cpp
@@ -22,7 +22,7 @@
 
 #include "slave/containerizer/provisioner.hpp"
 
-#include "slave/containerizer/provisioners/appc.hpp"
+#include "slave/containerizer/provisioners/appc/provisioner.hpp"
 
 using namespace process;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/0636f2b3/src/slave/containerizer/provisioners/appc.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc.cpp b/src/slave/containerizer/provisioners/appc.cpp
deleted file mode 100644
index d54b688..0000000
--- a/src/slave/containerizer/provisioners/appc.cpp
+++ /dev/null
@@ -1,380 +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 <mesos/type_utils.hpp>
-
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/dispatch.hpp>
-#include <process/process.hpp>
-
-#include <stout/foreach.hpp>
-#include <stout/hashset.hpp>
-#include <stout/os.hpp>
-#include <stout/stringify.hpp>
-#include <stout/strings.hpp>
-#include <stout/uuid.hpp>
-
-#include "slave/containerizer/provisioners/appc.hpp"
-
-#include "slave/containerizer/provisioners/backend.hpp"
-#include "slave/containerizer/provisioners/paths.hpp"
-
-#include "slave/containerizer/provisioners/appc/paths.hpp"
-#include "slave/containerizer/provisioners/appc/spec.hpp"
-#include "slave/containerizer/provisioners/appc/store.hpp"
-
-#include "slave/paths.hpp"
-
-using namespace process;
-using namespace mesos::internal::slave;
-
-using std::list;
-using std::string;
-using std::vector;
-
-using mesos::slave::ContainerState;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-
-class AppcProvisionerProcess : public Process<AppcProvisionerProcess>
-{
-public:
-  AppcProvisionerProcess(
-      const Flags& flags,
-      const string& root,
-      const Owned<Store>& store,
-      const hashmap<string, Owned<Backend>>& backends);
-
-  Future<Nothing> recover(
-      const list<ContainerState>& states,
-      const hashset<ContainerID>& orphans);
-
-  Future<string> provision(const ContainerID& containerId, const Image& image);
-
-  Future<bool> destroy(const ContainerID& containerId);
-
-private:
-  Future<string> _provision(const vector<string>& layers, const string& rootfs);
-
-  const Flags flags;
-
-  // Absolute path to the Appc provisioner root directory. It can be derived
-  // from '--work_dir' but we keep a separate copy here because we converted
-  // it into an absolute path so managed rootfs paths match the ones in
-  // 'mountinfo' (important if mount-based backends are used).
-  const string root;
-
-  const Owned<Store> store;
-  const hashmap<string, Owned<Backend>> backends;
-
-  struct Info
-  {
-    // Mappings: backend -> rootfsId -> rootfsPath.
-    hashmap<string, hashmap<string, string>> rootfses;
-  };
-
-  hashmap<ContainerID, Owned<Info>> infos;
-};
-
-
-// NOTE: Successful creation of the provisioner means its managed
-// directory under --work_dir is also created.
-Try<Owned<Provisioner>> AppcProvisioner::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  string _root =
-    slave::paths::getProvisionerDir(flags.work_dir, Image::APPC);
-
-  Try<Nothing> mkdir = os::mkdir(_root);
-  if (mkdir.isError()) {
-    return Error("Failed to create provisioner root directory '" +
-                 _root + "': " + mkdir.error());
-  }
-
-  Result<string> root = os::realpath(_root);
-  if (root.isError()) {
-    return Error(
-        "Failed to resolve the realpath of provisioner root directory '" +
-        _root + "': " + root.error());
-  }
-
-  CHECK_SOME(root); // Can't be None since we just created it.
-
-  Try<Owned<Store>> store = Store::create(flags);
-  if (store.isError()) {
-    return Error("Failed to create image store: " + store.error());
-  }
-
-  hashmap<string, Owned<Backend>> backends = Backend::create(flags);
-  if (backends.empty()) {
-    return Error("No usable provisioner backend created");
-  }
-
-  if (!backends.contains(flags.appc_backend)) {
-    return Error("The specified provisioner backend '" + flags.appc_backend +
-                 "'is unsupported");
-  }
-
-  return Owned<Provisioner>(new AppcProvisioner(
-      Owned<AppcProvisionerProcess>(new AppcProvisionerProcess(
-          flags,
-          root.get(),
-          store.get(),
-          backends))));
-}
-
-
-AppcProvisioner::AppcProvisioner(Owned<AppcProvisionerProcess> _process)
-  : process(_process)
-{
-  spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-AppcProvisioner::~AppcProvisioner()
-{
-  terminate(process.get());
-  wait(process.get());
-}
-
-
-Future<Nothing> AppcProvisioner::recover(
-    const list<ContainerState>& states,
-    const hashset<ContainerID>& orphans)
-{
-  return dispatch(
-      process.get(),
-      &AppcProvisionerProcess::recover,
-      states,
-      orphans);
-}
-
-
-Future<string> AppcProvisioner::provision(
-    const ContainerID& containerId,
-    const Image& image)
-{
-  return dispatch(
-      process.get(),
-      &AppcProvisionerProcess::provision,
-      containerId,
-      image);
-}
-
-
-Future<bool> AppcProvisioner::destroy(const ContainerID& containerId)
-{
-  return dispatch(
-      process.get(),
-      &AppcProvisionerProcess::destroy,
-      containerId);
-}
-
-
-AppcProvisionerProcess::AppcProvisionerProcess(
-    const Flags& _flags,
-    const string& _root,
-    const Owned<Store>& _store,
-    const hashmap<string, Owned<Backend>>& _backends)
-  : flags(_flags),
-    root(_root),
-    store(_store),
-    backends(_backends) {}
-
-
-Future<Nothing> AppcProvisionerProcess::recover(
-    const list<ContainerState>& states,
-    const hashset<ContainerID>& orphans)
-{
-  // Register living containers, including the ones that do not
-  // provision Appc images.
-  hashset<ContainerID> alive;
-
-  foreach (const ContainerState& state, states) {
-    if (state.executor_info().has_container() &&
-        state.executor_info().container().type() == ContainerInfo::MESOS) {
-      alive.insert(state.container_id());
-    }
-  }
-
-  // List provisioned containers; recover living ones; destroy unknown orphans.
-  // Note that known orphan containers are recovered as well and they will
-  // be destroyed by the containerizer using the normal cleanup path. See
-  // MESOS-2367 for details.
-  Try<hashmap<ContainerID, string>> containers =
-    provisioners::paths::listContainers(root);
-
-  if (containers.isError()) {
-    return Failure("Failed to list the containers managed by Appc "
-                   "provisioner: " + containers.error());
-  }
-
-  // If no container has been launched the 'containers' directory will be empty.
-  foreachkey (const ContainerID& containerId, containers.get()) {
-    if (alive.contains(containerId) || orphans.contains(containerId)) {
-      Owned<Info> info = Owned<Info>(new Info());
-
-      Try<hashmap<string, hashmap<string, string>>> rootfses =
-        provisioners::paths::listContainerRootfses(root, containerId);
-
-      if (rootfses.isError()) {
-        return Failure("Unable to list rootfses belonged to container '" +
-                       containerId.value() + "': " + rootfses.error());
-      }
-
-      foreachkey (const string& backend, rootfses.get()) {
-        if (!backends.contains(backend)) {
-          return Failure("Found rootfses managed by an unrecognized backend: " +
-                         backend);
-        }
-
-        info->rootfses.put(backend, rootfses.get()[backend]);
-      }
-
-      VLOG(1) << "Recovered container " << containerId;
-      infos.put(containerId, info);
-
-      continue;
-    }
-
-    // Destroy (unknown) orphan container's rootfses.
-    Try<hashmap<string, hashmap<string, string>>> rootfses =
-      provisioners::paths::listContainerRootfses(root, containerId);
-
-    if (rootfses.isError()) {
-      return Failure("Unable to find rootfses for container '" +
-                     containerId.value() + "': " + rootfses.error());
-    }
-
-    foreachkey (const string& backend, rootfses.get()) {
-      if (!backends.contains(backend)) {
-        return Failure("Found rootfses managed by an unrecognized backend: " +
-                       backend);
-      }
-
-      foreachvalue (const string& rootfs, rootfses.get()[backend]) {
-        VLOG(1) << "Destroying orphan rootfs " << rootfs;
-
-        // Not waiting for the destruction and we don't care about
-        // the return value.
-        backends.get(backend).get()->destroy(rootfs)
-          .onFailed([rootfs](const std::string& error) {
-            LOG(WARNING) << "Failed to destroy orphan rootfs '" << rootfs
-                         << "': "<< error;
-          });
-      }
-    }
-  }
-
-  LOG(INFO) << "Recovered Appc provisioner rootfses";
-
-  return store->recover()
-    .then([]() -> Future<Nothing> {
-      LOG(INFO) << "Recovered Appc image store";
-      return Nothing();
-    });
-}
-
-
-Future<string> AppcProvisionerProcess::provision(
-    const ContainerID& containerId,
-    const Image& image)
-{
-  if (image.type() != Image::APPC) {
-    return Failure("Unsupported container image type: " +
-                   stringify(image.type()));
-  }
-
-  if (!image.has_appc()) {
-    return Failure("Missing Appc image info");
-  }
-
-  string rootfsId = UUID::random().toString();
-  string rootfs = provisioners::paths::getContainerRootfsDir(
-      root, containerId, flags.appc_backend, rootfsId);
-
-  if (!infos.contains(containerId)) {
-    infos.put(containerId, Owned<Info>(new Info()));
-  }
-
-  infos[containerId]->rootfses[flags.appc_backend].put(rootfsId, rootfs);
-
-  // Get and then provision image layers from the store.
-  return store->get(image.appc())
-    .then(defer(self(), &Self::_provision, lambda::_1, rootfs));
-}
-
-
-Future<string> AppcProvisionerProcess::_provision(
-     const vector<string>& layers,
-     const string& rootfs)
-{
-  LOG(INFO) << "Provisioning image layers to rootfs '" << rootfs << "'";
-
-  CHECK(backends.contains(flags.appc_backend));
-  return backends.get(flags.appc_backend).get()->provision(layers, rootfs)
-    .then([rootfs]() -> Future<string> { return rootfs; });
-}
-
-
-Future<bool> AppcProvisionerProcess::destroy(const ContainerID& containerId)
-{
-  if (!infos.contains(containerId)) {
-    LOG(INFO) << "Ignoring destroy request for unknown container: "
-              << containerId;
-
-    return false;
-  }
-
-  // Unregister the container first. If destroy() fails, we can rely on
-  // recover() to retry it later.
-  Owned<Info> info = infos[containerId];
-  infos.erase(containerId);
-
-  list<Future<bool>> futures;
-  foreachkey (const string& backend, info->rootfses) {
-    foreachvalue (const string& rootfs, info->rootfses[backend]) {
-      if (!backends.contains(backend)) {
-        return Failure("Cannot destroy rootfs '" + rootfs +
-                       "' provisioned by an unknown backend '" + backend + "'");
-      }
-
-      LOG(INFO) << "Destroying container rootfs for container '"
-                << containerId << "' at '" << rootfs << "'";
-
-      futures.push_back(
-          backends.get(backend).get()->destroy(rootfs));
-    }
-  }
-
-  // TODO(xujyan): Revisit the usefulness of this return value.
-  return collect(futures)
-    .then([=](const list<bool>& results) -> Future<bool> {
-      return true;
-    });
-}
-
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/0636f2b3/src/slave/containerizer/provisioners/appc.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc.hpp b/src/slave/containerizer/provisioners/appc.hpp
deleted file mode 100644
index 68e82e3..0000000
--- a/src/slave/containerizer/provisioners/appc.hpp
+++ /dev/null
@@ -1,78 +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 __MESOS_APPC_HPP__
-#define __MESOS_APPC_HPP__
-
-#include <list>
-#include <string>
-#include <vector>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/hashmap.hpp>
-#include <stout/json.hpp>
-#include <stout/nothing.hpp>
-#include <stout/try.hpp>
-
-#include "slave/containerizer/provisioner.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace appc {
-
-// Forward declaration.
-class AppcProvisionerProcess;
-
-
-class AppcProvisioner : public Provisioner
-{
-public:
-  static Try<process::Owned<Provisioner>> create(
-      const Flags& flags,
-      Fetcher* fetcher);
-
-  ~AppcProvisioner();
-
-  virtual process::Future<Nothing> recover(
-      const std::list<mesos::slave::ContainerState>& states,
-      const hashset<ContainerID>& orphans);
-
-  virtual process::Future<std::string> provision(
-      const ContainerID& containerId,
-      const Image& image);
-
-  virtual process::Future<bool> destroy(const ContainerID& containerId);
-
-private:
-  explicit AppcProvisioner(process::Owned<AppcProvisionerProcess> process);
-
-  AppcProvisioner(const AppcProvisioner&); // Not copyable.
-  AppcProvisioner& operator=(const AppcProvisioner&); // Not assignable.
-
-  process::Owned<AppcProvisionerProcess> process;
-};
-
-} // namespace appc {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MESOS_APPC_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/0636f2b3/src/slave/containerizer/provisioners/appc/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc/provisioner.cpp b/src/slave/containerizer/provisioners/appc/provisioner.cpp
new file mode 100644
index 0000000..5204cbc
--- /dev/null
+++ b/src/slave/containerizer/provisioners/appc/provisioner.cpp
@@ -0,0 +1,379 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mesos/type_utils.hpp>
+
+#include <process/collect.hpp>
+#include <process/defer.hpp>
+#include <process/dispatch.hpp>
+#include <process/process.hpp>
+
+#include <stout/foreach.hpp>
+#include <stout/hashset.hpp>
+#include <stout/os.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+#include <stout/uuid.hpp>
+
+#include "slave/containerizer/provisioners/backend.hpp"
+#include "slave/containerizer/provisioners/paths.hpp"
+
+#include "slave/containerizer/provisioners/appc/paths.hpp"
+#include "slave/containerizer/provisioners/appc/provisioner.hpp"
+#include "slave/containerizer/provisioners/appc/spec.hpp"
+#include "slave/containerizer/provisioners/appc/store.hpp"
+
+#include "slave/paths.hpp"
+
+using namespace process;
+using namespace mesos::internal::slave;
+
+using std::list;
+using std::string;
+using std::vector;
+
+using mesos::slave::ContainerState;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace appc {
+
+class AppcProvisionerProcess : public Process<AppcProvisionerProcess>
+{
+public:
+  AppcProvisionerProcess(
+      const Flags& flags,
+      const string& root,
+      const Owned<Store>& store,
+      const hashmap<string, Owned<Backend>>& backends);
+
+  Future<Nothing> recover(
+      const list<ContainerState>& states,
+      const hashset<ContainerID>& orphans);
+
+  Future<string> provision(const ContainerID& containerId, const Image& image);
+
+  Future<bool> destroy(const ContainerID& containerId);
+
+private:
+  Future<string> _provision(const vector<string>& layers, const string& rootfs);
+
+  const Flags flags;
+
+  // Absolute path to the Appc provisioner root directory. It can be derived
+  // from '--work_dir' but we keep a separate copy here because we converted
+  // it into an absolute path so managed rootfs paths match the ones in
+  // 'mountinfo' (important if mount-based backends are used).
+  const string root;
+
+  const Owned<Store> store;
+  const hashmap<string, Owned<Backend>> backends;
+
+  struct Info
+  {
+    // Mappings: backend -> rootfsId -> rootfsPath.
+    hashmap<string, hashmap<string, string>> rootfses;
+  };
+
+  hashmap<ContainerID, Owned<Info>> infos;
+};
+
+
+// NOTE: Successful creation of the provisioner means its managed
+// directory under --work_dir is also created.
+Try<Owned<Provisioner>> AppcProvisioner::create(
+    const Flags& flags,
+    Fetcher* fetcher)
+{
+  string _root =
+    slave::paths::getProvisionerDir(flags.work_dir, Image::APPC);
+
+  Try<Nothing> mkdir = os::mkdir(_root);
+  if (mkdir.isError()) {
+    return Error("Failed to create provisioner root directory '" +
+                 _root + "': " + mkdir.error());
+  }
+
+  Result<string> root = os::realpath(_root);
+  if (root.isError()) {
+    return Error(
+        "Failed to resolve the realpath of provisioner root directory '" +
+        _root + "': " + root.error());
+  }
+
+  CHECK_SOME(root); // Can't be None since we just created it.
+
+  Try<Owned<Store>> store = Store::create(flags);
+  if (store.isError()) {
+    return Error("Failed to create image store: " + store.error());
+  }
+
+  hashmap<string, Owned<Backend>> backends = Backend::create(flags);
+  if (backends.empty()) {
+    return Error("No usable provisioner backend created");
+  }
+
+  if (!backends.contains(flags.appc_backend)) {
+    return Error("The specified provisioner backend '" + flags.appc_backend +
+                 "'is unsupported");
+  }
+
+  return Owned<Provisioner>(new AppcProvisioner(
+      Owned<AppcProvisionerProcess>(new AppcProvisionerProcess(
+          flags,
+          root.get(),
+          store.get(),
+          backends))));
+}
+
+
+AppcProvisioner::AppcProvisioner(Owned<AppcProvisionerProcess> _process)
+  : process(_process)
+{
+  spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+AppcProvisioner::~AppcProvisioner()
+{
+  terminate(process.get());
+  wait(process.get());
+}
+
+
+Future<Nothing> AppcProvisioner::recover(
+    const list<ContainerState>& states,
+    const hashset<ContainerID>& orphans)
+{
+  return dispatch(
+      process.get(),
+      &AppcProvisionerProcess::recover,
+      states,
+      orphans);
+}
+
+
+Future<string> AppcProvisioner::provision(
+    const ContainerID& containerId,
+    const Image& image)
+{
+  return dispatch(
+      process.get(),
+      &AppcProvisionerProcess::provision,
+      containerId,
+      image);
+}
+
+
+Future<bool> AppcProvisioner::destroy(const ContainerID& containerId)
+{
+  return dispatch(
+      process.get(),
+      &AppcProvisionerProcess::destroy,
+      containerId);
+}
+
+
+AppcProvisionerProcess::AppcProvisionerProcess(
+    const Flags& _flags,
+    const string& _root,
+    const Owned<Store>& _store,
+    const hashmap<string, Owned<Backend>>& _backends)
+  : flags(_flags),
+    root(_root),
+    store(_store),
+    backends(_backends) {}
+
+
+Future<Nothing> AppcProvisionerProcess::recover(
+    const list<ContainerState>& states,
+    const hashset<ContainerID>& orphans)
+{
+  // Register living containers, including the ones that do not
+  // provision Appc images.
+  hashset<ContainerID> alive;
+
+  foreach (const ContainerState& state, states) {
+    if (state.executor_info().has_container() &&
+        state.executor_info().container().type() == ContainerInfo::MESOS) {
+      alive.insert(state.container_id());
+    }
+  }
+
+  // List provisioned containers; recover living ones; destroy unknown orphans.
+  // Note that known orphan containers are recovered as well and they will
+  // be destroyed by the containerizer using the normal cleanup path. See
+  // MESOS-2367 for details.
+  Try<hashmap<ContainerID, string>> containers =
+    provisioners::paths::listContainers(root);
+
+  if (containers.isError()) {
+    return Failure("Failed to list the containers managed by Appc "
+                   "provisioner: " + containers.error());
+  }
+
+  // If no container has been launched the 'containers' directory will be empty.
+  foreachkey (const ContainerID& containerId, containers.get()) {
+    if (alive.contains(containerId) || orphans.contains(containerId)) {
+      Owned<Info> info = Owned<Info>(new Info());
+
+      Try<hashmap<string, hashmap<string, string>>> rootfses =
+        provisioners::paths::listContainerRootfses(root, containerId);
+
+      if (rootfses.isError()) {
+        return Failure("Unable to list rootfses belonged to container '" +
+                       containerId.value() + "': " + rootfses.error());
+      }
+
+      foreachkey (const string& backend, rootfses.get()) {
+        if (!backends.contains(backend)) {
+          return Failure("Found rootfses managed by an unrecognized backend: " +
+                         backend);
+        }
+
+        info->rootfses.put(backend, rootfses.get()[backend]);
+      }
+
+      VLOG(1) << "Recovered container " << containerId;
+      infos.put(containerId, info);
+
+      continue;
+    }
+
+    // Destroy (unknown) orphan container's rootfses.
+    Try<hashmap<string, hashmap<string, string>>> rootfses =
+      provisioners::paths::listContainerRootfses(root, containerId);
+
+    if (rootfses.isError()) {
+      return Failure("Unable to find rootfses for container '" +
+                     containerId.value() + "': " + rootfses.error());
+    }
+
+    foreachkey (const string& backend, rootfses.get()) {
+      if (!backends.contains(backend)) {
+        return Failure("Found rootfses managed by an unrecognized backend: " +
+                       backend);
+      }
+
+      foreachvalue (const string& rootfs, rootfses.get()[backend]) {
+        VLOG(1) << "Destroying orphan rootfs " << rootfs;
+
+        // Not waiting for the destruction and we don't care about
+        // the return value.
+        backends.get(backend).get()->destroy(rootfs)
+          .onFailed([rootfs](const std::string& error) {
+            LOG(WARNING) << "Failed to destroy orphan rootfs '" << rootfs
+                         << "': "<< error;
+          });
+      }
+    }
+  }
+
+  LOG(INFO) << "Recovered Appc provisioner rootfses";
+
+  return store->recover()
+    .then([]() -> Future<Nothing> {
+      LOG(INFO) << "Recovered Appc image store";
+      return Nothing();
+    });
+}
+
+
+Future<string> AppcProvisionerProcess::provision(
+    const ContainerID& containerId,
+    const Image& image)
+{
+  if (image.type() != Image::APPC) {
+    return Failure("Unsupported container image type: " +
+                   stringify(image.type()));
+  }
+
+  if (!image.has_appc()) {
+    return Failure("Missing Appc image info");
+  }
+
+  string rootfsId = UUID::random().toString();
+  string rootfs = provisioners::paths::getContainerRootfsDir(
+      root, containerId, flags.appc_backend, rootfsId);
+
+  if (!infos.contains(containerId)) {
+    infos.put(containerId, Owned<Info>(new Info()));
+  }
+
+  infos[containerId]->rootfses[flags.appc_backend].put(rootfsId, rootfs);
+
+  // Get and then provision image layers from the store.
+  return store->get(image.appc())
+    .then(defer(self(), &Self::_provision, lambda::_1, rootfs));
+}
+
+
+Future<string> AppcProvisionerProcess::_provision(
+     const vector<string>& layers,
+     const string& rootfs)
+{
+  LOG(INFO) << "Provisioning image layers to rootfs '" << rootfs << "'";
+
+  CHECK(backends.contains(flags.appc_backend));
+  return backends.get(flags.appc_backend).get()->provision(layers, rootfs)
+    .then([rootfs]() -> Future<string> { return rootfs; });
+}
+
+
+Future<bool> AppcProvisionerProcess::destroy(const ContainerID& containerId)
+{
+  if (!infos.contains(containerId)) {
+    LOG(INFO) << "Ignoring destroy request for unknown container: "
+              << containerId;
+
+    return false;
+  }
+
+  // Unregister the container first. If destroy() fails, we can rely on
+  // recover() to retry it later.
+  Owned<Info> info = infos[containerId];
+  infos.erase(containerId);
+
+  list<Future<bool>> futures;
+  foreachkey (const string& backend, info->rootfses) {
+    foreachvalue (const string& rootfs, info->rootfses[backend]) {
+      if (!backends.contains(backend)) {
+        return Failure("Cannot destroy rootfs '" + rootfs +
+                       "' provisioned by an unknown backend '" + backend + "'");
+      }
+
+      LOG(INFO) << "Destroying container rootfs for container '"
+                << containerId << "' at '" << rootfs << "'";
+
+      futures.push_back(
+          backends.get(backend).get()->destroy(rootfs));
+    }
+  }
+
+  // TODO(xujyan): Revisit the usefulness of this return value.
+  return collect(futures)
+    .then([=](const list<bool>& results) -> Future<bool> {
+      return true;
+    });
+}
+
+} // namespace appc {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/0636f2b3/src/slave/containerizer/provisioners/appc/provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc/provisioner.hpp b/src/slave/containerizer/provisioners/appc/provisioner.hpp
new file mode 100644
index 0000000..764b119
--- /dev/null
+++ b/src/slave/containerizer/provisioners/appc/provisioner.hpp
@@ -0,0 +1,78 @@
+/**
+ * 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 __APPC_PROVISIONER_HPP__
+#define __APPC_PROVISIONER_HPP__
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include <stout/hashmap.hpp>
+#include <stout/json.hpp>
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+#include "slave/containerizer/provisioner.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace appc {
+
+// Forward declaration.
+class AppcProvisionerProcess;
+
+
+class AppcProvisioner : public Provisioner
+{
+public:
+  static Try<process::Owned<Provisioner>> create(
+      const Flags& flags,
+      Fetcher* fetcher);
+
+  ~AppcProvisioner();
+
+  virtual process::Future<Nothing> recover(
+      const std::list<mesos::slave::ContainerState>& states,
+      const hashset<ContainerID>& orphans);
+
+  virtual process::Future<std::string> provision(
+      const ContainerID& containerId,
+      const Image& image);
+
+  virtual process::Future<bool> destroy(const ContainerID& containerId);
+
+private:
+  explicit AppcProvisioner(process::Owned<AppcProvisionerProcess> process);
+
+  AppcProvisioner(const AppcProvisioner&); // Not copyable.
+  AppcProvisioner& operator=(const AppcProvisioner&); // Not assignable.
+
+  process::Owned<AppcProvisionerProcess> process;
+};
+
+} // namespace appc {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __APPC_PROVISIONER_HPP__


[3/4] mesos git commit: Fixed an issue that caused provisioned filesystems specified in --default_container_info to be not recovered.

Posted by ya...@apache.org.
Fixed an issue that caused provisioned filesystems specified in --default_container_info to be not recovered.

Review: https://reviews.apache.org/r/38241


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d84524a2
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d84524a2
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d84524a2

Branch: refs/heads/master
Commit: d84524a2b8eb38977d0d80fa8a789a897eada01d
Parents: 3b2d929
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Wed Sep 9 16:42:33 2015 -0700
Committer: Jiang Yan Xu <ya...@jxu.me>
Committed: Thu Sep 10 11:31:43 2015 -0700

----------------------------------------------------------------------
 src/slave/containerizer/provisioners/appc/provisioner.cpp | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d84524a2/src/slave/containerizer/provisioners/appc/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc/provisioner.cpp b/src/slave/containerizer/provisioners/appc/provisioner.cpp
index aa4db48..e177e3c 100644
--- a/src/slave/containerizer/provisioners/appc/provisioner.cpp
+++ b/src/slave/containerizer/provisioners/appc/provisioner.cpp
@@ -208,12 +208,8 @@ Future<Nothing> AppcProvisionerProcess::recover(
   // Register living containers, including the ones that do not
   // provision Appc images.
   hashset<ContainerID> alive;
-
   foreach (const ContainerState& state, states) {
-    if (state.executor_info().has_container() &&
-        state.executor_info().container().type() == ContainerInfo::MESOS) {
-      alive.insert(state.container_id());
-    }
+    alive.insert(state.container_id());
   }
 
   // List provisioned containers; recover living ones; destroy unknown orphans.
@@ -272,7 +268,8 @@ Future<Nothing> AppcProvisionerProcess::recover(
       }
 
       foreachvalue (const string& rootfs, rootfses.get()[backend]) {
-        VLOG(1) << "Destroying orphan rootfs " << rootfs;
+        LOG(INFO) << "Destroying unknown orphan rootfs '" << rootfs
+                  << "' of container " << containerId;
 
         // Not waiting for the destruction and we don't care about
         // the return value.


[4/4] mesos git commit: Added an Appc provisioner recovery test.

Posted by ya...@apache.org.
Added an Appc provisioner recovery test.

Review: https://reviews.apache.org/r/38075


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/6ae0f80b
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/6ae0f80b
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/6ae0f80b

Branch: refs/heads/master
Commit: 6ae0f80b30a25111f8063fb45867c7eff59a3683
Parents: d84524a
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Wed Sep 9 16:58:05 2015 -0700
Committer: Jiang Yan Xu <ya...@jxu.me>
Committed: Thu Sep 10 11:43:05 2015 -0700

----------------------------------------------------------------------
 .../containerizer/appc_provisioner_tests.cpp    | 103 +++++++++++++++++++
 1 file changed, 103 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6ae0f80b/src/tests/containerizer/appc_provisioner_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/appc_provisioner_tests.cpp b/src/tests/containerizer/appc_provisioner_tests.cpp
index 34748b1..b1be2d8 100644
--- a/src/tests/containerizer/appc_provisioner_tests.cpp
+++ b/src/tests/containerizer/appc_provisioner_tests.cpp
@@ -18,6 +18,8 @@
 
 #include <string>
 
+#include <mesos/slave/isolator.hpp>
+
 #include <process/gtest.hpp>
 
 #include <stout/gtest.hpp>
@@ -25,6 +27,7 @@
 #include <stout/os.hpp>
 #include <stout/path.hpp>
 #include <stout/stringify.hpp>
+#include <stout/uuid.hpp>
 
 #include "slave/containerizer/provisioner.hpp"
 
@@ -293,6 +296,106 @@ TEST_F(AppcProvisionerTest, ROOT_Provision)
 }
 #endif // __linux__
 
+
+// This test verifies that a provisioner can recover the rootfs provisioned
+// by a previous provisioner and then destroy it. Note that we use the copy
+// backend in this test so Linux is not required.
+TEST_F(AppcProvisionerTest, Recover)
+{
+  // Create provisioner.
+  slave::Flags flags;
+  flags.appc_store_dir = path::join(os::getcwd(), "store");
+  flags.appc_provisioner_backend = "copy";
+  flags.provisioners = "appc";
+  flags.work_dir = "work_dir";
+
+  Fetcher fetcher;
+  Try<hashmap<Image::Type, Owned<Provisioner>>> provisioners1 =
+    Provisioner::create(flags, &fetcher);
+  ASSERT_SOME(provisioners1);
+  ASSERT_TRUE(provisioners1.get().contains(Image::APPC));
+
+  // Create a simple image in the store:
+  // <store>
+  // |--images
+  //    |--<id>
+  //       |--manifest
+  //       |--rootfs/tmp/test
+  JSON::Value manifest = JSON::parse(
+      "{"
+      "  \"acKind\": \"ImageManifest\","
+      "  \"acVersion\": \"0.6.1\","
+      "  \"name\": \"foo.com/bar\""
+      "}").get();
+
+  // The 'imageId' below has the correct format but it's not computed by
+  // hashing the tarball of the image. It's OK here as we assume
+  // the images under 'images' have passed such check when they are
+  // downloaded and validated.
+  string imageId =
+    "sha512-e77d96aa0240eedf134b8c90baeaf76dca8e78691836301d7498c84020446042e"
+    "797b296d6ab296e0954c2626bfb264322ebeb8f447dac4fac6511ea06bc61f0";
+
+  string imagePath = path::join(flags.appc_store_dir, "images", imageId);
+
+  ASSERT_SOME(os::mkdir(path::join(imagePath, "rootfs", "tmp")));
+  ASSERT_SOME(
+      os::write(path::join(imagePath, "rootfs", "tmp", "test"), "test"));
+  ASSERT_SOME(
+      os::write(path::join(imagePath, "manifest"), stringify(manifest)));
+
+  // Recover. This is when the image in the store is loaded.
+  AWAIT_READY(provisioners1.get()[Image::APPC]->recover({}, {}));
+
+  Image image;
+  image.mutable_appc()->set_name("foo.com/bar");
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  Future<string> rootfs =
+    provisioners1.get()[Image::APPC]->provision(containerId, image);
+  AWAIT_READY(rootfs);
+
+  // Create a new provisioner to recover the state from the container.
+  Try<hashmap<Image::Type, Owned<Provisioner>>> provisioners2 =
+    Provisioner::create(flags, &fetcher);
+  ASSERT_SOME(provisioners2);
+  ASSERT_TRUE(provisioners2.get().contains(Image::APPC));
+
+  mesos::slave::ContainerState state;
+
+  // Here we are using an ExecutorInfo in the ContainerState without a
+  // ContainerInfo. This is the situation where the Image is specified via
+  // --default_container_info so it's not part of the recovered ExecutorInfo.
+  state.mutable_container_id()->CopyFrom(containerId);
+
+  AWAIT_READY(provisioners2.get()[Image::APPC]->recover({state}, {}));
+
+  // It's possible for the user to provision two different rootfses
+  // from the same image.
+  AWAIT_READY(provisioners2.get()[Image::APPC]->provision(containerId, image));
+
+  Try<list<string>> rootfses = os::ls(path::join(
+      flags.work_dir,
+      "provisioners",
+      stringify(Image::APPC),
+      "containers",
+      containerId.value(),
+      "backends",
+      flags.appc_provisioner_backend,
+      "rootfses"));
+
+  ASSERT_SOME(rootfses);
+
+  // Verify that the rootfs is successfully provisioned.
+  EXPECT_EQ(2u, rootfses.get().size());
+
+  Future<bool> destroy = provisioners2.get()[Image::APPC]->destroy(containerId);
+  AWAIT_READY(destroy);
+  EXPECT_TRUE(destroy.get());
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {