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

jieyu pushed a commit to branch 1.6.x
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 7d979c79470c4adbb41d55af1953a398f91ad2cc
Author: Jie Yu <yujie....@gmail.com>
AuthorDate: Thu Jan 10 12:38:04 2019 -0800

    Kept `CNI_NETNS` unset in detach if network namespace is gone.
    
    We introduced a new agent flag in MESOS-9492 so that CNI configs can be
    persisted across reboot. This is for some CNI plugins to be able to
    cleanup IP allocated to the containers after a sudden reboot of the host
    (not all CNI plugins need this).
    
    It's important to unset `CNI_NETNS` environment variable after reboot
    when invoking CNI plugin "DEL" command so that it conforms to the spec.
    
    Review: https://reviews.apache.org/r/69706
    (cherry picked from commit 9863daca0baaa52984dc42c27c6e4e33c3b169b5)
---
 .../mesos/isolators/network/cni/cni.cpp            | 103 +++++++++++++++------
 1 file changed, 75 insertions(+), 28 deletions(-)

diff --git a/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp 
b/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
index 1ad3e2a..ecadfe4 100644
--- a/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
+++ b/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
@@ -1472,6 +1472,30 @@ Future<ContainerStatus> 
NetworkCniIsolatorProcess::status(
 }
 
 
+static Try<bool> isNetworkNamespaceHandle(const string& netNsHandle)
+{
+  // Determine if a path is a network namespace handle or not by
+  // checking its device number against that of '/proc'. If they are
+  // not the same, the given path shouldn't be a network namespac
+  // handle.
+  Try<dev_t> netNsHandleDev = os::stat::dev(netNsHandle);
+  if (netNsHandleDev.isError()) {
+    return Error(
+        "Failed to get the device number of '" + netNsHandle +
+        "': " + netNsHandleDev.error());
+  }
+
+  Try<dev_t> procDev = os::stat::dev("/proc");
+  if (procDev.isError()) {
+    return Error(
+        "Failed to get the device number of '/proc'"
+        ": " + procDev.error());
+  }
+
+  return netNsHandleDev.get() == procDev.get();
+}
+
+
 Future<Nothing> NetworkCniIsolatorProcess::cleanup(
     const ContainerID& containerId)
 {
@@ -1543,15 +1567,22 @@ Future<Nothing> NetworkCniIsolatorProcess::_cleanup(
     paths::getNamespacePath(rootDir.get(), containerId);
 
   if (os::exists(target)) {
-    Try<Nothing> unmount = fs::unmount(target);
-    if (unmount.isError()) {
-      return Failure(
-          "Failed to unmount the network namespace handle '" +
-          target + "': " + unmount.error());
+    Try<bool> isNetNsHandle = isNetworkNamespaceHandle(target);
+    if (isNetNsHandle.isError()) {
+      return Failure(isNetNsHandle.error());
     }
 
-    LOG(INFO) << "Unmounted the network namespace handle '"
-              << target << "' for container " << containerId;
+    if (isNetNsHandle.get()) {
+      Try<Nothing> unmount = fs::unmount(target);
+      if (unmount.isError()) {
+        return Failure(
+            "Failed to unmount the network namespace handle '" +
+            target + "': " + unmount.error());
+      }
+
+      LOG(INFO) << "Unmounted the network namespace handle '"
+                << target << "' for container " << containerId;
+    }
   }
 
   if (os::exists(containerDir)) {
@@ -1581,27 +1612,6 @@ Future<Nothing> NetworkCniIsolatorProcess::detach(
   const ContainerNetwork& containerNetwork =
     infos[containerId]->containerNetworks[networkName];
 
-  // Prepare environment variables for CNI plugin.
-  CHECK_SOME(flags.network_cni_plugins_dir);
-
-  map<string, string> environment;
-  environment["CNI_COMMAND"] = "DEL";
-  environment["CNI_CONTAINERID"] = stringify(containerId);
-  environment["CNI_PATH"] = flags.network_cni_plugins_dir.get();
-  environment["CNI_IFNAME"] = containerNetwork.ifName;
-  environment["CNI_NETNS"] =
-      paths::getNamespacePath(rootDir.get(), containerId);
-
-  // Some CNI plugins need to run "iptables" to set up IP Masquerade, so we
-  // need to set the "PATH" environment variable so that the plugin can locate
-  // the "iptables" executable file.
-  Option<string> value = os::getenv("PATH");
-  if (value.isSome()) {
-    environment["PATH"] = value.get();
-  } else {
-    environment["PATH"] = os::host_default_path();
-  }
-
   // Use the checkpointed CNI network configuration to call the
   // CNI plugin to detach the container from the CNI network.
   const string networkConfigPath = paths::getNetworkConfigPath(
@@ -1625,6 +1635,43 @@ Future<Nothing> NetworkCniIsolatorProcess::detach(
     return Nothing();
   }
 
+  // Prepare environment variables for CNI plugin.
+  CHECK_SOME(flags.network_cni_plugins_dir);
+
+  map<string, string> environment;
+  environment["CNI_COMMAND"] = "DEL";
+  environment["CNI_CONTAINERID"] = stringify(containerId);
+  environment["CNI_PATH"] = flags.network_cni_plugins_dir.get();
+  environment["CNI_IFNAME"] = containerNetwork.ifName;
+
+  // If the file is not a network namespace handle, do not set
+  // `CNI_NETNS`. This is possible after a reboot where all bind
+  // mounts of the network namespace handles are gone. According to
+  // the CNI spec, we should not set `CNI_NETNS` in such a case, but
+  // still call `DEL` command so that CNI plugins can do best effort
+  // cleanup (e.g., deallocating IP allocated for the container).
+  const string netNsHandle =
+    paths::getNamespacePath(rootDir.get(), containerId);
+
+  Try<bool> isNetNsHandle = isNetworkNamespaceHandle(netNsHandle);
+  if (isNetNsHandle.isError()) {
+    return Failure(isNetNsHandle.error());
+  }
+
+  if (isNetNsHandle.get()) {
+    environment["CNI_NETNS"] = netNsHandle;
+  }
+
+  // Some CNI plugins need to run "iptables" to set up IP Masquerade, so we
+  // need to set the "PATH" environment variable so that the plugin can locate
+  // the "iptables" executable file.
+  Option<string> value = os::getenv("PATH");
+  if (value.isSome()) {
+    environment["PATH"] = value.get();
+  } else {
+    environment["PATH"] = os::host_default_path();
+  }
+
   Try<JSON::Object> networkConfigJSON = getNetworkConfigJSON(
       networkName,
       networkConfigPath);

Reply via email to