mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2025-10-26 05:50:36 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			781 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			781 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/python
 | |
| # encoding: utf-8
 | |
| 
 | |
| # (c) 2016, Jiri Tyr <jiri.tyr@gmail.com>
 | |
| # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 | |
| 
 | |
| from __future__ import absolute_import, division, print_function
 | |
| __metaclass__ = type
 | |
| 
 | |
| 
 | |
| DOCUMENTATION = '''
 | |
| ---
 | |
| module: jenkins_plugin
 | |
| author: Jiri Tyr (@jtyr)
 | |
| short_description: Add or remove Jenkins plugin
 | |
| description:
 | |
|   - Ansible module which helps to manage Jenkins plugins.
 | |
| 
 | |
| options:
 | |
|   group:
 | |
|     type: str
 | |
|     description:
 | |
|       - Name of the Jenkins group on the OS.
 | |
|     default: jenkins
 | |
|   jenkins_home:
 | |
|     type: path
 | |
|     description:
 | |
|       - Home directory of the Jenkins user.
 | |
|     default: /var/lib/jenkins
 | |
|   mode:
 | |
|     type: raw
 | |
|     description:
 | |
|       - File mode applied on versioned plugins.
 | |
|     default: '0644'
 | |
|   name:
 | |
|     type: str
 | |
|     description:
 | |
|       - Plugin name.
 | |
|     required: yes
 | |
|   owner:
 | |
|     type: str
 | |
|     description:
 | |
|       - Name of the Jenkins user on the OS.
 | |
|     default: jenkins
 | |
|   state:
 | |
|     type: str
 | |
|     description:
 | |
|       - Desired plugin state.
 | |
|       - If the C(latest) is set, the check for new version will be performed
 | |
|         every time. This is suitable to keep the plugin up-to-date.
 | |
|     choices: [absent, present, pinned, unpinned, enabled, disabled, latest]
 | |
|     default: present
 | |
|   timeout:
 | |
|     type: int
 | |
|     description:
 | |
|       - Server connection timeout in secs.
 | |
|     default: 30
 | |
|   updates_expiration:
 | |
|     type: int
 | |
|     description:
 | |
|       - Number of seconds after which a new copy of the I(update-center.json)
 | |
|         file is downloaded. This is used to avoid the need to download the
 | |
|         plugin to calculate its checksum when C(latest) is specified.
 | |
|       - Set it to C(0) if no cache file should be used. In that case, the
 | |
|         plugin file will always be downloaded to calculate its checksum when
 | |
|         C(latest) is specified.
 | |
|     default: 86400
 | |
|   updates_url:
 | |
|     type: str
 | |
|     description:
 | |
|       - URL of the Update Centre.
 | |
|       - Used as the base URL to download the plugins and the
 | |
|         I(update-center.json) JSON file.
 | |
|     default: https://updates.jenkins.io
 | |
|   url:
 | |
|     type: str
 | |
|     description:
 | |
|       - URL of the Jenkins server.
 | |
|     default: http://localhost:8080
 | |
|   version:
 | |
|     type: str
 | |
|     description:
 | |
|       - Plugin version number.
 | |
|       - If this option is specified, all plugin dependencies must be installed
 | |
|         manually.
 | |
|       - It might take longer to verify that the correct version is installed.
 | |
|         This is especially true if a specific version number is specified.
 | |
|       - Quote the version to prevent the value to be interpreted as float. For
 | |
|         example if C(1.20) would be unquoted, it would become C(1.2).
 | |
|   with_dependencies:
 | |
|     description:
 | |
|       - Defines whether to install plugin dependencies.
 | |
|       - This option takes effect only if the I(version) is not defined.
 | |
|     type: bool
 | |
|     default: yes
 | |
| 
 | |
| notes:
 | |
|   - Plugin installation should be run under root or the same user which owns
 | |
|     the plugin files on the disk. Only if the plugin is not installed yet and
 | |
|     no version is specified, the API installation is performed which requires
 | |
|     only the Web UI credentials.
 | |
|   - It's necessary to notify the handler or call the I(service) module to
 | |
|     restart the Jenkins service after a new plugin was installed.
 | |
|   - Pinning works only if the plugin is installed and Jenkins service was
 | |
|     successfully restarted after the plugin installation.
 | |
|   - It is not possible to run the module remotely by changing the I(url)
 | |
|     parameter to point to the Jenkins server. The module must be used on the
 | |
|     host where Jenkins runs as it needs direct access to the plugin files.
 | |
| extends_documentation_fragment:
 | |
|   - url
 | |
|   - files
 | |
| '''
 | |
| 
 | |
| EXAMPLES = '''
 | |
| - name: Install plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: build-pipeline-plugin
 | |
| 
 | |
| - name: Install plugin without its dependencies
 | |
|   community.general.jenkins_plugin:
 | |
|     name: build-pipeline-plugin
 | |
|     with_dependencies: no
 | |
| 
 | |
| - name: Make sure the plugin is always up-to-date
 | |
|   community.general.jenkins_plugin:
 | |
|     name: token-macro
 | |
|     state: latest
 | |
| 
 | |
| - name: Install specific version of the plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: token-macro
 | |
|     version: "1.15"
 | |
| 
 | |
| - name: Pin the plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: token-macro
 | |
|     state: pinned
 | |
| 
 | |
| - name: Unpin the plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: token-macro
 | |
|     state: unpinned
 | |
| 
 | |
| - name: Enable the plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: token-macro
 | |
|     state: enabled
 | |
| 
 | |
| - name: Disable the plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: token-macro
 | |
|     state: disabled
 | |
| 
 | |
| - name: Uninstall plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: build-pipeline-plugin
 | |
|     state: absent
 | |
| 
 | |
| #
 | |
| # Example of how to authenticate
 | |
| #
 | |
| - name: Install plugin
 | |
|   community.general.jenkins_plugin:
 | |
|     name: build-pipeline-plugin
 | |
|     url_username: admin
 | |
|     url_password: p4ssw0rd
 | |
|     url: http://localhost:8888
 | |
| 
 | |
| #
 | |
| # Example of a Play which handles Jenkins restarts during the state changes
 | |
| #
 | |
| - name: Jenkins Master play
 | |
|   hosts: jenkins-master
 | |
|   vars:
 | |
|     my_jenkins_plugins:
 | |
|       token-macro:
 | |
|         enabled: yes
 | |
|       build-pipeline-plugin:
 | |
|         version: "1.4.9"
 | |
|         pinned: no
 | |
|         enabled: yes
 | |
|   tasks:
 | |
|     - name: Install plugins without a specific version
 | |
|       community.general.jenkins_plugin:
 | |
|         name: "{{ item.key }}"
 | |
|       register: my_jenkins_plugin_unversioned
 | |
|       when: >
 | |
|         'version' not in item.value
 | |
|       with_dict: "{{ my_jenkins_plugins }}"
 | |
| 
 | |
|     - name: Install plugins with a specific version
 | |
|       community.general.jenkins_plugin:
 | |
|         name: "{{ item.key }}"
 | |
|         version: "{{ item.value['version'] }}"
 | |
|       register: my_jenkins_plugin_versioned
 | |
|       when: >
 | |
|         'version' in item.value
 | |
|       with_dict: "{{ my_jenkins_plugins }}"
 | |
| 
 | |
|     - name: Initiate the fact
 | |
|       ansible.builtin.set_fact:
 | |
|         jenkins_restart_required: no
 | |
| 
 | |
|     - name: Check if restart is required by any of the versioned plugins
 | |
|       ansible.builtin.set_fact:
 | |
|         jenkins_restart_required: yes
 | |
|       when: item.changed
 | |
|       with_items: "{{ my_jenkins_plugin_versioned.results }}"
 | |
| 
 | |
|     - name: Check if restart is required by any of the unversioned plugins
 | |
|       ansible.builtin.set_fact:
 | |
|         jenkins_restart_required: yes
 | |
|       when: item.changed
 | |
|       with_items: "{{ my_jenkins_plugin_unversioned.results }}"
 | |
| 
 | |
|     - name: Restart Jenkins if required
 | |
|       ansible.builtin.service:
 | |
|         name: jenkins
 | |
|         state: restarted
 | |
|       when: jenkins_restart_required
 | |
| 
 | |
|     - name: Wait for Jenkins to start up
 | |
|       ansible.builtin.uri:
 | |
|         url: http://localhost:8080
 | |
|         status_code: 200
 | |
|         timeout: 5
 | |
|       register: jenkins_service_status
 | |
|       # Keep trying for 5 mins in 5 sec intervals
 | |
|       retries: 60
 | |
|       delay: 5
 | |
|       until: >
 | |
|          'status' in jenkins_service_status and
 | |
|          jenkins_service_status['status'] == 200
 | |
|       when: jenkins_restart_required
 | |
| 
 | |
|     - name: Reset the fact
 | |
|       ansible.builtin.set_fact:
 | |
|         jenkins_restart_required: no
 | |
|       when: jenkins_restart_required
 | |
| 
 | |
|     - name: Plugin pinning
 | |
|       community.general.jenkins_plugin:
 | |
|         name: "{{ item.key }}"
 | |
|         state: "{{ 'pinned' if item.value['pinned'] else 'unpinned'}}"
 | |
|       when: >
 | |
|         'pinned' in item.value
 | |
|       with_dict: "{{ my_jenkins_plugins }}"
 | |
| 
 | |
|     - name: Plugin enabling
 | |
|       community.general.jenkins_plugin:
 | |
|         name: "{{ item.key }}"
 | |
|         state: "{{ 'enabled' if item.value['enabled'] else 'disabled'}}"
 | |
|       when: >
 | |
|         'enabled' in item.value
 | |
|       with_dict: "{{ my_jenkins_plugins }}"
 | |
| '''
 | |
| 
 | |
| RETURN = '''
 | |
| plugin:
 | |
|     description: plugin name
 | |
|     returned: success
 | |
|     type: str
 | |
|     sample: build-pipeline-plugin
 | |
| state:
 | |
|     description: state of the target, after execution
 | |
|     returned: success
 | |
|     type: str
 | |
|     sample: "present"
 | |
| '''
 | |
| 
 | |
| from ansible.module_utils.basic import AnsibleModule, to_bytes
 | |
| from ansible.module_utils.six.moves import http_cookiejar as cookiejar
 | |
| from ansible.module_utils.six.moves.urllib.parse import urlencode
 | |
| from ansible.module_utils.urls import fetch_url, url_argument_spec
 | |
| from ansible.module_utils.six import text_type, binary_type
 | |
| from ansible.module_utils._text import to_native
 | |
| import base64
 | |
| import hashlib
 | |
| import io
 | |
| import json
 | |
| import os
 | |
| import tempfile
 | |
| import time
 | |
| 
 | |
| 
 | |
| class JenkinsPlugin(object):
 | |
|     def __init__(self, module):
 | |
|         # To be able to call fail_json
 | |
|         self.module = module
 | |
| 
 | |
|         # Shortcuts for the params
 | |
|         self.params = self.module.params
 | |
|         self.url = self.params['url']
 | |
|         self.timeout = self.params['timeout']
 | |
| 
 | |
|         # Crumb
 | |
|         self.crumb = {}
 | |
|         # Cookie jar for crumb session
 | |
|         self.cookies = None
 | |
| 
 | |
|         if self._csrf_enabled():
 | |
|             self.cookies = cookiejar.LWPCookieJar()
 | |
|             self.crumb = self._get_crumb()
 | |
| 
 | |
|         # Get list of installed plugins
 | |
|         self._get_installed_plugins()
 | |
| 
 | |
|     def _csrf_enabled(self):
 | |
|         csrf_data = self._get_json_data(
 | |
|             "%s/%s" % (self.url, "api/json"), 'CSRF')
 | |
| 
 | |
|         if 'useCrumbs' not in csrf_data:
 | |
|             self.module.fail_json(
 | |
|                 msg="Required fields not found in the Crumbs response.",
 | |
|                 details=csrf_data)
 | |
| 
 | |
|         return csrf_data['useCrumbs']
 | |
| 
 | |
|     def _get_json_data(self, url, what, **kwargs):
 | |
|         # Get the JSON data
 | |
|         r = self._get_url_data(url, what, **kwargs)
 | |
| 
 | |
|         # Parse the JSON data
 | |
|         try:
 | |
|             json_data = json.loads(to_native(r.read()))
 | |
|         except Exception as e:
 | |
|             self.module.fail_json(
 | |
|                 msg="Cannot parse %s JSON data." % what,
 | |
|                 details=to_native(e))
 | |
| 
 | |
|         return json_data
 | |
| 
 | |
|     def _get_url_data(
 | |
|             self, url, what=None, msg_status=None, msg_exception=None,
 | |
|             **kwargs):
 | |
|         # Compose default messages
 | |
|         if msg_status is None:
 | |
|             msg_status = "Cannot get %s" % what
 | |
| 
 | |
|         if msg_exception is None:
 | |
|             msg_exception = "Retrieval of %s failed." % what
 | |
| 
 | |
|         # Get the URL data
 | |
|         try:
 | |
|             response, info = fetch_url(
 | |
|                 self.module, url, timeout=self.timeout, cookies=self.cookies,
 | |
|                 headers=self.crumb, **kwargs)
 | |
| 
 | |
|             if info['status'] != 200:
 | |
|                 self.module.fail_json(msg=msg_status, details=info['msg'])
 | |
|         except Exception as e:
 | |
|             self.module.fail_json(msg=msg_exception, details=to_native(e))
 | |
| 
 | |
|         return response
 | |
| 
 | |
|     def _get_crumb(self):
 | |
|         crumb_data = self._get_json_data(
 | |
|             "%s/%s" % (self.url, "crumbIssuer/api/json"), 'Crumb')
 | |
| 
 | |
|         if 'crumbRequestField' in crumb_data and 'crumb' in crumb_data:
 | |
|             ret = {
 | |
|                 crumb_data['crumbRequestField']: crumb_data['crumb']
 | |
|             }
 | |
|         else:
 | |
|             self.module.fail_json(
 | |
|                 msg="Required fields not found in the Crum response.",
 | |
|                 details=crumb_data)
 | |
| 
 | |
|         return ret
 | |
| 
 | |
|     def _get_installed_plugins(self):
 | |
|         plugins_data = self._get_json_data(
 | |
|             "%s/%s" % (self.url, "pluginManager/api/json?depth=1"),
 | |
|             'list of plugins')
 | |
| 
 | |
|         # Check if we got valid data
 | |
|         if 'plugins' not in plugins_data:
 | |
|             self.module.fail_json(msg="No valid plugin data found.")
 | |
| 
 | |
|         # Create final list of installed/pined plugins
 | |
|         self.is_installed = False
 | |
|         self.is_pinned = False
 | |
|         self.is_enabled = False
 | |
| 
 | |
|         for p in plugins_data['plugins']:
 | |
|             if p['shortName'] == self.params['name']:
 | |
|                 self.is_installed = True
 | |
| 
 | |
|                 if p['pinned']:
 | |
|                     self.is_pinned = True
 | |
| 
 | |
|                 if p['enabled']:
 | |
|                     self.is_enabled = True
 | |
| 
 | |
|                 break
 | |
| 
 | |
|     def install(self):
 | |
|         changed = False
 | |
|         plugin_file = (
 | |
|             '%s/plugins/%s.jpi' % (
 | |
|                 self.params['jenkins_home'],
 | |
|                 self.params['name']))
 | |
| 
 | |
|         if not self.is_installed and self.params['version'] in [None, 'latest']:
 | |
|             if not self.module.check_mode:
 | |
|                 # Install the plugin (with dependencies)
 | |
|                 install_script = (
 | |
|                     'd = Jenkins.instance.updateCenter.getPlugin("%s")'
 | |
|                     '.deploy(); d.get();' % self.params['name'])
 | |
| 
 | |
|                 if self.params['with_dependencies']:
 | |
|                     install_script = (
 | |
|                         'Jenkins.instance.updateCenter.getPlugin("%s")'
 | |
|                         '.getNeededDependencies().each{it.deploy()}; %s' % (
 | |
|                             self.params['name'], install_script))
 | |
| 
 | |
|                 script_data = {
 | |
|                     'script': install_script
 | |
|                 }
 | |
|                 data = urlencode(script_data)
 | |
| 
 | |
|                 # Send the installation request
 | |
|                 r = self._get_url_data(
 | |
|                     "%s/scriptText" % self.url,
 | |
|                     msg_status="Cannot install plugin.",
 | |
|                     msg_exception="Plugin installation has failed.",
 | |
|                     data=data)
 | |
| 
 | |
|                 hpi_file = '%s/plugins/%s.hpi' % (
 | |
|                     self.params['jenkins_home'],
 | |
|                     self.params['name'])
 | |
| 
 | |
|                 if os.path.isfile(hpi_file):
 | |
|                     os.remove(hpi_file)
 | |
| 
 | |
|             changed = True
 | |
|         else:
 | |
|             # Check if the plugin directory exists
 | |
|             if not os.path.isdir(self.params['jenkins_home']):
 | |
|                 self.module.fail_json(
 | |
|                     msg="Jenkins home directory doesn't exist.")
 | |
| 
 | |
|             checksum_old = None
 | |
|             if os.path.isfile(plugin_file):
 | |
|                 # Make the checksum of the currently installed plugin
 | |
|                 with open(plugin_file, 'rb') as plugin_fh:
 | |
|                     plugin_content = plugin_fh.read()
 | |
|                 checksum_old = hashlib.sha1(plugin_content).hexdigest()
 | |
| 
 | |
|             if self.params['version'] in [None, 'latest']:
 | |
|                 # Take latest version
 | |
|                 plugin_url = (
 | |
|                     "%s/latest/%s.hpi" % (
 | |
|                         self.params['updates_url'],
 | |
|                         self.params['name']))
 | |
|             else:
 | |
|                 # Take specific version
 | |
|                 plugin_url = (
 | |
|                     "{0}/download/plugins/"
 | |
|                     "{1}/{2}/{1}.hpi".format(
 | |
|                         self.params['updates_url'],
 | |
|                         self.params['name'],
 | |
|                         self.params['version']))
 | |
| 
 | |
|             if (
 | |
|                     self.params['updates_expiration'] == 0 or
 | |
|                     self.params['version'] not in [None, 'latest'] or
 | |
|                     checksum_old is None):
 | |
| 
 | |
|                 # Download the plugin file directly
 | |
|                 r = self._download_plugin(plugin_url)
 | |
| 
 | |
|                 # Write downloaded plugin into file if checksums don't match
 | |
|                 if checksum_old is None:
 | |
|                     # No previously installed plugin
 | |
|                     if not self.module.check_mode:
 | |
|                         self._write_file(plugin_file, r)
 | |
| 
 | |
|                     changed = True
 | |
|                 else:
 | |
|                     # Get data for the MD5
 | |
|                     data = r.read()
 | |
| 
 | |
|                     # Make new checksum
 | |
|                     checksum_new = hashlib.sha1(data).hexdigest()
 | |
| 
 | |
|                     # If the checksum is different from the currently installed
 | |
|                     # plugin, store the new plugin
 | |
|                     if checksum_old != checksum_new:
 | |
|                         if not self.module.check_mode:
 | |
|                             self._write_file(plugin_file, data)
 | |
| 
 | |
|                         changed = True
 | |
|             elif self.params['version'] == 'latest':
 | |
|                 # Check for update from the updates JSON file
 | |
|                 plugin_data = self._download_updates()
 | |
| 
 | |
|                 # If the latest version changed, download it
 | |
|                 if checksum_old != to_bytes(plugin_data['sha1']):
 | |
|                     if not self.module.check_mode:
 | |
|                         r = self._download_plugin(plugin_url)
 | |
|                         self._write_file(plugin_file, r)
 | |
| 
 | |
|                     changed = True
 | |
| 
 | |
|         # Change file attributes if needed
 | |
|         if os.path.isfile(plugin_file):
 | |
|             params = {
 | |
|                 'dest': plugin_file
 | |
|             }
 | |
|             params.update(self.params)
 | |
|             file_args = self.module.load_file_common_arguments(params)
 | |
| 
 | |
|             if not self.module.check_mode:
 | |
|                 # Not sure how to run this in the check mode
 | |
|                 changed = self.module.set_fs_attributes_if_different(
 | |
|                     file_args, changed)
 | |
|             else:
 | |
|                 # See the comment above
 | |
|                 changed = True
 | |
| 
 | |
|         return changed
 | |
| 
 | |
|     def _download_updates(self):
 | |
|         updates_filename = 'jenkins-plugin-cache.json'
 | |
|         updates_dir = os.path.expanduser('~/.ansible/tmp')
 | |
|         updates_file = "%s/%s" % (updates_dir, updates_filename)
 | |
|         download_updates = True
 | |
| 
 | |
|         # Check if we need to download new updates file
 | |
|         if os.path.isfile(updates_file):
 | |
|             # Get timestamp when the file was changed last time
 | |
|             ts_file = os.stat(updates_file).st_mtime
 | |
|             ts_now = time.time()
 | |
| 
 | |
|             if ts_now - ts_file < self.params['updates_expiration']:
 | |
|                 download_updates = False
 | |
| 
 | |
|         updates_file_orig = updates_file
 | |
| 
 | |
|         # Download the updates file if needed
 | |
|         if download_updates:
 | |
|             url = "%s/update-center.json" % self.params['updates_url']
 | |
| 
 | |
|             # Get the data
 | |
|             r = self._get_url_data(
 | |
|                 url,
 | |
|                 msg_status="Remote updates not found.",
 | |
|                 msg_exception="Updates download failed.")
 | |
| 
 | |
|             # Write the updates file
 | |
|             update_fd, updates_file = tempfile.mkstemp()
 | |
|             os.write(update_fd, r.read())
 | |
| 
 | |
|             try:
 | |
|                 os.close(update_fd)
 | |
|             except IOError as e:
 | |
|                 self.module.fail_json(
 | |
|                     msg="Cannot close the tmp updates file %s." % updates_file,
 | |
|                     details=to_native(e))
 | |
| 
 | |
|         # Open the updates file
 | |
|         try:
 | |
|             f = io.open(updates_file, encoding='utf-8')
 | |
|         except IOError as e:
 | |
|             self.module.fail_json(
 | |
|                 msg="Cannot open temporal updates file.",
 | |
|                 details=to_native(e))
 | |
| 
 | |
|         i = 0
 | |
|         for line in f:
 | |
|             # Read only the second line
 | |
|             if i == 1:
 | |
|                 try:
 | |
|                     data = json.loads(line)
 | |
|                 except Exception as e:
 | |
|                     self.module.fail_json(
 | |
|                         msg="Cannot load JSON data from the tmp updates file.",
 | |
|                         details=to_native(e))
 | |
| 
 | |
|                 break
 | |
| 
 | |
|             i += 1
 | |
| 
 | |
|         # Move the updates file to the right place if we could read it
 | |
|         if download_updates:
 | |
|             # Make sure the destination directory exists
 | |
|             if not os.path.isdir(updates_dir):
 | |
|                 try:
 | |
|                     os.makedirs(updates_dir, int('0700', 8))
 | |
|                 except OSError as e:
 | |
|                     self.module.fail_json(
 | |
|                         msg="Cannot create temporal directory.",
 | |
|                         details=to_native(e))
 | |
| 
 | |
|             self.module.atomic_move(updates_file, updates_file_orig)
 | |
| 
 | |
|         # Check if we have the plugin data available
 | |
|         if 'plugins' not in data or self.params['name'] not in data['plugins']:
 | |
|             self.module.fail_json(
 | |
|                 msg="Cannot find plugin data in the updates file.")
 | |
| 
 | |
|         return data['plugins'][self.params['name']]
 | |
| 
 | |
|     def _download_plugin(self, plugin_url):
 | |
|         # Download the plugin
 | |
|         r = self._get_url_data(
 | |
|             plugin_url,
 | |
|             msg_status="Plugin not found.",
 | |
|             msg_exception="Plugin download failed.")
 | |
| 
 | |
|         return r
 | |
| 
 | |
|     def _write_file(self, f, data):
 | |
|         # Store the plugin into a temp file and then move it
 | |
|         tmp_f_fd, tmp_f = tempfile.mkstemp()
 | |
| 
 | |
|         if isinstance(data, (text_type, binary_type)):
 | |
|             os.write(tmp_f_fd, data)
 | |
|         else:
 | |
|             os.write(tmp_f_fd, data.read())
 | |
| 
 | |
|         try:
 | |
|             os.close(tmp_f_fd)
 | |
|         except IOError as e:
 | |
|             self.module.fail_json(
 | |
|                 msg='Cannot close the temporal plugin file %s.' % tmp_f,
 | |
|                 details=to_native(e))
 | |
| 
 | |
|         # Move the file onto the right place
 | |
|         self.module.atomic_move(tmp_f, f)
 | |
| 
 | |
|     def uninstall(self):
 | |
|         changed = False
 | |
| 
 | |
|         # Perform the action
 | |
|         if self.is_installed:
 | |
|             if not self.module.check_mode:
 | |
|                 self._pm_query('doUninstall', 'Uninstallation')
 | |
| 
 | |
|             changed = True
 | |
| 
 | |
|         return changed
 | |
| 
 | |
|     def pin(self):
 | |
|         return self._pinning('pin')
 | |
| 
 | |
|     def unpin(self):
 | |
|         return self._pinning('unpin')
 | |
| 
 | |
|     def _pinning(self, action):
 | |
|         changed = False
 | |
| 
 | |
|         # Check if the plugin is pinned/unpinned
 | |
|         if (
 | |
|                 action == 'pin' and not self.is_pinned or
 | |
|                 action == 'unpin' and self.is_pinned):
 | |
| 
 | |
|             # Perform the action
 | |
|             if not self.module.check_mode:
 | |
|                 self._pm_query(action, "%sning" % action.capitalize())
 | |
| 
 | |
|             changed = True
 | |
| 
 | |
|         return changed
 | |
| 
 | |
|     def enable(self):
 | |
|         return self._enabling('enable')
 | |
| 
 | |
|     def disable(self):
 | |
|         return self._enabling('disable')
 | |
| 
 | |
|     def _enabling(self, action):
 | |
|         changed = False
 | |
| 
 | |
|         # Check if the plugin is pinned/unpinned
 | |
|         if (
 | |
|                 action == 'enable' and not self.is_enabled or
 | |
|                 action == 'disable' and self.is_enabled):
 | |
| 
 | |
|             # Perform the action
 | |
|             if not self.module.check_mode:
 | |
|                 self._pm_query(
 | |
|                     "make%sd" % action.capitalize(),
 | |
|                     "%sing" % action[:-1].capitalize())
 | |
| 
 | |
|             changed = True
 | |
| 
 | |
|         return changed
 | |
| 
 | |
|     def _pm_query(self, action, msg):
 | |
|         url = "%s/pluginManager/plugin/%s/%s" % (
 | |
|             self.params['url'], self.params['name'], action)
 | |
| 
 | |
|         # Send the request
 | |
|         self._get_url_data(
 | |
|             url,
 | |
|             msg_status="Plugin not found. %s" % url,
 | |
|             msg_exception="%s has failed." % msg)
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     # Module arguments
 | |
|     argument_spec = url_argument_spec()
 | |
|     argument_spec.update(
 | |
|         group=dict(type='str', default='jenkins'),
 | |
|         jenkins_home=dict(type='path', default='/var/lib/jenkins'),
 | |
|         mode=dict(default='0644', type='raw'),
 | |
|         name=dict(type='str', required=True),
 | |
|         owner=dict(type='str', default='jenkins'),
 | |
|         state=dict(
 | |
|             choices=[
 | |
|                 'present',
 | |
|                 'absent',
 | |
|                 'pinned',
 | |
|                 'unpinned',
 | |
|                 'enabled',
 | |
|                 'disabled',
 | |
|                 'latest'],
 | |
|             default='present'),
 | |
|         timeout=dict(default=30, type="int"),
 | |
|         updates_expiration=dict(default=86400, type="int"),
 | |
|         updates_url=dict(default='https://updates.jenkins.io'),
 | |
|         url=dict(default='http://localhost:8080'),
 | |
|         url_password=dict(no_log=True),
 | |
|         version=dict(),
 | |
|         with_dependencies=dict(default=True, type='bool'),
 | |
|     )
 | |
|     # Module settings
 | |
|     module = AnsibleModule(
 | |
|         argument_spec=argument_spec,
 | |
|         add_file_common_args=True,
 | |
|         supports_check_mode=True,
 | |
|     )
 | |
| 
 | |
|     # Force basic authentication
 | |
|     module.params['force_basic_auth'] = True
 | |
| 
 | |
|     # Convert timeout to float
 | |
|     try:
 | |
|         module.params['timeout'] = float(module.params['timeout'])
 | |
|     except ValueError as e:
 | |
|         module.fail_json(
 | |
|             msg='Cannot convert %s to float.' % module.params['timeout'],
 | |
|             details=to_native(e))
 | |
| 
 | |
|     # Set version to latest if state is latest
 | |
|     if module.params['state'] == 'latest':
 | |
|         module.params['state'] = 'present'
 | |
|         module.params['version'] = 'latest'
 | |
| 
 | |
|     # Create some shortcuts
 | |
|     name = module.params['name']
 | |
|     state = module.params['state']
 | |
| 
 | |
|     # Initial change state of the task
 | |
|     changed = False
 | |
| 
 | |
|     # Instantiate the JenkinsPlugin object
 | |
|     jp = JenkinsPlugin(module)
 | |
| 
 | |
|     # Perform action depending on the requested state
 | |
|     if state == 'present':
 | |
|         changed = jp.install()
 | |
|     elif state == 'absent':
 | |
|         changed = jp.uninstall()
 | |
|     elif state == 'pinned':
 | |
|         changed = jp.pin()
 | |
|     elif state == 'unpinned':
 | |
|         changed = jp.unpin()
 | |
|     elif state == 'enabled':
 | |
|         changed = jp.enable()
 | |
|     elif state == 'disabled':
 | |
|         changed = jp.disable()
 | |
| 
 | |
|     # Print status of the change
 | |
|     module.exit_json(changed=changed, plugin=name, state=state)
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |