Support emitting warnings via zuul_return
We already have the infrastructure in place for adding warnings to the reporting. Plumb that through to zuul_return so jobs can do that on purpose as well. An example could be a post playbook that analyzes performance statistics and emits a warning about inefficient usage of the build node resources. Change-Id: I4c3b85dc8f4c69c55cbc6168b8a66afce8b50a97
This commit is contained in:
parent
ebb0b222ca
commit
9879cab0d1
@ -865,6 +865,25 @@ zuul.child_jobs is empty, all jobs will be marked as SKIPPED. Invalid child jobs
|
||||
are stripped and ignored, if only invalid jobs are listed it is the same as
|
||||
providing an empty list to zuul.child_jobs.
|
||||
|
||||
Leaving warnings
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
A job can leave warnings that will be appended to the comment zuul leaves on
|
||||
the change. Use *zuul_return* to add a list of warnings. For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
tasks:
|
||||
- zuul_return:
|
||||
data:
|
||||
zuul:
|
||||
warnings:
|
||||
- This warning will be posted on the change.
|
||||
|
||||
If *zuul_return* is invoked multiple times (e.g., via multiple
|
||||
playbooks), then the elements of **zuul.warnings** from each
|
||||
invocation will be appended.
|
||||
|
||||
Leaving file comments
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
5
releasenotes/notes/job-warnings-b72a9e80574b0969.yaml
Normal file
5
releasenotes/notes/job-warnings-b72a9e80574b0969.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
A job now can leave warning messages on the change using zuul_return.
|
||||
See :ref:`return_values` for examples.
|
17
tests/fixtures/config/return-warnings/git/common-config/zuul.yaml
vendored
Normal file
17
tests/fixtures/config/return-warnings/git/common-config/zuul.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
- pipeline:
|
||||
name: check
|
||||
manager: independent
|
||||
post-review: true
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
gerrit:
|
||||
Verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
Verified: -1
|
||||
|
||||
- job:
|
||||
name: base
|
||||
parent: null
|
14
tests/fixtures/config/return-warnings/git/org_project/playbooks/warnings.yaml
vendored
Normal file
14
tests/fixtures/config/return-warnings/git/org_project/playbooks/warnings.yaml
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
- hosts: localhost
|
||||
tasks:
|
||||
- name: Emit first warning
|
||||
zuul_return:
|
||||
data:
|
||||
zuul:
|
||||
warnings:
|
||||
- This is the first warning
|
||||
- name: Emit second warning
|
||||
zuul_return:
|
||||
data:
|
||||
zuul:
|
||||
warnings:
|
||||
- This is the second warning
|
8
tests/fixtures/config/return-warnings/git/org_project/zuul.yaml
vendored
Normal file
8
tests/fixtures/config/return-warnings/git/org_project/zuul.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
- job:
|
||||
name: emit-warnings
|
||||
run: playbooks/warnings.yaml
|
||||
|
||||
- project:
|
||||
check:
|
||||
jobs:
|
||||
- emit-warnings
|
8
tests/fixtures/config/return-warnings/main.yaml
vendored
Normal file
8
tests/fixtures/config/return-warnings/main.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
- tenant:
|
||||
name: tenant-one
|
||||
source:
|
||||
gerrit:
|
||||
config-projects:
|
||||
- common-config
|
||||
untrusted-projects:
|
||||
- org/project
|
@ -6622,3 +6622,24 @@ class TestDefaultAnsibleVersion(AnsibleZuulTestCase):
|
||||
dict(name='ansible-28', result='SUCCESS', changes='1,1'),
|
||||
dict(name='ansible-29', result='SUCCESS', changes='1,1'),
|
||||
], ordered=False)
|
||||
|
||||
|
||||
class TestReturnWarnings(AnsibleZuulTestCase):
|
||||
tenant_config_file = 'config/return-warnings/main.yaml'
|
||||
|
||||
def test_return_warnings(self):
|
||||
"""
|
||||
Tests that jobs can emit custom warnings that get reported.
|
||||
"""
|
||||
|
||||
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
||||
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.assertHistory([
|
||||
dict(name='emit-warnings', result='SUCCESS', changes='1,1'),
|
||||
])
|
||||
|
||||
self.assertTrue(A.reported)
|
||||
self.assertIn('This is the first warning', A.messages[0])
|
||||
self.assertIn('This is the second warning', A.messages[0])
|
||||
|
@ -39,36 +39,33 @@ def merge_dict(dict_a, dict_b):
|
||||
return dict_b
|
||||
|
||||
|
||||
def merge_zuul_list(dict_a, dict_b, key):
|
||||
value_a = dict_a.get('zuul', {}).get(key, [])
|
||||
value_b = dict_b.get('zuul', {}).get(key, [])
|
||||
if not isinstance(value_a, list):
|
||||
value_a = []
|
||||
if not isinstance(value_b, list):
|
||||
value_b = []
|
||||
return value_a + value_b
|
||||
|
||||
|
||||
def merge_data(dict_a, dict_b):
|
||||
"""
|
||||
Merge dict_a into dict_b, handling any special cases for zuul variables
|
||||
"""
|
||||
artifacts = merge_artifacts(dict_a, dict_b)
|
||||
artifacts = merge_zuul_list(dict_a, dict_b, 'artifacts')
|
||||
file_comments = merge_file_comments(dict_a, dict_b)
|
||||
warnings = merge_zuul_list(dict_a, dict_b, 'warnings')
|
||||
merge_dict(dict_a, dict_b)
|
||||
if artifacts:
|
||||
dict_b.setdefault('zuul', {})['artifacts'] = artifacts
|
||||
if file_comments:
|
||||
dict_b.setdefault("zuul", {})["file_comments"] = file_comments
|
||||
if warnings:
|
||||
dict_b.setdefault('zuul', {})['warnings'] = warnings
|
||||
return dict_b
|
||||
|
||||
|
||||
def merge_artifacts(dict_a, dict_b):
|
||||
"""Merge artifacts from both dictionary
|
||||
|
||||
The artifacts from both dictionaries will be merged (additive) into a new
|
||||
list.
|
||||
"""
|
||||
artifacts_a = dict_a.get('zuul', {}).get("artifacts", [])
|
||||
if not isinstance(artifacts_a, list):
|
||||
artifacts_a = []
|
||||
artifacts_b = dict_b.get('zuul', {}).get("artifacts", [])
|
||||
if not isinstance(artifacts_b, list):
|
||||
artifacts_b = []
|
||||
artifacts = artifacts_a + artifacts_b
|
||||
return artifacts
|
||||
|
||||
|
||||
def merge_file_comments(dict_a, dict_b):
|
||||
"""Merge file_comments from both dictionary.
|
||||
|
||||
|
@ -19,8 +19,8 @@ import time
|
||||
import voluptuous as v
|
||||
|
||||
from zuul.lib.logutil import get_annotated_logger
|
||||
from zuul.lib.result_data import get_artifacts_from_result_data
|
||||
from zuul.reporter import BaseReporter
|
||||
from zuul.lib.artifacts import get_artifacts_from_result_data
|
||||
|
||||
|
||||
class SQLReporter(BaseReporter):
|
||||
|
@ -37,6 +37,7 @@ from urllib.parse import urlsplit
|
||||
|
||||
from zuul.lib.ansible import AnsibleManager
|
||||
from zuul.lib.gearworker import ZuulGearWorker
|
||||
from zuul.lib.result_data import get_warnings_from_result_data
|
||||
from zuul.lib.yamlutil import yaml
|
||||
from zuul.lib.config import get_default
|
||||
from zuul.lib.logutil import get_annotated_logger
|
||||
@ -1175,6 +1176,7 @@ class AnsibleJob(object):
|
||||
data = self.getResultData()
|
||||
warnings = []
|
||||
self.mapLines(merger, args, data, item_commit, warnings)
|
||||
warnings.extend(get_warnings_from_result_data(data, logger=self.log))
|
||||
result_data = json.dumps(dict(result=result,
|
||||
warnings=warnings,
|
||||
data=data))
|
||||
|
@ -21,7 +21,7 @@ artifact = {
|
||||
'metadata': dict,
|
||||
}
|
||||
|
||||
zuul_data = {
|
||||
artifact_data = {
|
||||
'zuul': {
|
||||
'log_url': str,
|
||||
'artifacts': [artifact],
|
||||
@ -30,12 +30,22 @@ zuul_data = {
|
||||
v.Extra: object,
|
||||
}
|
||||
|
||||
artifact_schema = v.Schema(zuul_data)
|
||||
warning_data = {
|
||||
'zuul': {
|
||||
'log_url': str,
|
||||
'warnings': [str],
|
||||
v.Extra: object,
|
||||
},
|
||||
v.Extra: object,
|
||||
}
|
||||
|
||||
artifact_schema = v.Schema(artifact_data)
|
||||
warning_schema = v.Schema(warning_data)
|
||||
|
||||
|
||||
def validate_artifact_schema(data):
|
||||
def validate_schema(data, schema):
|
||||
try:
|
||||
artifact_schema(data)
|
||||
schema(data)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
@ -43,7 +53,7 @@ def validate_artifact_schema(data):
|
||||
|
||||
def get_artifacts_from_result_data(result_data, logger=None):
|
||||
ret = []
|
||||
if validate_artifact_schema(result_data):
|
||||
if validate_schema(result_data, artifact_schema):
|
||||
artifacts = result_data.get('zuul', {}).get(
|
||||
'artifacts', [])
|
||||
default_url = result_data.get('zuul', {}).get(
|
||||
@ -71,3 +81,12 @@ def get_artifacts_from_result_data(result_data, logger=None):
|
||||
logger.debug("Result data did not pass artifact schema "
|
||||
"validation: %s", result_data)
|
||||
return ret
|
||||
|
||||
|
||||
def get_warnings_from_result_data(result_data, logger=None):
|
||||
if validate_schema(result_data, warning_schema):
|
||||
return result_data.get('zuul', {}).get('warnings', [])
|
||||
else:
|
||||
if logger:
|
||||
logger.debug("Result data did not pass warnings schema "
|
||||
"validation: %s", result_data)
|
@ -31,7 +31,7 @@ import jsonpath_rw
|
||||
|
||||
from zuul import change_matcher
|
||||
from zuul.lib.config import get_default
|
||||
from zuul.lib.artifacts import get_artifacts_from_result_data
|
||||
from zuul.lib.result_data import get_artifacts_from_result_data
|
||||
from zuul.lib.logutil import get_annotated_logger
|
||||
from zuul.lib.capabilities import capabilities_registry
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user