Repository: ansible/ansible

Issue:
1.6 :: Synchronize module loses trailing slash when used in a role with relative source path
Hello,
##### Issue Type:

Bug Report
##### Ansible Version:

1.6
##### Environment

:: host :: ArchLinux
:: managed ::  Debian 7
##### Summary:

Just found a problem with synchronization of folder contents when relative source path specified in the role
It makes sense when we synchronize content to folder with other name
Ansible loses trailing slash when absolute local path generated
##### Steps To Reproduce:

Example playbook for debug on local box (`test-synchronize.yml`):

``` yaml
- name: 'Synchronize :: Test trailing slash :: task'
  hosts: vagrant
  remote_user: vagrant
  sudo_user: root
  sudo: True

  tasks:
   - name: 'Sync files'
     synchronize: src=tmp/testABC/
                  dest=/tmp/testXYZ/
                  recursive=yes
                  delete=yes
```

and same with role (`test-synchronize-role.yml`):

``` yaml
- name: 'Synchronize :: Test trailing slash :: role'
  hosts: vagrant
  remote_user: vagrant
  sudo_user: root
  sudo: True

  roles:
   - test-sync
```

Role code (`roles/test-sync/main.yml`):

``` yaml

---

- name: 'Sync files'
  synchronize: src=tmp/testABC/
               dest=/tmp/testXYZ/
               recursive=yes
               delete=yes
```

Content of source folder:

```
» tree tmp
tmp
└── testABC
    ├── a
    ├── b
    └── c
```

`tmp` folder located on same level as playbooks
##### Expected Results:

It is OK when no role used to invoke synchronize
Playbook run output: http://pastebin.com/zfR1N9YY
Managed machine content:

```
# tree /tmp/testXYZ/
/tmp/testXYZ/
├── a
├── b
└── c
```
##### Actual Results:

Now I run playbook with role over results of previous playbook
Playbook run output: http://pastebin.com/tdmtNX0U
Managed machine content:

```
# tree testXYZ/
testXYZ/
├── a
├── b
├── c
└── testABC
    ├── a
    ├── b
    └── c
```

Verbose log output of playbook shows lack of trailing slash on source path for rsync:
`/home/xt/projects/ADYAX/adyax-ansible-v2/prj/tmp/testABC` (should be `/home/xt/projects/ADYAX/adyax-ansible-v2/prj/tmp/testABC/`)



Discussion:
I have made some research and found parts of code responsible of local absolute path generation for rsync souce path
It is synchronize action plugin https://github.com/ansible/ansible/blob/release1.6.0/lib/ansible/runner/action_plugins/synchronize.py#L34
And `path_dwim` and `path_dwim_relative` functions on Ansible utils
https://github.com/ansible/ansible/blob/release1.6.0/lib/ansible/utils/__init__.py#L285

Construction `os.path.abspath(os.path.join(basedir, given))` always return path without trailing slash, even if it specified in `given`


Resolving patch:
diff --git a/lib/ansible/runner/action_plugins/synchronize.py b/lib/ansible/runner/action_plugins/synchronize.py
--- a/lib/ansible/runner/action_plugins/synchronize.py
+++ b/lib/ansible/runner/action_plugins/synchronize.py
@@ -32,7 +32,12 @@ def _get_absolute_path(self, path=None):
         if 'vars' in self.inject:
             if '_original_file' in self.inject['vars']:
                 # roles
+                original_path = path
                 path = utils.path_dwim_relative(self.inject['_original_file'], 'files', path, self.runner.basedir)
+                if original_path and original_path[-1] == '/' and path[-1] != '/':
+                    # make sure the dwim'd path ends in a trailing "/"
+                    # if the original path did
+                    path += '/'
             elif 'inventory_dir' in self.inject['vars']:
                 # non-roles
                 abs_dir = os.path.abspath(self.inject['vars']['inventory_dir'])


Test patch: