Merge "Support emitting warnings via zuul_return"
This commit is contained in:
commit
c51bfeb8cf
|
@ -884,6 +884,25 @@ zuul.child_jobs is empty, all jobs will be marked as SKIPPED. Invalid dependent
|
||||||
are stripped and ignored, if only invalid jobs are listed it is the same as
|
are stripped and ignored, if only invalid jobs are listed it is the same as
|
||||||
providing an empty list to zuul.child_jobs.
|
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
|
Leaving file comments
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A job now can leave warning messages on the change using zuul_return.
|
||||||
|
See :ref:`return_values` for examples.
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
- job:
|
||||||
|
name: emit-warnings
|
||||||
|
run: playbooks/warnings.yaml
|
||||||
|
|
||||||
|
- project:
|
||||||
|
check:
|
||||||
|
jobs:
|
||||||
|
- emit-warnings
|
|
@ -0,0 +1,8 @@
|
||||||
|
- tenant:
|
||||||
|
name: tenant-one
|
||||||
|
source:
|
||||||
|
gerrit:
|
||||||
|
config-projects:
|
||||||
|
- common-config
|
||||||
|
untrusted-projects:
|
||||||
|
- org/project
|
|
@ -6818,3 +6818,24 @@ class TestDefaultAnsibleVersion(AnsibleZuulTestCase):
|
||||||
dict(name='ansible-28', result='SUCCESS', changes='1,1'),
|
dict(name='ansible-28', result='SUCCESS', changes='1,1'),
|
||||||
dict(name='ansible-29', result='SUCCESS', changes='1,1'),
|
dict(name='ansible-29', result='SUCCESS', changes='1,1'),
|
||||||
], ordered=False)
|
], 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
|
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):
|
def merge_data(dict_a, dict_b):
|
||||||
"""
|
"""
|
||||||
Merge dict_a into dict_b, handling any special cases for zuul variables
|
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)
|
file_comments = merge_file_comments(dict_a, dict_b)
|
||||||
|
warnings = merge_zuul_list(dict_a, dict_b, 'warnings')
|
||||||
merge_dict(dict_a, dict_b)
|
merge_dict(dict_a, dict_b)
|
||||||
if artifacts:
|
if artifacts:
|
||||||
dict_b.setdefault('zuul', {})['artifacts'] = artifacts
|
dict_b.setdefault('zuul', {})['artifacts'] = artifacts
|
||||||
if file_comments:
|
if file_comments:
|
||||||
dict_b.setdefault("zuul", {})["file_comments"] = file_comments
|
dict_b.setdefault("zuul", {})["file_comments"] = file_comments
|
||||||
|
if warnings:
|
||||||
|
dict_b.setdefault('zuul', {})['warnings'] = warnings
|
||||||
return dict_b
|
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):
|
def merge_file_comments(dict_a, dict_b):
|
||||||
"""Merge file_comments from both dictionary.
|
"""Merge file_comments from both dictionary.
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,8 @@ import time
|
||||||
import voluptuous as v
|
import voluptuous as v
|
||||||
|
|
||||||
from zuul.lib.logutil import get_annotated_logger
|
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.reporter import BaseReporter
|
||||||
from zuul.lib.artifacts import get_artifacts_from_result_data
|
|
||||||
|
|
||||||
|
|
||||||
class SQLReporter(BaseReporter):
|
class SQLReporter(BaseReporter):
|
||||||
|
|
|
@ -37,6 +37,7 @@ from urllib.parse import urlsplit
|
||||||
|
|
||||||
from zuul.lib.ansible import AnsibleManager
|
from zuul.lib.ansible import AnsibleManager
|
||||||
from zuul.lib.gearworker import ZuulGearWorker
|
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.yamlutil import yaml
|
||||||
from zuul.lib.config import get_default
|
from zuul.lib.config import get_default
|
||||||
from zuul.lib.logutil import get_annotated_logger
|
from zuul.lib.logutil import get_annotated_logger
|
||||||
|
@ -1178,6 +1179,7 @@ class AnsibleJob(object):
|
||||||
data = self.getResultData()
|
data = self.getResultData()
|
||||||
warnings = []
|
warnings = []
|
||||||
self.mapLines(merger, args, data, item_commit, 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,
|
result_data = json.dumps(dict(result=result,
|
||||||
warnings=warnings,
|
warnings=warnings,
|
||||||
data=data))
|
data=data))
|
||||||
|
|
|
@ -21,7 +21,7 @@ artifact = {
|
||||||
'metadata': dict,
|
'metadata': dict,
|
||||||
}
|
}
|
||||||
|
|
||||||
zuul_data = {
|
artifact_data = {
|
||||||
'zuul': {
|
'zuul': {
|
||||||
'log_url': str,
|
'log_url': str,
|
||||||
'artifacts': [artifact],
|
'artifacts': [artifact],
|
||||||
|
@ -30,12 +30,22 @@ zuul_data = {
|
||||||
v.Extra: object,
|
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:
|
try:
|
||||||
artifact_schema(data)
|
schema(data)
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -43,7 +53,7 @@ def validate_artifact_schema(data):
|
||||||
|
|
||||||
def get_artifacts_from_result_data(result_data, logger=None):
|
def get_artifacts_from_result_data(result_data, logger=None):
|
||||||
ret = []
|
ret = []
|
||||||
if validate_artifact_schema(result_data):
|
if validate_schema(result_data, artifact_schema):
|
||||||
artifacts = result_data.get('zuul', {}).get(
|
artifacts = result_data.get('zuul', {}).get(
|
||||||
'artifacts', [])
|
'artifacts', [])
|
||||||
default_url = result_data.get('zuul', {}).get(
|
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 "
|
logger.debug("Result data did not pass artifact schema "
|
||||||
"validation: %s", result_data)
|
"validation: %s", result_data)
|
||||||
return ret
|
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)
|
|
@ -33,7 +33,7 @@ import jsonpath_rw
|
||||||
|
|
||||||
from zuul import change_matcher
|
from zuul import change_matcher
|
||||||
from zuul.lib.config import get_default
|
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.logutil import get_annotated_logger
|
||||||
from zuul.lib.capabilities import capabilities_registry
|
from zuul.lib.capabilities import capabilities_registry
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue