Repository: ansible/ansible

Issue:
ansible-galaxy install ignores -p switch when roles_path is configured
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Name of the module, plugin, task or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
ansible-galaxy

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0
  config file = /tmp/foo/ansible.cfg
  configured module search path = [u'<HOME>/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = <HOME>/.local/python2/ansible-devel/lib/python2.7/site-packages/ansible
  executable location = <HOME>/.local/python2/ansible-devel/bin/ansible
  python version = 2.7.14 (default, Sep 20 2017, 01:25:59) [GCC 7.2.0]
```

```
ansible 2.4.2.0
  config file = /tmp/foo/ansible.cfg
  configured module search path = [u'<HOME>/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = <HOME>/.local/python2/ansible-release/lib/python2.7/site-packages/ansible
  executable location = <HOME>/.local/python2/ansible-release/bin/ansible
  python version = 2.7.14 (default, Sep 20 2017, 01:25:59) [GCC 7.2.0]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->

```
ANSIBLE_COW_SELECTION(/tmp/foo/ansible.cfg) = random
ANSIBLE_NOCOWS(env: ANSIBLE_NOCOWS) = True
ANSIBLE_PIPELINING(/tmp/foo/ansible.cfg) = True
ANSIBLE_SSH_ARGS(/tmp/foo/ansible.cfg) = -F ssh_config
DEFAULT_HOST_LIST(/tmp/foo/ansible.cfg) = [u'/tmp/foo/hosts']
DEFAULT_ROLES_PATH(/tmp/foo/ansible.cfg) = [u'/tmp/foo/roles', u'/tmp/roles', u'/tmp', u'/']
DEFAULT_VAULT_PASSWORD_FILE(/tmp/foo/ansible.cfg) = /tmp/foo/vault
HOST_KEY_CHECKING(/tmp/foo/ansible.cfg) = False
RETRY_FILES_ENABLED(/tmp/foo/ansible.cfg) = False
```

* **NOTE**: `roles_path` is configured to: `../:../../:../roles:roles/`


##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
N/A

##### SUMMARY
<!--- Explain the problem briefly -->
ansible-galaxy install ignores command line switch `-p` (`--roles-path`) if `roles_path` is configured via ansible.cfg. ansible-galaxy install keeps telling the role would be installed already.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

```bash
install -d /tmp/ansible-galaxy-install-ignore-roles-path-bug
cd /tmp/ansible-galaxy-install-ignore-roles-path-bug
git clone https://github.com/retr0h/ansible-etcd.git
ansible-galaxy init roles-path-bug
cd roles-path-bug
cat <<EOF > ansible.cfg
[defaults]
roles_path = ../:../../:../roles:roles/
EOF
cat <<EOF > requirements.yml
---
- src: https://github.com/retr0h/ansible-etcd.git
  name: ansible-etcd
EOF
ansible-galaxy install -r requirements.yml -p roles/
```

<!--- Paste example playbooks or commands between quotes below -->
```bash
ansible-galaxy install -r requirements.yml -p roles/
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->

* Command line switches win over configuration values (**always!**)
* `roles/` directory getting created and role from requirements.yml installed inside `roles/` directory

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

* ansible-galaxy install states the role would already be installed and asks to use `--force` to replace it.

In my scenario, having roles and `"projects"` (bundling dependencies and inventories in git repositories) in the same directory, this might delete content on the disk which has not yet been pushed to a remote git service (as it happened to me yesterday).

<!--- Paste verbatim command output between quotes below -->
```
- extracting ansible-etcd to /tmp/ansible-galaxy-install-ignore-roles-path-bug/ansible-etcd
 [WARNING]: - ansible-etcd was NOT installed successfully: the specified role
ansible-etcd appears to already exist. Use --force to replace it.

ERROR! - you can use --ignore-errors to skip failed roles and finish processing the list.
```



Discussion:
Files identified in the description:
* [lib/ansible/galaxy](https://github.com/ansible/ansible/blob/devel/lib/ansible/galaxy)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/lib/ansible/galaxy/role.py b/lib/ansible/galaxy/role.py
--- a/lib/ansible/galaxy/role.py
+++ b/lib/ansible/galaxy/role.py
@@ -71,17 +71,11 @@ def __init__(self, galaxy, name, src=None, version=None, scm=None, path=None):
                 path = os.path.join(path, self.name)
             self.path = path
         else:
-            for role_path_dir in galaxy.roles_paths:
-                role_path = os.path.join(role_path_dir, self.name)
-                if os.path.exists(role_path):
-                    self.path = role_path
-                    break
-            else:
-                # use the first path by default
-                self.path = os.path.join(galaxy.roles_paths[0], self.name)
-                # create list of possible paths
-                self.paths = [x for x in galaxy.roles_paths]
-                self.paths = [os.path.join(x, self.name) for x in self.paths]
+            # use the first path by default
+            self.path = os.path.join(galaxy.roles_paths[0], self.name)
+            # create list of possible paths
+            self.paths = [x for x in galaxy.roles_paths]
+            self.paths = [os.path.join(x, self.name) for x in self.paths]
 
     def __repr__(self):
         """


Test patch:
diff --git a/test/integration/targets/ansible-galaxy/aliases b/test/integration/targets/ansible-galaxy/aliases
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/ansible-galaxy/aliases
@@ -0,0 +1 @@
+posix/ci/group3
diff --git a/test/integration/targets/ansible-galaxy/runme.sh b/test/integration/targets/ansible-galaxy/runme.sh
new file mode 100755
--- /dev/null
+++ b/test/integration/targets/ansible-galaxy/runme.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+
+set -eux -o pipefail
+
+# Need a relative custom roles path for testing various scenarios of -p
+galaxy_relative_rolespath="my/custom/roles/path"
+
+# Prep the local git repo with a role and make a tar archive so we can test
+# different things
+galaxy_local_test_role="test-role"
+galaxy_local_test_role_dir=$(mktemp -d)
+galaxy_local_test_role_git_repo="${galaxy_local_test_role_dir}/${galaxy_local_test_role}"
+galaxy_local_test_role_tar="${galaxy_local_test_role_dir}/${galaxy_local_test_role}.tar"
+pushd "${galaxy_local_test_role_dir}"
+    ansible-galaxy init "${galaxy_local_test_role}"
+    pushd "${galaxy_local_test_role}"
+        git init .
+
+        # Prep git, becuase it doesn't work inside a docker container without it
+        git config user.email "tester@ansible.com"
+        git config user.name "Ansible Tester"
+
+        git add .
+        git commit -m "local testing ansible galaxy role"
+        git archive \
+            --format=tar \
+            --prefix="${galaxy_local_test_role}/" \
+            master > "${galaxy_local_test_role_tar}"
+    popd # "${galaxy_local_test_role}"
+popd # "${galaxy_local_test_role_dir}"
+
+# Status message function (f_ to designate that it's a function)
+f_ansible_galaxy_status()
+{
+
+    printf "### Testing ansible-galaxy: %s\n" "${@}"
+}
+
+# Galaxy install test case
+#
+# Install local git repo
+f_ansible_galaxy_status "install of local git repo"
+galaxy_testdir=$(mktemp -d)
+pushd "${galaxy_testdir}"
+
+    ansible-galaxy install git+file:///"${galaxy_local_test_role_git_repo}"
+
+    # Test that the role was installed to the expected directory
+    [[ -d "${HOME}/.ansible/roles/${galaxy_local_test_role}" ]]
+popd # ${galaxy_testdir}
+rm -fr "${galaxy_testdir}"
+
+# Galaxy install test case
+#
+# Install local git repo and ensure that if a role_path is passed, it is in fact used
+f_ansible_galaxy_status "install of local git repo with -p \$role_path"
+galaxy_testdir=$(mktemp -d)
+pushd "${galaxy_testdir}"
+    mkdir -p "${galaxy_relative_rolespath}"
+
+    ansible-galaxy install git+file:///"${galaxy_local_test_role_git_repo}" -p "${galaxy_relative_rolespath}"
+
+    # Test that the role was installed to the expected directory
+    [[ -d "${galaxy_relative_rolespath}/${galaxy_local_test_role}" ]]
+popd # ${galaxy_testdir}
+rm -fr "${galaxy_testdir}"
+
+# Galaxy install test case
+#
+# Ensure that if both a role_file and role_path is provided, they are both
+# honored
+#
+# Protect against regression (GitHub Issue #35217)
+#   https://github.com/ansible/ansible/issues/35217
+
+f_ansible_galaxy_status \
+    "install of local git repo and local tarball with -p \$role_path and -r \$role_file" \
+    "Protect against regression (Issue #35217)"
+galaxy_testdir=$(mktemp -d)
+pushd "${galaxy_testdir}"
+
+    git clone "${galaxy_local_test_role_git_repo}" "${galaxy_local_test_role}"
+    ansible-galaxy init roles-path-bug
+    pushd roles-path-bug
+        cat <<EOF > ansible.cfg
+[defaults]
+roles_path = ../:../../:../roles:roles/
+EOF
+        cat <<EOF > requirements.yml
+---
+- src: ${galaxy_local_test_role_tar}
+  name: ${galaxy_local_test_role}
+EOF
+
+        ansible-galaxy install -r requirements.yml -p roles/
+    popd # roles-path-bug
+
+    # Test that the role was installed to the expected directory
+    [[ -d "${galaxy_testdir}/roles-path-bug/roles/${galaxy_local_test_role}" ]]
+
+popd # ${galaxy_testdir}
+rm -fr "${galaxy_testdir}"
+
+
+rm -fr "${galaxy_local_test_role_dir}"