diff --git a/changelogs/fragments/82878-fetch-dest-is-dir.yml b/changelogs/fragments/82878-fetch-dest-is-dir.yml new file mode 100644 index 00000000000..bf9c3bf088a --- /dev/null +++ b/changelogs/fragments/82878-fetch-dest-is-dir.yml @@ -0,0 +1,4 @@ +bugfixes: +- >- + fetch - add error message when using ``dest`` with a trailing slash that becomes a local directory - + https://github.com/ansible/ansible/issues/82878 diff --git a/lib/ansible/plugins/action/fetch.py b/lib/ansible/plugins/action/fetch.py index 4c62382ba7d..b7b6f30f9f8 100644 --- a/lib/ansible/plugins/action/fetch.py +++ b/lib/ansible/plugins/action/fetch.py @@ -149,6 +149,10 @@ class ActionModule(ActionBase): # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) + + if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')): + raise AnsibleActionFail( + f"calculated dest '{dest}' is an existing directory, use another path that does not point to an existing directory") if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml index d0bf9bdc42f..94941ed72cc 100644 --- a/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml +++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml @@ -28,6 +28,28 @@ register: failed_fetch_dest_dir ignore_errors: true +- block: + - name: create local dir for test + file: + path: "{{ output_dir }}/test dir/orig" + state: directory + delegate_to: localhost + + - name: Dest is a path that is calculated as an existing directory, should fail + fetch: + src: "{{ remote_tmp_dir }}/orig" + dest: "{{ output_dir }}/test dir/" + flat: true + register: failed_detch_dest_calc_dir + ignore_errors: true + + always: + - name: remote local dir for test + file: + path: "{{ output_dir }}/test dir" + state: absent + delegate_to: localhost + - name: Test unreachable fetch: src: "{{ remote_tmp_dir }}/orig" @@ -48,4 +70,6 @@ - failed_fetch_no_access.msg is search('file is not readable') - failed_fetch_dest_dir is failed - failed_fetch_dest_dir.msg is search('dest is an existing directory') + - failed_detch_dest_calc_dir is failed + - failed_detch_dest_calc_dir.msg is search("calculated dest '" ~ output_dir ~ "/test dir/orig' is an existing directory") - unreachable_fetch is unreachable