Compare jobs files with their hash and update templates
Change-Id: I6e739248892451332a3fb6c32be09104535e49e5
diff --git a/_grains/jenkins.py b/_grains/jenkins.py
new file mode 100644
index 0000000..b8f85d7
--- /dev/null
+++ b/_grains/jenkins.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+import json
+
+def main():
+ output = { "jenkins_plugins" : {} }
+
+ list_plugin_groovy = """\
+ pluginList = []
+ Jenkins.instance.pluginManager.plugins.each{ pluginList << ("'${it.shortName}@${it.version}'")}
+ print pluginList
+ """
+ call_result = __salt__['jenkins_common.call_groovy_script'](list_plugin_groovy, [])
+
+ plugins = json.loads(call_result)
+
+ for plugin in plugins:
+ plugin_fields = plugin.split('@')
+ output["jenkins_plugins"][plugin_fields[0]] = {"version": plugin_fields[1]}
+
+ if output:
+ return output
+ else:
+ return None
diff --git a/_states/jenkins_job.py b/_states/jenkins_job.py
index 74be81c..20ff131 100644
--- a/_states/jenkins_job.py
+++ b/_states/jenkins_job.py
@@ -11,6 +11,14 @@
# Import XML parser
import xml.etree.ElementTree as ET
+import hashlib
+
+# Jenkins
+try:
+ import jenkins
+ HAS_JENKINS = True
+except ImportError:
+ HAS_JENKINS = False
log = logging.getLogger(__name__)
@@ -19,7 +27,7 @@
'''
Only load if jenkins_common module exist.
'''
- if 'jenkins_common.call_groovy_script' not in __salt__:
+ if HAS_JENKINS and 'jenkins_common.call_groovy_script' not in __salt__:
return (
False,
'The jenkins_job state module cannot be loaded: '
@@ -28,17 +36,7 @@
def _elements_equal(e1, e2):
- if e1.tag != e2.tag:
- return False
- if e1.text != e2.text:
- return False
- if e1.tail != e2.tail:
- return False
- if e1.attrib != e2.attrib:
- return False
- if len(e1) != len(e2):
- return False
- return all(_elements_equal(c1, c2) for c1, c2 in zip(e1, e2))
+ return hashlib.md5(e1).hexdigest() == hashlib.md5(e2).hexdigest()
def present(name,
@@ -63,22 +61,29 @@
ret['changes'][name] = status
ret['comment'] = 'Job %s %s' % (name, status.lower())
else:
- _job_exists = __salt__['jenkins.job_exists'](name)
- if _job_exists:
+ _current_job_config = ''
+ _job_exists = True
+ try:
_current_job_config = __salt__['jenkins.get_job_config'](name)
+ except jenkins.NotFoundException:
+ _job_exists = False
+
+ if _job_exists:
buf = six.moves.StringIO(_current_job_config)
- oldXML = ET.fromstring(buf.read())
+ oldXMLstring = buf.read()
cached_source_path = __salt__['cp.cache_file'](config, __env__)
with salt.utils.fopen(cached_source_path) as _fp:
- newXML = ET.fromstring(_fp.read())
- if not _elements_equal(oldXML, newXML):
+ newXMLstring = _fp.read()
+ if not _elements_equal(oldXMLstring.strip(), newXMLstring.strip()):
+ oldXML = ET.fromstring(oldXMLstring)
+ newXML = ET.fromstring(newXMLstring)
diff = difflib.unified_diff(
ET.tostringlist(oldXML, encoding='utf8', method='xml'),
ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='')
__salt__['jenkins.update_job'](name, config, __env__)
ret['changes'][name] = ''.join(diff)
- ret['comment'].append('Job {0} updated.'.format(name))
+ ret['comment'] = 'Job {0} updated.'.format(name)
else:
cached_source_path = __salt__['cp.cache_file'](config, __env__)
diff --git a/jenkins/files/jobs/_common.xml b/jenkins/files/jobs/_common.xml
index b93135f..5922fc1 100644
--- a/jenkins/files/jobs/_common.xml
+++ b/jenkins/files/jobs/_common.xml
@@ -12,9 +12,13 @@
<org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty/>
{%- endif %}
{%- if job.get('scm', {}).github is defined %}
- <com.coravy.hudson.plugins.github.GithubProjectProperty plugin="github@1.21.1">
+ <com.coravy.hudson.plugins.github.GithubProjectProperty plugin="github@{% salt['grains.get']('jenkins_plugins:github:version') %}">
<projectUrl>{{ job.scm.github.url }}</projectUrl>
- <displayName>{{ job.scm.github.name|default("") }}</displayName>
+ {%- if job.scm.github.name is defined %}
+ <displayName>{{ job.scm.github.name }}</displayName>
+ {%- else %}
+ <displayName/>
+ {%- endif %}
</com.coravy.hudson.plugins.github.GithubProjectProperty>
{%- endif %}
<org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
@@ -23,7 +27,7 @@
{%- if trigger.enabled|default(True) %}
{%- if type == 'reverse' %}
<jenkins.triggers.ReverseBuildTrigger>
- <spec></spec>
+ <spec/>
<upstreamProjects>{{ trigger.projects|join(',') if trigger.projects is defined else trigger.project }}</upstreamProjects>
<threshold>
<name>{{ trigger.state|default('SUCCESS') }}</name>
@@ -33,8 +37,8 @@
</threshold>
</jenkins.triggers.ReverseBuildTrigger>
{%- elif type == 'github' %}
- <com.cloudbees.jenkins.GitHubPushTrigger plugin="github@1.21.1">
- <spec></spec>
+ <com.cloudbees.jenkins.GitHubPushTrigger plugin="github@{% salt['grains.get']('jenkins_plugins:github:version') %}">
+ <spec/>
</com.cloudbees.jenkins.GitHubPushTrigger>
{%- elif type == 'pollscm' %}
<hudson.triggers.SCMTrigger>
@@ -42,7 +46,7 @@
<ignorePostCommitHooks>false</ignorePostCommitHooks>
</hudson.triggers.SCMTrigger>
{%- elif type == 'gerrit' %}
- <com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritTrigger plugin="gerrit-trigger@2.23.0">
+ <com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritTrigger plugin="gerrit-trigger@{% salt['grains.get']('jenkins_plugins:gerrit-trigger:version') %}">
<spec></spec>
<gerritProjects>
{%- for pname, project in trigger.project.iteritems() %}
@@ -76,17 +80,52 @@
<silentStartMode>{{ trigger.get('silent_start_mode', False)|lower }}</silentStartMode>
<escapeQuotes>true</escapeQuotes>
<nameAndEmailParameterMode>PLAIN</nameAndEmailParameterMode>
- <dependencyJobsNames></dependencyJobsNames>
+ <dependencyJobsNames/>
<commitMessageParameterMode>BASE64</commitMessageParameterMode>
<changeSubjectParameterMode>PLAIN</changeSubjectParameterMode>
<commentTextParameterMode>BASE64</commentTextParameterMode>
- <buildStartMessage>{{ trigger.get('message', {}).build_start|default("") }}</buildStartMessage>
- <buildFailureMessage>{{ trigger.get('message', {}).build_failure|default("") }}</buildFailureMessage>
- <buildSuccessfulMessage>{{ trigger.get('message', {}).build_successful|default("") }}</buildSuccessfulMessage>
- <buildUnstableMessage>{{ trigger.get('message', {}).build_unstable|default("") }}</buildUnstableMessage>
- <buildNotBuiltMessage>{{ trigger.get('message', {}).build_not_built|default("") }}</buildNotBuiltMessage>
- <buildUnsuccessfulFilepath>{{ trigger.get('message', {}).build_unsuccessful_filepath|default("") }}</buildUnsuccessfulFilepath>
- <customUrl>{{ trigger.custom_url|default('') }}</customUrl>
+ {%- set message = trigger.get('message', {}).build_start|default("") %}
+ {%- if message != "" %}
+ <buildStartMessage>{{ message }}</buildStartMessage>
+ {%- else %}
+ <buildStartMessage/>
+ {%- endif %}
+ {%- set message = trigger.get('message', {}).build_failure|default("") %}
+ {%- if message != "" %}
+ <buildFailureMessage>{{ message }}</buildFailureMessage>
+ {%- else %}
+ <buildFailureMessage/>
+ {%- endif %}
+ {%- set message = trigger.get('message', {}).build_successful|default("") %}
+ {%- if message != "" %}
+ <buildSuccessfulMessage>{{ message }}</buildSuccessfulMessage>
+ {%- else %}
+ <buildSuccessfulMessage/>
+ {%- endif %}
+ {%- set message = trigger.get('message', {}).build_unstable|default("") %}
+ {%- if message != "" %}
+ <buildUnstableMessage>{{ message }}</buildUnstableMessage>
+ {%- else %}
+ <buildUnstableMessage/>
+ {%- endif %}
+ {%- set message = trigger.get('message', {}).build_not_built|default("") %}
+ {%- if message != "" %}
+ <buildNotBuiltMessage>{{ message }}</buildNotBuiltMessage>
+ {%- else %}
+ <buildNotBuiltMessage/>
+ {%- endif %}
+ {%- set message = trigger.get('message', {}).build_unsuccessful_filepath|default("") %}
+ {%- if message != "" %}
+ <buildUnsuccessfulFilepath>{{ message }}</buildUnsuccessfulFilepath>
+ {%- else %}
+ <buildUnsuccessfulFilepath/>
+ {%- endif %}
+ {%- set message = trigger.get('message', {}).custom_url|default("") %}
+ {%- if message != "" %}
+ <customUrl>{{ message }}</customUrl>
+ {%- else %}
+ <customUrl/>
+ {%- endif %}
<serverName>{{ trigger.server|default('__ANY__') }}</serverName>
<triggerOnEvents>
{%- for ename, actions in trigger.event.iteritems() %}
diff --git a/jenkins/files/jobs/_parameters.xml b/jenkins/files/jobs/_parameters.xml
index 1a0100d..b42b19b 100755
--- a/jenkins/files/jobs/_parameters.xml
+++ b/jenkins/files/jobs/_parameters.xml
@@ -4,7 +4,11 @@
{%- for param_name, param in job.param.iteritems() %}
<hudson.model.{{ param.get('type', 'string')|capitalize }}ParameterDefinition>
<name>{{ param_name }}</name>
- <description>{{ param.get('description', '')|e }}</description>
+ {%- if param.description is defined %}
+ <description>{{ description }}</description>
+ {%- else %}
+ <description/>
+ {%- endif %}
{%- if param.get('type', 'string')|lower == "choice" %}
<choices class="java.util.Arrays$ArrayList">
<a class="string-array">
@@ -14,7 +18,11 @@
</a>
</choices>
{%- endif %}
- <defaultValue>{{ param.get('default', '')|e }}</defaultValue>
+ {%- if param.default is defined %}
+ <defaultValue>{{ param.get('default') }}</defaultValue>
+ {%- else %}
+ <defaultValue/>
+ {%- endif %}
</hudson.model.{{ param.get('type', 'string')|capitalize }}ParameterDefinition>
{%- endfor %}
</parameterDefinitions>
diff --git a/jenkins/files/jobs/workflow-scm.xml b/jenkins/files/jobs/workflow-scm.xml
index 57c3756..dba25d1 100644
--- a/jenkins/files/jobs/workflow-scm.xml
+++ b/jenkins/files/jobs/workflow-scm.xml
@@ -2,13 +2,11 @@
{%- if job is not defined -%}
{%- set job = salt['pillar.get']('jenkins:client:job:'+job_name) -%}
{%- endif -%}
-
-<?xml version='1.0' encoding='UTF-8'?>
-<flow-definition plugin="workflow-job@2.6">
+<?xml version="1.0" encoding='UTF-8'?><flow-definition plugin="workflow-job@{% salt['grains.get']('jenkins_plugins:workflow-job:version') %}">
{%- include "jenkins/files/jobs/_common.xml" %}
- <definition class="org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition" plugin="workflow-cps@2.13">
+ <definition class="org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition" plugin=workflow-cps@"{% salt['grains.get']('jenkins_plugins:workflow-cps:version') %}">
{%- if job.scm.get('type', 'git') == 'git' %}
- <scm class="hudson.plugins.git.GitSCM" plugin="git@2.5.3">
+ <scm class="hudson.plugins.git.GitSCM" plugin="git@{% salt['grains.get']('jenkins_plugins:git:version') %}">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
diff --git a/jenkins/files/jobs/workflow.xml b/jenkins/files/jobs/workflow.xml
index 0469729..7400d39 100644
--- a/jenkins/files/jobs/workflow.xml
+++ b/jenkins/files/jobs/workflow.xml
@@ -16,7 +16,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<flow-definition plugin="workflow-job@2.5">
{%- include "jenkins/files/jobs/_common.xml" %}
- <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@2.12">
+ <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@{% salt['grains.get']('jenkins_plugins:workflow-cps:version') %}">
<script>// libraries
{%- if job.libs is defined %}
{%- for lib in job.libs %}