summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nixos/modules/services/misc/mesos-master.nix21
-rw-r--r--nixos/modules/services/misc/mesos-slave.nix98
-rw-r--r--nixos/tests/mesos.nix97
-rw-r--r--nixos/tests/mesos_test.py72
-rw-r--r--pkgs/applications/networking/cluster/mesos/default.nix94
-rw-r--r--pkgs/applications/networking/cluster/mesos/maven_repo.patch13
-rw-r--r--pkgs/applications/networking/cluster/mesos/nixos.patch444
-rw-r--r--pkgs/applications/networking/cluster/mesos/rb36610.patch7
-rw-r--r--pkgs/applications/networking/cluster/mesos/rb51324.patch71
-rw-r--r--pkgs/applications/networking/cluster/mesos/rb51325.patch157
-rw-r--r--pkgs/development/interpreters/python/build-python-package-setuptools.nix2
-rw-r--r--pkgs/development/libraries/protobuf/generic.nix22
-rw-r--r--pkgs/top-level/python-packages.nix23
13 files changed, 749 insertions, 372 deletions
diff --git a/nixos/modules/services/misc/mesos-master.nix b/nixos/modules/services/misc/mesos-master.nix
index 99583ebeebd5..0523c6549ed6 100644
--- a/nixos/modules/services/misc/mesos-master.nix
+++ b/nixos/modules/services/misc/mesos-master.nix
@@ -16,12 +16,30 @@ in {
type = types.bool;
};
+ ip = mkOption {
+ description = "IP address to listen on.";
+ default = "0.0.0.0";
+ type = types.str;
+ };
+
port = mkOption {
description = "Mesos Master port";
default = 5050;
type = types.int;
};
+ advertiseIp = mkOption {
+ description = "IP address advertised to reach this master.";
+ default = null;
+ type = types.nullOr types.str;
+ };
+
+ advertisePort = mkOption {
+ description = "Port advertised to reach this Mesos master.";
+ default = null;
+ type = types.nullOr types.int;
+ };
+
zk = mkOption {
description = ''
ZooKeeper URL (used for leader election amongst masters).
@@ -84,7 +102,10 @@ in {
serviceConfig = {
ExecStart = ''
${pkgs.mesos}/bin/mesos-master \
+ --ip=${cfg.ip} \
--port=${toString cfg.port} \
+ ${optionalString (cfg.advertiseIp != null) "--advertise_ip=${cfg.advertiseIp}"} \
+ ${optionalString (cfg.advertisePort != null) "--advertise_port=${toString cfg.advertisePort}"} \
${if cfg.quorum == 0
then "--registry=in_memory"
else "--zk=${cfg.zk} --registry=replicated_log --quorum=${toString cfg.quorum}"} \
diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix
index 9ddecb6fe30c..47be10274d3b 100644
--- a/nixos/modules/services/misc/mesos-slave.nix
+++ b/nixos/modules/services/misc/mesos-slave.nix
@@ -12,7 +12,23 @@ let
attribsArg = optionalString (cfg.attributes != {})
"--attributes=${mkAttributes cfg.attributes}";
- containerizers = [ "mesos" ] ++ (optional cfg.withDocker "docker");
+ containerizersArg = concatStringsSep "," (
+ lib.unique (
+ cfg.containerizers ++ (optional cfg.withDocker "docker")
+ )
+ );
+
+ imageProvidersArg = concatStringsSep "," (
+ lib.unique (
+ cfg.imageProviders ++ (optional cfg.withDocker "docker")
+ )
+ );
+
+ isolationArg = concatStringsSep "," (
+ lib.unique (
+ cfg.isolation ++ (optionals cfg.withDocker [ "filesystem/linux" "docker/runtime"])
+ )
+ );
in {
@@ -27,7 +43,7 @@ in {
ip = mkOption {
description = "IP address to listen on.";
default = "0.0.0.0";
- type = types.string;
+ type = types.str;
};
port = mkOption {
@@ -36,6 +52,53 @@ in {
type = types.int;
};
+ advertiseIp = mkOption {
+ description = "IP address advertised to reach this agent.";
+ default = null;
+ type = types.nullOr types.str;
+ };
+
+ advertisePort = mkOption {
+ description = "Port advertised to reach this agent.";
+ default = null;
+ type = types.nullOr types.int;
+ };
+
+ containerizers = mkOption {
+ description = ''
+ List of containerizer implementations to compose in order to provide
+ containerization. Available options are mesos and docker.
+ The order the containerizers are specified is the order they are tried.
+ '';
+ default = [ "mesos" ];
+ type = types.listOf types.str;
+ };
+
+ imageProviders = mkOption {
+ description = "List of supported image providers, e.g., APPC,DOCKER.";
+ default = [ ];
+ type = types.listOf types.str;
+ };
+
+ imageProvisionerBackend = mkOption {
+ description = ''
+ Strategy for provisioning container rootfs from images,
+ e.g., aufs, bind, copy, overlay.
+ '';
+ default = "copy";
+ type = types.str;
+ };
+
+ isolation = mkOption {
+ description = ''
+ Isolation mechanisms to use, e.g., posix/cpu,posix/mem, or
+ cgroups/cpu,cgroups/mem, or network/port_mapping, or `gpu/nvidia` for nvidia
+ specific gpu isolation.
+ '';
+ default = [ "posix/cpu" "posix/mem" ];
+ type = types.listOf types.str;
+ };
+
master = mkOption {
description = ''
May be one of:
@@ -57,6 +120,16 @@ in {
type = types.bool;
};
+ dockerRegistry = mkOption {
+ description = ''
+ The default url for pulling Docker images.
+ It could either be a Docker registry server url,
+ or a local path in which Docker image archives are stored.
+ '';
+ default = null;
+ type = types.nullOr (types.either types.str types.path);
+ };
+
workDir = mkOption {
description = "The Mesos work directory.";
default = "/var/lib/mesos/slave";
@@ -96,28 +169,45 @@ in {
host = "aabc123";
os = "nixos"; };
};
+
+ executorEnvironmentVariables = mkOption {
+ description = ''
+ The environment variables that should be passed to the executor, and thus subsequently task(s).
+ '';
+ default = {
+ PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
+ };
+ type = types.attrsOf types.str;
+ };
};
};
-
config = mkIf cfg.enable {
systemd.services.mesos-slave = {
description = "Mesos Slave";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
- environment.MESOS_CONTAINERIZERS = concatStringsSep "," containerizers;
+ path = [ pkgs.stdenv.shellPackage ];
serviceConfig = {
ExecStart = ''
${pkgs.mesos}/bin/mesos-slave \
+ --containerizers=${containerizersArg} \
+ --image_providers=${imageProvidersArg} \
+ --image_provisioner_backend=${cfg.imageProvisionerBackend} \
+ --isolation=${isolationArg} \
--ip=${cfg.ip} \
--port=${toString cfg.port} \
+ ${optionalString (cfg.advertiseIp != null) "--advertise_ip=${cfg.advertiseIp}"} \
+ ${optionalString (cfg.advertisePort != null) "--advertise_port=${toString cfg.advertisePort}"} \
--master=${cfg.master} \
--work_dir=${cfg.workDir} \
--logging_level=${cfg.logLevel} \
${attribsArg} \
${optionalString cfg.withHadoop "--hadoop-home=${pkgs.hadoop}"} \
${optionalString cfg.withDocker "--docker=${pkgs.docker}/libexec/docker/docker"} \
+ ${optionalString (cfg.dockerRegistry != null) "--docker_registry=${cfg.dockerRegistry}"} \
+ --executor_environment_variables=${lib.escapeShellArg (builtins.toJSON cfg.executorEnvironmentVariables)} \
${toString cfg.extraCmdLineOptions}
'';
PermissionsStartOnly = true;
diff --git a/nixos/tests/mesos.nix b/nixos/tests/mesos.nix
index 3610603aeba2..6e9af126f032 100644
--- a/nixos/tests/mesos.nix
+++ b/nixos/tests/mesos.nix
@@ -1,32 +1,91 @@
-import ./make-test.nix ({ pkgs, ...} : {
- name = "simple";
+import ./make-test.nix ({ pkgs, ...} : rec {
+ name = "mesos";
meta = with pkgs.stdenv.lib.maintainers; {
- maintainers = [ offline ];
+ maintainers = [ offline kamilchm cstrahan ];
};
- machine = { config, pkgs, ... }: {
- services.zookeeper.enable = true;
- virtualisation.docker.enable = true;
- services.mesos = {
- slave = {
- enable = true;
- master = "zk://localhost:2181/mesos";
- attributes = {
- tag1 = "foo";
- tag2 = "bar";
- };
+ nodes = {
+ master = { config, pkgs, ... }: {
+ networking.firewall.enable = false;
+ services.zookeeper.enable = true;
+ services.mesos.master = {
+ enable = true;
+ zk = "zk://master:2181/mesos";
};
- master = {
- enable = true;
- zk = "zk://localhost:2181/mesos";
+ };
+
+ slave = { config, pkgs, ... }: {
+ networking.firewall.enable = false;
+ networking.nat.enable = true;
+ virtualisation.docker.enable = true;
+ services.mesos = {
+ slave = {
+ enable = true;
+ master = "master:5050";
+ dockerRegistry = registry;
+ executorEnvironmentVariables = {
+ PATH = "/run/current-system/sw/bin";
+ };
+ };
};
};
};
+ simpleDocker = pkgs.dockerTools.buildImage {
+ name = "echo";
+ contents = [ pkgs.stdenv.shellPackage pkgs.coreutils ];
+ config = {
+ Env = [
+ # When shell=true, mesos invokes "sh -c '<cmd>'", so make sure "sh" is
+ # on the PATH.
+ "PATH=${pkgs.stdenv.shellPackage}/bin:${pkgs.coreutils}/bin"
+ ];
+ Entrypoint = [ "echo" ];
+ };
+ };
+
+ registry = pkgs.runCommand "registry" { } ''
+ mkdir -p $out
+ cp ${simpleDocker} $out/echo:latest.tar
+ '';
+
+ testFramework = pkgs.pythonPackages.buildPythonPackage {
+ name = "mesos-tests";
+ propagatedBuildInputs = [ pkgs.mesos ];
+ catchConflicts = false;
+ src = ./mesos_test.py;
+ phases = [ "installPhase" "fixupPhase" ];
+ installPhase = ''
+ mkdir $out
+ cp $src $out/mesos_test.py
+ chmod +x $out/mesos_test.py
+
+ echo "done" > test.result
+ tar czf $out/test.tar.gz test.result
+ '';
+ };
+
testScript =
''
startAll;
- $machine->waitForUnit("mesos-master.service");
- $machine->waitForUnit("mesos-slave.service");
+ $master->waitForUnit("mesos-master.service");
+ $slave->waitForUnit("mesos-slave.service");
+
+ $master->waitForOpenPort(5050);
+ $slave->waitForOpenPort(5051);
+
+ # is slave registred?
+ $master->waitUntilSucceeds("curl -s --fail http://master:5050/master/slaves".
+ " | grep -q \"\\\"hostname\\\":\\\"slave\\\"\"");
+
+ # try to run docker image
+ $master->succeed("${pkgs.mesos}/bin/mesos-execute --master=master:5050".
+ " --resources=\"cpus:0.1;mem:32\" --name=simple-docker".
+ " --containerizer=mesos --docker_image=echo:latest".
+ " --shell=true --command=\"echo done\" | grep -q TASK_FINISHED");
+
+ # simple command with .tar.gz uri
+ $master->succeed("${testFramework}/mesos_test.py master ".
+ "${testFramework}/test.tar.gz");
'';
})
diff --git a/nixos/tests/mesos_test.py b/nixos/tests/mesos_test.py
new file mode 100644
index 000000000000..be8bb32e49a7
--- /dev/null
+++ b/nixos/tests/mesos_test.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+import uuid
+import time
+import subprocess
+import os
+
+import sys
+
+from mesos.interface import Scheduler
+from mesos.native import MesosSchedulerDriver
+from mesos.interface import mesos_pb2
+
+def log(msg):
+ process = subprocess.Popen("systemd-cat", stdin=subprocess.PIPE)
+ (out,err) = process.communicate(msg)
+
+class NixosTestScheduler(Scheduler):
+ def __init__(self):
+ self.master_ip = sys.argv[1]
+ self.download_uri = sys.argv[2]
+
+ def resourceOffers(self, driver, offers):
+ log("XXX got resource offer")
+
+ offer = offers[0]
+ task = self.new_task(offer)
+ uri = task.command.uris.add()
+ uri.value = self.download_uri
+ task.command.value = "cat test.result"
+ driver.launchTasks(offer.id, [task])
+
+ def statusUpdate(self, driver, update):
+ log("XXX status update")
+ if update.state == mesos_pb2.TASK_FAILED:
+ log("XXX test task failed with message: " + update.message)
+ driver.stop()
+ sys.exit(1)
+ elif update.state == mesos_pb2.TASK_FINISHED:
+ driver.stop()
+ sys.exit(0)
+
+ def new_task(self, offer):
+ task = mesos_pb2.TaskInfo()
+ id = uuid.uuid4()
+ task.task_id.value = str(id)
+ task.slave_id.value = offer.slave_id.value
+ task.name = "task {}".format(str(id))
+
+ cpus = task.resources.add()
+ cpus.name = "cpus"
+ cpus.type = mesos_pb2.Value.SCALAR
+ cpus.scalar.value = 0.1
+
+ mem = task.resources.add()
+ mem.name = "mem"
+ mem.type = mesos_pb2.Value.SCALAR
+ mem.scalar.value = 32
+
+ return task
+
+if __name__ == '__main__':
+ log("XXX framework started")
+
+ framework = mesos_pb2.FrameworkInfo()
+ framework.user = "root"
+ framework.name = "nixos-test-framework"
+ driver = MesosSchedulerDriver(
+ NixosTestScheduler(),
+ framework,
+ sys.argv[1] + ":5050"
+ )
+ driver.run()
diff --git a/pkgs/applications/networking/cluster/mesos/default.nix b/pkgs/applications/networking/cluster/mesos/default.nix
index 8857e6ba4e35..818848f6a7f1 100644
--- a/pkgs/applications/networking/cluster/mesos/default.nix
+++ b/pkgs/applications/networking/cluster/mesos/default.nix
@@ -2,16 +2,27 @@
, automake115x, libtool, unzip, gnutar, jdk, maven, python, wrapPython
, setuptools, boto, pythonProtobuf, apr, subversion, gzip, systemd
, leveldb, glog, perf, utillinux, libnl, iproute, openssl, libevent
-, ethtool, coreutils
+, ethtool, coreutils, which, iptables
, bash
}:
let
mavenRepo = import ./mesos-deps.nix { inherit stdenv curl; };
soext = if stdenv.system == "x86_64-darwin" then "dylib" else "so";
+ # `tar -z` requires gzip on $PATH, so wrap tar.
+ # At some point, we should try to patch mesos so we add gzip to the PATH when
+ # tar is invoked. I think that only needs to be done here:
+ # src/common/command_utils.cpp
+ # https://github.com/NixOS/nixpkgs/issues/13783
+ tarWithGzip = lib.overrideDerivation gnutar (oldAttrs: {
+ buildInputs = (oldAttrs.buildInputs or []) ++ [ makeWrapper ];
+ postInstall = (oldAttrs.postInstall or "") + ''
+ wrapProgram $out/bin/tar --prefix PATH ":" "${gzip}/bin"
+ '';
+ });
in stdenv.mkDerivation rec {
- version = "1.0.1";
+ version = "1.1.0";
name = "mesos-${version}";
enableParallelBuilding = true;
@@ -19,17 +30,14 @@ in stdenv.mkDerivation rec {
src = fetchurl {
url = "mirror://apache/mesos/${version}/${name}.tar.gz";
- sha256 = "1hdh2wh11ck98ycfrxfzgivgk2pjl3638vkyw14xj7faj9qxjlz0";
+ sha256 = "1hdjd4syyp88l0bnh88bhzvn9466ad2ysfp9pq3kwj3qzwg5jv8g";
};
patches = [
# https://reviews.apache.org/r/36610/
+ # TODO: is this still needed?
./rb36610.patch
- # https://issues.apache.org/jira/browse/MESOS-6013
- ./rb51324.patch
- ./rb51325.patch
-
# see https://github.com/cstrahan/mesos/tree/nixos-${version}
./nixos.patch
];
@@ -46,33 +54,55 @@ in stdenv.mkDerivation rec {
pythonProtobuf
];
+ # note that we *must* statically link libprotobuf.
+ # if we dynamically link the lib, we get these errors:
+ # https://github.com/NixOS/nixpkgs/pull/19064#issuecomment-255082684
preConfigure = ''
- substituteInPlace 3rdparty/stout/include/stout/os/posix/fork.hpp \
- --subst-var-by sh ${bash}/bin/bash
+ substituteInPlace 3rdparty/stout/include/stout/os/posix/chown.hpp \
+ --subst-var-by chown ${coreutils}/bin/chown
+
+ substituteInPlace 3rdparty/stout/Makefile.am \
+ --replace "-lprotobuf" \
+ "${pythonProtobuf.protobuf.lib}/lib/libprotobuf.a"
- substituteInPlace 3rdparty/stout/include/stout/os/posix/shell.hpp \
+ substituteInPlace 3rdparty/stout/include/stout/os/posix/fork.hpp \
--subst-var-by sh ${bash}/bin/bash
- substituteInPlace src/Makefile.am \
- --subst-var-by mavenRepo ${mavenRepo}
+ substituteInPlace 3rdparty/stout/include/stout/posix/os.hpp \
+ --subst-var-by tar ${tarWithGzip}/bin/tar
substituteInPlace src/cli/mesos-scp \
--subst-var-by scp ${openssh}/bin/scp
+ substituteInPlace src/common/command_utils.cpp \
+ --subst-var-by curl ${curl}/bin/curl \
+ --subst-var-by gzip ${gzip}/bin/gzip \
+ --subst-var-by sha512sum ${coreutils}/bin/sha512sum \
+ --subst-var-by tar ${tarWithGzip}/bin/tar
+
substituteInPlace src/launcher/fetcher.cpp \
+ --subst-var-by cp ${coreutils}/bin/cp \
--subst-var-by gzip ${gzip}/bin/gzip \
- --subst-var-by tar ${gnutar}/bin/tar \
+ --subst-var-by tar ${tarWithGzip}/bin/tar \
--subst-var-by unzip ${unzip}/bin/unzip
substituteInPlace src/python/cli/src/mesos/cli.py \
--subst-var-by mesos-resolve $out/bin/mesos-resolve
+ substituteInPlace src/python/native_common/ext_modules.py.in \
+ --replace "-lprotobuf" \
+ "${pythonProtobuf.protobuf.lib}/lib/libprotobuf.a"
+
+ substituteInPlace src/slave/containerizer/mesos/isolators/gpu/volume.cpp \
+ --subst-var-by cp ${coreutils}/bin/cp \
+ --subst-var-by which ${which}/bin/which
+
substituteInPlace src/slave/containerizer/mesos/isolators/posix/disk.cpp \
- --subst-var-by du ${coreutils}/bin/du \
- --subst-var-by cp ${coreutils}/bin/cp
+ --subst-var-by du ${coreutils}/bin/du
substituteInPlace src/slave/containerizer/mesos/provisioner/backends/copy.cpp \
- --subst-var-by cp ${coreutils}/bin/cp
+ --subst-var-by cp ${coreutils}/bin/cp \
+ --subst-var-by rm ${coreutils}/bin/rm
substituteInPlace src/uri/fetchers/copy.cpp \
--subst-var-by cp ${coreutils}/bin/cp
@@ -83,23 +113,48 @@ in stdenv.mkDerivation rec {
substituteInPlace src/uri/fetchers/docker.cpp \
--subst-var-by curl ${curl}/bin/curl
+ substituteInPlace src/Makefile.am \
+ --subst-var-by mavenRepo ${mavenRepo} \
+ --replace "-lprotobuf" \
+ "${pythonProtobuf.protobuf.lib}/lib/libprotobuf.a"
+
'' + lib.optionalString stdenv.isLinux ''
substituteInPlace src/linux/perf.cpp \
--subst-var-by perf ${perf}/bin/perf
+ substituteInPlace src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp \
+ --subst-var-by mount ${utillinux}/bin/mount
+
+ substituteInPlace src/slave/containerizer/mesos/isolators/filesystem/linux.cpp \
+ --subst-var-by mount ${utillinux}/bin/mount
+
substituteInPlace src/slave/containerizer/mesos/isolators/filesystem/shared.cpp \
--subst-var-by mount ${utillinux}/bin/mount
+ substituteInPlace src/slave/containerizer/mesos/isolators/gpu/isolator.cpp \
+ --subst-var-by mount ${utillinux}/bin/mount
+
substituteInPlace src/slave/containerizer/mesos/isolators/namespaces/pid.cpp \
--subst-var-by mount ${utillinux}/bin/mount
+ substituteInPlace src/slave/containerizer/mesos/isolators/network/cni/cni.cpp \
+ --subst-var-by mount ${utillinux}/bin/mount
+
+ substituteInPlace src/slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp \
+ --subst-var-by iptables ${iptables}/bin/iptables
+
substituteInPlace src/slave/containerizer/mesos/isolators/network/port_mapping.cpp \
- --subst-var-by tc ${iproute}/bin/tc \
+ --subst-var-by ethtool ${ethtool}/sbin/ethtool \
--subst-var-by ip ${iproute}/bin/ip \
--subst-var-by mount ${utillinux}/bin/mount \
- --subst-var-by sh ${stdenv.shell} \
- --subst-var-by ethtool ${ethtool}/sbin/ethtool
+ --subst-var-by tc ${iproute}/bin/tc
+
+ substituteInPlace src/slave/containerizer/mesos/isolators/volume/image.cpp \
+ --subst-var-by mount ${utillinux}/bin/mount
+
+ substituteInPlace src/slave/containerizer/mesos/isolators/volume/sandbox_path.cpp \
+ --subst-var-by mount ${utillinux}/bin/mount
'';
configureFlags = [
@@ -108,7 +163,6 @@ in stdenv.mkDerivation rec {
"--with-svn=${subversion.dev}"
"--with-leveldb=${leveldb}"
"--with-glog=${glog}"
- "--with-glog=${glog}"
"--enable-optimize"
"--disable-python-dependency-install"
"--enable-ssl"
diff --git a/pkgs/applications/networking/cluster/mesos/maven_repo.patch b/pkgs/applications/networking/cluster/mesos/maven_repo.patch
deleted file mode 100644
index 9ee12976fde1..000000000000
--- a/pkgs/applications/networking/cluster/mesos/maven_repo.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/src/Makefile.am b/src/Makefile.am
-index ae2740a..1df91a7 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -1310,7 +1310,7 @@ if HAS_JAVA
-
- $(MESOS_JAR): $(MESOS_JAR_SOURCE) $(MESOS_JAR_GENERATED) java/mesos.pom
- @echo "Building mesos-$(PACKAGE_VERSION).jar ..."
-- @cd $(abs_top_builddir)/src/java && $(MVN) -f mesos.pom clean package
-+ @cd $(abs_top_builddir)/src/java && $(MVN) -f mesos.pom -Dmaven.repo.local=@mavenRepo@ clean package
-
- # Convenience library for JNI bindings.
- # TODO(Charles Reiss): We really should be building the Java library
diff --git a/pkgs/applications/networking/cluster/mesos/nixos.patch b/pkgs/applications/networking/cluster/mesos/nixos.patch
index 032357e452db..78e374b8d6bc 100644
--- a/pkgs/applications/networking/cluster/mesos/nixos.patch
+++ b/pkgs/applications/networking/cluster/mesos/nixos.patch
@@ -1,5 +1,18 @@
+diff --git a/3rdparty/stout/include/stout/os/posix/chown.hpp b/3rdparty/stout/include/stout/os/posix/chown.hpp
+index c82e2e574..15d332107 100644
+--- a/3rdparty/stout/include/stout/os/posix/chown.hpp
++++ b/3rdparty/stout/include/stout/os/posix/chown.hpp
+@@ -34,7 +34,7 @@ inline Try<Nothing> chown(
+ // TODO(bmahler): Consider walking the file tree instead. We would need
+ // to be careful to not miss dotfiles.
+ std::string command =
+- "chown -R " + stringify(uid) + ':' + stringify(gid) + " '" + path + "'";
++ "@chown@ -R " + stringify(uid) + ':' + stringify(gid) + " '" + path + "'";
+
+ int status = os::system(command);
+ if (status != 0) {
diff --git a/3rdparty/stout/include/stout/os/posix/fork.hpp b/3rdparty/stout/include/stout/os/posix/fork.hpp
-index a29967d..290b98b 100644
+index a29967dcb..290b98b50 100644
--- a/3rdparty/stout/include/stout/os/posix/fork.hpp
+++ b/3rdparty/stout/include/stout/os/posix/fork.hpp
@@ -369,7 +369,7 @@ private:
@@ -11,48 +24,97 @@ index a29967d..290b98b 100644
EXIT(EXIT_FAILURE)
<< "Failed to execute '" << command << "': " << os::strerror(errno);
} else if (wait.isSome()) {
-diff --git a/3rdparty/stout/include/stout/os/posix/shell.hpp b/3rdparty/stout/include/stout/os/posix/shell.hpp
-index 1d73ae5..9bf89b5 100644
---- a/3rdparty/stout/include/stout/os/posix/shell.hpp
-+++ b/3rdparty/stout/include/stout/os/posix/shell.hpp
-@@ -37,7 +37,7 @@ namespace Shell {
- // received by the callee, usually the command name and `arg1` is the
- // second command argument received by the callee.
-
--constexpr const char* name = "sh";
-+constexpr const char* name = "@sh@";
- constexpr const char* arg0 = "sh";
- constexpr const char* arg1 = "-c";
+diff --git a/3rdparty/stout/include/stout/posix/os.hpp b/3rdparty/stout/include/stout/posix/os.hpp
+index c37e64db6..d3d87b7f0 100644
+--- a/3rdparty/stout/include/stout/posix/os.hpp
++++ b/3rdparty/stout/include/stout/posix/os.hpp
+@@ -375,7 +375,7 @@ inline Option<std::string> getenv(const std::string& key)
+ inline Try<Nothing> tar(const std::string& path, const std::string& archive)
+ {
+ Try<std::string> tarOut =
+- os::shell("tar %s %s %s", "-czf", archive.c_str(), path.c_str());
++ os::shell("@tar@ %s %s %s", "-czf", archive.c_str(), path.c_str());
+ if (tarOut.isError()) {
+ return Error("Failed to archive " + path + ": " + tarOut.error());
diff --git a/src/Makefile.am b/src/Makefile.am
-index 28dd151..36fc6ec 100644
+index 3bcc0f2df..e5cbc57e8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
-@@ -1528,7 +1528,8 @@ if HAS_JAVA
+@@ -1545,7 +1545,7 @@ if HAS_JAVA
$(MESOS_JAR): $(MESOS_JAR_SOURCE) $(MESOS_JAR_GENERATED) java/mesos.pom
@echo "Building mesos-$(PACKAGE_VERSION).jar ..."
- @cd $(abs_top_builddir)/src/java && $(MVN) -B -f mesos.pom clean package
+ @cd $(abs_top_builddir)/src/java && $(MVN) -B -f mesos.pom -Dmaven.repo.local=@mavenRepo@ clean package
-+
# Convenience library for JNI bindings.
# TODO(Charles Reiss): We really should be building the Java library
diff --git a/src/cli/mesos-scp b/src/cli/mesos-scp
-index a71ab07..feed8c4 100755
+index a71ab0708..1043d1b3c 100755
--- a/src/cli/mesos-scp
+++ b/src/cli/mesos-scp
-@@ -19,7 +19,7 @@ if sys.version_info < (2,6,0):
+@@ -19,7 +19,8 @@ if sys.version_info < (2,6,0):
def scp(host, src, dst):
- cmd = 'scp -pr %s %s' % (src, host + ':' + dst)
+ cmd = '@scp@ -pr %s %s' % (src, host + ':' + dst)
++
try:
process = subprocess.Popen(
cmd,
+diff --git a/src/common/command_utils.cpp b/src/common/command_utils.cpp
+index 09e805140..90bf65896 100644
+--- a/src/common/command_utils.cpp
++++ b/src/common/command_utils.cpp
+@@ -140,7 +140,7 @@ Future<Nothing> tar(
+
+ argv.emplace_back(input);
+
+- return launch("tar", argv)
++ return launch("@tar@", argv)
+ .then([]() { return Nothing(); });
+ }
+
+@@ -162,7 +162,7 @@ Future<Nothing> untar(
+ argv.emplace_back(directory.get());
+ }
+
+- return launch("tar", argv)
++ return launch("@tar@", argv)
+ .then([]() { return Nothing(); });
+ }
+
+@@ -170,7 +170,7 @@ Future<Nothing> untar(
+ Future<string> sha512(const Path& input)
+ {
+ #ifdef __linux__
+- const string cmd = "sha512sum";
++ const string cmd = "@sha512sum@";
+ vector<string> argv = {
+ cmd,
+ input // Input file to compute shasum.
+@@ -206,7 +206,7 @@ Future<Nothing> gzip(const Path& input)
+ input
+ };
+
+- return launch("gzip", argv)
++ return launch("@gzip@", argv)
+ .then([]() { return Nothing(); });
+ }
+
+@@ -219,7 +219,7 @@ Future<Nothing> decompress(const Path& input)
+ input
+ };
+
+- return launch("gzip", argv)
++ return launch("@gzip@", argv)
+ .then([]() { return Nothing(); });
+ }
+
diff --git a/src/launcher/fetcher.cpp b/src/launcher/fetcher.cpp
-index 4456c28..e22c8fc 100644
+index 4456c2813..e22c8fc03 100644
--- a/src/launcher/fetcher.cpp
+++ b/src/launcher/fetcher.cpp
@@ -68,13 +68,13 @@ static Try<bool> extract(
@@ -82,11 +144,11 @@ index 4456c28..e22c8fc 100644
LOG(INFO) << "Copying resource with command:" << command;
diff --git a/src/linux/perf.cpp b/src/linux/perf.cpp
-index ea823b3..170f54d 100644
+index aa31982eb..8b5331b17 100644
--- a/src/linux/perf.cpp
+++ b/src/linux/perf.cpp
-@@ -125,7 +125,7 @@ private:
- // NOTE: The watchdog process places perf in its own process group
+@@ -127,7 +127,7 @@ private:
+ // NOTE: The supervisor childhook places perf in its own process group
// and will kill the perf process when the parent dies.
Try<Subprocess> _perf = subprocess(
- "perf",
@@ -104,37 +166,51 @@ index ea823b3..170f54d 100644
command << " --event " << event;
}
diff --git a/src/linux/systemd.cpp b/src/linux/systemd.cpp
-index 619aa27..c1cbfe4 100644
+index 6318f48fc..394d88d47 100644
--- a/src/linux/systemd.cpp
+++ b/src/linux/systemd.cpp
-@@ -196,12 +196,19 @@ bool exists()
+@@ -196,13 +196,21 @@ bool exists()
// This is static as the init system should not change while we are running.
static const bool exists = []() -> bool {
// (1) Test whether `/sbin/init` links to systemd.
- const Result<string> realpath = os::realpath("/sbin/init");
- if (realpath.isError() || realpath.isNone()) {
- LOG(WARNING) << "Failed to test /sbin/init for systemd environment: "
-- << realpath.error();
+- << (realpath.isError() ? realpath.error()
+- : "does not exist");
-
- return false;
-+ // cstrahan: first assume we're on NixOS, then try non-NixOS
++ // cstrahan(nixos): first assume we're on NixOS, then try non-NixOS
+ Result<string> realpath = os::realpath("/run/current-system/systemd/lib/systemd/systemd");
+ Result<string> realpathNixOS = realpath;
+ if (realpathNixOS.isError() || realpathNixOS.isNone()) {
+ Result<string> realpathNonNixOS = realpath = os::realpath("/sbin/init");
+ if (realpathNonNixOS.isError() || realpathNonNixOS.isNone()) {
+ LOG(WARNING) << "Failed to test /run/current-system/systemd/lib/systemd/systemd for systemd environment: "
-+ << realpathNixOS.error();
++ << (realpathNixOS.isError() ? realpathNixOS.error()
++ : "does not exist");
+ LOG(WARNING) << "Failed to test /sbin/init for systemd environment: "
-+ << realpathNonNixOS.error();
++ << (realpathNonNixOS.isError() ? realpathNonNixOS.error()
++ : "does not exist");
+
+ return false;
+ }
}
CHECK_SOME(realpath);
+@@ -278,6 +286,10 @@ Path hierarchy()
+
+ Try<Nothing> daemonReload()
+ {
++ // cstrahan(nixos): should we patch these `systemctl`s?
++ // probably don't want to hard-code a particular systemd store path here,
++ // but if we use /run/current-system/sw/bin/systemctl,
++ // we won't be able to support non-NixOS distros.
+ Try<string> daemonReload = os::shell("systemctl daemon-reload");
+ if (daemonReload.isError()) {
+ return Error("Failed to reload systemd daemon: " + daemonReload.error());
diff --git a/src/python/cli/src/mesos/cli.py b/src/python/cli/src/mesos/cli.py
-index f342992..354abf4 100644
+index f342992e0..354abf443 100644
--- a/src/python/cli/src/mesos/cli.py
+++ b/src/python/cli/src/mesos/cli.py
@@ -40,7 +40,7 @@ def resolve(master):
@@ -146,11 +222,70 @@ index f342992..354abf4 100644
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
+diff --git a/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp b/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
+index af9f3736b..f8554d414 100644
+--- a/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
++++ b/src/slave/containerizer/mesos/isolators/docker/volume/isolator.cpp
+@@ -499,7 +499,7 @@ Future<Option<ContainerLaunchInfo>> DockerVolumeIsolatorProcess::_prepare(
+ // unsafe arbitrary commands).
+ CommandInfo* command = launchInfo.add_pre_exec_commands();
+ command->set_shell(false);
+- command->set_value("mount");
++ command->set_value("@mount@");
+ command->add_arguments("mount");
+ command->add_arguments("-n");
+ command->add_arguments("--rbind");
+diff --git a/src/slave/containerizer/mesos/isolators/filesystem/linux.cpp b/src/slave/containerizer/mesos/isolators/filesystem/linux.cpp
+index df16b8fee..4a17475bd 100644
+--- a/src/slave/containerizer/mesos/isolators/filesystem/linux.cpp
++++ b/src/slave/containerizer/mesos/isolators/filesystem/linux.cpp
+@@ -159,9 +159,9 @@ Try<Isolator*> LinuxFilesystemIsolatorProcess::create(const Flags& flags)
+ // here because 'create' will only be invoked during
+ // initialization.
+ Try<string> mount = os::shell(
+- "mount --bind %s %s && "
+- "mount --make-private %s && "
+- "mount --make-shared %s",
++ "@mount@ --bind %s %s && "
++ "@mount@ --make-private %s && "
++ "@mount@ --make-shared %s",
+ workDir->c_str(),
+ workDir->c_str(),
+ workDir->c_str(),
+@@ -180,8 +180,8 @@ Try<Isolator*> LinuxFilesystemIsolatorProcess::create(const Flags& flags)
+ LOG(INFO) << "Making '" << workDir.get() << "' a shared mount";
+
+ Try<string> mount = os::shell(
+- "mount --make-private %s && "
+- "mount --make-shared %s",
++ "@mount@ --make-private %s && "
++ "@mount@ --make-shared %s",
+ workDir->c_str(),
+ workDir->c_str());
+
+@@ -404,7 +404,7 @@ Try<vector<CommandInfo>> LinuxFilesystemIsolatorProcess::getPreExecCommands(
+
+ CommandInfo command;
+ command.set_shell(false);
+- command.set_value("mount");
++ command.set_value("@mount@");
+ command.add_arguments("mount");
+ command.add_arguments