Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix jenkins agent check when build does not yet have results #1060

Merged
merged 3 commits into from
Aug 29, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions checks.d/jenkins.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ def _get_build_results(self, instance_key, job_dir):
except Exception:
continue

# If the metadata does not include the results, then
# the build is still running so do not process it yet.
build_result = build_metadata.get('result', None)
if build_result is None:
break

output = {
'job_name': job_name,
'timestamp': timestamp,
Expand Down
158 changes: 158 additions & 0 deletions tests/test_jenkins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import unittest
import os
from collections import defaultdict
import datetime
import tempfile
import shutil
import logging
import xml.etree.ElementTree as ET

from tests.common import get_check

logger = logging.getLogger(__file__)

DATETIME_FORMAT = '%Y-%m-%d_%H-%M-%S'
LOG_DATA = 'Finished: SUCCESS'

SUCCESSFUL_BUILD = {'number': '99', 'result': 'SUCCESS', 'duration': '60'}
NO_RESULTS_YET = {'number': '99', 'duration': '60'}
UNSUCCESSFUL_BUILD = {'number': '99', 'result': 'ABORTED', 'duration': '60'}

CONFIG = """
init_config:

instances:
- name: default
jenkins_home: <JENKINS_HOME>
"""

def dict_to_xml(metadata_dict):
""" Convert a dict to xml for use in a build.xml file """
build = ET.Element('build')
for k, v in metadata_dict.iteritems():
node = ET.SubElement(build, k)
node.text = v

return ET.tostring(build)

def write_file(file_name, log_data):
with open(file_name, 'w') as log_file:
log_file.write(log_data)

class TestJenkins(unittest.TestCase):

def setUp(self):
self.tmp_dir = tempfile.mkdtemp()
self.config_yaml = CONFIG.replace('<JENKINS_HOME>', self.tmp_dir)
self._create_old_build()

def tearDown(self):
# Clean up the temp directory
shutil.rmtree(self.tmp_dir)

def _create_old_build(self):
# As coded, the jenkins dd agent needs more than one result
# in order to get the last valid build.
# Create one for yesterday.
metadata = dict_to_xml(SUCCESSFUL_BUILD)
yesterday = datetime.date.today() - datetime.timedelta(days=1)
self._populate_build_dir(metadata, yesterday)

def _create_check(self):
# Create the jenkins check
self.check, instances = get_check('jenkins', self.config_yaml)
self.instance = instances[0]

def _populate_build_dir(self, metadata, time=None):
# The jenkins dd agent requires the build metadata file and a log file of results
time = time or datetime.datetime.now()
datestring = time.strftime(DATETIME_FORMAT)
build_dir = os.path.join(self.tmp_dir, 'jobs', 'foo', 'builds', datestring)
os.makedirs(build_dir)

log_file = os.path.join(build_dir, 'log')
log_data = LOG_DATA
write_file(log_file, log_data)

metadata_file = os.path.join(build_dir, 'build.xml')
build_metadata = metadata
write_file(metadata_file, build_metadata)

def testParseBuildLog(self):
"""
Test doing a jenkins check. This will parse the logs but since there was no
previous high watermark no event will be created.
"""
metadata = dict_to_xml(SUCCESSFUL_BUILD)

self._populate_build_dir(metadata)
self._create_check()
self.check.check(self.instance)

# The check method does not return anything, so this testcase passes
# if the high_watermark was set and no exceptions were raised.
self.assertTrue(self.check.high_watermarks[self.instance['name']]['foo'] > 0)

def testCheckSuccessfulEvent(self):
"""
Test that a successful build will create the correct metrics.
"""
metadata = dict_to_xml(SUCCESSFUL_BUILD)

self._populate_build_dir(metadata)
self._create_check()

# Set the high_water mark so that the next check will create events
self.check.high_watermarks['default'] = defaultdict(lambda: 0)

self.check.check(self.instance)

results = self.check.get_metrics()
metrics = [r[0] for r in results]

assert len(metrics) == 2
assert 'jenkins.job.success' in metrics
assert 'jenkins.job.duration' in metrics

def testCheckUnsuccessfulEvent(self):
"""
Test that an unsuccessful build will create the correct metrics.
"""
metadata = dict_to_xml(UNSUCCESSFUL_BUILD)

self._populate_build_dir(metadata)
self._create_check()

# Set the high_water mark so that the next check will create events
self.check.high_watermarks['default'] = defaultdict(lambda: 0)

self.check.check(self.instance)

results = self.check.get_metrics()
metrics = [r[0] for r in results]

assert len(metrics) == 2
assert 'jenkins.job.failure' in metrics
assert 'jenkins.job.duration' in metrics

def testCheckWithRunningBuild(self):
"""
Test under the conditions of a jenkins build still running.
The build.xml file will exist but it will not yet have a result.
"""
metadata = dict_to_xml(NO_RESULTS_YET)

self._populate_build_dir(metadata)
self._create_check()

# Set the high_water mark so that the next check will create events
self.check.high_watermarks['default'] = defaultdict(lambda: 0)

self.check.check(self.instance)

# The check method does not return anything, so this testcase passes
# if the high_watermark was NOT updated and no exceptions were raised.
assert self.check.high_watermarks[self.instance['name']]['foo'] == 0

if __name__ == '__main__':
unittest.main()