From dbd8d0a4925e1ad6b8b3f9001862b98817a5f8df Mon Sep 17 00:00:00 2001
From: Rick Elrod <rick@elrod.me>
Date: Mon, 23 Mar 2020 17:28:53 -0500
Subject: [PATCH] Pull timezone's incidental file coverage into file tests
 (#68247)

* Remove some unreachable code in the file module

Remove some cases in file.py which are covered by conditionals a few
lines earlier. Remove the duplicate code which will never be hit.

Signed-off-by: Rick Elrod <rick@elrod.me>

* Restore incidental file coverage from timezone module

Signed-off-by: Rick Elrod <rick@elrod.me>

* Combine two conditionals, add a changelog entry

Signed-off-by: Rick Elrod <rick@elrod.me>

* Make new test syntax consistent, add two stat tests

Signed-off-by: Rick Elrod <rick@elrod.me>
---
 .../68247-file-unreachable-code.yaml          |  2 +
 lib/ansible/modules/files/file.py             | 18 +-------
 .../targets/file/tasks/state_link.yml         | 43 +++++++++++++++++++
 3 files changed, 46 insertions(+), 17 deletions(-)
 create mode 100644 changelogs/fragments/68247-file-unreachable-code.yaml

diff --git a/changelogs/fragments/68247-file-unreachable-code.yaml b/changelogs/fragments/68247-file-unreachable-code.yaml
new file mode 100644
index 00000000000..ea2c01a8232
--- /dev/null
+++ b/changelogs/fragments/68247-file-unreachable-code.yaml
@@ -0,0 +1,2 @@
+bugfixes:
+  - file - Removed unreachable code in module
diff --git a/lib/ansible/modules/files/file.py b/lib/ansible/modules/files/file.py
index 7ec3c3117e8..bd313baf9e8 100644
--- a/lib/ansible/modules/files/file.py
+++ b/lib/ansible/modules/files/file.py
@@ -707,7 +707,7 @@ def ensure_symlink(path, src, follow, force, timestamps):
     diff = initial_diff(path, 'link', prev_state)
     changed = False
 
-    if prev_state == 'absent':
+    if prev_state in ('hard', 'file', 'directory', 'absent'):
         changed = True
     elif prev_state == 'link':
         b_old_src = os.readlink(b_path)
@@ -715,22 +715,6 @@ def ensure_symlink(path, src, follow, force, timestamps):
             diff['before']['src'] = to_native(b_old_src, errors='strict')
             diff['after']['src'] = src
             changed = True
-    elif prev_state == 'hard':
-        changed = True
-        if not force:
-            raise AnsibleModuleError(results={'msg': 'Cannot link because a hard link exists at destination',
-                                              'dest': path, 'src': src})
-    elif prev_state == 'file':
-        changed = True
-        if not force:
-            raise AnsibleModuleError(results={'msg': 'Cannot link because a file exists at destination',
-                                              'dest': path, 'src': src})
-    elif prev_state == 'directory':
-        changed = True
-        if os.path.exists(b_path):
-            if not force:
-                raise AnsibleModuleError(results={'msg': 'Cannot link because a file exists at destination',
-                                                  'dest': path, 'src': src})
     else:
         raise AnsibleModuleError(results={'msg': 'unexpected position reached', 'dest': path, 'src': src})
 
diff --git a/test/integration/targets/file/tasks/state_link.yml b/test/integration/targets/file/tasks/state_link.yml
index 55f959de810..15927881d8d 100644
--- a/test/integration/targets/file/tasks/state_link.yml
+++ b/test/integration/targets/file/tasks/state_link.yml
@@ -360,3 +360,46 @@
     that:
       - src_state is failed
       - "'src option requires state to be' in src_state.msg"
+
+# Test creating a symlink when the destination exists and is a file
+- name: create a test file
+  copy:
+    dest: '{{ output_dir }}/file.txt'
+    content: 'this is a test file\n'
+    mode: 0666
+
+- name: Create a symlink with dest already a file
+  file:
+    src: '{{ output_file }}'
+    dest: '{{ output_dir }}/file.txt'
+    state: link
+  ignore_errors: true
+  register: dest_is_existing_file_fail
+
+- name: Stat to make sure the symlink was not created
+  stat:
+    path: '{{ output_dir }}/file.txt'
+    follow: false
+  register: dest_is_existing_file_fail_stat
+
+- name: Forcefully a symlink with dest already a file
+  file:
+    src: '{{ output_file }}'
+    dest: '{{ output_dir }}/file.txt'
+    state: link
+    force: true
+  register: dest_is_existing_file_force
+
+- name: Stat to make sure the symlink was created
+  stat:
+    path: '{{ output_dir }}/file.txt'
+    follow: false
+  register: dest_is_existing_file_force_stat
+
+- assert:
+    that:
+    - dest_is_existing_file_fail is failed
+    - not dest_is_existing_file_fail_stat.stat.islnk
+    - dest_is_existing_file_force is changed
+    - dest_is_existing_file_force_stat.stat.exists
+    - dest_is_existing_file_force_stat.stat.islnk