diff options
-rw-r--r-- | changelogs/fragments/get_url_bsd_style_digest.yml | 3 | ||||
-rw-r--r-- | lib/ansible/modules/get_url.py | 55 | ||||
-rw-r--r-- | test/integration/targets/get_url/tasks/main.yml | 22 | ||||
-rw-r--r-- | test/units/modules/test_get_url.py | 45 |
4 files changed, 104 insertions, 21 deletions
diff --git a/changelogs/fragments/get_url_bsd_style_digest.yml b/changelogs/fragments/get_url_bsd_style_digest.yml new file mode 100644 index 0000000000..fe4a6f288c --- /dev/null +++ b/changelogs/fragments/get_url_bsd_style_digest.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - get_url - add support for BSD-style checksum digest file (https://github.com/ansible/ansible/issues/84476). diff --git a/lib/ansible/modules/get_url.py b/lib/ansible/modules/get_url.py index 52c812c0c6..563ae5a61e 100644 --- a/lib/ansible/modules/get_url.py +++ b/lib/ansible/modules/get_url.py @@ -460,6 +460,37 @@ def is_url(checksum): return urlsplit(checksum).scheme in supported_schemes +def parse_digest_lines(filename, lines): + """Returns a list of tuple containing the filename and digest depending upon + the lines provided + + Args: + filename (str): Name of the filename, used only when the digest is one-liner + lines (list): A list of lines containing filenames and checksums + """ + checksum_map = [] + BSD_DIGEST_LINE = re.compile(r'^(\w+) ?\((?P<path>.+)\) ?= (?P<digest>[\w.]+)$') + GNU_DIGEST_LINE = re.compile(r'^(?P<digest>[\w.]+) ([ *])(?P<path>.+)$') + + if len(lines) == 1 and len(lines[0].split()) == 1: + # Only a single line with a single string + # treat it as a checksum only file + checksum_map.append((lines[0], filename)) + return checksum_map + # The assumption here is the file is in the format of + # checksum filename + for line in lines: + match = BSD_DIGEST_LINE.match(line) + if match: + checksum_map.append((match.group('digest'), match.group('path'))) + else: + match = GNU_DIGEST_LINE.match(line) + if match: + checksum_map.append((match.group('digest'), match.group('path').lstrip("./"))) + + return checksum_map + + # ============================================================== # main @@ -527,31 +558,13 @@ def main(): if is_url(checksum): checksum_url = checksum # download checksum file to checksum_tmpsrc - checksum_tmpsrc, checksum_info = url_get(module, checksum_url, dest, use_proxy, last_mod_time, force, timeout, headers, tmp_dest, - unredirected_headers=unredirected_headers, ciphers=ciphers, use_netrc=use_netrc) + checksum_tmpsrc, _dummy = url_get(module, checksum_url, dest, use_proxy, last_mod_time, force, timeout, headers, tmp_dest, + unredirected_headers=unredirected_headers, ciphers=ciphers, use_netrc=use_netrc) with open(checksum_tmpsrc) as f: lines = [line.rstrip('\n') for line in f] os.remove(checksum_tmpsrc) - checksum_map = [] filename = url_filename(url) - if len(lines) == 1 and len(lines[0].split()) == 1: - # Only a single line with a single string - # treat it as a checksum only file - checksum_map.append((lines[0], filename)) - else: - # The assumption here is the file is in the format of - # checksum filename - for line in lines: - # Split by one whitespace to keep the leading type char ' ' (whitespace) for text and '*' for binary - parts = line.split(" ", 1) - if len(parts) == 2: - # Remove the leading type char, we expect - if parts[1].startswith((" ", "*",)): - parts[1] = parts[1][1:] - - # Append checksum and path without potential leading './' - checksum_map.append((parts[0], parts[1].lstrip("./"))) - + checksum_map = parse_digest_lines(filename=filename, lines=lines) # Look through each line in the checksum file for a hash corresponding to # the filename in the url, returning the first hash that is found. for cksum in (s for (s, f) in checksum_map if f == filename): diff --git a/test/integration/targets/get_url/tasks/main.yml b/test/integration/targets/get_url/tasks/main.yml index 66bd129368..0ec6afd202 100644 --- a/test/integration/targets/get_url/tasks/main.yml +++ b/test/integration/targets/get_url/tasks/main.yml @@ -376,6 +376,15 @@ 30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 *not_target1.txt d0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b *not_target2.txt +- name: create sha256 checksum file of src in BSD-style checksum (--tag) + copy: + dest: '{{ files_dir }}/sha256sum_bsd_style.txt' + content: | + SHA256 (27617.txt) = b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. + SHA256 (71420.txt) = b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. + SHA256 (not_target1.txt) = 30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 + SHA256 (not_target2.txt) = d0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b + # completing 27617 with bug 54390 - name: create sha256 checksum only with no filename inside copy: @@ -463,6 +472,17 @@ path: "{{ remote_tmp_dir }}/27617sha256_with_dot.txt" register: stat_result_sha256_with_file_scheme +- name: download src with sha256 checksum url with BSD style checksum + get_url: + url: 'http://localhost:{{ http_port }}/27617.txt' + dest: '{{ remote_tmp_dir }}/27617sha256_with_bsd_style.txt' + checksum: 'sha256:file://{{ files_dir }}/sha256sum_bsd_style.txt' + register: result_sha256_with_bsd_style + +- stat: + path: "{{ remote_tmp_dir }}/27617sha256_with_bsd_style.txt" + register: stat_result_sha256_with_bsd_style + - name: download 71420.txt with sha1 checksum url get_url: url: 'http://localhost:{{ http_port }}/71420.txt' @@ -538,11 +558,13 @@ - result_sha256_with_dot is changed - result_sha256_with_asterisk is changed - result_sha256_with_file_scheme is changed + - result_sha256_with_bsd_style is changed - "stat_result_sha1.stat.exists == true" - "stat_result_sha256.stat.exists == true" - "stat_result_sha256_with_dot.stat.exists == true" - "stat_result_sha256_with_asterisk.stat.exists == true" - "stat_result_sha256_with_file_scheme.stat.exists == true" + - stat_result_sha256_with_bsd_style.stat.exists - result_sha1_71420 is changed - result_sha256_71420 is changed - result_sha256_with_dot_71420 is changed diff --git a/test/units/modules/test_get_url.py b/test/units/modules/test_get_url.py new file mode 100644 index 0000000000..9e09634193 --- /dev/null +++ b/test/units/modules/test_get_url.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import annotations + +import pytest + +from ansible.modules.get_url import parse_digest_lines + + +@pytest.mark.parametrize( + ("lines", "expected"), + [ + pytest.param( + [ + "a97e6837f60cec6da4491bab387296bbcd72bdba", + ], + [("a97e6837f60cec6da4491bab387296bbcd72bdba", "sample.txt")], + id="single-line-digest", + ), + pytest.param( + [ + "a97e6837f60cec6da4491bab387296bbcd72bdba sample.txt", + ], + [("a97e6837f60cec6da4491bab387296bbcd72bdba", "sample.txt")], + id="GNU-style-digest", + ), + pytest.param( + [ + "SHA256 (sample.txt) = b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006.", + ], + [ + ( + "b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006.", + "sample.txt", + ) + ], + id="BSD-style-digest", + ), + ], +) +def test_parse_digest_lines(lines, expected): + filename = "sample.txt" + assert parse_digest_lines(filename, lines) == expected |