Add state to manage Jira sites
Closes-PROD: https://mirantis.jira.com/browse/PROD-19349
Change-Id: Ic5a2a397b3a6e7d4159c9393317ce984dda320d0
diff --git a/README.rst b/README.rst
index d641952..d721873 100644
--- a/README.rst
+++ b/README.rst
@@ -766,6 +766,39 @@
'My Category To Remove:
enabled: false
+Jira sites management from client (requires
+`JIRA <https://plugins.jenkins.io/jira>`_ plugin)
+
+.. code-block:: yaml
+
+ # Remove all sites
+ jenkins:
+ client:
+ jira:
+ enabled: False
+
+.. code-block:: yaml
+
+ jenkins:
+ client:
+ jira:
+ sites:
+ 'http://my.jira.site/':
+ link_url: 'http://alternative.link/'
+ http_auth: false
+ use_wiki_notation: false
+ record_scm: false
+ disable_changelog: false
+ issue_pattern: ''
+ any_build_result: false
+ user: 'username'
+ password: 'passwd'
+ conn_timeout: 10
+ visible_for_group: ''
+ visible_for_project: ''
+ timestamps: false
+ timestamp_format: ''
+
Usage
=====
diff --git a/_states/jenkins_jira.py b/_states/jenkins_jira.py
new file mode 100644
index 0000000..739b248
--- /dev/null
+++ b/_states/jenkins_jira.py
@@ -0,0 +1,71 @@
+import json
+import logging
+
+logger = logging.getLogger(__name__)
+
+def __virtual__():
+ '''
+ Only load if jenkins_common module exist.
+ '''
+ if 'jenkins_common.call_groovy_script' not in __salt__:
+ return (
+ False,
+ 'The jenkins_jira state module cannot be loaded: '
+ 'jenkins_common not found')
+ return True
+
+def present (name, sites, **kwargs):
+ """
+ Jenkins Jira instance state method
+
+ :param name: ID name
+ :param sites:Jira sites dict
+ :
+ :sites[name] params:
+ :param link_url: root URL of JIRA installation for "normal" access
+ :param http_auth: connect to JIRA using HTTP Basic Authentication
+ :param use_wiki_notation: enable if JIRA supports Wiki notation
+ :param record_scm: record scm changes in JIRAA
+ :param disable_changelog: do not create JIRA hyperlinks in the changeset
+ :param issue_pattern: custom pattern to search for JIRA issue ids
+ :param any_build_result: update issues on any build result
+ :param user: JIRA user name
+ :param password: JIRA user password
+ :param conn_timeout: connection timeout for JIRA REST API calls
+ :param visible_for_group: allow to read comments for JIRA group
+ :param visible_for_project: allow to read comments for JIRA project
+ :param timestamps: enable SCM change date and time entries
+ :param timestamp_format: timestamp format
+ :
+ :returns: salt-specified state dict
+ """
+
+ template = __salt__['jenkins_common.load_template'](
+ 'salt://jenkins/files/groovy/jira.template',
+ __env__)
+ return __salt__['jenkins_common.api_call'](name, template,
+ ["CREATED", "EXISTS"],
+ {
+ 'sites': json.dumps(sites),
+ 'absent': False
+ },
+ 'JIRA server')
+
+def absent(name):
+ """
+ Jenkins Jira instance absence state method
+
+ :param name: ID name
+ :returns: salt-specified state dict
+ """
+ template = __salt__['jenkins_common.load_template'](
+ 'salt://jenkins/files/groovy/jira.template',
+ __env__)
+ return __salt__['jenkins_common.api_call'](name, template,
+ ["REMOVED", "NOT PRESENT"],
+ {
+ 'sites': '{}',
+ 'absent': True
+ },
+ 'JIRA server')
+
diff --git a/jenkins/client/init.sls b/jenkins/client/init.sls
index 7b0726f..46fb104 100644
--- a/jenkins/client/init.sls
+++ b/jenkins/client/init.sls
@@ -47,6 +47,9 @@
{%- if client.throttle_category is defined %}
- jenkins.client.throttle_category
{%- endif %}
+{%- if client.jira is defined %}
+ - jenkins.client.jira
+{%- endif %}
# execute job enforcements as last
{%- if client.job is defined %}
diff --git a/jenkins/client/jira.sls b/jenkins/client/jira.sls
new file mode 100644
index 0000000..39d7105
--- /dev/null
+++ b/jenkins/client/jira.sls
@@ -0,0 +1,29 @@
+{% from "jenkins/map.jinja" import client with context %}
+{% if client.jira.get('enabled', True) %}
+jenkins_jira_enable:
+ jenkins_jira.present:
+ - sites: {
+ {% for name, site in client.jira.get('sites',[]).iteritems() %}
+ '{{ name }}': {
+ link_url: '{{ site.get('link_url', name) }}',
+ http_auth: {{ site.get('http_auth', false) }},
+ use_wiki_notation: {{ site.get('use_wiki_notation', false) }},
+ record_scm: {{ site.get('record_scm', false) }},
+ disable_changelog: {{ site.get('disable_changelog', false) }},
+ issue_pattern: '{{ site.get('issue_pattern', '') }}',
+ any_build_result: {{ site.get('any_build_result', false) }},
+ user: '{{ site.get('user', '') }}',
+ password: '{{ site.get('password', '') }}',
+ conn_timeout: {{ site.get('conn_timeout', 10) }},
+ visible_for_group: '{{ site.get('visible_for_group', '') }}',
+ visible_for_project: '{{ site.get('visible_for_project', '') }}',
+ timestamps: {{ site.get('timestamps', false) }},
+ timestamp_format: '{{ site.get('timestamp_format', '') }}'
+ },
+ {% endfor %}
+ }
+{% else %}
+jenkins_jira_disable:
+ jenkins_jira.absent
+{% endif %}
+
diff --git a/jenkins/files/groovy/jira.template b/jenkins/files/groovy/jira.template
new file mode 100644
index 0000000..33d6340
--- /dev/null
+++ b/jenkins/files/groovy/jira.template
@@ -0,0 +1,147 @@
+#!groovy
+import jenkins.model.*
+import java.net.URL
+import hudson.plugins.jira.JiraProjectProperty
+import hudson.plugins.jira.JiraSite
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.StaplerRequest
+
+def sites = JSONObject.fromObject('${sites}')
+def doRemove = '${absent}'.toBoolean()
+
+def isNotApplicable = true
+def doCreate = false
+def isRemoved = false
+
+if (Jenkins.instance.pluginManager.activePlugins.find {
+ it.shortName == "jira"}) {
+ isNotApplicable = false
+
+ def descriptor = Jenkins.getInstance().getDescriptorByType(
+ JiraProjectProperty.DescriptorImpl.class)
+
+ def _sites = descriptor.getSites()
+
+ if (doRemove) {
+ if (_sites.size() > 0) {
+ isRemoved = true
+ def sitesCollection = []
+ def sitesParams = [
+ sites: sitesCollection
+ ]
+ def jiraSites = sitesCollection.collect { it -> it as JiraSite }
+ def req = [
+ bindJSONToList: { clazz, data -> jiraSites }
+ ] as org.kohsuke.stapler.StaplerRequest
+ descriptor.configure(req, new JSONObject())
+ }
+ } else {
+ def _site
+
+ // Recreate list in order to remove non-defined sites
+ _sites.each{
+ if (sites[it.url.toString()] == null) {
+ doCreate = true
+ }
+ }
+
+ sites.each{ name, params ->
+ _site = _sites.find{
+ it.url.toString() == (name ?: null) &&
+ it.alternativeUrl.toString() == (params['link_url'] ?: null) &&
+ it.userName == (params['user'] ?: null) &&
+ it.password.getPlainText() == params['password'] &&
+ it.supportsWikiStyleComment == params['use_wiki_notation'] &&
+ it.recordScmChanges == params['record_scm'] &&
+ it.userPattern.toString() == (params['issue_pattern'] ?: "null") &&
+ it.updateJiraIssueForAllStatus == params['any_build_result'] &&
+ it.groupVisibility == (params['visible_for_group'] ?: null) &&
+ it.roleVisibility == (params['visible_for_project'] ?: null) &&
+ it.useHTTPAuth == params['http_auth']
+ }
+ if (!_site) {
+ doCreate = true
+ }
+ }
+
+ if (doCreate) {
+ def sitesCollection = []
+ def siteEntry = []
+ sites.each{ name, params ->
+ siteEntry = [
+ new URL(name),
+ new URL(params['link_url']),
+ params['user'],
+ params['password'],
+ params['use_wiki_notation'],
+ params['record_scm'],
+ params['issue_pattern'],
+ params['any_build_result'],
+ params['visible_for_group'],
+ params['visible_for_project'],
+ params['http_auth']
+ ]
+ sitesCollection.add(siteEntry)
+ }
+ def sitesParams = [
+ sites: sitesCollection
+ ]
+ def jiraSites = sitesCollection.collect { it -> it as JiraSite }
+ def req = [
+ bindJSONToList: { clazz, data -> jiraSites }
+ ] as org.kohsuke.stapler.StaplerRequest
+ descriptor.configure(req, new JSONObject())
+ }
+
+ // update sites list
+ _sites = descriptor.getSites()
+
+ sites.each{ name, params ->
+ _site = _sites.find{
+ it.url.toString() == (name ?: null) &&
+ it.alternativeUrl.toString() == (params['link_url'] ?: null) &&
+ it.userName == (params['user'] ?: null) &&
+ it.password.getPlainText() == params['password'] &&
+ it.supportsWikiStyleComment == params['use_wiki_notation'] &&
+ it.recordScmChanges == params['record_scm'] &&
+ it.userPattern.toString() == (params['issue_pattern'] ?: "null") &&
+ it.updateJiraIssueForAllStatus == params['any_build_result'] &&
+ it.groupVisibility == (params['visible_for_group'] ?: null) &&
+ it.roleVisibility == (params['visible_for_project'] ?: null) &&
+ it.useHTTPAuth == params['http_auth']
+ }
+
+ if (_site.disableChangelogAnnotations != params['disable_changelog']) {
+ _site.setDisableChangelogAnnotations(params['disable_changelog'])
+ doCreate = true
+ }
+
+ if (_site.timeout != params['conn_timeout']) {
+ _site.timeout = params['conn_timeout']
+ doCreate = true
+ }
+
+ if (_site.appendChangeTimestamp != params['timestamps']) {
+ _site.appendChangeTimestamp = params['timestamps']
+ doCreate = true
+ }
+
+ if (_site.dateTimePattern != params['timestamp_format']) {
+ _site.dateTimePattern = params['timestamp_format']
+ doCreate = true
+ }
+ }
+ }
+}
+
+if (isNotApplicable) {
+ print("NOT APPLICABLE")
+} else if (doCreate) {
+ print("CREATED")
+} else if (isRemoved) {
+ print("REMOVED")
+} else if (doRemove) {
+ print("NOT PRESENT")
+} else {
+ print("EXISTS")
+}