From b7e38dfa52024c1363cdd37fac3fce52ad421f08 Mon Sep 17 00:00:00 2001
From: Alexander Korsunsky <A.Korsunsky@gmail.com>
Date: Wed, 30 Oct 2019 17:17:11 +0100
Subject: [PATCH] copy - check for changes beyond first level of subdirectories
 (#58323)

Add integration test for copy: deep recursive with remote_src=True
---
 ...3-copy-deep-recursive-with-remote_src.yaml |  2 +
 lib/ansible/modules/files/copy.py             |  3 +
 test/integration/targets/copy/tasks/tests.yml | 56 +++++++++++++++++++
 3 files changed, 61 insertions(+)
 create mode 100644 changelogs/fragments/58323-copy-deep-recursive-with-remote_src.yaml

diff --git a/changelogs/fragments/58323-copy-deep-recursive-with-remote_src.yaml b/changelogs/fragments/58323-copy-deep-recursive-with-remote_src.yaml
new file mode 100644
index 00000000000..b8106f92326
--- /dev/null
+++ b/changelogs/fragments/58323-copy-deep-recursive-with-remote_src.yaml
@@ -0,0 +1,2 @@
+bugfixes:
+- copy - recursive copy with ``remote_src=yes`` now recurses beyond first level. (Fixes https://github.com/ansible/ansible/issues/58284)
diff --git a/lib/ansible/modules/files/copy.py b/lib/ansible/modules/files/copy.py
index 6912439af6f..21aa74c9379 100644
--- a/lib/ansible/modules/files/copy.py
+++ b/lib/ansible/modules/files/copy.py
@@ -485,6 +485,9 @@ def copy_common_dirs(src, dest, module):
         left_only_changed = copy_left_only(b_src_item_path, b_dest_item_path, module)
         if diff_files_changed or left_only_changed:
             changed = True
+
+        # recurse into subdirectory
+        changed = changed or copy_common_dirs(os.path.join(src, item), os.path.join(dest, item), module)
     return changed
 
 
diff --git a/test/integration/targets/copy/tasks/tests.yml b/test/integration/targets/copy/tasks/tests.yml
index 52c6bb39b80..c2e4f1e4661 100644
--- a/test/integration/targets/copy/tasks/tests.yml
+++ b/test/integration/targets/copy/tasks/tests.yml
@@ -1927,6 +1927,62 @@
       - "stat_testcase4_local_follow_false_remote_dir_src_link_file12.stat.exists"
       - "stat_testcase4_local_follow_false_remote_dir_src_link_file12.stat.islnk"
 
+- block:
+  - name: execute - Clone the source directory on remote
+    copy:
+      remote_src: True
+      src: '{{ remote_dir }}/remote_dir_src/'
+      dest: '{{ remote_dir }}/testcase5_remote_src_subdirs_src'
+  - name: Create a 2nd level subdirectory
+    file:
+      path: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/subdir/subdir2/'
+      state: directory
+  - name: execute - Copy the directory on remote
+    copy:
+      remote_src: True
+      src: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/'
+      dest: '{{ remote_dir }}/testcase5_remote_src_subdirs_dest'
+      local_follow: True
+  - name: execute - Create a new file in the subdir
+    copy:
+      dest: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/subdir/subdir2/file13'
+      content: 'very new file'
+  - name: gather - Stat the testcase5_remote_src_subdirs_src/subdir/subdir2/file13
+    stat:
+      path: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/subdir/subdir2/file13'
+  - name: execute - Copy the directory on remote
+    copy:
+      remote_src: True
+      src: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/'
+      dest: '{{ remote_dir }}/testcase5_remote_src_subdirs_dest/'
+    register: testcase5_new
+  - name: execute - Edit a file in the subdir
+    copy:
+      dest: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/subdir/subdir2/file13'
+      content: 'NOT hello world 12'
+  - name: gather - Stat the testcase5_remote_src_subdirs_src/subdir/subdir2/file13
+    stat:
+      path: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/subdir/subdir2/file13'
+    register: stat_testcase5_remote_src_subdirs_file13_before
+  - name: execute - Copy the directory on remote
+    copy:
+      remote_src: True
+      src: '{{ remote_dir }}/testcase5_remote_src_subdirs_src/'
+      dest: '{{ remote_dir }}/testcase5_remote_src_subdirs_dest/'
+    register: testcase5_edited
+  - name: gather - Stat the testcase5_remote_src_subdirs_dest/subdir/subdir2/file13
+    stat:
+      path: '{{ remote_dir }}/testcase5_remote_src_subdirs_dest/subdir/subdir2/file13'
+    register: stat_testcase5_remote_src_subdirs_file13
+  - name: assert - remote_dir_src has copied with local_follow False.
+    assert:
+      that:
+      - testcase5_new is changed
+      - testcase5_edited is changed
+      - "stat_testcase5_remote_src_subdirs_file13.stat.exists"
+      - "stat_testcase5_remote_src_subdirs_file13_before.stat.checksum == stat_testcase5_remote_src_subdirs_file13.stat.checksum"
+
+
 ## test copying the directory on remote with chown