Skip to content

Commit

Permalink
[repo_setup] Download rhos-release using kerberos
Browse files Browse the repository at this point in the history
If the RPM name points to a URL we now use a custom plugin to fetch the
content. If the endpoint challenges the plugins with SPNEGO
authentication and a kerberos ticket is present the plugin will
authenticate itself using the ticket.
  • Loading branch information
pablintino authored and openshift-merge-bot[bot] committed Jan 13, 2025
1 parent 0484493 commit 7a168d2
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 1 deletion.
11 changes: 11 additions & 0 deletions docs/dictionary/en-custom.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
aaabbcc
abcdefghij
addr
afuscoar
alertmanager
ansible
ansibleee
Expand Down Expand Up @@ -105,6 +106,7 @@ ctl
ctlplane
ctrl
ctx
cve
customizations
dashboard
dataplane
Expand Down Expand Up @@ -165,6 +167,7 @@ fbqufbqkfbzxrja
fci
fedoraproject
fil
filesystem
fips
firewalld
flbxutz
Expand All @@ -174,6 +177,7 @@ freefonts
frmo
fsid
fultonj
fusco
fwcybtb
gapped
genericcloud
Expand Down Expand Up @@ -296,13 +300,15 @@ manpage
mawxlihjizaogicbjyxbzig
mawxlihjizcbwb
maxdepth
mcs
mellanox
metallb
metalsmith
mgmt
mins
minsizegigabytes
mlnx
mls
modprobe
mountpoints
mtcylje
Expand Down Expand Up @@ -464,7 +470,11 @@ scansettingbinding
scap
scp
sdk
selevel
selinux
serole
setype
seuser
sha
shiftstack
shiftstackclient
Expand All @@ -473,6 +483,7 @@ sizepercent
skbg
skiplist
specificities
spnego
spxzvbhvtzxmsihbyb
src
sshkey
Expand Down
192 changes: 192 additions & 0 deletions plugins/modules/url_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#!/usr/bin/python

# Copyright: (c) 2025, Pablo Rodriguez <[email protected]>
# Apache License Version 2.0 (see LICENSE)

__metaclass__ = type

DOCUMENTATION = r"""
---
module: url_request
short_description: Downloads/fetches the content of a SPNEGO secured URL
extends_documentation_fragment:
- files
description:
- Downloads/fetches the content of a SPNEGO secured URL
- A kerberos ticket should be already issued
author:
- Adrian Fusco (@afuscoar)
- Pablo Rodriguez (@pablintino)
options:
url:
description:
- The URL to retrieve the content from
required: True
type: str
verify_ssl:
description:
- Enables/disables using TLS to reach the URL
required: False
type: bool
default: true
dest:
description:
- Path to the destination file/dir where the content should be downloaded
- If not provided the content won't be written into disk
required: False
type: str
"""

EXAMPLES = r"""
- name: Get some content
url_request:
url: "http://someurl.local/resource"
dest: "{{ ansible_user_dir }}/content.raw"
mode: "0644"
register: _fetched_content
- name: Show base64 content
debug:
msg: "{{ _fetched_content.response_b64 }}"
"""

RETURN = r"""
status_code:
description: HTTP response code
type: int
returned: returned request
content_type:
description: HTTP response Content-Type header content
type: str
returned: returned request
headers:
description: HTTP response headers
type: dict
returned: returned request
response_b64:
description: Returned content base64 encoded
type: str
returned: successful request
response_json:
description: Returned content as a dict
type: str
returned: successful request that returned application/json
path:
description: Written file path
type: str
returned: successful request
"""

import base64
import os.path
import re

from ansible.module_utils.basic import AnsibleModule


try:
from requests import get

python_requests_installed = True
except ImportError:
python_requests_installed = False
try:
from requests_kerberos import HTTPKerberosAuth, OPTIONAL

python_requests_kerberos_installed = True
except ImportError:
python_requests_kerberos_installed = False


def main():
module_args = {
"url": {"type": "str", "required": True},
"verify_ssl": {"type": "bool", "default": True},
"dest": {"type": "str", "required": False},
}

result = {
"changed": False,
}

module = AnsibleModule(
argument_spec=module_args, supports_check_mode=False, add_file_common_args=True
)

if not python_requests_installed:
module.fail_json(msg="requests required for this module.")

if not python_requests_kerberos_installed:
module.fail_json(msg="requests_kerberos required for this module.")

url = module.params["url"]
verify_ssl = module.params["verify_ssl"]

auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
try:
response = get(url=url, auth=auth, verify=verify_ssl, allow_redirects=True)

result["status_code"] = response.status_code
result["headers"] = dict(response.headers)
result["content_type"] = response.headers.get("Content-Type", None)

if response.status_code < 200 or response.status_code >= 300:
module.fail_json(
msg=f"Error fetching the information {response.status_code}: {response.text}"
)

result["response_b64"] = base64.b64encode(response.content)
if "application/json" in result["content_type"]:
try:
result["response_json"] = response.json()
except ValueError as e:
module.fail_json(msg=f"Error with the JSON response: {str(e)}")

if "dest" in module.params:
dest = module.params["dest"]
if (
os.path.exists(dest)
and os.path.isdir(dest)
and "content-disposition" in response.headers
):
# Destination is a directory but the filename is available in Content-Disposition
filename = re.findall(
"filename=(.+)", response.headers["content-disposition"]
)
dest = filename[0] if filename else None
elif os.path.exists(dest) and os.path.isdir(dest):
# Destination is a directory but we cannot guess the filename from Content-Disposition
dest = None

if not dest:
# Reached if dest points to a directory and:
# - Content-Disposition not available
# - Cannot extract the filename part from the Content-Disposition header
module.fail_json(
msg="Destination points to a directory and the filename cannot be retrieved from the response"
)

exists = os.path.exists(dest)
original_sha1 = module.sha1(dest) if exists else None
with open(dest, mode="wb") as file:
file.write(response.content)
file_args = module.load_file_common_arguments(module.params, path=dest)
result["changed"] = (
(not exists)
or (module.sha1(dest) != original_sha1)
or module.set_fs_attributes_if_different(file_args, result["changed"])
)
result["path"] = dest

except Exception as e:
module.fail_json(msg=f"Error fetching the information: {str(e)}")

module.exit_json(**result)


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions roles/repo_setup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ using `cifmw_repo_setup_src` role default var.
* `cifmw_repo_setup_rhos_release_rpm`: (String) URL to rhos-release RPM.
* `cifmw_repo_setup_rhos_release_args`: (String) Parameters to pass down to `rhos-release`.
* `cifmw_repo_setup_rhos_release_gpg_check`: (Bool) Skips the gpg check during rhos-release rpm installation. Defaults to `True`.
* `cifmw_repo_setup_rhos_release_path`: (String) The path where the rhos-release rpm is downloaded. Defaults to `{{ cifmw_repo_setup_basedir }}/rhos-release`.

## Notes

Expand Down
1 change: 1 addition & 0 deletions roles/repo_setup/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ cifmw_repo_setup_component_promotion_tag: component-ci-testing
# cifmw_repo_setup_rhos_release_args: <arguments for rhos-release utility>
cifmw_repo_setup_enable_rhos_release: false
cifmw_repo_setup_rhos_release_gpg_check: true
cifmw_repo_setup_rhos_release_path: "{{ cifmw_repo_setup_basedir }}/rhos-release"
23 changes: 22 additions & 1 deletion roles/repo_setup/tasks/rhos_release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
---
- name: Download RHOS Release if the given rpm is a URL
when: cifmw_repo_setup_rhos_release_rpm is url
block:
- name: Create download directory
ansible.builtin.file:
path: "{{ cifmw_repo_setup_rhos_release_path }}"
state: directory
mode: "0755"

- name: Download the RPM
cifmw.general.url_request:
url: "{{ cifmw_repo_setup_rhos_release_rpm }}"
dest: "{{ cifmw_repo_setup_rhos_release_path }}/rhos-release.rpm"
mode: "0644"
register: _cifmw_repo_setup_url_get

- name: Install RHOS Release tool
become: true
ansible.builtin.package:
name: "{{ cifmw_repo_setup_rhos_release_rpm }}"
name: >-
{{
cifmw_repo_setup_rhos_release_rpm
if cifmw_repo_setup_rhos_release_rpm is not url
else _cifmw_repo_setup_url_get.path
}}
state: present
disable_gpg_check: "{{ cifmw_repo_setup_rhos_release_gpg_check | bool }}"

Expand Down
1 change: 1 addition & 0 deletions tests/sanity/ignore.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ plugins/modules/generate_make_tasks.py validate-modules:missing-gplv3-license #
plugins/modules/tempest_list_allowed.py validate-modules:missing-gplv3-license # ignore license check
plugins/modules/tempest_list_skipped.py validate-modules:missing-gplv3-license # ignore license check
plugins/modules/cephx_key.py validate-modules:missing-gplv3-license # ignore license check
plugins/modules/url_request.py validate-modules:missing-gplv3-license # ignore license check

0 comments on commit 7a168d2

Please sign in to comment.