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/04/12 20:33:14 UTC
mesos git commit: Added `subcommand` to `network/cni` isolator.
Repository: mesos
Updated Branches:
refs/heads/master b0045415d -> 2d7577855
Added `subcommand` to `network/cni` isolator.
The `subcommand` allow configuring the container hostname and setting up
network files in the container such as /etc/hosts, /etc/hostname,
/etc/resolv.conf. This will allow for correct name to IP resolution
within the container network namespace.
Review: https://reviews.apache.org/r/45954/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/2d757785
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/2d757785
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/2d757785
Branch: refs/heads/master
Commit: 2d75778557d3d29291c4ec98d00d6784010be769
Parents: b004541
Author: Avinash sridharan <av...@mesosphere.io>
Authored: Tue Apr 12 09:02:39 2016 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Tue Apr 12 11:32:58 2016 -0700
----------------------------------------------------------------------
.../mesos/isolators/network/cni/cni.cpp | 195 ++++++++++++++++++-
.../mesos/isolators/network/cni/cni.hpp | 34 ++++
2 files changed, 224 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/2d757785/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 159152a..ac01abc 100644
--- a/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
+++ b/src/slave/containerizer/mesos/isolators/network/cni/cni.cpp
@@ -14,6 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <iostream>
#include <list>
#include <set>
@@ -22,6 +23,7 @@
#include <process/subprocess.hpp>
#include <stout/os.hpp>
+#include <stout/net.hpp>
#include "linux/fs.hpp"
#include "linux/ns.hpp"
@@ -32,19 +34,21 @@ namespace io = process::io;
namespace paths = mesos::internal::slave::cni::paths;
namespace spec = mesos::internal::slave::cni::spec;
+using std::cerr;
+using std::endl;
using std::list;
+using std::map;
using std::set;
using std::string;
-using std::vector;
-using std::map;
using std::tuple;
+using std::vector;
-using process::Future;
-using process::Owned;
using process::Failure;
-using process::Subprocess;
+using process::Future;
using process::NO_SETSID;
+using process::Owned;
using process::PID;
+using process::Subprocess;
using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
@@ -1074,6 +1078,187 @@ Future<Nothing> NetworkCniIsolatorProcess::_detach(
"from network '" + networkName + "': " + output.get());
}
+
+// Implementation of subcommand to setup relevant network files and
+// hostname in the container UTS and mount namespace.
+const char* NetworkCniIsolatorSetup::NAME = "network-cni-setup";
+
+
+NetworkCniIsolatorSetup::Flags::Flags()
+{
+ add(&pid, "pid", "PID of the container");
+
+ add(&hostname, "hostname", "Hostname of the container");
+
+ add(&rootfs,
+ "rootfs",
+ "Path to rootfs for the container on the host-file system");
+
+ add(&etc_hosts_path,
+ "etc_hosts_path",
+ "Path in the host file system for 'hosts' file");
+
+ add(&etc_hostname_path,
+ "etc_hostname_path",
+ "Path in the host file system for 'hostname' file");
+
+ add(&etc_resolv_conf,
+ "etc_resolv_conf",
+ "Path in the host file system for 'resolv.conf'");
+}
+
+
+int NetworkCniIsolatorSetup::execute()
+{
+ // NOTE: This method has to be run in a new mount namespace.
+
+ if (flags.help) {
+ cerr << "Usage: " << name() << " [OPTIONS]" << endl
+ << "Supported options:" << endl
+ << flags.usage();
+ return EXIT_SUCCESS;
+ }
+
+ if (flags.hostname.isNone()) {
+ cerr << "Hostname not specified" << endl;
+ return EXIT_FAILURE;
+ }
+
+ if (flags.pid.isNone()) {
+ cerr << "Container PID not specified" << endl;
+ return EXIT_FAILURE;
+ }
+
+ if (flags.etc_hosts_path.isNone()) {
+ cerr << "Path to 'hosts' not specified" <<endl;
+ return EXIT_FAILURE;
+ } else if (!os::exists(flags.etc_hosts_path.get())) {
+ cerr << "Unable to find '" << flags.etc_hosts_path.get() << "'" << endl;
+ return EXIT_FAILURE;
+ }
+
+ if (flags.etc_hostname_path.isNone()) {
+ cerr << "Path to 'hostname' not specified" << endl;
+ return EXIT_FAILURE;
+ } else if (!os::exists(flags.etc_hostname_path.get())) {
+ cerr << "Unable to find '" << flags.etc_hostname_path.get() << "'" << endl;
+ return EXIT_FAILURE;
+ }
+
+ if (flags.etc_resolv_conf.isNone()) {
+ cerr << "Path to 'resolv.conf' not specified." << endl;
+ return EXIT_FAILURE;
+ } else if (!os::exists(flags.etc_resolv_conf.get())) {
+ cerr << "Unable to find '" << flags.etc_resolv_conf.get() << "'" << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Enter the mount namespace.
+ Try<Nothing> setns = ns::setns(flags.pid.get(), "mnt");
+ if (setns.isError()) {
+ cerr << "Failed to enter the mount namespace of pid "
+ << flags.pid.get() << ": " << setns.error() << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Enter the UTS namespace.
+ setns = ns::setns(flags.pid.get(), "uts");
+ if (setns.isError()) {
+ cerr << "Failed to enter the UTS namespace of pid "
+ << flags.pid.get() << ": " << setns.error() << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Setup hostname in container's UTS namespace.
+ Try<Nothing> setHostname = net::setHostname(flags.hostname.get());
+ if (setHostname.isError()) {
+ cerr << "Failed to set the hostname of the container to '"
+ << flags.hostname.get() << "': " << setHostname.error() << endl;
+ return EXIT_FAILURE;
+ }
+
+ LOG(INFO) << "Set hostname to '" << flags.hostname.get() << "'" << endl;
+
+ // TODO(jieyu): Currently there seems to be a race between the
+ // filesystem isolator and other isolators to execute the `isolate`
+ // method. This results in the rootfs of the container not being
+ // marked as slave + recursive which can result in the mounts in the
+ // container mnt namespace propagating back into the host mnt
+ // namespace. This is dangerous, since these mounts won't be cleared
+ // in the host mnt namespace once the container mnt namespace is
+ // destroyed (when the process dies). To avoid any leakage we mark
+ // the root as a SLAVE recursively to avoid any propagation of
+ // mounts in the container mnt namespace back into the host mnt
+ // namespace.
+ Try<Nothing> mount = fs::mount(
+ None(),
+ "/",
+ None(),
+ MS_SLAVE | MS_REC,
+ NULL);
+
+ if (mount.isError()) {
+ cerr << "Failed to mark `/` as a SLAVE mount: " << mount.error() << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Initialize the host path and container path for the set of files
+ // that need to be setup in the container file system.
+ //
+ // NOTE: An empty rootfs implies that the container is using the
+ // host file system.
+ string rootfs = "";
+ if (flags.rootfs.isSome()) {
+ rootfs = flags.rootfs.get();
+ }
+
+ hashmap<string, string> files;
+ files["/etc/hosts"] = flags.etc_hosts_path.get();
+ files["/etc/hostname"] = flags.etc_hostname_path.get();
+ files["/etc/resolv.conf"] = flags.etc_resolv_conf.get();
+
+ foreachpair (const string& file, const string& source, files) {
+ string target = file;
+
+ if (flags.rootfs.isSome()) {
+ target = path::join(flags.rootfs.get(), file);
+ }
+
+ if (!os::exists(target)) {
+ // NOTE: We only create the mount point if container has its own
+ // image. Otherwise, we'll just fail because we don't want to
+ // pollute the host filesystem.
+ if (flags.rootfs.isNone()) {
+ cerr << "Mount point '" << target << "' does not exist "
+ << "on the host filesystem"<< endl;
+ return EXIT_FAILURE;
+ }
+
+ Try<Nothing> touch = os::touch(target);
+ if (touch.isError()) {
+ cerr << "Failed to create the mount point '" << target
+ << "' in the container rootfs" << endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ mount = fs::mount(
+ source,
+ target,
+ None(),
+ MS_BIND,
+ NULL);
+
+ if (mount.isError()) {
+ cerr << "Failed to bind mount from '" << source << "' to '"
+ << target << "': " << mount.error() << endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
} // namespace slave {
} // namespace internal {
} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/2d757785/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 654137c..3f64bc2 100644
--- a/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp
+++ b/src/slave/containerizer/mesos/isolators/network/cni/cni.hpp
@@ -19,6 +19,8 @@
#include <process/subprocess.hpp>
+#include <stout/subcommand.hpp>
+
#include "slave/flags.hpp"
#include "slave/containerizer/mesos/isolator.hpp"
@@ -161,6 +163,38 @@ private:
hashmap<ContainerID, process::Owned<Info>> infos;
};
+
+// A subcommand to setup container hostname and mount the hosts,
+// resolv.conf and hostname from the host file system into the
+// container's file system. The hostname needs to be setup in the
+// container's UTS namespace, and the files need to be bind mounted in
+// the container's mnt namespace.
+class NetworkCniIsolatorSetup : public Subcommand
+{
+public:
+ static const char* NAME;
+
+ struct Flags : public flags::FlagsBase
+ {
+ Flags();
+
+ Option<pid_t> pid;
+ Option<std::string> hostname;
+ Option<std::string> rootfs;
+ Option<std::string> etc_hosts_path;
+ Option<std::string> etc_hostname_path;
+ Option<std::string> etc_resolv_conf;
+ };
+
+ NetworkCniIsolatorSetup() : Subcommand(NAME) {}
+
+ Flags flags;
+
+protected:
+ virtual int execute();
+ virtual flags::FlagsBase* getFlags() { return &flags; }
+};
+
} // namespace slave {
} // namespace internal {
} // namespace mesos {