mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-08-04 05:04:22 -07:00
Merge b95ab803f6
into 84b5d38c51
This commit is contained in:
commit
dd67f7e39d
3 changed files with 277 additions and 19 deletions
6
changelogs/fragments/10346-jenkins-plugins-fixes.yml
Normal file
6
changelogs/fragments/10346-jenkins-plugins-fixes.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
bugfixes:
|
||||||
|
- "jenkins_plugin - install latest compatible version instead of latest (https://github.com/ansible-collections/community.general/issues/854, https://github.com/ansible-collections/community.general/pull/10346)."
|
||||||
|
- "jenkins_plugin - separate Jenkins and external URL credentials (https://github.com/ansible-collections/community.general/issues/4419, https://github.com/ansible-collections/community.general/pull/10346)."
|
||||||
|
|
||||||
|
minor_changes:
|
||||||
|
- "jenkins_plugin - install dependencies for specific version (https://github.com/ansible-collections/community.general/issue/4995, https://github.com/ansible-collections/community.general/pull/10346)."
|
|
@ -74,6 +74,18 @@ options:
|
||||||
- A list of base URL(s) to retrieve C(update-center.json), and direct plugin files from.
|
- A list of base URL(s) to retrieve C(update-center.json), and direct plugin files from.
|
||||||
- This can be a list since community.general 3.3.0.
|
- This can be a list since community.general 3.3.0.
|
||||||
default: ['https://updates.jenkins.io', 'http://mirrors.jenkins.io']
|
default: ['https://updates.jenkins.io', 'http://mirrors.jenkins.io']
|
||||||
|
updates_url_username:
|
||||||
|
description:
|
||||||
|
- If using a custom O(updates_url), set this as the username of the user with access to the URL.
|
||||||
|
- If the custom O(updates_url) does not require authentication, this can be left empty.
|
||||||
|
type: str
|
||||||
|
version_added: 11.2.0
|
||||||
|
updates_url_password:
|
||||||
|
description:
|
||||||
|
- If using a custom O(updates_url), set this as the password of the user with access to the URL.
|
||||||
|
- If the custom O(updates_url) does not require authentication, this can be left empty.
|
||||||
|
type: str
|
||||||
|
version_added: 11.2.0
|
||||||
update_json_url_segment:
|
update_json_url_segment:
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
|
@ -81,6 +93,13 @@ options:
|
||||||
- A list of URL segment(s) to retrieve the update center JSON file from.
|
- A list of URL segment(s) to retrieve the update center JSON file from.
|
||||||
default: ['update-center.json', 'updates/update-center.json']
|
default: ['update-center.json', 'updates/update-center.json']
|
||||||
version_added: 3.3.0
|
version_added: 3.3.0
|
||||||
|
plugin_versions_url_segment:
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
description:
|
||||||
|
- A list of URL segment(s) to retrieve the plugin versions JSON file from.
|
||||||
|
default: ['plugin-versions.json', 'current/plugin-versions.json']
|
||||||
|
version_added: 11.2.0
|
||||||
latest_plugins_url_segments:
|
latest_plugins_url_segments:
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
|
@ -112,7 +131,8 @@ options:
|
||||||
with_dependencies:
|
with_dependencies:
|
||||||
description:
|
description:
|
||||||
- Defines whether to install plugin dependencies.
|
- Defines whether to install plugin dependencies.
|
||||||
- This option takes effect only if the O(version) is not defined.
|
- In earlier versions, this option had no effect when a specific O(version) was set.
|
||||||
|
Since community.general 11.2.0, dependencies are also installed for versioned plugins.
|
||||||
type: bool
|
type: bool
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
|
@ -124,6 +144,9 @@ notes:
|
||||||
- Pinning works only if the plugin is installed and Jenkins service was successfully restarted after the plugin installation.
|
- 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 O(url) parameter to point to the Jenkins server. The module
|
- It is not possible to run the module remotely by changing the O(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.
|
must be used on the host where Jenkins runs as it needs direct access to the plugin files.
|
||||||
|
- If using a custom O(updates_url), ensure that the URL provides a C(plugin-versions.json) file.
|
||||||
|
This file must include metadata for all available plugin versions to support version compatibility resolution.
|
||||||
|
The file should be in the same format as the one provided by Jenkins update center (https://updates.jenkins.io/current/plugin-versions.json).
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- ansible.builtin.url
|
- ansible.builtin.url
|
||||||
- ansible.builtin.files
|
- ansible.builtin.files
|
||||||
|
@ -315,11 +338,13 @@ import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import time
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, to_bytes
|
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 import http_cookiejar as cookiejar
|
||||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
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.urls import fetch_url, url_argument_spec, basic_auth_header
|
||||||
from ansible.module_utils.six import text_type, binary_type
|
from ansible.module_utils.six import text_type, binary_type
|
||||||
from ansible.module_utils.common.text.converters import to_native
|
from ansible.module_utils.common.text.converters import to_native
|
||||||
|
|
||||||
|
@ -340,14 +365,24 @@ class JenkinsPlugin(object):
|
||||||
self.url = self.params['url']
|
self.url = self.params['url']
|
||||||
self.timeout = self.params['timeout']
|
self.timeout = self.params['timeout']
|
||||||
|
|
||||||
|
# Authentication for non-Jenkins calls
|
||||||
|
self.updates_url_credentials = {}
|
||||||
|
if self.params.get('updates_url_username') and self.params.get('updates_url_password'):
|
||||||
|
self.updates_url_credentials["Authorization"] = basic_auth_header(self.params['updates_url_username'], self.params['updates_url_password'])
|
||||||
|
|
||||||
# Crumb
|
# Crumb
|
||||||
self.crumb = {}
|
self.crumb = {}
|
||||||
|
|
||||||
|
# Authentication for Jenkins calls
|
||||||
|
if self.params.get('url_username') and self.params.get('url_password'):
|
||||||
|
self.crumb["Authorization"] = basic_auth_header(self.params['url_username'], self.params['url_password'])
|
||||||
|
|
||||||
# Cookie jar for crumb session
|
# Cookie jar for crumb session
|
||||||
self.cookies = None
|
self.cookies = None
|
||||||
|
|
||||||
if self._csrf_enabled():
|
if self._csrf_enabled():
|
||||||
self.cookies = cookiejar.LWPCookieJar()
|
self.cookies = cookiejar.LWPCookieJar()
|
||||||
self.crumb = self._get_crumb()
|
self._get_crumb()
|
||||||
|
|
||||||
# Get list of installed plugins
|
# Get list of installed plugins
|
||||||
self._get_installed_plugins()
|
self._get_installed_plugins()
|
||||||
|
@ -390,10 +425,14 @@ class JenkinsPlugin(object):
|
||||||
err_msg = None
|
err_msg = None
|
||||||
try:
|
try:
|
||||||
self.module.debug("fetching url: %s" % url)
|
self.module.debug("fetching url: %s" % url)
|
||||||
|
|
||||||
|
is_jenkins_call = url.startswith(self.url)
|
||||||
|
self.module.params['force_basic_auth'] = is_jenkins_call
|
||||||
|
|
||||||
response, info = fetch_url(
|
response, info = fetch_url(
|
||||||
self.module, url, timeout=self.timeout, cookies=self.cookies,
|
self.module, url, timeout=self.timeout, cookies=self.cookies,
|
||||||
headers=self.crumb, **kwargs)
|
headers=self.crumb if is_jenkins_call else self.updates_url_credentials or self.crumb,
|
||||||
|
**kwargs)
|
||||||
if info['status'] == 200:
|
if info['status'] == 200:
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
|
@ -422,9 +461,13 @@ class JenkinsPlugin(object):
|
||||||
|
|
||||||
# Get the URL data
|
# Get the URL data
|
||||||
try:
|
try:
|
||||||
|
is_jenkins_call = url.startswith(self.url)
|
||||||
|
self.module.params['force_basic_auth'] = is_jenkins_call
|
||||||
|
|
||||||
response, info = fetch_url(
|
response, info = fetch_url(
|
||||||
self.module, url, timeout=self.timeout, cookies=self.cookies,
|
self.module, url, timeout=self.timeout, cookies=self.cookies,
|
||||||
headers=self.crumb, **kwargs)
|
headers=self.crumb if is_jenkins_call else self.updates_url_credentials or self.crumb,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
if info['status'] != 200:
|
if info['status'] != 200:
|
||||||
if dont_fail:
|
if dont_fail:
|
||||||
|
@ -444,16 +487,12 @@ class JenkinsPlugin(object):
|
||||||
"%s/%s" % (self.url, "crumbIssuer/api/json"), 'Crumb')
|
"%s/%s" % (self.url, "crumbIssuer/api/json"), 'Crumb')
|
||||||
|
|
||||||
if 'crumbRequestField' in crumb_data and 'crumb' in crumb_data:
|
if 'crumbRequestField' in crumb_data and 'crumb' in crumb_data:
|
||||||
ret = {
|
self.crumb[crumb_data['crumbRequestField']] = crumb_data['crumb']
|
||||||
crumb_data['crumbRequestField']: crumb_data['crumb']
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
self.module.fail_json(
|
self.module.fail_json(
|
||||||
msg="Required fields not found in the Crum response.",
|
msg="Required fields not found in the Crum response.",
|
||||||
details=crumb_data)
|
details=crumb_data)
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def _get_installed_plugins(self):
|
def _get_installed_plugins(self):
|
||||||
plugins_data = self._get_json_data(
|
plugins_data = self._get_json_data(
|
||||||
"%s/%s" % (self.url, "pluginManager/api/json?depth=1"),
|
"%s/%s" % (self.url, "pluginManager/api/json?depth=1"),
|
||||||
|
@ -467,6 +506,7 @@ class JenkinsPlugin(object):
|
||||||
self.is_installed = False
|
self.is_installed = False
|
||||||
self.is_pinned = False
|
self.is_pinned = False
|
||||||
self.is_enabled = False
|
self.is_enabled = False
|
||||||
|
self.installed_plugins = plugins_data['plugins']
|
||||||
|
|
||||||
for p in plugins_data['plugins']:
|
for p in plugins_data['plugins']:
|
||||||
if p['shortName'] == self.params['name']:
|
if p['shortName'] == self.params['name']:
|
||||||
|
@ -480,6 +520,40 @@ class JenkinsPlugin(object):
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def _install_dependencies(self):
|
||||||
|
dependencies = self._get_versioned_dependencies()
|
||||||
|
self.dependencies_states = []
|
||||||
|
|
||||||
|
for dep_name, dep_version in dependencies.items():
|
||||||
|
if not any(p['shortName'] == dep_name and p['version'] == dep_version for p in self.installed_plugins):
|
||||||
|
dep_params = self.params.copy()
|
||||||
|
dep_params['name'] = dep_name
|
||||||
|
dep_params['version'] = dep_version
|
||||||
|
dep_module = AnsibleModule(
|
||||||
|
argument_spec=self.module.argument_spec,
|
||||||
|
supports_check_mode=self.module.check_mode
|
||||||
|
)
|
||||||
|
dep_module.params = dep_params
|
||||||
|
dep_plugin = JenkinsPlugin(dep_module)
|
||||||
|
if not dep_plugin.install():
|
||||||
|
self.dependencies_states.append(
|
||||||
|
{
|
||||||
|
'name': dep_name,
|
||||||
|
'version': dep_version,
|
||||||
|
'state': 'absent'})
|
||||||
|
else:
|
||||||
|
self.dependencies_states.append(
|
||||||
|
{
|
||||||
|
'name': dep_name,
|
||||||
|
'version': dep_version,
|
||||||
|
'state': 'present'})
|
||||||
|
else:
|
||||||
|
self.dependencies_states.append(
|
||||||
|
{
|
||||||
|
'name': dep_name,
|
||||||
|
'version': dep_version,
|
||||||
|
'state': 'present'})
|
||||||
|
|
||||||
def _install_with_plugin_manager(self):
|
def _install_with_plugin_manager(self):
|
||||||
if not self.module.check_mode:
|
if not self.module.check_mode:
|
||||||
# Install the plugin (with dependencies)
|
# Install the plugin (with dependencies)
|
||||||
|
@ -540,6 +614,10 @@ class JenkinsPlugin(object):
|
||||||
plugin_content = plugin_fh.read()
|
plugin_content = plugin_fh.read()
|
||||||
checksum_old = hashlib.sha1(plugin_content).hexdigest()
|
checksum_old = hashlib.sha1(plugin_content).hexdigest()
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
if self.params['with_dependencies']:
|
||||||
|
self._install_dependencies()
|
||||||
|
|
||||||
if self.params['version'] in [None, 'latest']:
|
if self.params['version'] in [None, 'latest']:
|
||||||
# Take latest version
|
# Take latest version
|
||||||
plugin_urls = self._get_latest_plugin_urls()
|
plugin_urls = self._get_latest_plugin_urls()
|
||||||
|
@ -612,6 +690,58 @@ class JenkinsPlugin(object):
|
||||||
urls.append("{0}/{1}/{2}.hpi".format(base_url, update_segment, self.params['name']))
|
urls.append("{0}/{1}/{2}.hpi".format(base_url, update_segment, self.params['name']))
|
||||||
return urls
|
return urls
|
||||||
|
|
||||||
|
def _get_latest_compatible_plugin_version(self, plugin_name=None):
|
||||||
|
if not hasattr(self, 'jenkins_version'):
|
||||||
|
self.module.params['force_basic_auth'] = True
|
||||||
|
resp, info = fetch_url(self.module, self.url)
|
||||||
|
raw_version = info.get("x-jenkins")
|
||||||
|
self.jenkins_version = self.parse_version(raw_version)
|
||||||
|
name = plugin_name or self.params['name']
|
||||||
|
cache_path = "{}/ansible_jenkins_plugin_cache.json".format(self.params['jenkins_home'])
|
||||||
|
plugin_version_urls = []
|
||||||
|
for base_url in self.params['updates_url']:
|
||||||
|
for update_json in self.params['plugin_versions_url_segment']:
|
||||||
|
plugin_version_urls.append("{}/{}".format(base_url, update_json))
|
||||||
|
|
||||||
|
try: # Check if file is saved localy
|
||||||
|
if os.path.exists(cache_path):
|
||||||
|
file_mtime = os.path.getmtime(cache_path)
|
||||||
|
else:
|
||||||
|
file_mtime = 0
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
if now - file_mtime >= 86400:
|
||||||
|
response = self._get_urls_data(plugin_version_urls, what="plugin-versions.json")
|
||||||
|
plugin_data = json.loads(to_native(response.read()), object_pairs_hook=OrderedDict)
|
||||||
|
|
||||||
|
# Save it to file for next time
|
||||||
|
with open(cache_path, "w") as f:
|
||||||
|
json.dump(plugin_data, f)
|
||||||
|
|
||||||
|
with open(cache_path, "r") as f:
|
||||||
|
plugin_data = json.load(f)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if os.path.exists(cache_path):
|
||||||
|
os.remove(cache_path)
|
||||||
|
self.module.fail_json(msg="Failed to parse plugin-versions.json", details=to_native(e))
|
||||||
|
|
||||||
|
plugin_versions = plugin_data.get("plugins", {}).get(name)
|
||||||
|
if not plugin_versions:
|
||||||
|
self.module.fail_json(msg="Plugin '{}' not found.".format(name))
|
||||||
|
|
||||||
|
sorted_versions = list(reversed(plugin_versions.items()))
|
||||||
|
|
||||||
|
for idx, (version_title, version_info) in enumerate(sorted_versions):
|
||||||
|
required_core = version_info.get("requiredCore", "0.0")
|
||||||
|
if self.parse_version(required_core) <= self.jenkins_version:
|
||||||
|
return 'latest' if idx == 0 else version_title
|
||||||
|
|
||||||
|
self.module.warn(
|
||||||
|
"No compatible version found for plugin '{}'. "
|
||||||
|
"Installing latest version.".format(name))
|
||||||
|
return 'latest'
|
||||||
|
|
||||||
def _get_versioned_plugin_urls(self):
|
def _get_versioned_plugin_urls(self):
|
||||||
urls = []
|
urls = []
|
||||||
for base_url in self.params['updates_url']:
|
for base_url in self.params['updates_url']:
|
||||||
|
@ -626,6 +756,18 @@ class JenkinsPlugin(object):
|
||||||
urls.append("{0}/{1}".format(base_url, update_json))
|
urls.append("{0}/{1}".format(base_url, update_json))
|
||||||
return urls
|
return urls
|
||||||
|
|
||||||
|
def _get_versioned_dependencies(self):
|
||||||
|
# Get dependencies for the specified plugin version
|
||||||
|
plugin_data = self._download_updates()['dependencies']
|
||||||
|
|
||||||
|
dependencies_info = {
|
||||||
|
dep["name"]: self._get_latest_compatible_plugin_version(dep["name"])
|
||||||
|
for dep in plugin_data
|
||||||
|
if not dep.get("optional", False)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dependencies_info
|
||||||
|
|
||||||
def _download_updates(self):
|
def _download_updates(self):
|
||||||
try:
|
try:
|
||||||
updates_file, download_updates = download_updates_file(self.params['updates_expiration'])
|
updates_file, download_updates = download_updates_file(self.params['updates_expiration'])
|
||||||
|
@ -779,6 +921,10 @@ class JenkinsPlugin(object):
|
||||||
msg_exception="%s has failed." % msg,
|
msg_exception="%s has failed." % msg,
|
||||||
method="POST")
|
method="POST")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_version(version_str):
|
||||||
|
return tuple(int(x) for x in version_str.split('.'))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Module arguments
|
# Module arguments
|
||||||
|
@ -803,8 +949,12 @@ def main():
|
||||||
updates_expiration=dict(default=86400, type="int"),
|
updates_expiration=dict(default=86400, type="int"),
|
||||||
updates_url=dict(type="list", elements="str", default=['https://updates.jenkins.io',
|
updates_url=dict(type="list", elements="str", default=['https://updates.jenkins.io',
|
||||||
'http://mirrors.jenkins.io']),
|
'http://mirrors.jenkins.io']),
|
||||||
|
updates_url_username=dict(type="str"),
|
||||||
|
updates_url_password=dict(type="str", no_log=True),
|
||||||
update_json_url_segment=dict(type="list", elements="str", default=['update-center.json',
|
update_json_url_segment=dict(type="list", elements="str", default=['update-center.json',
|
||||||
'updates/update-center.json']),
|
'updates/update-center.json']),
|
||||||
|
plugin_versions_url_segment=dict(type="list", elements="str", default=['plugin-versions.json',
|
||||||
|
'current/plugin-versions.json']),
|
||||||
latest_plugins_url_segments=dict(type="list", elements="str", default=['latest']),
|
latest_plugins_url_segments=dict(type="list", elements="str", default=['latest']),
|
||||||
versioned_plugins_url_segments=dict(type="list", elements="str", default=['download/plugins', 'plugins']),
|
versioned_plugins_url_segments=dict(type="list", elements="str", default=['download/plugins', 'plugins']),
|
||||||
url=dict(default='http://localhost:8080'),
|
url=dict(default='http://localhost:8080'),
|
||||||
|
@ -819,9 +969,6 @@ def main():
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Force basic authentication
|
|
||||||
module.params['force_basic_auth'] = True
|
|
||||||
|
|
||||||
# Convert timeout to float
|
# Convert timeout to float
|
||||||
try:
|
try:
|
||||||
module.params['timeout'] = float(module.params['timeout'])
|
module.params['timeout'] = float(module.params['timeout'])
|
||||||
|
@ -829,11 +976,17 @@ def main():
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg='Cannot convert %s to float.' % module.params['timeout'],
|
msg='Cannot convert %s to float.' % module.params['timeout'],
|
||||||
details=to_native(e))
|
details=to_native(e))
|
||||||
|
# Instantiate the JenkinsPlugin object
|
||||||
|
jp = JenkinsPlugin(module)
|
||||||
|
|
||||||
# Set version to latest if state is latest
|
# Set version to latest if state is latest
|
||||||
if module.params['state'] == 'latest':
|
if module.params['state'] == 'latest':
|
||||||
module.params['state'] = 'present'
|
module.params['state'] = 'present'
|
||||||
module.params['version'] = 'latest'
|
module.params['version'] = jp._get_latest_compatible_plugin_version()
|
||||||
|
|
||||||
|
# Set version to latest compatible version if version is latest
|
||||||
|
if module.params['version'] == 'latest':
|
||||||
|
module.params['version'] = jp._get_latest_compatible_plugin_version()
|
||||||
|
|
||||||
# Create some shortcuts
|
# Create some shortcuts
|
||||||
name = module.params['name']
|
name = module.params['name']
|
||||||
|
@ -842,9 +995,6 @@ def main():
|
||||||
# Initial change state of the task
|
# Initial change state of the task
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
# Instantiate the JenkinsPlugin object
|
|
||||||
jp = JenkinsPlugin(module)
|
|
||||||
|
|
||||||
# Perform action depending on the requested state
|
# Perform action depending on the requested state
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
changed = jp.install()
|
changed = jp.install()
|
||||||
|
@ -860,7 +1010,7 @@ def main():
|
||||||
changed = jp.disable()
|
changed = jp.disable()
|
||||||
|
|
||||||
# Print status of the change
|
# Print status of the change
|
||||||
module.exit_json(changed=changed, plugin=name, state=state)
|
module.exit_json(changed=changed, plugin=name, state=state, dependencies=jp.dependencies_states if hasattr(jp, 'dependencies_states') else None)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -6,9 +6,16 @@ from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import json
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from ansible_collections.community.general.plugins.modules.jenkins_plugin import JenkinsPlugin
|
from ansible_collections.community.general.plugins.modules.jenkins_plugin import JenkinsPlugin
|
||||||
from ansible.module_utils.common._collections_compat import Mapping
|
from ansible.module_utils.common._collections_compat import Mapping
|
||||||
|
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import (
|
||||||
|
MagicMock,
|
||||||
|
patch,
|
||||||
|
)
|
||||||
|
from ansible.module_utils.urls import basic_auth_header
|
||||||
|
|
||||||
|
|
||||||
def pass_function(*args, **kwargs):
|
def pass_function(*args, **kwargs):
|
||||||
|
@ -190,3 +197,98 @@ def isInList(l, i):
|
||||||
if item == i:
|
if item == i:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@patch("ansible_collections.community.general.plugins.modules.jenkins_plugin.fetch_url")
|
||||||
|
def test__get_latest_compatible_plugin_version(fetch_mock, mocker):
|
||||||
|
"test the latest compatible plugin version retrieval"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"url": "http://fake.jenkins.server",
|
||||||
|
"timeout": 30,
|
||||||
|
"name": "git",
|
||||||
|
"version": "latest",
|
||||||
|
"updates_url": ["https://some.base.url"],
|
||||||
|
"plugin_versions_url_segment": ["plugin-versions.json"],
|
||||||
|
"latest_plugins_url_segments": ["test_latest"],
|
||||||
|
"jenkins_home": "/var/lib/jenkins",
|
||||||
|
}
|
||||||
|
module = mocker.Mock()
|
||||||
|
module.params = params
|
||||||
|
|
||||||
|
jenkins_info = {"x-jenkins": "2.263.1"}
|
||||||
|
jenkins_response = MagicMock()
|
||||||
|
jenkins_response.read.return_value = b"{}"
|
||||||
|
|
||||||
|
plugin_data = {
|
||||||
|
"plugins": {
|
||||||
|
"git": OrderedDict([
|
||||||
|
("4.8.2", {"requiredCore": "2.263.1"}),
|
||||||
|
("4.8.3", {"requiredCore": "2.263.1"}),
|
||||||
|
("4.9.0", {"requiredCore": "2.289.1"}),
|
||||||
|
("4.9.1", {"requiredCore": "2.289.1"}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin_versions_response = MagicMock()
|
||||||
|
plugin_versions_response.read.return_value = json.dumps(plugin_data).encode("utf-8")
|
||||||
|
plugin_versions_info = {"status": 200}
|
||||||
|
|
||||||
|
def fetch_url_side_effect(module, url, **kwargs):
|
||||||
|
if "plugin-versions.json" in url:
|
||||||
|
return (plugin_versions_response, plugin_versions_info)
|
||||||
|
else:
|
||||||
|
return (jenkins_response, jenkins_info)
|
||||||
|
|
||||||
|
fetch_mock.side_effect = fetch_url_side_effect
|
||||||
|
|
||||||
|
JenkinsPlugin._csrf_enabled = lambda self: False
|
||||||
|
JenkinsPlugin._get_installed_plugins = lambda self: None
|
||||||
|
|
||||||
|
jenkins_plugin = JenkinsPlugin(module)
|
||||||
|
latest_version = jenkins_plugin._get_latest_compatible_plugin_version()
|
||||||
|
assert latest_version == '4.8.3'
|
||||||
|
|
||||||
|
|
||||||
|
@patch("ansible_collections.community.general.plugins.modules.jenkins_plugin.fetch_url")
|
||||||
|
def test__get_urls_data_sets_correct_headers(fetch_mock, mocker):
|
||||||
|
params = {
|
||||||
|
"url": "http://jenkins.example.com",
|
||||||
|
"timeout": 30,
|
||||||
|
"name": "git",
|
||||||
|
"jenkins_home": "/var/lib/jenkins",
|
||||||
|
"updates_url": ["http://updates.example.com"],
|
||||||
|
"latest_plugins_url_segments": ["latest"],
|
||||||
|
"update_json_url_segment": ["update-center.json"],
|
||||||
|
"versioned_plugins_url_segments": ["plugins"],
|
||||||
|
"url_username": "jenkins_user",
|
||||||
|
"url_password": "jenkins_pass",
|
||||||
|
"updates_url_username": "update_user",
|
||||||
|
"updates_url_password": "update_pass",
|
||||||
|
}
|
||||||
|
module = mocker.Mock()
|
||||||
|
module.params = params
|
||||||
|
|
||||||
|
dummy_response = MagicMock()
|
||||||
|
fetch_mock.return_value = (dummy_response, {"status": 200})
|
||||||
|
|
||||||
|
JenkinsPlugin._csrf_enabled = lambda self: False
|
||||||
|
JenkinsPlugin._get_installed_plugins = lambda self: None
|
||||||
|
|
||||||
|
jp = JenkinsPlugin(module)
|
||||||
|
|
||||||
|
update_url = "http://updates.example.com/plugin-versions.json"
|
||||||
|
jp._get_urls_data([update_url])
|
||||||
|
|
||||||
|
jenkins_url = "http://jenkins.example.com/some-endpoint"
|
||||||
|
jp._get_urls_data([jenkins_url])
|
||||||
|
|
||||||
|
calls = fetch_mock.call_args_list
|
||||||
|
|
||||||
|
dummy, kwargs_2 = calls[1]
|
||||||
|
jenkins_auth = basic_auth_header("jenkins_user", "jenkins_pass")
|
||||||
|
assert kwargs_2["headers"]["Authorization"] == jenkins_auth
|
||||||
|
|
||||||
|
dummy, kwargs_1 = calls[0]
|
||||||
|
updates_auth = basic_auth_header("update_user", "update_pass")
|
||||||
|
assert kwargs_1["headers"]["Authorization"] == updates_auth
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue