From 666dfdc55163d74c5ab8557dad53c137ef054e1d Mon Sep 17 00:00:00 2001
From: Adam Miller <admiller@redhat.com>
Date: Wed, 15 May 2019 01:45:55 -0500
Subject: [PATCH] YUM - handle enable of non-existent repo (#53286)

---
 .../fragments/yum-enable-missing-repo.yaml    |  3 ++
 lib/ansible/modules/packaging/os/yum.py       | 37 +++++++++++++------
 test/integration/targets/yum/tasks/yum.yml    | 37 +++++++++++++++++++
 3 files changed, 66 insertions(+), 11 deletions(-)
 create mode 100644 changelogs/fragments/yum-enable-missing-repo.yaml

diff --git a/changelogs/fragments/yum-enable-missing-repo.yaml b/changelogs/fragments/yum-enable-missing-repo.yaml
new file mode 100644
index 00000000000..15cc854f75c
--- /dev/null
+++ b/changelogs/fragments/yum-enable-missing-repo.yaml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+  - "yum - gracefully handle failure case of enabling a non existent repo, as the yum cli does (Fixes https://github.com/ansible/ansible/issues/52582)"
diff --git a/lib/ansible/modules/packaging/os/yum.py b/lib/ansible/modules/packaging/os/yum.py
index f765538a141..b16dcaa31d5 100644
--- a/lib/ansible/modules/packaging/os/yum.py
+++ b/lib/ansible/modules/packaging/os/yum.py
@@ -332,7 +332,7 @@ EXAMPLES = '''
 '''
 
 from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
+from ansible.module_utils._text import to_native, to_text
 from ansible.module_utils.urls import fetch_url
 from ansible.module_utils.yumdnf import YumDnf, yumdnf_argument_spec
 
@@ -390,6 +390,26 @@ class YumModule(YumDnf):
         self.pkg_mgr_name = "yum"
         self.lockfile = '/var/run/yum.pid'
 
+    def _enablerepos_with_error_checking(self, yumbase):
+        # NOTE: This seems unintuitive, but it mirrors yum's CLI bahavior
+        if len(self.enablerepo) == 1:
+            try:
+                yumbase.repos.enableRepo(self.enablerepo[0])
+            except yum.Errors.YumBaseError as e:
+                if u'repository not found' in to_text(e):
+                    self.module.fail_json(msg="Repository %s not found." % self.enablerepo[0])
+                else:
+                    raise e
+        else:
+            for rid in self.enablerepo:
+                try:
+                    yumbase.repos.enableRepo(rid)
+                except yum.Errors.YumBaseError as e:
+                    if u'repository not found' in to_text(e):
+                        self.module.warn("Repository %s not found." % rid)
+                    else:
+                        raise e
+
     def yum_base(self):
         my = yum.YumBase()
         my.preconf.debuglevel = 0
@@ -459,8 +479,7 @@ class YumModule(YumDnf):
                 my = self.yum_base()
                 for rid in self.disablerepo:
                     my.repos.disableRepo(rid)
-                for rid in self.enablerepo:
-                    my.repos.enableRepo(rid)
+                self._enablerepos_with_error_checking(my)
 
                 e, m, _ = my.rpmdb.matchPackageNames([pkgspec])
                 pkgs = e + m
@@ -514,8 +533,7 @@ class YumModule(YumDnf):
                 my = self.yum_base()
                 for rid in self.disablerepo:
                     my.repos.disableRepo(rid)
-                for rid in self.enablerepo:
-                    my.repos.enableRepo(rid)
+                self._enablerepos_with_error_checking(my)
 
                 e, m, _ = my.pkgSack.matchPackageNames([pkgspec])
                 pkgs = e + m
@@ -554,8 +572,7 @@ class YumModule(YumDnf):
                 my = self.yum_base()
                 for rid in self.disablerepo:
                     my.repos.disableRepo(rid)
-                for rid in self.enablerepo:
-                    my.repos.enableRepo(rid)
+                self._enablerepos_with_error_checking(my)
 
                 pkgs = my.returnPackagesByDep(pkgspec) + my.returnInstalledPackagesByDep(pkgspec)
                 if not pkgs:
@@ -595,8 +612,7 @@ class YumModule(YumDnf):
                 my = self.yum_base()
                 for rid in self.disablerepo:
                     my.repos.disableRepo(rid)
-                for rid in self.enablerepo:
-                    my.repos.enableRepo(rid)
+                self._enablerepos_with_error_checking(my)
 
                 try:
                     pkgs = my.returnPackagesByDep(req_spec) + my.returnInstalledPackagesByDep(req_spec)
@@ -1438,8 +1454,7 @@ class YumModule(YumDnf):
                 current_repos = my.repos.repos.keys()
                 if self.enablerepo:
                     try:
-                        for rid in self.enablerepo:
-                            my.repos.enableRepo(rid)
+                        self._enablerepos_with_error_checking(my)
                         new_repos = my.repos.repos.keys()
                         for i in new_repos:
                             if i not in current_repos:
diff --git a/test/integration/targets/yum/tasks/yum.yml b/test/integration/targets/yum/tasks/yum.yml
index 29da9925602..12027d7a2f3 100644
--- a/test/integration/targets/yum/tasks/yum.yml
+++ b/test/integration/targets/yum/tasks/yum.yml
@@ -92,6 +92,43 @@
         - "yum_result is success"
         - "not yum_result is changed"
 
+# This test case is unfortunately distro specific because we have to specify
+# repo names which are not the same across Fedora/RHEL/CentOS for base/updates
+- name: install sos again with missing repo enablerepo
+  yum:
+    name: sos
+    state: present
+    enablerepo:
+      - "thisrepodoesnotexist"
+      - "base"
+      - "updates"
+    disablerepo: "*"
+  register: yum_result
+  when: ansible_distribution == 'CentOS'
+- name: verify no change on fourth install with missing repo enablerepo (yum)
+  assert:
+    that:
+        - "yum_result is success"
+        - "yum_result is not changed"
+  when: ansible_distribution == 'CentOS'
+
+- name: install sos again with only missing repo enablerepo
+  yum:
+    name: sos
+    state: present
+    enablerepo: "thisrepodoesnotexist"
+  ignore_errors: true
+  register: yum_result
+- name: verify no change on fifth install with only missing repo enablerepo (yum)
+  assert:
+    that:
+        - "yum_result is not success"
+  when: ansible_pkg_mgr == 'yum'
+- name: verify no change on fifth install with only missing repo enablerepo (dnf)
+  assert:
+    that:
+        - "yum_result is success"
+  when: ansible_pkg_mgr == 'dnf'
 
 # INSTALL AGAIN WITH LATEST
 - name: install sos again with state latest in check mode