Repository: ansible/ansible

Issue:
yum_repository module always reports the status as changed

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report


##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
yum_repository
##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
# ansible --version
ansible 2.3.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.5 (default, Oct 11 2015, 17:47:16) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
pointed inventory at a directory containing my inventory sources
##### 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.
-->
RHEL 7.2
##### SUMMARY
<!--- Explain the problem briefly -->
If I run a task that creates yum repo file(s) in /etc/yum.repos.d, it always says they changed.
##### 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.
-->
Run the playbook pasted below twice. I would expect the 2nd run to show nothing changing.
<!--- Paste example playbooks or commands between quotes below -->
```yaml
---

- hosts: localhost
  vars:
    yum_servers:
      - server1
      - server2
    yum_repositories:
      - name: centos72
        description: CentOS-7.2 - Base
        baseurl: distros/CentOS/7/7.2.1511
      - name: centosplus
        description: CentOS-7 - Plus
        baseurl: distros/CentOS/7/centosplus/x86_64
        enabled: no
      - name: misc-rhel7
        description: misc YUM repo
        baseurl: repos/misc-rhel7
      - name: epel
        description: Extra Packages for Enterprise Linux 7
        baseurl: distros/Fedora-EPEL/7/x86_64
      - name: extras
        description: CentOS-7 - Extras
        baseurl: distros/CentOS/7/extras/x86_64
        enabled: no
      - name: updates
        description: CentOS-7 - Updates
        baseurl: distros/CentOS/7/updates/x86_64
        enabled: no
      - name: centos72-base
        description: CentOS-7 - Base
        baseurl: distros/CentOS/7/os/x86_64
        enabled: no
  tasks:
    - yum_repository:
        name: "{{item.name}}"
        description: "{{item.description}}"
        baseurl: |
          {% for yum_server in yum_servers %}
          https://{{yum_server}}/{{item.baseurl}}
          {% endfor %}
        enabled: "{{item.enabled|default(omit)}}"
      with_items: "{{yum_repositories}}"
      loop_control:
        label: "{{item.description}}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
The 2nd run should have reported no changes.
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
The second run showed changes.

I think that the multiple baseurl's are confusing yum_repository when it comes to deciding if something has changed. There is at least one extra newline in result.diff.after compared to result.diff.before .
<!--- Paste verbatim command output between quotes below -->
```
# ansible-playbook ~/yumrepo.yml 

PLAY [localhost] *************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [yum_repository] ********************************************************************************************************************************************************************************************************
changed: [localhost] => (item=CentOS-7.2 - Base)
changed: [localhost] => (item=CentOS-7 - Plus)
changed: [localhost] => (item=misc YUM repo)
changed: [localhost] => (item=Extra Packages for Enterprise Linux 7)
changed: [localhost] => (item=CentOS-7 - Extras)
changed: [localhost] => (item=CentOS-7 - Updates)
changed: [localhost] => (item=CentOS-7 - Base)

PLAY RECAP *******************************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0   

# ansible-playbook ~/yumrepo.yml 

PLAY [localhost] *************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [yum_repository] ********************************************************************************************************************************************************************************************************
changed: [localhost] => (item=CentOS-7.2 - Base)
changed: [localhost] => (item=CentOS-7 - Plus)
changed: [localhost] => (item=misc YUM repo)
changed: [localhost] => (item=Extra Packages for Enterprise Linux 7)
changed: [localhost] => (item=CentOS-7 - Extras)
changed: [localhost] => (item=CentOS-7 - Updates)
changed: [localhost] => (item=CentOS-7 - Base)

PLAY RECAP *******************************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0
```



Discussion:
cc @jtyr
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Thanks for reporting this, @GriffithLea. The problem is in the formatting of the `baseurl` string. Basically, there is a difference between how the string is written into the file and how it's read by the `configparser` Python module. You can use the following formatting to achieve what you need and still maintain the compatible formatting:

```
- yum_repository:
    name: "{{ item.name }}"
    description: "{{ item.description }}"
    baseurl: "{{ 'https://' ~ ( yum_servers | join('/' ~ item.baseurl ~ '\nhttps://') ) ~ '/' ~ item.baseurl }}"
    enabled: "{{ item.enabled|default(omit) }}"
  with_items: "{{ yum_repositories }}"
```

Using this kind of templating in playbook is not recommended and therefore I'm closing this issue as `notabug`.
Thanks, @jtyr. I dimly understand what your edit to my task is doing, but can you point me to some documentation that would help me understand it more fully? I've not come across anything that looks like that construct in all my Ansible travels. 😁 

BTW, at first I thought your fix would only help me if yum_servers|length == 2, but I see now that it does work with a list of arbitrary length.

By the way, would it be possible to have yum_repository.baseurl be a list? That would greatly simplify things.

Resolving patch:
diff --git a/lib/ansible/modules/packaging/os/yum_repository.py b/lib/ansible/modules/packaging/os/yum_repository.py
--- a/lib/ansible/modules/packaging/os/yum_repository.py
+++ b/lib/ansible/modules/packaging/os/yum_repository.py
@@ -9,10 +9,11 @@
 __metaclass__ = type
 
 
-ANSIBLE_METADATA = {'metadata_version': '1.0',
-                    'status': ['stableinterface'],
-                    'supported_by': 'core'}
-
+ANSIBLE_METADATA = {
+    'metadata_version': '1.0',
+    'status': ['stableinterface'],
+    'supported_by': 'core'
+}
 
 DOCUMENTATION = '''
 ---
@@ -47,6 +48,7 @@
     description:
       - URL to the directory where the yum repository's 'repodata' directory
         lives.
+      - It can also be a list of multiple URLs.
       - This or the I(mirrorlist) parameter is required if I(state) is set to
         C(present).
   cost:
@@ -130,6 +132,7 @@
     default: null
     description:
       - A URL pointing to the ASCII-armored GPG key file for the repository.
+      - It can also be a list of multiple URLs.
   http_caching:
     required: false
     choices: [all, packages, none]
@@ -635,7 +638,7 @@ def main():
         argument_spec=dict(
             async=dict(type='bool'),
             bandwidth=dict(),
-            baseurl=dict(),
+            baseurl=dict(type='list'),
             cost=dict(),
             deltarpm_metadata_percentage=dict(),
             deltarpm_percentage=dict(),
@@ -647,7 +650,7 @@ def main():
             file=dict(),
             gpgcakey=dict(),
             gpgcheck=dict(type='bool'),
-            gpgkey=dict(),
+            gpgkey=dict(type='list'),
             http_caching=dict(choices=['all', 'packages', 'none']),
             include=dict(),
             includepkgs=dict(),
@@ -717,6 +720,13 @@ def main():
     module.params['name'] = module.params['description']
     del module.params['description']
 
+    # Change list type to string for baseurl and gpgkey
+    for list_param in ['baseurl', 'gpgkey']:
+        if (
+                list_param in module.params and
+                module.params[list_param] is not None):
+            module.params[list_param] = "\n".join(module.params[list_param])
+
     # Define repo file name if it doesn't exist
     if module.params['file'] is None:
         module.params['file'] = module.params['repoid']


Test patch:
diff --git a/test/integration/targets/yum_repository/tasks/yum_repository_centos.yml b/test/integration/targets/yum_repository/tasks/yum_repository_centos.yml
--- a/test/integration/targets/yum_repository/tasks/yum_repository_centos.yml
+++ b/test/integration/targets/yum_repository/tasks/yum_repository_centos.yml
@@ -146,3 +146,31 @@
 - name: check Idempotant
   assert:
     that: not epel_add.changed
+
+- name: Test list for baseurl and gpgkey
+  yum_repository:
+    name: listtest
+    description: Testing list feature
+    baseurl:
+      - https://download.fedoraproject.org/pub/epel/{{ ansible_distribution_major_version }}/{{ ansible_architecture }}/
+      - https://download2.fedoraproject.org/pub/epel/{{ ansible_distribution_major_version }}/{{ ansible_architecture }}/
+    gpgkey:
+      - gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}
+      - gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG2-KEY-EPEL-{{ ansible_distribution_major_version }}
+
+- set_fact:
+    repofile: "{{ lookup('file', '/etc/yum.repos.d/listtest.repo') }}"
+
+- name: Assert that lists were properly inserted
+  assert:
+    that:
+      - "'download.fedoraproject.org' in repofile"
+      - "'download2.fedoraproject.org' in repofile"
+      - "'RPM-GPG-KEY-EPEL' in repofile"
+      - "'RPM-GPG2-KEY-EPEL' in repofile"
+    value:
+
+- name: Cleanup list test repo
+  yum_repository:
+    name: listtest
+    state: absent