Factor XmlJobGenerator out of YamlParser.

Separate XML generation from Yaml parsing/interpreting. The goal here
is to allow different sources to provide data for XML generation,
including external API users writing job definitions in pure Python or
JJB developers who would like to work on alternative Yaml parsing code
since the current YamlParser has very likely reached the limits of
what can be reasonably done with one giant expandYaml procedure.

Change-Id: I9da848acac7e944c0e07286b7399b2e1956a58a5
This commit is contained in:
Wayne Warren 2016-01-03 12:19:44 -08:00 committed by Thanh Ha
parent ae1fb60f16
commit aaae83c623
No known key found for this signature in database
GPG Key ID: B0CB27E00DA095AA
5 changed files with 52 additions and 34 deletions

View File

@ -20,6 +20,7 @@ import time
from jenkins_jobs.builder import Builder
from jenkins_jobs.parser import YamlParser
from jenkins_jobs.registry import ModuleRegistry
from jenkins_jobs.xml_config import XmlJobGenerator
from jenkins_jobs.errors import JenkinsJobsException
import jenkins_jobs.cli.subcommand.base as base
@ -74,19 +75,21 @@ class UpdateSubCommand(base.BaseSubCommand):
# Generate XML
parser = YamlParser(jjb_config)
registry = ModuleRegistry(jjb_config, builder.plugins_list)
xml_generator = XmlJobGenerator(registry)
parser.load_files(options.path)
registry.set_parser_data(parser.data)
parser.expandYaml(registry, options.names)
parser.generateXML(registry)
job_data_list = parser.expandYaml(registry, options.names)
xml_jobs = xml_generator.generateXML(job_data_list)
jobs = parser.jobs
step = time.time()
logging.debug('%d XML files generated in %ss',
len(jobs), str(step - orig))
return builder, parser.xml_jobs
return builder, xml_jobs
def execute(self, options, jjb_config):

View File

@ -21,14 +21,12 @@ import io
import itertools
import logging
import os
import pkg_resources
from jenkins_jobs.constants import MAGIC_MANAGE_STRING
from jenkins_jobs.errors import JenkinsJobsException
from jenkins_jobs.formatter import deep_format
import jenkins_jobs.local_yaml as local_yaml
from jenkins_jobs import utils
from jenkins_jobs.xml_config import XmlJob
logger = logging.getLogger(__name__)
@ -73,7 +71,6 @@ class YamlParser(object):
def __init__(self, jjb_config=None):
self.data = {}
self.jobs = []
self.xml_jobs = []
self.jjb_config = jjb_config
self.keep_desc = jjb_config.yamlparser['keep_descriptions']
@ -309,6 +306,7 @@ class YamlParser(object):
"specified".format(job['name']))
self.jobs.remove(job)
seen.add(job['name'])
return self.jobs
def _expandYamlForTemplateJob(self, project, template, jobs_glob=None):
dimensions = []
@ -367,24 +365,3 @@ class YamlParser(object):
# The \n\n is not hard coded, because they get stripped if the
# project does not otherwise have a description.
return "\n\n" + MAGIC_MANAGE_STRING
def generateXML(self, registry):
for job in self.jobs:
self.xml_jobs.append(self.getXMLForJob(job, registry))
def getXMLForJob(self, data, registry):
kind = data.get('project-type', 'freestyle')
for ep in pkg_resources.iter_entry_points(
group='jenkins_jobs.projects', name=kind):
Mod = ep.load()
mod = Mod(registry)
xml = mod.root_xml(data)
self.gen_xml(xml, data, registry)
job = XmlJob(xml, data['name'])
return job
def gen_xml(self, xml, data, registry):
for module in registry.modules:
if hasattr(module, 'gen_xml'):
module.gen_xml(xml, data)

View File

@ -16,6 +16,7 @@
# Manage Jenkins XML config file output.
import hashlib
import pkg_resources
from xml.dom import minidom
import xml.etree.ElementTree as XML
@ -53,3 +54,35 @@ class XmlJob(object):
def output(self):
out = minidom.parseString(XML.tostring(self.xml, encoding='UTF-8'))
return out.toprettyxml(indent=' ', encoding='utf-8')
class XmlJobGenerator(object):
""" This class is responsible for generating Jenkins Configuration XML from
a compatible intermediate representation of Jenkins Jobs.
"""
def __init__(self, registry):
self.registry = registry
def generateXML(self, jobdict_list):
xml_jobs = []
for job in jobdict_list:
xml_jobs.append(self.__getXMLForJob(job))
return xml_jobs
def __getXMLForJob(self, data):
kind = data.get('project-type', 'freestyle')
for ep in pkg_resources.iter_entry_points(
group='jenkins_jobs.projects', name=kind):
Mod = ep.load()
mod = Mod(self.registry)
xml = mod.root_xml(data)
self.__gen_xml(xml, data)
job = XmlJob(xml, data['name'])
return job
def __gen_xml(self, xml, data):
for module in self.registry.modules:
if hasattr(module, 'gen_xml'):
module.gen_xml(xml, data)

View File

@ -42,6 +42,7 @@ from jenkins_jobs.modules import project_multijob
from jenkins_jobs.parser import YamlParser
from jenkins_jobs.registry import ModuleRegistry
from jenkins_jobs.xml_config import XmlJob
from jenkins_jobs.xml_config import XmlJobGenerator
# This dance deals with the fact that we want unittest.mock if
# we're on Python 3.4 and later, and non-stdlib mock otherwise.
@ -202,15 +203,17 @@ class SingleJobTestCase(BaseTestCase):
registry = ModuleRegistry(config)
registry.set_parser_data(parser.data)
# Generate the XML tree
parser.expandYaml(registry)
parser.generateXML(registry)
job_data_list = parser.expandYaml(registry)
parser.xml_jobs.sort(key=operator.attrgetter('name'))
# Generate the XML tree
xml_generator = XmlJobGenerator(registry)
xml_jobs = xml_generator.generateXML(job_data_list)
xml_jobs.sort(key=operator.attrgetter('name'))
# Prettify generated XML
pretty_xml = u"\n".join(job.output().decode('utf-8')
for job in parser.xml_jobs)
for job in xml_jobs)
self.assertThat(
pretty_xml,

View File

@ -130,7 +130,8 @@ class TestTests(CmdTestsBase):
e = self.assertRaises(UnicodeError, jenkins_jobs.execute)
self.assertIn("'ascii' codec can't encode character", str(e))
@mock.patch('jenkins_jobs.cli.subcommand.update.YamlParser.generateXML')
@mock.patch(
'jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML')
@mock.patch('jenkins_jobs.cli.subcommand.update.ModuleRegistry')
def test_plugins_info_stub_option(self, registry_mock, generateXML_mock):
"""
@ -154,7 +155,8 @@ class TestTests(CmdTestsBase):
registry_mock.assert_called_with(mock.ANY,
plugins_info_list)
@mock.patch('jenkins_jobs.cli.subcommand.update.YamlParser.generateXML')
@mock.patch(
'jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML')
@mock.patch('jenkins_jobs.cli.subcommand.update.ModuleRegistry')
def test_bogus_plugins_info_stub_option(self, registry_mock,
generateXML_mock):