diff --git a/.yamllint b/.yamllint index 73d53b68..a6707e29 100644 --- a/.yamllint +++ b/.yamllint @@ -45,9 +45,9 @@ rules: braces: min-spaces-inside: 0 max-spaces-inside: 1 - #octal-values: - # forbid-implicit-octal: true - # forbid-explicit-octal: true + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true comments: min-spaces-from-content: 1 comments-indentation: false diff --git a/.yamllint-examples b/.yamllint-examples index bb3b7f74..062ac5aa 100644 --- a/.yamllint-examples +++ b/.yamllint-examples @@ -46,9 +46,9 @@ rules: braces: min-spaces-inside: 0 max-spaces-inside: 1 - #octal-values: - # forbid-implicit-octal: true - # forbid-explicit-octal: true + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true comments: min-spaces-from-content: 1 comments-indentation: false diff --git a/changelogs/fragments/1074-mode-octal.yml b/changelogs/fragments/1074-mode-octal.yml new file mode 100644 index 00000000..7b8ee081 --- /dev/null +++ b/changelogs/fragments/1074-mode-octal.yml @@ -0,0 +1,2 @@ +minor_changes: + - "docker_container_copy_into - add ``mode_parse`` parameter which determines how ``mode`` is parsed (https://github.com/ansible-collections/community.docker/pull/1074)." diff --git a/plugins/modules/docker_container_copy_into.py b/plugins/modules/docker_container_copy_into.py index 3c48f485..7d2a144e 100644 --- a/plugins/modules/docker_container_copy_into.py +++ b/plugins/modules/docker_container_copy_into.py @@ -95,8 +95,26 @@ options: description: - The file mode to use when writing the file to disk. - Will use the file's mode from the source system if this option is not provided. - - Note that if you provide an octal number as a string, Ansible will parse it as a B(decimal) number. - type: int + - This option is parsed depending on how O(mode_parse) is set. + type: raw + mode_parse: + description: + - Determines how to parse the O(mode) parameter. + type: str + choices: + legacy: + - Parses the value of O(mode) as an integer. + - Note that if you provide an octal number as a string to O(mode), it will be parsed as a B(decimal) number. + If you provide an octal integer directly, though, it will work as expected. + - This has been the default behavior of the module since it was added to community.docker. + modern: + - Parses the value of O(mode) as an octal string, or takes the integer value if an integer has been provided. + - This is how M(ansible.builtin.copy) treats its O(ansible.builtin.copy#module:mode) option. + octal_string_only: + - Rejects everything that is not a string that can be parsed as an octal number. + - Use this value to ensure that no accidental conversion to integers happen. + default: legacy + version_added: 4.6.0 force: description: - If set to V(true), force writing the file (without performing any idempotency checks). @@ -134,7 +152,8 @@ EXAMPLES = r""" container_path: /bin/runme owner_id: 0 # root group_id: 0 # root - mode: 0755 # readable and executable by all users, writable by root + mode: "0755" # readable and executable by all users, writable by root + mode_parse: modern # ensure that strings passed for 'mode' are passed as octal numbers """ RETURN = r""" @@ -153,6 +172,8 @@ import stat import traceback from ansible.module_utils._text import to_bytes, to_native, to_text +from ansible.module_utils.common.validation import check_type_int +from ansible.module_utils.six import integer_types, string_types from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException, NotFound @@ -761,6 +782,20 @@ def copy_content_into_container(client, container, content, container_path, foll client.module.exit_json(**result) +def parse_modern(mode): + if isinstance(mode, string_types): + return int(to_native(mode), 8) + if isinstance(mode, integer_types): + return mode + raise TypeError('must be an octal string or an integer, got {mode!r}'.format(mode=mode)) + + +def parse_octal_string_only(mode): + if isinstance(mode, string_types): + return int(to_native(mode), 8) + raise TypeError('must be an octal string, got {mode!r}'.format(mode=mode)) + + def main(): argument_spec = dict( container=dict(type='str', required=True), @@ -770,7 +805,8 @@ def main(): local_follow=dict(type='bool', default=True), owner_id=dict(type='int'), group_id=dict(type='int'), - mode=dict(type='int'), + mode=dict(type='raw'), + mode_parse=dict(type='str', choices=['legacy', 'modern', 'octal_string_only'], default='legacy'), force=dict(type='bool'), content=dict(type='str', no_log=True), content_is_b64=dict(type='bool', default=False), @@ -802,6 +838,20 @@ def main(): content = client.module.params['content'] max_file_size_for_diff = client.module.params['_max_file_size_for_diff'] or 1 + if mode is not None: + mode_parse = client.module.params['mode_parse'] + try: + if mode_parse == 'legacy': + mode = check_type_int(mode) + elif mode_parse == 'modern': + mode = parse_modern(mode) + elif mode_parse == 'octal_string_only': + mode = parse_octal_string_only(mode) + except (TypeError, ValueError) as e: + client.fail("Error while parsing 'mode': {error}".format(error=e)) + if mode < 0: + client.fail("'mode' must not be negative; got {mode}".format(mode=mode)) + if content is not None: if client.module.params['content_is_b64']: try: diff --git a/tests/integration/targets/docker_container_copy_into/tasks/tests/content.yml b/tests/integration/targets/docker_container_copy_into/tasks/tests/content.yml index b49c05a9..5f295ddd 100644 --- a/tests/integration/targets/docker_container_copy_into/tasks/tests/content.yml +++ b/tests/integration/targets/docker_container_copy_into/tasks/tests/content.yml @@ -56,7 +56,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: false register: result_1 @@ -67,7 +68,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: true register: result_1_diff @@ -78,7 +80,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern register: result_2 - name: Copy content (idempotent, check mode) @@ -87,7 +90,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: false register: result_3 @@ -98,7 +102,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: true register: result_3_diff @@ -109,7 +114,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern register: result_4 - name: Copy content (idempotent, check mode, base 64) @@ -118,7 +124,8 @@ content: "{{ 'Content 1\n' | b64encode }}" content_is_b64: true container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: false register: result_3b64 @@ -129,7 +136,8 @@ content: "{{ 'Content 1\n' | b64encode }}" content_is_b64: true container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: true register: result_3b64_diff @@ -140,7 +148,8 @@ content: "{{ 'Content 1\n' | b64encode }}" content_is_b64: true container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern register: result_4b64 - name: Dump file @@ -161,7 +170,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern force: true check_mode: true diff: false @@ -173,7 +183,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern force: true check_mode: true diff: true @@ -185,7 +196,8 @@ content: | Content 1 container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern force: true register: result_7 @@ -207,7 +219,8 @@ content: | Some other content container_path: '/file' - mode: 0777 + mode: "0777" + mode_parse: modern owner_id: 123 group_id: 321 force: false @@ -221,7 +234,8 @@ content: | Some other content container_path: '/file' - mode: 0777 + mode: "0777" + mode_parse: modern owner_id: 123 group_id: 321 force: false @@ -235,7 +249,8 @@ content: | Some other content container_path: '/file' - mode: 0777 + mode: "0777" + mode_parse: modern owner_id: 123 group_id: 321 force: false @@ -253,6 +268,117 @@ chdir: /root register: result_11 +- name: Copy content (octal mode, mode_parse=modern) + docker_container_copy_into: + container: '{{ cname }}' + content: | + Content 1 + container_path: '/file' + # yamllint disable + mode: 0770 + # yamllint enable + mode_parse: modern + owner_id: 0 + group_id: 0 + register: result_12 + +- name: Dump file + docker_container_exec: + container: '{{ cname }}' + argv: + - /bin/sh + - "-c" + - >- + cat /file | base64; + stat -c '%s %a %F %u %g %N' /file > /dev/stderr + chdir: /root + register: result_13 + +- name: Copy content (octal mode, mode_parse=legacy) + docker_container_copy_into: + container: '{{ cname }}' + content: | + Content 1 + container_path: '/file' + # yamllint disable + mode: 0707 + # yamllint enable + mode_parse: legacy + owner_id: 0 + group_id: 0 + register: result_14 + +- name: Dump file + docker_container_exec: + container: '{{ cname }}' + argv: + - /bin/sh + - "-c" + - >- + cat /file | base64; + stat -c '%s %a %F %u %g %N' /file > /dev/stderr + chdir: /root + register: result_15 + +- name: Copy content (string mode, mode_parse=legacy) + docker_container_copy_into: + container: '{{ cname }}' + content: | + Content 1 + container_path: '/file' + mode: "420" + mode_parse: legacy + owner_id: 0 + group_id: 0 + register: result_16 + +- name: Dump file + docker_container_exec: + container: '{{ cname }}' + argv: + - /bin/sh + - "-c" + - >- + cat /file | base64; + stat -c '%s %a %F %u %g %N' /file > /dev/stderr + chdir: /root + register: result_17 + +- name: Copy content (string mode, mode_parse=octal_string_only) + docker_container_copy_into: + container: '{{ cname }}' + content: | + Content 1 + container_path: '/file' + mode: "0600" + mode_parse: octal_string_only + owner_id: 0 + group_id: 0 + register: result_18 + +- name: Dump file + docker_container_exec: + container: '{{ cname }}' + argv: + - /bin/sh + - "-c" + - >- + cat /file | base64; + stat -c '%s %a %F %u %g %N' /file > /dev/stderr + chdir: /root + register: result_19 + +- name: Restore state for next tasks + docker_container_copy_into: + container: '{{ cname }}' + content: | + Content 1 + container_path: '/file' + mode: "0644" + mode_parse: octal_string_only + owner_id: 0 + group_id: 0 + - name: Check results assert: that: @@ -302,6 +428,18 @@ - result_10 is not changed - result_11.stdout | b64decode == 'Content 1\n' - result_11.stderr == '10 644 regular file 0 0 /file' + - result_12 is changed + - result_13.stdout | b64decode == 'Content 1\n' + - result_13.stderr == '10 770 regular file 0 0 /file' + - result_14 is changed + - result_15.stdout | b64decode == 'Content 1\n' + - result_15.stderr == '10 707 regular file 0 0 /file' + - result_16 is changed + - result_17.stdout | b64decode == 'Content 1\n' + - result_17.stderr == '10 644 regular file 0 0 /file' + - result_18 is changed + - result_19.stdout | b64decode == 'Content 1\n' + - result_19.stderr == '10 600 regular file 0 0 /file' ######################### Follow link - idempotence @@ -323,7 +461,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: true check_mode: true diff: false @@ -335,7 +474,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: true check_mode: true diff: true @@ -347,7 +487,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: true register: result_2 @@ -370,7 +511,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: true force: true check_mode: true @@ -383,7 +525,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: true force: true check_mode: true @@ -396,7 +539,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: true force: true register: result_5 @@ -454,7 +598,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: false check_mode: true diff: false @@ -466,7 +611,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: false check_mode: true diff: true @@ -478,7 +624,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern follow: false register: result_2 @@ -488,7 +635,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: false register: result_3 @@ -499,7 +647,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: true register: result_3_diff @@ -510,7 +659,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern register: result_4 - name: Dump file @@ -531,7 +681,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern force: true check_mode: true diff: false @@ -543,7 +694,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern force: true check_mode: true diff: true @@ -555,7 +707,8 @@ content: | Content 1 container_path: '/lnk' - mode: 0644 + mode: "0644" + mode_parse: modern force: true register: result_7 @@ -615,7 +768,8 @@ content: | Content 1 container_path: '/dir' - mode: 0644 + mode: "0644" + mode_parse: modern follow: false check_mode: true diff: false @@ -627,7 +781,8 @@ content: | Content 1 container_path: '/dir' - mode: 0644 + mode: "0644" + mode_parse: modern follow: false check_mode: true diff: true @@ -639,7 +794,8 @@ content: | Content 1 container_path: '/dir' - mode: 0644 + mode: "0644" + mode_parse: modern follow: false register: result_2 @@ -649,7 +805,8 @@ content: | Content 1 container_path: '/dir' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: false register: result_3 @@ -660,7 +817,8 @@ content: | Content 1 container_path: '/dir' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: true register: result_3_diff @@ -671,7 +829,8 @@ content: | Content 1 container_path: '/dir' - mode: 0644 + mode: "0644" + mode_parse: modern register: result_4 - name: Dump file @@ -719,7 +878,8 @@ Content 2 Extra line container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: false register: result_1 @@ -731,7 +891,8 @@ Content 2 Extra line container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern check_mode: true diff: true register: result_1_diff @@ -743,7 +904,8 @@ Content 2 Extra line container_path: '/file' - mode: 0644 + mode: "0644" + mode_parse: modern register: result_2 - name: Dump file @@ -781,7 +943,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: false register: result_1 @@ -793,7 +956,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: true register: result_1_diff @@ -805,7 +969,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern register: result_2 - name: Copy content (idempotent, check mode) @@ -815,7 +980,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: false register: result_3 @@ -827,7 +993,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: true register: result_3_diff @@ -839,7 +1006,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern register: result_4 - name: Dump file @@ -885,7 +1053,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -899,7 +1068,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -913,7 +1083,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_2 @@ -925,7 +1096,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -939,7 +1111,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -953,7 +1126,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_4 @@ -977,7 +1151,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 13 group_id: 13 check_mode: true @@ -991,7 +1166,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 13 group_id: 13 check_mode: true @@ -1005,7 +1181,8 @@ Content 2 Extra line container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 13 group_id: 13 register: result_7 @@ -1068,7 +1245,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -1081,7 +1259,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -1094,7 +1273,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_2 @@ -1105,7 +1285,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -1118,7 +1299,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -1131,7 +1313,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_4 @@ -1142,7 +1325,8 @@ content: | Content 1 container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern register: result_5 ignore_errors: true diff --git a/tests/integration/targets/docker_container_copy_into/tasks/tests/file.yml b/tests/integration/targets/docker_container_copy_into/tasks/tests/file.yml index b6fca448..96f43d5b 100644 --- a/tests/integration/targets/docker_container_copy_into/tasks/tests/file.yml +++ b/tests/integration/targets/docker_container_copy_into/tasks/tests/file.yml @@ -34,7 +34,7 @@ dest: '{{ remote_tmp_dir }}/file_1' content: | Content 1 - mode: 0644 + mode: "0644" - name: Create file 2 copy: @@ -42,7 +42,7 @@ content: |- Content 2 Extra line - mode: 0644 + mode: "0644" - name: Create link 1 file: @@ -50,7 +50,7 @@ state: link src: file_1 follow: false - mode: 0644 + mode: "0644" - name: Create link 2 file: @@ -59,7 +59,7 @@ src: dead force: true follow: false - mode: 0644 + mode: "0644" ################################################################################################ # Do tests @@ -173,7 +173,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0777 + mode: "0777" + mode_parse: modern owner_id: 123 group_id: 321 force: false @@ -186,7 +187,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0777 + mode: "0777" + mode_parse: modern owner_id: 123 group_id: 321 force: false @@ -199,7 +201,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0777 + mode: "0777" + mode_parse: modern owner_id: 123 group_id: 321 force: false @@ -684,7 +687,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: false register: result_1 @@ -694,7 +698,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: true register: result_1_diff @@ -704,7 +709,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern register: result_2 - name: Copy file (idempotent, check mode) @@ -712,7 +718,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: false register: result_3 @@ -722,7 +729,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern check_mode: true diff: true register: result_3_diff @@ -732,7 +740,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern register: result_4 - name: Dump file @@ -776,7 +785,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -788,7 +798,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -800,7 +811,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_2 @@ -810,7 +822,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -822,7 +835,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -834,7 +848,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_4 @@ -856,7 +871,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 13 group_id: 13 check_mode: true @@ -868,7 +884,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 13 group_id: 13 check_mode: true @@ -880,7 +897,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_2' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 13 group_id: 13 register: result_7 @@ -942,7 +960,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -954,7 +973,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -966,7 +986,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_2 @@ -976,7 +997,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -988,7 +1010,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 check_mode: true @@ -1000,7 +1023,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern owner_id: 12 group_id: 910 register: result_4 @@ -1010,7 +1034,8 @@ container: '{{ cname }}' path: '{{ remote_tmp_dir }}/file_1' container_path: '/file' - mode: 0707 + mode: "0707" + mode_parse: modern register: result_5 ignore_errors: true diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 14ec3dfd..0a0b790b 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -2,5 +2,6 @@ plugins/inventory/docker_containers.py yamllint:unparsable-with-libyaml plugins/inventory/docker_machine.py yamllint:unparsable-with-libyaml plugins/inventory/docker_swarm.py yamllint:unparsable-with-libyaml +plugins/modules/docker_container_copy_into.py validate-modules:invalid-documentation plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin plugins/modules/docker_image_build.py validate-modules:invalid-documentation diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index 82720e59..e50c576e 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -1,5 +1,6 @@ plugins/inventory/docker_containers.py yamllint:unparsable-with-libyaml plugins/inventory/docker_machine.py yamllint:unparsable-with-libyaml plugins/inventory/docker_swarm.py yamllint:unparsable-with-libyaml +plugins/modules/docker_container_copy_into.py validate-modules:invalid-documentation plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin plugins/modules/docker_image_build.py validate-modules:invalid-documentation diff --git a/tests/unit/plugins/modules/test_docker_container_copy_into.py b/tests/unit/plugins/modules/test_docker_container_copy_into.py new file mode 100644 index 00000000..dfbf5983 --- /dev/null +++ b/tests/unit/plugins/modules/test_docker_container_copy_into.py @@ -0,0 +1,68 @@ +# Copyright 2025 Felix Fontein +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible_collections.community.docker.plugins.modules.docker_container_copy_into import parse_modern, parse_octal_string_only + + +@pytest.mark.parametrize("input, expected", [ + ('0777', 0o777), + ('777', 0o777), + ('0o777', 0o777), + ('0755', 0o755), + ('755', 0o755), + ('0o755', 0o755), + ('0644', 0o644), + ('644', 0o644), + ('0o644', 0o644), + (' 0644 ', 0o644), + (' 644 ', 0o644), + (' 0o644 ', 0o644), + ('-1', -1), +]) +def test_parse_string(input, expected): + assert parse_modern(input) == expected + assert parse_octal_string_only(input) == expected + + +@pytest.mark.parametrize("input", [ + 0o777, + 0o755, + 0o644, + 12345, + 123456789012345678901234567890123456789012345678901234567890, +]) +def test_parse_int(input): + assert parse_modern(input) == input + with pytest.raises(TypeError, match="^must be an octal string, got {value}L?$".format(value=input)): + parse_octal_string_only(input) + + +@pytest.mark.parametrize("input", [ + 1.0, + 755.5, + [], + {}, +]) +def test_parse_bad_type(input): + with pytest.raises(TypeError, match="^must be an octal string or an integer, got "): + parse_modern(input) + with pytest.raises(TypeError, match="^must be an octal string, got "): + parse_octal_string_only(input) + + +@pytest.mark.parametrize("input", [ + "foo", + "8", + "9", +]) +def test_parse_bad_value(input): + with pytest.raises(ValueError): + parse_modern(input) + with pytest.raises(ValueError): + parse_octal_string_only(input)