diff options
-rw-r--r-- | tests/unit/test_v3.py | 24 | ||||
-rw-r--r-- | zuul/executor/client.py | 1 | ||||
-rw-r--r-- | zuul/executor/server.py | 38 | ||||
-rw-r--r-- | zuul/model.py | 2 | ||||
-rw-r--r-- | zuul/reporter/__init__.py | 8 |
5 files changed, 60 insertions, 13 deletions
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index f87773b16..734c45c95 100644 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -797,6 +797,30 @@ class TestRoles(ZuulTestCase): dict(name='project-test', result='SUCCESS', changes='1,1'), ]) + def test_role_error(self): + conf = textwrap.dedent( + """ + - job: + name: project-test + roles: + - zuul: common-config + + - project: + name: org/project + check: + jobs: + - project-test + """) + + file_dict = {'.zuul.yaml': conf} + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + self.assertIn( + '- project-test project-test : ERROR Unable to find role', + A.messages[-1]) + class TestShadow(ZuulTestCase): tenant_config_file = 'config/shadow/main.yaml' diff --git a/zuul/executor/client.py b/zuul/executor/client.py index 2a205bf33..9768bc165 100644 --- a/zuul/executor/client.py +++ b/zuul/executor/client.py @@ -376,6 +376,7 @@ class ExecutorClient(object): build.node_name = data.get('node_name') if result is None: result = data.get('result') + build.error_detail = data.get('error_detail') if result is None: if (build.build_set.getTries(build.job.name) >= build.job.attempts): diff --git a/zuul/executor/server.py b/zuul/executor/server.py index 824a47ad8..0246223a9 100644 --- a/zuul/executor/server.py +++ b/zuul/executor/server.py @@ -41,6 +41,16 @@ COMMANDS = ['stop', 'pause', 'unpause', 'graceful', 'verbose', DEFAULT_FINGER_PORT = 79 +class ExecutorError(Exception): + """A non-transient run-time executor error + + This class represents error conditions detected by the executor + when preparing to run a job which we know are consistently fatal. + Zuul should not reschedule the build in these cases. + """ + pass + + class Watchdog(object): def __init__(self, timeout, function, args): self.timeout = timeout @@ -115,8 +125,8 @@ class SshAgent(object): subprocess.check_output(['ssh-add', key_path], env=env, stderr=subprocess.PIPE) except subprocess.CalledProcessError as e: - self.log.error('ssh-add failed. stdout: %s, stderr: %s', - e.output, e.stderr) + self.log.exception('ssh-add failed. stdout: %s, stderr: %s', + e.output, e.stderr) raise self.log.info('Added SSH Key {}'.format(key_path)) @@ -744,6 +754,11 @@ class AnsibleJob(object): self.executor_server.keep_jobdir, str(self.job.unique)) self._execute() + except ExecutorError as e: + result_data = json.dumps(dict(result='ERROR', + error_detail=e.args[0])) + self.log.debug("Sending result: %s" % (result_data,)) + self.job.sendWorkComplete(result_data) except Exception: self.log.exception("Exception while executing job") self.job.sendWorkException(traceback.format_exc()) @@ -913,8 +928,9 @@ class AnsibleJob(object): project_name, project_default_branch) repo.checkoutLocalBranch(project_default_branch) else: - raise Exception("Project %s does not have the default branch %s" % - (project_name, project_default_branch)) + raise ExecutorError("Project %s does not have the " + "default branch %s" % + (project_name, project_default_branch)) def runPlaybooks(self, args): result = None @@ -1005,9 +1021,9 @@ class AnsibleJob(object): ''' for entry in os.listdir(path): if os.path.isdir(entry) and entry.endswith('_plugins'): - raise Exception( - "Ansible plugin dir %s found adjacent to playbook %s in" - " non-trusted repo." % (entry, path)) + raise ExecutorError( + "Ansible plugin dir %s found adjacent to playbook %s in " + "non-trusted repo." % (entry, path)) def findPlaybook(self, path, required=False, trusted=False): for ext in ['.yaml', '.yml']: @@ -1018,7 +1034,7 @@ class AnsibleJob(object): self._blockPluginDirs(playbook_dir) return fn if required: - raise Exception("Unable to find playbook %s" % path) + raise ExecutorError("Unable to find playbook %s" % path) return None def preparePlaybooks(self, args): @@ -1036,7 +1052,7 @@ class AnsibleJob(object): break if self.jobdir.playbook is None: - raise Exception("No valid playbook found") + raise ExecutorError("No valid playbook found") for playbook in args['post_playbooks']: jobdir_playbook = self.jobdir.addPostPlaybook() @@ -1124,7 +1140,7 @@ class AnsibleJob(object): self._blockPluginDirs(os.path.join(d, entry)) return d # It is neither a bare role, nor a collection of roles - raise Exception("Unable to find role in %s" % (path,)) + raise ExecutorError("Unable to find role in %s" % (path,)) def prepareZuulRole(self, jobdir_playbook, role, args, root): self.log.debug("Prepare zuul role for %s" % (role,)) @@ -1162,7 +1178,7 @@ class AnsibleJob(object): link = os.path.join(root, name) link = os.path.realpath(link) if not link.startswith(os.path.realpath(root)): - raise Exception("Invalid role name %s", name) + raise ExecutorError("Invalid role name %s", name) os.symlink(path, link) role_path = self.findRole(link, trusted=jobdir_playbook.trusted) diff --git a/zuul/model.py b/zuul/model.py index 3c0774095..1df70dbc0 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -1098,6 +1098,7 @@ class Build(object): self.url = None self.result = None self.result_data = {} + self.error_detail = None self.build_set = None self.execute_time = time.time() self.start_time = None @@ -1118,6 +1119,7 @@ class Build(object): def getSafeAttributes(self): return Attributes(uuid=self.uuid, result=self.result, + error_detail=self.error_detail, result_data=self.result_data) diff --git a/zuul/reporter/__init__.py b/zuul/reporter/__init__.py index 95b9208ce..49181a77f 100644 --- a/zuul/reporter/__init__.py +++ b/zuul/reporter/__init__.py @@ -138,7 +138,11 @@ class BaseReporter(object, metaclass=abc.ABCMeta): elapsed = ' in %ds' % (s) else: elapsed = '' + if build.error_detail: + error = ' ' + build.error_detail + else: + error = '' name = job.name + ' ' - ret += '- %s%s : %s%s%s\n' % (name, url, result, elapsed, - voting) + ret += '- %s%s : %s%s%s%s\n' % (name, url, result, error, + elapsed, voting) return ret |