[reprotest] 01/04: main, build: Support domain_host variation, add a --print-sudoers feature

Ximin Luo infinity0 at debian.org
Thu Oct 26 22:01:04 UTC 2017


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository reprotest.

commit 9b34f0a3044b84c39bd897c47a426256cf0a88b1
Author: Ximin Luo <infinity0 at debian.org>
Date:   Thu Oct 26 22:27:22 2017 +0200

    main, build: Support domain_host variation, add a --print-sudoers feature
---
 README.rst            |  45 ++++++++----
 debian/changelog      |   2 +
 debian/rules          |   2 +-
 debian/tests/control  |   4 +-
 reprotest/__init__.py |  25 +++++--
 reprotest/build.py    | 197 ++++++++++++++++++++++++++++++++++++++++----------
 reprotest/presets.py  |   4 +-
 7 files changed, 212 insertions(+), 67 deletions(-)

diff --git a/README.rst b/README.rst
index eddc0c0..baff2d1 100644
--- a/README.rst
+++ b/README.rst
@@ -219,6 +219,11 @@ of names is given in the --help text for --variations.
 Most variations do not have parameters, and for them only the + and - operators
 are relevant. The variations that accept parameters are:
 
+domain_host.use_sudo
+    An integer, whether to use sudo(1) together with unshare(1) to change the
+    system hostname and domainname. 0 means don't use sudo; any non-zero value
+    means to use sudo. Default is 0, however this is not recommended and make
+    may your build fail, see "Varying the domain and host names" for details.
 environment.variables
     A semicolon-separated ordered set, specifying environment variables that
     reprotest should try to vary. Default is "REPROTEST_CAPTURE_ENVIRONMENT".
@@ -269,6 +274,22 @@ means to vary home, time (the last given value for --variations), timezone, and
 Varying the user or group
 =========================
 
+Doing this without sudo *may* result in your build failing.
+
+Failure is likely if your build must do system-related things - as opposed to
+only processing bits and bytes. This is because it runs in a separate namespace
+where your non-privileged user looks like it is "root", but this prevents the
+filesystem from recognising files owned by the real "root" user, amongst other
+things. This is a limitation of unshare(1) and it is not possible work around
+this in reprotest without heavy effort.
+
+Therefore, it is recommended to run this variation with use_sudo=1. To avoid
+password prompts, see the section "Avoid sudo(1) password prompts" below.
+
+
+Varying the user or group
+=========================
+
 If you also vary fileordering at the same time (this is the case by default),
 each user you use needs to be in the "fuse" group. Do that by running `usermod
 -aG fuse $OTHERUSER` as root.
@@ -280,22 +301,14 @@ There is currently no good way to do this. The following is a very brittle and
 unclean solution. You will have to decide for yourself if it's worth it for
 your use-case::
 
-    $ OTHERUSER=(YOUR OTHER USER HERE)
-    $ a="[a-zA-Z0-9]"
-    $ cat <<EOF | sudo tee -a /etc/sudoers.d/local-reprotest
-    $USER ALL = ($OTHERUSER) NOPASSWD: ALL
-    $USER ALL = NOPASSWD: /bin/chown -h -R --from=$OTHERUSER $USER /tmp/autopkgtest.$a$a$a$a$a$a/const_build_path/
-    $USER ALL = NOPASSWD: /bin/chown -h -R --from=$OTHERUSER $USER /tmp/autopkgtest.$a$a$a$a$a$a/build-experiment-[1-9]/
-    $USER ALL = NOPASSWD: /bin/chown -h -R --from=$OTHERUSER $USER /tmp/autopkgtest.$a$a$a$a$a$a/build-experiment-[1-9]-before-disorderfs/
-    $USER ALL = NOPASSWD: /bin/chown -h -R --from=$USER $OTHERUSER /tmp/autopkgtest.$a$a$a$a$a$a/const_build_path/
-    $USER ALL = NOPASSWD: /bin/chown -h -R --from=$USER $OTHERUSER /tmp/autopkgtest.$a$a$a$a$a$a/build-experiment-[1-9]/
-    $USER ALL = NOPASSWD: /bin/chown -h -R --from=$USER $OTHERUSER /tmp/autopkgtest.$a$a$a$a$a$a/build-experiment-[1-9]-before-disorderfs/
-    EOF
-
-Repeat this for each user you'd like to use. Obviously, don't pick a privileged
-user for this purpose, such as root.
-
-(Simplifying the above using wildcards would open up passwordless access to
+    $ reprotest --print-sudoers \
+        --variations=user_group.available+=guest-builder,domain_host.use_sudo=1 \
+        | sudo EDITOR=tee visudo -f /etc/sudoers.d/local-reprotest
+
+Make sure you set the variations you actually want to use. Obviously, don't
+pick privileged users for this purpose, such as root.
+
+(Simplifying the output using wildcards, would open up passwordless access to
 chown anything on your system, because wildcards here match whitespace. I don't
 know what the sudo authors were thinking.)
 
diff --git a/debian/changelog b/debian/changelog
index 7917c7f..4099380 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,8 @@
 reprotest (0.7.4) UNRELEASED; urgency=medium
 
   * Hopefully fix the autopkgtest tests.
+  * Add a domain_host variation.
+  * Add a --print-sudoers feature.
 
  -- Ximin Luo <infinity0 at debian.org>  Fri, 20 Oct 2017 12:33:21 +0200
 
diff --git a/debian/rules b/debian/rules
index 604b183..6ee3062 100755
--- a/debian/rules
+++ b/debian/rules
@@ -11,7 +11,7 @@ export PYBUILD_NAME = reprotest
 #
 # To be user-friendly the user_group variation defaults to ON but is a no-op.
 # This causes tests to fail since they expect something to be captured. So ignore it here
-export REPROTEST_TEST_DONTVARY = fileordering,user_group
+export REPROTEST_TEST_DONTVARY = fileordering,user_group,domain_host
 
 override_dh_auto_configure:
 	test $$(python3 setup.py --version) = $$(dpkg-parsechangelog -SVersion)
diff --git a/debian/tests/control b/debian/tests/control
index 3e74024..f669c59 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,5 +1,5 @@
-Test-Command: env REPROTEST_TEST_DONTVARY=fileordering,user_group pytest-3 -m 'not need_builddeps'
+Test-Command: env REPROTEST_TEST_DONTVARY=fileordering,user_group,domain_host pytest-3 -m 'not need_builddeps'
 Depends: @, python3-pytest, faketime, locales-all
 
-Test-Command: env REPROTEST_TEST_DONTVARY=fileordering,user_group pytest-3 -m 'need_builddeps'
+Test-Command: env REPROTEST_TEST_DONTVARY=fileordering,user_group,domain_host pytest-3 -m 'need_builddeps'
 Depends: @builddeps@
diff --git a/reprotest/__init__.py b/reprotest/__init__.py
index 0bdf055..52dd1b8 100644
--- a/reprotest/__init__.py
+++ b/reprotest/__init__.py
@@ -605,6 +605,9 @@ def cli_parser():
         'implemented very well and may leave cruft on your system.')
     group3.add_argument('--dry-run', action='store_true', default=False,
         help='Don\'t run the builds, just print what would happen.')
+    group3.add_argument('--print-sudoers', action='store_true', default=False,
+        help='Print a sudoers file for passwordless operation using the given '
+        '--variations, useful for user_group.available, domain_host.use_sudo.')
 
     return parser
 
@@ -647,6 +650,14 @@ def command_line(parser, argv):
     return args
 
 
+def get_main_spec(parsed_args):
+    variations = [parsed_args.variations] + parsed_args.vary
+    if parsed_args.dont_vary:
+        logging.warn("--dont-vary is deprecated; use --vary=-$variation instead")
+        variations += ["-%s" % a for x in parsed_args.dont_vary for a in x.split(",")]
+    return VariationSpec().extend(variations)
+
+
 def run(argv, dry_run=None):
     # Argparse exits with status code 2 if something goes wrong, which
     # is already the right status exit code for reprotest.
@@ -655,12 +666,17 @@ def run(argv, dry_run=None):
     config_args = config_to_args(parser, parsed_args.config_file)
     # Command-line arguments override config file settings.
     parsed_args = command_line(parser, config_args + argv)
+    dry_run = parsed_args.dry_run or dry_run
 
     verbosity = parsed_args.verbosity
     adtlog.verbosity = verbosity - 1
     logging.basicConfig(level=30-10*verbosity)
     logging.debug('%r', parsed_args)
 
+    if not dry_run and parsed_args.print_sudoers:
+        build.print_sudoers(get_main_spec(parsed_args))
+        return 0
+
     # Decide which form of the CLI we're using
     build_command, source_root = None, None
     first_arg = parsed_args.__dict__['source_root|build_command']
@@ -729,12 +745,7 @@ def run(argv, dry_run=None):
             source_pattern = values.source_pattern + (" " + source_pattern if source_pattern else "")
 
     # Variations args
-    variations = [parsed_args.variations] + parsed_args.vary
-    if parsed_args.dont_vary:
-        logging.warn("--dont-vary is deprecated; use --vary=-$variation instead")
-        variations += ["-%s" % a for x in parsed_args.dont_vary for a in x.split(",")]
-    spec = VariationSpec().extend(variations)
-    specs = [spec]
+    specs = [get_main_spec(parsed_args)]
     if parsed_args.auto_build:
         check_func = check_auto
     elif parsed_args.env_build:
@@ -776,7 +787,7 @@ def run(argv, dry_run=None):
                             source_pattern, no_clean_on_error, diffoscope_args)
 
     check_args = (test_args, testbed_args, build_variations)
-    if parsed_args.dry_run or dry_run:
+    if dry_run:
         return check_args
     else:
         try:
diff --git a/reprotest/build.py b/reprotest/build.py
index 4f4fe99..17fa7bc 100644
--- a/reprotest/build.py
+++ b/reprotest/build.py
@@ -45,7 +45,7 @@ def basename(p):
     return os.path.normpath(os.path.basename(os.path.normpath(p)))
 
 
-class Build(collections.namedtuple('_Build', 'build_command setup cleanup env tree')):
+class Build(collections.namedtuple('_Build', 'build_command setup cleanup env tree aux_tree')):
     '''Holds the shell ASTs and various other data, used to execute each build.
 
     Fields:
@@ -64,18 +64,26 @@ class Build(collections.namedtuple('_Build', 'build_command setup cleanup env tr
             if no_clean_on_error is given and setup or build_command failed.
         env (types.MappingProxyType): Immutable mapping of the environment.
         tree (str): Path to the source root where the build should take place.
+        aux_tree (str): Path where auxilliary files are stored by reprotest.
+            When using cls.from_command(), this is automatically created and
+            cleaned up by the build script.
     '''
 
     @classmethod
     def from_command(cls, build_command, env, tree):
-        return cls(
+        aux_tree = os.path.join(dirname(tree), basename(tree) + '-aux')
+        _ = cls(
             build_command = shell_syn.Command.make(
                 "sh", "-ec", shlex.quote(str(build_command))),
             setup = shell_syn.AndList(),
             cleanup = shell_syn.List(),
             env = env,
             tree = tree,
+            aux_tree = aux_tree,
         )
+        _ = _.append_setup_exec('mkdir', '-p', aux_tree)
+        _ = _.prepend_cleanup_exec('rm', '-rf', aux_tree)
+        return _
 
     def add_env(self, key, value):
         '''Helper function for adding a key-value pair to an immutable mapping.'''
@@ -95,7 +103,7 @@ class Build(collections.namedtuple('_Build', 'build_command setup cleanup env tr
     def prepend_to_build_command(self, *prefix):
         '''Prepend a wrapper command onto the build_command.'''
         new_command = shell_syn.Command(
-            cmd_prefix=shell_syn.CmdPrefix(prefix),
+            cmd_prefix=shell_syn.CmdPrefix(map(shlex.quote, prefix)),
             cmd_suffix=self.build_command)
         return self._replace(build_command=new_command)
 
@@ -148,17 +156,21 @@ class Build(collections.namedtuple('_Build', 'build_command setup cleanup env tr
         if self.cleanup:
             cleanup = shell_syn.List.make("__c=0") + self.cleanup + \
                       shell_syn.List.make("exit $__c")
-            main_script = "if ( run_build ); then ( cleanup ); "
-            if no_clean_on_error:
-                main_script += "fi"
-            else:
-                main_script += """\
-else
+            # TODO: the below can be extended with a custom command. shell
+            # doesn't work yet though; we need to hook into autopkgtest better.
+            whether_to_clean = '! ' + str(bool(no_clean_on_error)).lower()
+            main_script = """\
+trap '( cleanup )' HUP INT QUIT ABRT TERM PIPE # FIXME doesn't quite work reliably yet
+
+if ( run_build ); then ( cleanup ); else
     __x=$?; # save the exit code of run_build
-    if ( cleanup ); then :; else echo >&2 "cleanup failed with exit code $?"; fi;
+    if ( {0} ); then
+        if ( cleanup ); then :; else echo >&2 "cleanup failed with exit code $?"; fi;
+    fi
     exit $__x
 fi
-"""
+""".format(whether_to_clean)
+
             return """\
 run_build() {{
     {0}
@@ -195,11 +207,48 @@ def environment(ctx, build, vary):
             added += [(k, v)]
     return build.modify_env(added, removed)
 
-# FIXME: this requires superuser privileges.
-# Probably need to couple with "namespace" UTS unshare when not running in a
-# virtual_server, see below for details
-# def domain_host(ctx, script, env, tree):
-#     return script, env, tree
+def domain_host(ctx, build, vary):
+    if not vary:
+        return build
+    hostname = "reprotest-capture-hostname"
+    domainname = "reprotest-capture-domainname"
+    _ = build
+
+    # TODO: below only works on linux, of course..
+    if ctx.spec.domain_host.use_sudo:
+        ns_uts, ns_mnt = ('%s/ns-%s' % (build.aux_tree, ns) for ns in ("uts", "mnt"))
+        _ = _.append_setup_exec('touch', ns_mnt, ns_uts)
+        # make ns_mnt have propagation=private, required for --mount=$ns_mnt
+        _ = _.append_setup_exec('sudo', 'mount', '-B', ns_mnt, ns_mnt)
+        _ = _.append_setup_exec('sudo', 'mount', '--make-private', ns_mnt)
+        _ = _.prepend_cleanup_exec('sudo', 'umount', ns_mnt)
+        # create our unshare
+        ns_args = ['--mount=%s' % ns_mnt, '--uts=%s' % ns_uts]
+        _ = _.append_setup_exec('sudo', 'unshare', *ns_args, 'true')
+        _ = _.prepend_cleanup_exec('sudo', 'umount', ns_mnt)
+        _ = _.prepend_cleanup_exec('sudo', 'umount', ns_uts)
+        # configure our unshare
+        nsenter = ['sudo', 'nsenter'] + ns_args
+        _ = _.append_setup_exec(*nsenter, 'hostname', hostname)
+        _ = _.append_setup_exec(*nsenter, 'domainname', domainname)
+        # the mount -B hack suppresses spurious sudo(1) warnings about "unable to resolve host"
+        _ = _.append_setup_exec('sh', '-ec',
+            'echo "127.0.0.1 {1}" > {0}/hosts && cat /etc/hosts >> {0}/hosts'.format(build.aux_tree, hostname))
+        _ = _.append_setup_exec(*nsenter, 'mount', '-B', '%s/hosts' % build.aux_tree, '/etc/hosts')
+        # wrap our build command
+        _ = _.prepend_to_build_command('sudo', '-E', 'nsenter', *ns_args, *make_sudo_command(*current_user_group()))
+    else:
+        logging.warn("Not using sudo for domain_host; it is recommended. Your build may fail.")
+        logging.warn("Be sure to `echo 1 > /proc/sys/kernel/unprivileged_userns_clone` if on a Debian system.")
+        if "user_group" in ctx.spec and ctx.spec.user_group.available:
+            logging.error("Incompatible variations: domain_host.use_sudo False, user_group.available non-empty.")
+            raise ValueError("Incompatible variations; check the log for details.")
+        _ = _.prepend_to_build_command(*"unshare -r --uts".split(),
+            "sh", "-ec", r"""
+            hostname {1}
+            domainname "{2}"
+            """.format(build.aux_tree, hostname, domainname) + '"$@"', "-")
+    return _
 
 # Note: this has to go before fileordering because we can't move mountpoints
 # TODO: this variation makes it impossible to parallelise the build, for most
@@ -324,6 +373,30 @@ def umask(ctx, build, vary):
     else:
         return build.append_setup_exec('umask', '0002')
 
+
+def current_user_group():
+    return getpass.getuser(), grp.getgrgid(os.getgid()).gr_name
+
+
+def make_sudo_command(user, group):
+    assert user or group
+    userarg = ['-u', user] if user else []
+    grouparg = ['-g', group] if group else []
+    return ['sudo', '-E'] + userarg + grouparg + ['env',
+        '-u', 'SUDO_COMMAND', '-u', 'SUDO_GID', '-u', 'SUDO_UID', '-u', 'SUDO_USER']
+
+def parse_user_group(user_group):
+    if not user_group or user_group == ':':
+        raise ValueError("user_group is empty: '%s'" % user_group)
+    if ":" in user_group:
+        user, group = user_group.split(":", 1)
+        if user:
+            return user, group
+        else:
+            return None, group
+    else:
+        return user_group, None
+
 # Note: this needs to go before anything that might need to run setup commands
 # as the other user (e.g. due to permissions).
 @tool_required("sudo")
@@ -337,34 +410,26 @@ def user_group(ctx, build, vary):
         "alternatively, suppress this warning with --variations=-user_group")
         return build
 
-    olduser = getpass.getuser()
-    oldgroup = grp.getgrgid(os.getgid()).gr_name
+    olduser, oldgroup = current_user_group()
     user_group = random.choice(list(set(ctx.spec.user_group.available) - set([(olduser, oldgroup)])))
-    if ":" in user_group:
-        user, group = user_group.split(":", 1)
-        if user:
-            sudo_command = ('sudo', '-E', '-u', user, '-g', group)
-        else:
-            user = olduser
-            sudo_command = ('sudo', '-E', '-g', group)
-    else:
-        user = user_group # "user" is used below
-        sudo_command = ('sudo', '-E', '-u', user)
+    user, group = parse_user_group(user_group)
+    sudo_command = make_sudo_command(user, group)
+    if not user:
+        user = olduser
     binpath = os.path.join(dirname(build.tree), 'bin')
 
-    _ = build.prepend_to_build_command(*sudo_command,
-        *["env", "-u", "SUDO_COMMAND", "-u", "SUDO_GID", "-u", "SUDO_UID", "-u", "SUDO_USER"])
+    _ = build.prepend_to_build_command(*sudo_command)
     # disorderfs needs to run as a different user.
     # we prefer that to running it as root, principle of least-privilege.
     _ = _.append_setup_exec('sh', '-ec', r'''
-mkdir -p "{0}"
-printf '#!/bin/sh\n{1} /usr/bin/disorderfs "$@"\n' > "{0}"/disorderfs
-chmod +x "{0}"/disorderfs
-printf '#!/bin/sh\n{1} /bin/mkdir "$@"\n' > "{0}"/mkdir
-chmod +x "{0}"/mkdir
-printf '#!/bin/sh\n{1} /bin/fusermount "$@"\n' > "{0}"/fusermount
-chmod +x "{0}"/fusermount
-'''.format(binpath, " ".join(map(shlex.quote, sudo_command))))
+        mkdir -p "{0}"
+        printf '#!/bin/sh\n{1} /usr/bin/disorderfs "$@"\n' > "{0}"/disorderfs
+        chmod +x "{0}"/disorderfs
+        printf '#!/bin/sh\n{1} /bin/mkdir "$@"\n' > "{0}"/mkdir
+        chmod +x "{0}"/mkdir
+        printf '#!/bin/sh\n{1} /bin/fusermount "$@"\n' > "{0}"/fusermount
+        chmod +x "{0}"/fusermount
+    '''.format(binpath, " ".join(map(shlex.quote, sudo_command))))
     _ = _.prepend_cleanup_exec('sh', '-ec',
         'cd "{0}" && rm -f disorderfs mkdir fusermount'.format(binpath))
     _ = _.append_setup_exec_raw('export', 'PATH="%s:$PATH"' % binpath)
@@ -382,8 +447,8 @@ VARIATIONS = collections.OrderedDict([
     ('build_path', build_path),
     ('user_group', user_group),
     # ('cpu', cpu),
-    # ('domain_host', domain_host),
     ('fileordering', fileordering),
+    ('domain_host', domain_host), # needs to run after all other mounts have been set
     ('home', home),
     ('kernel', kernel),
     ('locales', locales),
@@ -435,6 +500,12 @@ class UserGroupVariation(collections.namedtuple('_UserGroupVariation', 'availabl
         return cls(mdiffconf.strlist_set(";"))
 
 
+class DomainHostVariation(collections.namedtuple('_DomainHostVariation', 'use_sudo')):
+    @classmethod
+    def default(cls):
+        return cls(0)
+
+
 class VariationSpec(mdiffconf.ImmutableNamespace):
     @classmethod
     def default(cls, variations=VARIATIONS):
@@ -442,6 +513,7 @@ class VariationSpec(mdiffconf.ImmutableNamespace):
             "environment": EnvironmentVariation.default(),
             "user_group": UserGroupVariation.default(),
             "time": TimeVariation.default(),
+            "domain_host": DomainHostVariation.default(),
         }
         return cls(**{k: default_overrides.get(k, True) for k in variations})
 
@@ -492,6 +564,53 @@ class Variations(collections.namedtuple('_Variations', 'spec verbosity')):
         return AttributeReplacer(self, [])
 
 
+def print_sudoers(spec):
+    logging.warn("This feature is EXPERIMENTAL, use at your own risk.")
+    logging.warn("The output may be out-of-date, please file bugs if it doesn't work...")
+
+    user, group = current_user_group()
+    a = "[a-zA-Z0-9]"
+    b = "/tmp/autopkgtest.{0}{0}{0}{0}{0}{0}".format(a)
+    bx = os.path.join(b, "build-experiment-[1-9]")
+    variables = {
+        "user": user,
+        "group": group,
+        "base": b,
+        "base_ex": bx,
+    }
+
+    if "user_group" in spec and spec.user_group.available:
+        user_groups = [parse_user_group(user_group) for user_group in spec.user_group.available]
+        users = sorted(set(user for user, group in user_groups if user))
+        for otheruser in users:
+            print("""\
+# Rules for varying user_group with user %(otheruser)s
+%(user)s ALL = (%(otheruser)s) NOPASSWD: ALL
+%(user)s ALL = NOPASSWD: /bin/chown -h -R --from=%(otheruser)s %(user)s %(base)s/const_build_path/
+%(user)s ALL = NOPASSWD: /bin/chown -h -R --from=%(otheruser)s %(user)s %(base_ex)s/
+%(user)s ALL = NOPASSWD: /bin/chown -h -R --from=%(otheruser)s %(user)s %(base_ex)s-before-disorderfs/
+%(user)s ALL = NOPASSWD: /bin/chown -h -R --from=%(user)s %(otheruser)s %(base)s/const_build_path/
+%(user)s ALL = NOPASSWD: /bin/chown -h -R --from=%(user)s %(otheruser)s %(base_ex)s/
+%(user)s ALL = NOPASSWD: /bin/chown -h -R --from=%(user)s %(otheruser)s %(base_ex)s-before-disorderfs/
+""" % dict(**variables, **{
+        "otheruser": otheruser
+    }))
+
+    if "domain_host" in spec and spec.domain_host.use_sudo:
+        print("""\
+# Rules for varying domain_host
+%(user)s ALL = NOPASSWD: /bin/mount -B %(base_ex)s-aux/ns-mnt %(base_ex)s-aux/ns-mnt
+%(user)s ALL = NOPASSWD: /bin/mount --make-private %(base_ex)s-aux/ns-mnt
+%(user)s ALL = NOPASSWD: /usr/bin/unshare --mount=%(base_ex)s-aux/ns-mnt --uts=%(base_ex)s-aux/ns-uts true
+%(user)s ALL = NOPASSWD: /usr/bin/nsenter --mount=%(base_ex)s-aux/ns-mnt --uts=%(base_ex)s-aux/ns-uts hostname reprotest-*
+%(user)s ALL = NOPASSWD: /usr/bin/nsenter --mount=%(base_ex)s-aux/ns-mnt --uts=%(base_ex)s-aux/ns-uts domainname reprotest-*
+%(user)s ALL = NOPASSWD: /usr/bin/nsenter --mount=%(base_ex)s-aux/ns-mnt --uts=%(base_ex)s-aux/ns-uts mount -B %(base_ex)s-aux/hosts /etc/hosts
+%(user)s ALL = NOPASSWD:SETENV: /usr/bin/nsenter --mount=%(base_ex)s-aux/ns-mnt --uts=%(base_ex)s-aux/ns-uts sudo -E -u %(user)s -g %(group)s env *
+%(user)s ALL = NOPASSWD: /bin/umount %(base_ex)s-aux/ns-mnt
+%(user)s ALL = NOPASSWD: /bin/umount %(base_ex)s-aux/ns-uts
+""" % variables)
+
+
 if __name__ == "__main__":
     import sys
     d = VariationSpec()
diff --git a/reprotest/presets.py b/reprotest/presets.py
index 413bae0..c588593 100644
--- a/reprotest/presets.py
+++ b/reprotest/presets.py
@@ -75,7 +75,7 @@ PRESET_DEB_DIR = ReprotestPreset(
 def preset_deb_schroot(fn, preset):
     return preset.re_replace.build_command("(.*)", lambda m: r"""
         if [ "$(id -u)" = 0 ]; then
-            sudo -E -u "$LOGNAME" sh -ec {0};
+            sudo -E -u "$SUDO_USER" env -u SUDO_USER sh -ec {0};
         else
             sh -ec {0};
         fi
@@ -83,7 +83,7 @@ def preset_deb_schroot(fn, preset):
         # schroot starts us off as root, we drop privs here to do the actual build
     ).set.testbed_init(
         # need to symlink /etc/mtab to work around a fusermount(1) deficiency
-        'apt-get -y --no-install-recommends install disorderfs faketime locales-all sudo util-linux; \
+        'apt-get -y --no-install-recommends install disorderfs fakeroot faketime locales-all sudo util-linux; \
         test -c /dev/fuse || mknod -m 666 /dev/fuse c 10 229; \
         test -f /etc/mtab || ln -s ../proc/self/mounts /etc/mtab'
     ).set.testbed_build_pre(

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/reprotest.git



More information about the Reproducible-commits mailing list