summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Thursfield <sam.thursfield@codethink.co.uk>2014-12-09 13:04:26 +0000
committerSam Thursfield <sam.thursfield@codethink.co.uk>2014-12-09 13:58:53 +0000
commit7e67d265288dee88018475fb72e63dcca14e494c (patch)
tree6bdc9d5e52fbcffbb387bb14a4da55b869ac4189
parentcb880f3554d9cf8daa3839dcd7a7ed2c336df85b (diff)
downloadmorph-7e67d265288dee88018475fb72e63dcca14e494c.tar.gz
Give less scary error messages when a containerised command fails
This affects errors encountered at build time and at system-integration time. New errors look like this: ERROR: Command failed: baserock/system-integration/02-install-gerrit-gerrit-installation-binaries-misc-0000: Containerisation settings: {'mounts': (('dev/shm', 'tmpfs', 'none'), ('tmp', 'tmpfs', 'none')), 'mount_proc': True, 'root': '/var/tmp/staging/tmp1YQ2yN/minimal-system-x86_64-generic.inst'} Error output: + install -D /usr/share/gerrit/gerrit-2.9.war /home/gerrit2/gerrit/gerrit-2.9.war -o gerrit2 -g gerrit2 -m 644 install: can't change ownership of /home/gerrit2/gerrit/gerrit-2.9.war: Operation not permitted Previously the error message would have been this: Command failed: unshare --mount -- sh -ec. mount --make-rprivate / root="$1" shift while true; do case "$1" in --) shift break ;; *) mount_point="$1" mount_type="$2" mount_source="$3" shift 3 path="$root/$mount_point" mount -t "$mount_type" "$mount_source" "$path" ;; esac done exec "$@" - /var/tmp/staging/tmppeA1Iw/gerrit-x86_64.inst/ dev/shm tmpfs none tmp tmpfs none -- linux-user-chroot --chdir . --mount-proc proc /var/tmp/staging/tmppeA1Iw/gerrit-x86_64.inst/ baserock/system-integration/02-install-gerrit-gerrit-installation-binaries-misc-0000 + install -D /usr/share/gerrit/gerrit-2.9.war /home/gerrit2/gerrit/gerrit-2.9.war -o gerrit2 -g gerrit2 -m 644 install: can't change ownership of /home/gerrit2/gerrit/gerrit-2.9.war: Operation not permitted
-rw-r--r--morphlib/app.py9
-rw-r--r--morphlib/builder2.py17
-rw-r--r--morphlib/stagingarea.py41
-rw-r--r--morphlib/util.py16
4 files changed, 61 insertions, 22 deletions
diff --git a/morphlib/app.py b/morphlib/app.py
index 930e023d..b5bcb601 100644
--- a/morphlib/app.py
+++ b/morphlib/app.py
@@ -348,7 +348,7 @@ class Morph(cliapp.Application):
self.output.write('%s %s\n' % (timestamp, text))
self.output.flush()
- def runcmd(self, argv, *args, **kwargs):
+ def _prepare_for_runcmd(self, argv, args, kwargs):
if 'env' not in kwargs:
kwargs['env'] = dict(os.environ)
@@ -377,9 +377,14 @@ class Morph(cliapp.Application):
morphlib.util.log_environment_changes(self, kwargs['env'], prev)
self.prev_env = kwargs['env']
- # run the command line
+ def runcmd(self, argv, *args, **kwargs):
+ self._prepare_for_runcmd(argv, args, kwargs)
return cliapp.Application.runcmd(self, argv, *args, **kwargs)
+ def runcmd_unchecked(self, argv, *args, **kwargs):
+ self._prepare_for_runcmd(argv, args, kwargs)
+ return cliapp.Application.runcmd_unchecked(self, argv, *args, **kwargs)
+
def parse_args(self, args, configs_only=False):
return self.settings.parse_args(args,
configs_only=configs_only,
diff --git a/morphlib/builder2.py b/morphlib/builder2.py
index f71f21db..9e158ea1 100644
--- a/morphlib/builder2.py
+++ b/morphlib/builder2.py
@@ -665,11 +665,18 @@ class SystemBuilder(BuilderBase): # pragma: no cover
)
try:
for bin in sorted(os.listdir(sys_integration_dir)):
- self.app.runcmd(
- morphlib.util.containerised_cmdline(
- [os.path.join(SYSTEM_INTEGRATION_PATH, bin)],
- root=rootdir, mounts=to_mount, mount_proc=True),
- env=env)
+ argv = [os.path.join(SYSTEM_INTEGRATION_PATH, bin)]
+ container_config = dict(
+ root=rootdir, mounts=to_mount, mount_proc=True)
+ cmdline = morphlib.util.containerised_cmdline(
+ argv, **container_config)
+ exit, out, err = self.app.runcmd_unchecked(
+ cmdline, env=env)
+ if exit != 0:
+ logging.debug('Command returned code %i', exit)
+ msg = error_message_for_containerised_commandline(
+ argv, err, container_config)
+ raise cliapp.AppException(msg)
except BaseException, e:
self.app.status(
msg='Error while running system integration commands',
diff --git a/morphlib/stagingarea.py b/morphlib/stagingarea.py
index b676d4db..7060382a 100644
--- a/morphlib/stagingarea.py
+++ b/morphlib/stagingarea.py
@@ -274,22 +274,33 @@ class StagingArea(object):
else:
binds = ()
+ container_config=dict(
+ cwd=kwargs.pop('cwd', '/'),
+ root=chroot_dir,
+ mounts=self.to_mount,
+ binds=binds,
+ mount_proc=mount_proc,
+ writable_paths=do_not_mount_dirs)
+
cmdline = morphlib.util.containerised_cmdline(
- argv, cwd=kwargs.pop('cwd', '/'),
- root=chroot_dir, mounts=self.to_mount,
- binds=binds, mount_proc=mount_proc,
- writable_paths=do_not_mount_dirs)
- try:
- if kwargs.get('logfile') != None:
- logfile = kwargs.pop('logfile')
- teecmd = ['tee', '-a', logfile]
- return self._app.runcmd(cmdline, teecmd, **kwargs)
- else:
- return self._app.runcmd(cmdline, **kwargs)
- except cliapp.AppException as e:
- raise cliapp.AppException('In staging area %s: running '
- 'command \'%s\' failed.' %
- (self.dirname, ' '.join(argv)))
+ argv, **container_config)
+
+ if kwargs.get('logfile') != None:
+ logfile = kwargs.pop('logfile')
+ teecmd = ['tee', '-a', logfile]
+ exit, out, err = self._app.runcmd_unchecked(
+ cmdline, teecmd, **kwargs)
+ else:
+ exit, out, err = self._app.runcmd_unchecked(cmdline, **kwargs)
+
+ if exit == 0:
+ return out
+ else:
+ logging.debug('Command returned code %i', exit)
+ msg = morphlib.util.error_message_for_containerised_commandline(
+ argv, err, container_config)
+ raise cliapp.AppException(
+ 'In staging area %s: %s' % (self.dirname, msg))
def abort(self): # pragma: no cover
'''Handle what to do with a staging area in the case of failure.
diff --git a/morphlib/util.py b/morphlib/util.py
index 6f735387..736af92e 100644
--- a/morphlib/util.py
+++ b/morphlib/util.py
@@ -15,11 +15,13 @@
import contextlib
import itertools
+import logging
import os
import re
import subprocess
import textwrap
+import cliapp
import fs.osfs
import morphlib
@@ -626,3 +628,17 @@ def containerised_cmdline(args, cwd='.', root='/', binds=(),
cmdargs.append(root)
cmdargs.extend(args)
return unshared_cmdline(cmdargs, root=root, **kwargs)
+
+
+def error_message_for_containerised_commandline(
+ argv, err, container_kwargs): # pragma: no cover
+ '''Return a semi-readable error message for a containerised command.'''
+
+ # This function should do some formatting of the container_kwargs dict,
+ # rather than just dumping it in the error message, but that is better than
+ # nothing.
+
+ return 'Command failed: %s:\n' \
+ 'Containerisation settings: %s\n' \
+ 'Error output:\n%s' \
+ % (' '.join(argv), container_kwargs, err)