infra-specs/specs/jenkins-job-builder_2.0.0-api-changes.rst
Wayne 924990fb9e Jenkins Job Builder 2.0.0 API Changes/Rewrite
* to ensure that JJB classes are useful to third party users of the library
* to clearly define public interfaces for JJB classes and document the
  purpose/role of these classes in the API
* to eliminate unuseful class relationships in favor of rationally-tiered
  delegation of program behavior
* to reduce the importance of the YamlParser as the only source of data
  structures necessary for XML generation

Change-Id: I1f2e8c85654413f49d6a5fa61cd8d0c8b5ef5e03
2015-09-11 13:47:25 -07:00

301 lines
11 KiB
ReStructuredText

::
Copyright (c) 2015 Wayne Warren
This work is licensed under a Creative Commons Attribution 3.0
Unported License.
http://creativecommons.org/licenses/by/3.0/legalcode
===============================
JJB 2.0.0 API Changes
===============================
https://storyboard.openstack.org/#!/story/2000258
The goal of this spec is to define a series of backwards-incompatible API
changes that will necessitate a 2.x release of Jenkins Job Builder. The
high-level goals of these changes are:
* to ensure that the various classes are useful outside of JJB itself where
appropriate
* to clearly define public interfaces for various classes and document the
purpose/role of these classes in the API
* to eliminate unuseful class relationships in favor of tiered delegation of
program behavior
* to reduce the importance of the YamlParser as the only source of the data
structures necessary for XML generation
Problem Description
===================
Jenkins Job Builder has history of organic, piecemeal development wherein
patches tend to be highly focused on improving one or another aspect of the
program. For the most part this is understandable, expected even--the modular
implementation of XML generation on a per-Jenkins-plugin basis lends itself well
to this style of development. However when it comes to changes to the core
abstractions, in particular the Builder and YamlParser clases, many ad hoc
design decisions have led to tightly bound implementations that make the overall
API difficult if not impossible to reuse as a library.
It's true that yes, we can import jenkins_jobs.Builder or
jenkins_jobs.parser.YamlParser to use them programmatically outside of JJB
proper, but it is difficult to imagine anyone actually doing this given the
current constructors for these classes as well as their relationships--Builder
objects actually own references to YamlParser objects when there are no clear
benefits to this organization save for expediency during JJB development.
The YamlParser itself actually does more than "parse" YAML, it is also
responsible for generating XmlJob objects from the yaml data it handles. This
means that all the lovely XML building code that makes JJB truly special cannot
reliably be accessed using JJB as a library since it depends on implementation
details of the YamlParser (specifically the xml_jobs attribute).
In addition to the tight binding between classes that make them practically
unusable, there is an almost impossibly tight binding between code in
jenkins_jobs.execute() and jenkins_jobs.Builder, which depends on code in
jenkins_jobs.execute() to munge various command line options and configuration
data, ie to obtain user, password, plugins_list, cache settings, etc. To top it
off, jenkins_jobs.Builder ends up taking the "config" object *anyway*, which
should make us question to what purpose jenkins_jobs.execute() is even handling
various pieces of configuration logic at all--arguably, this logic ought to live
in a separate "Config" class.
The problems described up to this point indicate a need to clean up the API, but
one issue that really deserves to be driven home is the essential character of
the YamlParser to everything that JJB does. Because of the tight bindings
between classes described above, understanding the tricky behavior of the
YamlParser is absolutely necessary to construct anything more simple than a
collection of "job" objects or even a simple set of "project"s containing lists
of "job-template"s.
This is primarily problematic because if JJB is going to offer a programmatic
API, it should be possible to use it to do something more interesting than the
exact same thing that is being done in jenkins_jobs.execute(). For example, it
should be possible to define one's own set of abstraction classes in Python that
can be used to generate the data used by jenkins_jobs.Builder to POST jobs to a
Jenkins server.
JJB plugin modules all take a "parser" argument, but rarely if ever is this
parser argument used in these modules. This parser is intended to be a reference
to a YamlParser instance but the benefit of this relationship does not really
outweigh its cost in terms of tight binding between plugin module and the means
used to obtain JJB data structures actually necessary for generating XML; its
primary use case most of the time is to provide access to the ModuleRegitry
object owned by the YamlParser. It would make more sense to replace this with an
explicit reference to the ModuleRegistry itself.
A Side Note on Macros
---------------------
A problematic JJB feature when it comes to cleaning up the API is the "macro".
The current implementation leads to an unnecessarily tight binding between
the YamlParser and the ModuleRegistry because the ModuleRegistry and XML
generation code require the YamlParser's xml_jobs attribute in order to
determine whether or not a component is a macro. This tight binding is
problematic because it means we need to mock the YamlParser's xml_jobs data
structure in order to use any other means of producing JJB data structures than
the YamlParser itself. As of right now, it is fine to mock YamlParser's xml_jobs
data structure with an empty dictionary but some detail of the way it is
accessed in the ModuleRegistry or XML generation code could easily change that
would cause unforeseen problems when attempting to avoid using the YamlParser.
Proposed Changes
================
Modularize Command Line Parsing
-------------------------------
Since so much of JJB's API depends on JJB's configuration file and command line
options, it makes sense to modularize these in such a way that they can easily
be used by third party scripts/tools. This will be done using a dedicated
"Config" class.
This will allow constructors for other JJB classes such as the Builder and
YamlParser to be simplified--rather than taking both a "config" object and a
number of other parameters they will instead take a single "config" parameter.
Redefine Programmatic APIs
--------------------------
The following classes will need their APIs redesigned to make them usable by
third parties:
* YamlParser
* ModuleRegistry
* Builder
These rewrites should be accompanied by docstrings that explicitly define the
purpose of these classes so that these purposes can be considered in the context
of future patches against JJB that attempt to make foundational changes to their
behavior.
YamlParser
''''''''''
The current YamlParser exposes all of its methods as "public" methods even when
there is no obvious or implicit value in doing so. The goal for rewriting its
API will be to provide explicit "public" methods that other classes can call to
get information from it.
ModuleRegistry
''''''''''''''
The new ModuleRegistry will serve a dual role; in addition to registering JJB
modules for use during XML generation it will also provide an API for modules to
use to access information other than the given JJB data. For example, it should
provide access to Jenkins plugin version information to allow modules to
implement plugin version dependent XML.
Add XmlBuilder class
--------------------
The XmlBuilder class should define a method that takes as input a dictionary
where:
* keys are job names, and
* values are all the key/value pairs necessary to produce an XmlJob for the
named job
This method should produce as output a list of XmlJobs that can then be passed
to a Builder method either to produce test output or to update jobs on the
configured Jenkins instance.
Alternate names for this class might be JenkinsXmlBuilder,
JenkinsJobConfigBuilder.
Replace references to YamlParser with references to ModuleRegistry in modules
-----------------------------------------------------------------------------
This addresses the need to decouple plugin modules from the YamlParser while
also allowing us to access components of the ModuleRegistry when necessary (ie,
accessing Jenkins plugins_info to generate different XML for different versions
of plugins).
Rewrite JJB Macro Implementation
--------------------------------
The goal of this rewrite should be primarily to remove the need for referencing
the YamlParser's raw yaml data structure while generating XML. This way the
proposed XmlBuilder class does not have to be concerned with any particular
details of the YamlParser--reducing the risk that implementation of one of these
classes has unforeseen side effects on the other's behavior.
One method that has been proposed is to implement a secondary module registry
that specifically handles macros.
Implementation
==============
Assignee(s)
-----------
Primary assignee:
Wayne Warren, wayne+launchpad@sdf.org
Gerrit Topic
------------
Use Gerrit topic "jjb-2.0.0-api" for all patches related to this spec.
.. code-block:: bash
git-review -t jjb-2.0.0-api
Work Items
----------
Development Process
'''''''''''''''''''
All work for this spec will target a feature branch of JJB called "2.0.x" which
will contain all backwards-incompatible changes targeted for this release,
including any work covered by other specs targeting the same release.
Once the work is complete, we will release Jenkins Job Builder 2.0.0.
Code Changes
''''''''''''
* Break command line parsing function into dynamically-loaded, modular
components for consumption outside of the primary JJB command line tool.
* Create function that specifically handles pre-Builder processing
of command line options and configuration settings; this will be necessary to
make the Builder class usable as an API to be consumed externally.
(Alternatively, consider ways to clean up the Builder constructor in such a
way that doesn't need to take the full "config" object)
* Separate XML generation code from YamlParser, move into new class "XmlBuilder"
that takes a ModuleRegistry object in its constructor and contains various
methods that take one or more JJB job dictionaries and output a corresponding
number of XmlJob objects.
* Separate YamlParser from YamlInterpreter:
* YamlParser will be responsible for loading a given list of YAML files
* YamlInterpreter will be responsible for applying JJB's DSL semantics to the
loaded lists of dictionaries.
* Refactor Builder methods and their calling contexts to simplify the interface;
the "update_jobs" method, for instance, should simply take a list of XmlJobs
to update.
Repositories
------------
None
Servers
-------
None
DNS Entries
-----------
None
Documentation
-------------
Documentation changes may be necessary as this work is primarily intended to
make existing JJB API usable outside of the jenkins_jobs.cmd module.
Security
--------
None
Testing
-------
Backwards Compatibility
'''''''''''''''''''''''
No existing test scenarios will be changed to support this spec; however, there
will need to be changes to test fixture base classes to support the new API
changes.
We will also monitor openstack-infra's JJB output to ensure that it does not
change because of this spec.
New Tests
'''''''''
Unit tests will explicitly validate the behavior of the following:
* the Builder class' "public" methods
* the YamlParser class' "public" methods
* the XmlBuilder class' "public" methods
* the Config class' "public" methods
Dependencies
============
Python Support
--------------
We intend to continue supporting the same Python versions supported by JJB 1.x:
* Python 2.7
* Python 3.4