mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-08-06 06:04:24 -07:00
Return to fetch_url
This commit is contained in:
parent
6414ae1453
commit
c47e2856be
3 changed files with 104 additions and 65 deletions
|
@ -1,6 +1,6 @@
|
||||||
bugfixes:
|
bugfixes:
|
||||||
- "jenkins plugins plugin - install latest compatible version instead of latest (https://github.com/ansible-collections/community.general/pull/10346)."
|
- "jenkins_plugin - install latest compatible version instead of latest (https://github.com/ansible-collections/community.general/issues/854)."
|
||||||
- "jenkins plugins plugin - seperate Jenkins and external url credentials (https://github.com/ansible-collections/community.general/pull/10346)."
|
- "jenkins_plugin - seperate Jenkins and external URL credentials (https://github.com/ansible-collections/community.general/issues/4419)."
|
||||||
|
|
||||||
minor_changes:
|
minor_changes:
|
||||||
- "jenkins plugins plugin - install dependencies for specific version (https://github.com/ansible-collections/community.general/pull/10346)."
|
- "jenkins_plugin - install dependencies for specific version (https://github.com/ansible-collections/community.general/issue/4995)."
|
||||||
|
|
|
@ -76,14 +76,16 @@ options:
|
||||||
default: ['https://updates.jenkins.io', 'http://mirrors.jenkins.io']
|
default: ['https://updates.jenkins.io', 'http://mirrors.jenkins.io']
|
||||||
updates_url_username:
|
updates_url_username:
|
||||||
description:
|
description:
|
||||||
- If using a custom O(updates_url), set this as the username of the user with access to the url.
|
- 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.
|
- If the custom O(updates_url) does not require authentication, this can be left empty.
|
||||||
type: str
|
type: str
|
||||||
|
version_added: 11.1.0
|
||||||
updates_url_password:
|
updates_url_password:
|
||||||
description:
|
description:
|
||||||
- If using a custom O(updates_url), set this as the password of the user with access to the url.
|
- 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.
|
- If the custom O(updates_url) does not require authentication, this can be left empty.
|
||||||
type: str
|
type: str
|
||||||
|
version_added: 11.1.0
|
||||||
update_json_url_segment:
|
update_json_url_segment:
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
|
@ -122,6 +124,8 @@ options:
|
||||||
with_dependencies:
|
with_dependencies:
|
||||||
description:
|
description:
|
||||||
- Defines whether to install plugin dependencies.
|
- Defines whether to install plugin dependencies.
|
||||||
|
- In earlier versions, this option had no effect when a specific C(version) was set.
|
||||||
|
- Since community.general 11.1.0, dependencies are also installed for versioned plugins.
|
||||||
type: bool
|
type: bool
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
|
@ -325,13 +329,12 @@ import json
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import base64
|
|
||||||
from collections import OrderedDict
|
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, open_url
|
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
|
||||||
|
|
||||||
|
@ -355,18 +358,14 @@ class JenkinsPlugin(object):
|
||||||
# Authentication for non-Jenkins calls
|
# Authentication for non-Jenkins calls
|
||||||
self.updates_url_credentials = {}
|
self.updates_url_credentials = {}
|
||||||
if self.params.get('updates_url_username') and self.params.get('updates_url_password'):
|
if self.params.get('updates_url_username') and self.params.get('updates_url_password'):
|
||||||
auth = "{}:{}".format(self.params['updates_url_username'], self.params['updates_url_password']).encode("utf-8")
|
self.updates_url_credentials["Authorization"] = basic_auth_header(self.params['updates_url_username'], self.params['updates_url_password'])
|
||||||
b64_auth = base64.b64encode(auth).decode("ascii")
|
|
||||||
self.updates_url_credentials["Authorization"] = "Basic {}".format(b64_auth)
|
|
||||||
|
|
||||||
# Crumb
|
# Crumb
|
||||||
self.crumb = {}
|
self.crumb = {}
|
||||||
|
|
||||||
# Authentication for Jenkins calls
|
# Authentication for Jenkins calls
|
||||||
if self.params.get('url_username') and self.params.get('url_password'):
|
if self.params.get('url_username') and self.params.get('url_password'):
|
||||||
auth = "{}:{}".format(self.params['url_username'], self.params['url_password']).encode("utf-8")
|
self.crumb["Authorization"] = basic_auth_header(self.params['url_username'], self.params['url_password'])
|
||||||
b64_auth = base64.b64encode(auth).decode("ascii")
|
|
||||||
self.crumb["Authorization"] = "Basic {}".format(b64_auth)
|
|
||||||
|
|
||||||
# Cookie jar for crumb session
|
# Cookie jar for crumb session
|
||||||
self.cookies = None
|
self.cookies = None
|
||||||
|
@ -418,16 +417,18 @@ class JenkinsPlugin(object):
|
||||||
self.module.debug("fetching url: %s" % url)
|
self.module.debug("fetching url: %s" % url)
|
||||||
|
|
||||||
is_jenkins_call = url.startswith(self.url)
|
is_jenkins_call = url.startswith(self.url)
|
||||||
|
self.module.params['force_basic_auth'] = is_jenkins_call
|
||||||
|
|
||||||
response = open_url(
|
response, info = fetch_url(
|
||||||
url, timeout=self.timeout,
|
self.module, url, timeout=self.timeout, cookies=self.cookies,
|
||||||
cookies=self.cookies if is_jenkins_call else None,
|
headers=self.crumb if is_jenkins_call else self.updates_url_credentials or self.crumb,
|
||||||
headers=self.crumb if is_jenkins_call else self.updates_url_credentials, **kwargs)
|
**kwargs)
|
||||||
if response.getcode() == 200:
|
if info['status'] == 200:
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
err_msg = ("%s. fetching url %s failed. response code: %s" % (msg_status, url, response.getcode()))
|
err_msg = ("%s. fetching url %s failed. response code: %s" % (msg_status, url, info['status']))
|
||||||
|
if info['status'] > 400: # extend error message
|
||||||
|
err_msg = "%s. response body: %s" % (err_msg, info['body'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err_msg = "%s. fetching url %s failed. error msg: %s" % (msg_status, url, to_native(e))
|
err_msg = "%s. fetching url %s failed. error msg: %s" % (msg_status, url, to_native(e))
|
||||||
finally:
|
finally:
|
||||||
|
@ -451,19 +452,18 @@ class JenkinsPlugin(object):
|
||||||
# Get the URL data
|
# Get the URL data
|
||||||
try:
|
try:
|
||||||
is_jenkins_call = url.startswith(self.url)
|
is_jenkins_call = url.startswith(self.url)
|
||||||
response = open_url(
|
self.module.params['force_basic_auth'] = is_jenkins_call
|
||||||
url, timeout=self.timeout,
|
|
||||||
cookies=self.cookies if is_jenkins_call else None,
|
|
||||||
headers=self.crumb if is_jenkins_call else self.updates_url_credentials, **kwargs)
|
|
||||||
|
|
||||||
if response.getcode() != 200:
|
response, info = fetch_url(
|
||||||
|
self.module, url, timeout=self.timeout, cookies=self.cookies,
|
||||||
|
headers=self.crumb if is_jenkins_call else self.updates_url_credentials or self.crumb,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
if info['status'] != 200:
|
||||||
if dont_fail:
|
if dont_fail:
|
||||||
raise FailedInstallingWithPluginManager("HTTP {}".format(response.getcode()))
|
raise FailedInstallingWithPluginManager(info['msg'])
|
||||||
else:
|
else:
|
||||||
self.module.fail_json(
|
self.module.fail_json(msg=msg_status, details=info['msg'])
|
||||||
msg=msg_status,
|
|
||||||
details="Received status code {} from {}".format(response.getcode(), url)
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if dont_fail:
|
if dont_fail:
|
||||||
raise FailedInstallingWithPluginManager(e)
|
raise FailedInstallingWithPluginManager(e)
|
||||||
|
@ -679,6 +679,7 @@ class JenkinsPlugin(object):
|
||||||
|
|
||||||
def _get_latest_compatible_plugin_version(self, plugin_name=None):
|
def _get_latest_compatible_plugin_version(self, plugin_name=None):
|
||||||
if not hasattr(self, 'jenkins_version'):
|
if not hasattr(self, 'jenkins_version'):
|
||||||
|
self.module.params['force_basic_auth'] = True
|
||||||
resp, info = fetch_url(self.module, self.url)
|
resp, info = fetch_url(self.module, self.url)
|
||||||
raw_version = info.get("x-jenkins")
|
raw_version = info.get("x-jenkins")
|
||||||
self.jenkins_version = self.parse_version(raw_version)
|
self.jenkins_version = self.parse_version(raw_version)
|
||||||
|
@ -694,9 +695,9 @@ class JenkinsPlugin(object):
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError("Cache file is outdated.")
|
raise FileNotFoundError("Cache file is outdated.")
|
||||||
except Exception:
|
except Exception:
|
||||||
response = open_url("https://updates.jenkins.io/current/plugin-versions.json") # Get list of plugins and their dependencies
|
response, info = fetch_url(self.module, "https://updates.jenkins.io/current/plugin-versions.json") # Get list of plugins and their dependencies
|
||||||
|
|
||||||
if response.getcode() != 200:
|
if info['status'] != 200:
|
||||||
self.module.fail_json(msg="Failed to fetch plugin-versions.json", details=info)
|
self.module.fail_json(msg="Failed to fetch plugin-versions.json", details=info)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -949,9 +950,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'])
|
||||||
|
|
|
@ -7,7 +7,6 @@ __metaclass__ = type
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import json
|
import json
|
||||||
import socket
|
|
||||||
from collections import OrderedDict
|
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
|
||||||
|
@ -16,6 +15,7 @@ from ansible_collections.community.internal_test_tools.tests.unit.compat.mock im
|
||||||
MagicMock,
|
MagicMock,
|
||||||
patch,
|
patch,
|
||||||
)
|
)
|
||||||
|
from ansible.module_utils.urls import basic_auth_header
|
||||||
|
|
||||||
|
|
||||||
def pass_function(*args, **kwargs):
|
def pass_function(*args, **kwargs):
|
||||||
|
@ -199,9 +199,8 @@ def isInList(l, i):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@patch("ansible_collections.community.general.plugins.modules.jenkins_plugin.open_url")
|
|
||||||
@patch("ansible_collections.community.general.plugins.modules.jenkins_plugin.fetch_url")
|
@patch("ansible_collections.community.general.plugins.modules.jenkins_plugin.fetch_url")
|
||||||
def test__get_latest_compatible_plugin_version(fetch_mock, open_mock, mocker):
|
def test__get_latest_compatible_plugin_version(fetch_mock, mocker):
|
||||||
"test the latest compatible plugin version retrieval"
|
"test the latest compatible plugin version retrieval"
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
|
@ -216,37 +215,79 @@ def test__get_latest_compatible_plugin_version(fetch_mock, open_mock, mocker):
|
||||||
module = mocker.Mock()
|
module = mocker.Mock()
|
||||||
module.params = params
|
module.params = params
|
||||||
|
|
||||||
mock_response = MagicMock()
|
jenkins_info = {"x-jenkins": "2.263.1"}
|
||||||
mock_response.read.return_value = b""
|
jenkins_response = MagicMock()
|
||||||
fetch_mock.return_value = (mock_response, {"x-jenkins": "2.263.1"})
|
jenkins_response.read.return_value = b"{}"
|
||||||
|
|
||||||
try:
|
plugin_data = {
|
||||||
socket.gethostbyname("updates.jenkins.io")
|
"plugins": {
|
||||||
online = True
|
"git": OrderedDict([
|
||||||
except socket.gaierror:
|
("4.8.2", {"requiredCore": "2.263.1"}),
|
||||||
online = False
|
("4.8.3", {"requiredCore": "2.263.1"}),
|
||||||
|
("4.9.0", {"requiredCore": "2.289.1"}),
|
||||||
# Mock the open_url to simulate the response from Jenkins update center if tests are run offline
|
("4.9.1", {"requiredCore": "2.289.1"}),
|
||||||
if not online:
|
])
|
||||||
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"}),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mock_open_resp = MagicMock()
|
}
|
||||||
mock_open_resp.getcode.return_value = 200
|
plugin_versions_response = MagicMock()
|
||||||
mock_open_resp.read.return_value = json.dumps(plugin_data).encode("utf-8")
|
plugin_versions_response.read.return_value = json.dumps(plugin_data).encode("utf-8")
|
||||||
open_mock.return_value = mock_open_resp
|
plugin_versions_info = {"status": 200}
|
||||||
|
|
||||||
JenkinsPlugin._csrf_enabled = pass_function
|
def fetch_url_side_effect(module, url, **kwargs):
|
||||||
JenkinsPlugin._get_installed_plugins = pass_function
|
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)
|
jenkins_plugin = JenkinsPlugin(module)
|
||||||
|
|
||||||
latest_version = jenkins_plugin._get_latest_compatible_plugin_version()
|
latest_version = jenkins_plugin._get_latest_compatible_plugin_version()
|
||||||
assert latest_version == '4.8.3'
|
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