Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[repo_setup] Download rhos-release using kerberos #2632

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
evallesp marked this conversation as resolved.
Show resolved Hide resolved
"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"
evallesp marked this conversation as resolved.
Show resolved Hide resolved
)

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:
evallesp marked this conversation as resolved.
Show resolved Hide resolved
- 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:
evallesp marked this conversation as resolved.
Show resolved Hide resolved
url: "{{ cifmw_repo_setup_rhos_release_rpm }}"
dest: "{{ cifmw_repo_setup_rhos_release_path }}/rhos-release.rpm"
mode: "0644"
frenzyfriday marked this conversation as resolved.
Show resolved Hide resolved
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
Loading