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

copy, avoid moving non temp remote 'non' files #79102

Merged
merged 2 commits into from Oct 17, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/dont_move_non_files.yml
@@ -0,0 +1,2 @@
bugfixes:
- copy module will no longer move 'non files' set as src when remote_src=true.
31 changes: 18 additions & 13 deletions lib/ansible/modules/copy.py
Expand Up @@ -576,23 +576,24 @@ def main():
module.params['mode'] = '0%03o' % stat.S_IMODE(os.stat(b_src).st_mode)
mode = module.params['mode']

changed = False

checksum_dest = None
checksum_src = None
md5sum_src = None

if os.path.isfile(src):
checksum_src = module.sha1(src)
else:
checksum_src = None

# Backwards compat only. This will be None in FIPS mode
try:
if os.path.isfile(src):
try:
checksum_src = module.sha1(src)
except (OSError, IOError) as e:
module.warn("Unable to calculate src checksum, assuming change: %s" % to_native(e))
try:
# Backwards compat only. This will be None in FIPS mode
md5sum_src = module.md5(src)
else:
md5sum_src = None
except ValueError:
md5sum_src = None

changed = False
except ValueError:
pass
elif remote_src and not os.path.isdir(src):
module.fail_json("Cannot copy invalid source '%s': not a file" % to_native(src))

if checksum and checksum_src != checksum:
module.fail_json(
Expand Down Expand Up @@ -658,6 +659,7 @@ def main():

backup_file = None
if checksum_src != checksum_dest or os.path.islink(b_dest):

if not module.check_mode:
try:
if backup:
Expand All @@ -681,8 +683,10 @@ def main():
(rc, out, err) = module.run_command(validate % src)
if rc != 0:
module.fail_json(msg="failed to validate", exit_status=rc, stdout=out, stderr=err)

b_mysrc = b_src
if remote_src and os.path.isfile(b_src):

_, b_mysrc = tempfile.mkstemp(dir=os.path.dirname(b_dest))

shutil.copyfile(b_src, b_mysrc)
Expand All @@ -702,6 +706,7 @@ def main():
# assume unwanted ACLs by default
src_has_acls = True

# at this point we should always have tmp file
module.atomic_move(b_mysrc, dest, unsafe_writes=module.params['unsafe_writes'])

if PY3 and hasattr(os, 'listxattr') and platform.system() == 'Linux' and not remote_src:
Expand Down
3 changes: 3 additions & 0 deletions test/integration/targets/copy/tasks/main.yml
Expand Up @@ -96,6 +96,9 @@
- 'diff_output.diff[0].before == ""'
- '"Ansible managed" in diff_output.diff[0].after'

- name: tests with remote_src and non files
import_tasks: src_remote_file_is_not_file.yml

always:
- name: Cleaning
file:
Expand Down
@@ -0,0 +1,39 @@
- name: test remote src non files
vars:
destfile: '{{remote_dir}}/whocares'
block:
- name: mess with dev/null
copy:
src: /dev/null
dest: "{{destfile}}"
remote_src: true
become: true
register: dev_null_fail
ignore_errors: true

- name: ensure we failed
assert:
that:
- dev_null_fail is failed
- "'not a file' in dev_null_fail.msg"

- name: now with file existing
file: state=touch path="{{destfile}}"

- name: mess with dev/null again
copy:
src: /dev/null
dest: "{{destfile}}"
remote_src: true
become: true
register: dev_null_fail
ignore_errors: true

- name: ensure we failed, again
assert:
that:
- dev_null_fail is failed
- "'not a file' in dev_null_fail.msg"
always:
- name: cleanup
file: state=absent path="{{destfile}}"