From 3b1714152551fa8d3bd2adcfc117c2c354844fc5 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Thu, 3 May 2018 17:50:43 -0700 Subject: [PATCH] Fix for file module with symlinks to nonexistent target (#39635) * Fix for file module with symlinks to nonexistent target When creating a symlink to a nonexistent target, creating the symlink would work but subsequent runs of the task would fail because it was trying to operate on the target instead of the symlink. Fixes #39558 (cherry picked from commit 4f664f8ff6b3647e681ed1f74d96eace2ee7f114) --- changelogs/fragments/file-nonexistent-link.yaml | 5 +++++ lib/ansible/modules/files/file.py | 13 +++++++++++-- test/integration/targets/file/tasks/main.yml | 9 +++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 changelogs/fragments/file-nonexistent-link.yaml diff --git a/changelogs/fragments/file-nonexistent-link.yaml b/changelogs/fragments/file-nonexistent-link.yaml new file mode 100644 index 00000000000..67a799b1402 --- /dev/null +++ b/changelogs/fragments/file-nonexistent-link.yaml @@ -0,0 +1,5 @@ +--- +bugfixes: + - file module - Fix error when running a task which assures a symlink to + a nonexistent file exists for the second and subsequent times + (https://github.com/ansible/ansible/issues/39558) diff --git a/lib/ansible/modules/files/file.py b/lib/ansible/modules/files/file.py index 997d8401e93..53b4575e6b4 100644 --- a/lib/ansible/modules/files/file.py +++ b/lib/ansible/modules/files/file.py @@ -352,7 +352,6 @@ def main(): module.exit_json(path=path, changed=changed, diff=diff) elif state in ('link', 'hard'): - if not os.path.islink(b_path) and os.path.isdir(b_path): relpath = path else: @@ -436,7 +435,16 @@ def main(): if module.check_mode and not os.path.exists(b_path): module.exit_json(dest=path, src=src, changed=changed, diff=diff) - changed = module.set_fs_attributes_if_different(file_args, changed, diff, expand=False) + # Whenever we create a link to a nonexistent target we know that the nonexistent target + # cannot have any permissions set on it. Skip setting those and emit a warning (the user + # can set follow=False to remove the warning) + if (state == 'link' and params['follow'] and os.path.islink(params['path']) and + not os.path.exists(file_args['path'])): + module.warn('Cannot set fs attributes on a non-existent symlink target. follow should be' + ' set to False to avoid this.') + else: + changed = module.set_fs_attributes_if_different(file_args, changed, diff, expand=False) + module.exit_json(dest=path, src=src, changed=changed, diff=diff) elif state == 'touch': @@ -469,5 +477,6 @@ def main(): module.fail_json(path=path, msg='unexpected position reached') + if __name__ == '__main__': main() diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml index fd770c40305..b01df23e535 100644 --- a/test/integration/targets/file/tasks/main.yml +++ b/test/integration/targets/file/tasks/main.yml @@ -303,6 +303,15 @@ that: - "file13_result.changed == true" +- name: Prove idempotence of force creation soft link to non existent + file: src=/noneexistent dest={{output_dir}}/soft2.txt state=link force=yes + register: file13a_result + +- name: verify that the link to nonexistent is idempotent + assert: + that: + - "file13a_result.changed == false" + - name: remove directory foobar file: path={{output_dir}}/foobar state=absent register: file14_result