Repository: mesos
Updated Branches:
  refs/heads/master f09ae5bcb -> 0845ec043


Added the `_isolate` method.

Once the `isolate` is successful, the `_isolate` method calls out the
`mesos-cni-helper` to setup the /etc/hosts, /etc/hostname and
/etc/resolv.conf for the container.

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


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

Branch: refs/heads/master
Commit: 0845ec04395faeb05a518a81c89c87b726dc8711
Parents: f09ae5b
Author: Avinash sridharan <avin...@mesosphere.io>
Authored: Wed Apr 13 17:15:21 2016 -0700
Committer: Jie Yu <yujie....@gmail.com>
Committed: Wed Apr 13 17:42:33 2016 -0700

----------------------------------------------------------------------
 .../mesos/isolators/network/cni/cni.cpp         | 199 +++++++++++++++++--
 .../mesos/isolators/network/cni/cni.hpp         |  21 +-
 2 files changed, 204 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/0845ec04/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp 
b/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
index ac01abc..6e3fe11 100644
--- a/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
+++ b/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
@@ -40,6 +40,7 @@ using std::list;
 using std::map;
 using std::set;
 using std::string;
+using std::stringstream;
 using std::tuple;
 using std::vector;
 
@@ -74,7 +75,9 @@ Try<Isolator*> NetworkCniIsolatorProcess::create(const Flags& 
flags)
   if (flags.network_cni_plugins_dir.isNone() &&
       flags.network_cni_config_dir.isNone()) {
     return new MesosIsolator(Owned<MesosIsolatorProcess>(
-        new NetworkCniIsolatorProcess(hashmap<string, NetworkConfigInfo>())));
+        new NetworkCniIsolatorProcess(
+            flags,
+            hashmap<string, NetworkConfigInfo>())));
   }
 
   // Check for root permission.
@@ -310,6 +313,7 @@ Try<Isolator*> NetworkCniIsolatorProcess::create(const 
Flags& flags)
 
   return new MesosIsolator(Owned<MesosIsolatorProcess>(
       new NetworkCniIsolatorProcess(
+          flags,
           networkConfigs,
           rootDir.get(),
           pluginDir.get())));
@@ -559,7 +563,12 @@ Future<Option<ContainerLaunchInfo>> 
NetworkCniIsolatorProcess::prepare(
   }
 
   if (!containerNetworks.empty()) {
-    infos.put(containerId, Owned<Info>(new Info(containerNetworks)));
+    if (containerConfig.has_rootfs()) {
+      Owned<Info> info(new Info(containerNetworks, containerConfig.rootfs()));
+      infos.put(containerId, info);
+    } else {
+      infos.put(containerId, Owned<Info>(new Info(containerNetworks)));
+    }
 
     ContainerLaunchInfo launchInfo;
 
@@ -645,20 +654,184 @@ Future<Nothing> NetworkCniIsolatorProcess::isolate(
   // to make sure DEL on plugin is not called (via 'cleanup()') if some
   // ADD on plugin is still pending.
   return await(futures)
-    .then([](const list<Future<Nothing>>& attaches) -> Future<Nothing> {
-      vector<string> messages;
-      foreach (const Future<Nothing>& attach, attaches) {
-        if (!attach.isReady()) {
-          messages.push_back(
-            attach.isFailed() ? attach.failure() : "discarded");
-        }
+    .then(defer(
+        PID<NetworkCniIsolatorProcess>(this),
+        &NetworkCniIsolatorProcess::_isolate,
+        containerId,
+        pid,
+        lambda::_1));
+}
+
+
+Future<Nothing> NetworkCniIsolatorProcess::_isolate(
+    const ContainerID& containerId,
+    pid_t pid,
+    const list<Future<Nothing>>& attaches)
+{
+  vector<string> messages;
+  foreach (const Future<Nothing>& attach, attaches) {
+    if (!attach.isReady()) {
+      messages.push_back(
+          attach.isFailed() ? attach.failure() : "discarded");
+    }
+  }
+
+  if (!messages.empty()) {
+    return Failure(strings::join("\n", messages));
+  }
+
+  CHECK(infos.contains(containerId));
+
+  const string containerDir =
+    paths::getContainerDir(rootDir.get(), containerId.value());
+
+  CHECK(os::exists(containerDir));
+
+  // Create the network file.
+  string hostsPath = path::join(containerDir, "hosts");
+  string hostnamePath = path::join(containerDir, "hostname");
+  string resolvPath = path::join(containerDir, "resolv.conf");
+
+  // Update the `hostname` file.
+  Try<Nothing> write = os::write(hostnamePath, stringify(containerId));
+  if (write.isError()) {
+    return Failure(
+        "Failed to write the hostname to '" + hostnamePath +
+        "': " + write.error());
+  }
+
+  // Update the `hosts` file.
+  // TODO(jieyu): Currently we support only IPv4.
+  stringstream hosts;
+
+  hosts << "127.0.0.1 localhost" << endl;
+  foreachvalue (const ContainerNetwork& network,
+                infos[containerId]->containerNetworks) {
+    // NOTE: Update /etc/hosts with hostname and IP address. In case
+    // there are multiple IP addreses associated with the container we
+    // pick the first one.
+    if (network.cniNetworkInfo.isSome() && network.cniNetworkInfo->has_ip4()) {
+      // IP are always stored in CIDR notation so need to retrieve the
+      // address without the subnet mask.
+      Try<net::IPNetwork> ip = net::IPNetwork::parse(
+          network.cniNetworkInfo->ip4().ip(),
+          AF_INET);
+
+      if (ip.isError()) {
+        return Failure(
+            "Unable to parse the IP address " +
+            network.cniNetworkInfo->ip4().ip() +
+            " for the container: " + ip.error());
       }
 
-      if (messages.empty()) {
-        return Nothing();
-      } else {
-        return Failure(strings::join("\n", messages));
+      hosts << ip->address() << " " << containerId << endl;
+      break;
+    }
+  }
+
+  write = os::write(hostsPath, hosts.str());
+  if (write.isError()) {
+    return Failure(
+        "Failed to write the 'hosts' file at '" +
+        hostsPath + "': " + write.error());
+  }
+
+  // Update 'resolv.conf' with nameservers learned from IPAM. In case
+  // IPAM has not specified a DNS then we set the container
+  // 'resolv.conf' to be the same as the host 'resolv.conf'
+  // ('/etc/resolv.conf').
+  stringstream resolv;
+  foreachvalue (const ContainerNetwork& network,
+                infos[containerId]->containerNetworks) {
+    if (network.cniNetworkInfo.isNone() || !network.cniNetworkInfo->has_dns()) 
{
+      continue;
+    }
+
+    foreach (const string& nameserver,
+             network.cniNetworkInfo->dns().nameservers()) {
+      resolv << "nameserver " << nameserver << endl;
+    }
+  }
+
+  // If `resolv` does not have any nameserver set `resolvPath` to
+  // '/etc/resolv.conf'.
+  if (resolv.str().size() == 0) {
+    if (!os::exists("/etc/resolv.conf")){
+      return Failure("Cannot find host /etc/resolv.conf");
+    }
+
+    resolvPath = "/etc/resolv.conf";
+
+    LOG(INFO) << "Unable to find DNS nameservers for container "
+              << containerId << ". Using host '/etc/resolv.conf'";
+  } else {
+    LOG(INFO) << "DNS nameservers for container " << containerId
+              << " are:\n" << resolv.str();
+
+    write = os::write(resolvPath, resolv.str());
+    if (write.isError()) {
+      return Failure(
+          "Failed to write 'resolv.conf' file at '" +
+          resolvPath + "': " + write.error());
+    }
+  }
+
+  // Setup the required network files and the hostname in the
+  // container's filesystem and UTS namespace.
+  NetworkCniIsolatorSetup setup;
+  setup.flags.pid = pid;
+  setup.flags.hostname = stringify(containerId);
+  setup.flags.rootfs = infos[containerId]->rootfs;
+  setup.flags.etc_hosts_path = hostsPath;
+  setup.flags.etc_hostname_path = hostnamePath;
+  setup.flags.etc_resolv_conf = resolvPath;
+
+  vector<string> argv(2);
+  argv[0] = "mesos-containerizer";
+  argv[1] = NetworkCniIsolatorSetup::NAME;
+
+  Try<Subprocess> s = subprocess(
+      path::join(flags.launcher_dir, "mesos-containerizer"),
+      argv,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PIPE(),
+      NO_SETSID,
+      setup.flags);
+
+  if (s.isError()) {
+    return Failure(
+        "Failed to execute the setup helper subprocess: " + s.error());
+  }
+
+  return await(s->status(), io::read(s->err().get()))
+    .then([](const tuple<
+        Future<Option<int>>,
+        Future<string>>& t) -> Future<Nothing> {
+      Future<Option<int>> status = std::get<0>(t);
+      if (!status.isReady()) {
+        return Failure(
+            "Failed to get the exit status of the setup helper subprocess: " +
+            (status.isFailed() ? status.failure() : "discarded"));
       }
+
+      if (status->isNone()) {
+        return Failure("Failed to reap the setup helper subprocess");
+      }
+
+      Future<string> err = std::get<1>(t);
+      if (!err.isReady()) {
+        return Failure(
+            "Failed to read stderr from the helper subprocess: " +
+            (err.isFailed() ? err.failure() : "discarded"));
+      }
+
+      if (status.get() != 0) {
+        return Failure(
+            "Failed to setup hostname and network files: " + err.get());
+      }
+
+      return Nothing();
     });
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/0845ec04/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp 
b/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp
index 3f64bc2..a06bb89 100644
--- a/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp
+++ b/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp
@@ -102,21 +102,34 @@ private:
 
   struct Info
   {
-    Info (const hashmap<std::string, ContainerNetwork>& _containerNetworks)
-      : containerNetworks (_containerNetworks) {}
+    Info (const hashmap<std::string, ContainerNetwork>& _containerNetworks,
+          const Option<std::string> _rootfs = None())
+      : containerNetworks (_containerNetworks),
+        rootfs(_rootfs) {}
 
     // CNI network information keyed by network name.
     hashmap<std::string, ContainerNetwork> containerNetworks;
+
+    // Rootfs of the container file system. In case the container uses
+    // the host file system, this will be `None`.
+    Option<std::string> rootfs;
   };
 
   NetworkCniIsolatorProcess(
+      const Flags _flags,
       const hashmap<std::string, NetworkConfigInfo>& _networkConfigs,
       const Option<std::string>& _rootDir = None(),
       const Option<std::string>& _pluginDir = None())
-    : networkConfigs(_networkConfigs),
+    : flags(_flags),
+      networkConfigs(_networkConfigs),
       rootDir(_rootDir),
       pluginDir(_pluginDir) {}
 
+  process::Future<Nothing> _isolate(
+      const ContainerID& containerId,
+      pid_t pid,
+      const list<process::Future<Nothing>>& attaches);
+
   Try<Nothing> _recover(
       const ContainerID& containerId,
       const Option<mesos::slave::ContainerState>& state = None());
@@ -150,6 +163,8 @@ private:
       const ContainerID& containerId,
       const std::list<process::Future<Nothing>>& detaches);
 
+  const Flags flags;
+
   // CNI network configurations keyed by network name.
   hashmap<std::string, NetworkConfigInfo> networkConfigs;
 

Reply via email to