Repository: ansible/ansible
Pull request: yum: fix releasever for latest
##### SUMMARY
<!--- Describe the change below, including rationale and design decisions -->
Fixes #78058 
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Bugfix Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
yum

Problem statement:
yum module "latest" and releasever errors out
### Summary

The yum module fails when releasever is used and state=latest

### Issue Type

Bug Report

### Component Name

yum

### Ansible Version

```console
$ ansible --version
ansible [core 2.11.12]
```


### Configuration

```console
# if using a version older than ansible-core 2.12 you should omit the '-t all'
$ ansible-config dump --only-changed -t all
```


### OS / Environment

SL7

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- name: ww_slurm_4, install slurm and munge packages
  yum:
    name: "{{ item.1 }}"
    state: "latest"
    installroot: "{{ ww_image_root }}/{{ item.0 }}/rootfs"
    # releasever: "{{ ansible_distribution_major_version }}"
    releasever: "7"
    disable_gpg_check: true
  loop: "{{ ww_images | product(['slurm-slurmd', 'slurm-pam_slurm', 'munge'] | list )}}"
  register: ww_import
  notify: build ww container
```


### Expected Results

I expect this to update the package in the chroot environment or stay the same.  Present works, but latest does not.  when releasever is commented both latest and present work.

### Actual Results

```console
failed: [queue] (item=['compute', 'munge']) => {"ansible_loop_var": "item", "changed": false, "item": ["compute", "munge"], "module_stderr": "Shared connection to 10.214.69.133 closed.\r\n", "module_stdout": "\r\nTraceback (most recent call last):\r\n  File \"/home/admin/.ansible/tmp/ansible-tmp-1655242496.2998235-85460-72716949132464/AnsiballZ_yum.py\", line 100, in <module>\r\n    _ansiballz_main()\r\n  File \"/home/admin/.ansible/tmp/ansible-tmp-1655242496.2998235-85460-72716949132464/AnsiballZ_yum.py\", line 92, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/admin/.ansible/tmp/ansible-tmp-1655242496.2998235-85460-72716949132464/AnsiballZ_yum.py\", line 41, in invoke_module\r\n    run_name='__main__', alter_sys=True)\r\n  File \"/usr/lib64/python2.7/runpy.py\", line 176, in run_module\r\n    fname, loader, pkg_name)\r\n  File \"/usr/lib64/python2.7/runpy.py\", line 82, in _run_module_code\r\n    mod_name, mod_fname, mod_loader, pkg_name)\r\n  File \"/usr/lib64/python2.7/runpy.py\", line 72, in _run_code\r\n    exec code in run_globals\r\n  File \"/tmp/ansible_ansible.legacy.yum_payload_wj2O6u/ansible_ansible.legacy.yum_payload.zip/ansible/modules/yum.py\", line 1728, in <module>\r\n  File \"/tmp/ansible_ansible.legacy.yum_payload_wj2O6u/ansible_ansible.legacy.yum_payload.zip/ansible/modules/yum.py\", line 1724, in main\r\n  File \"/tmp/ansible_ansible.legacy.yum_payload_wj2O6u/ansible_ansible.legacy.yum_payload.zip/ansible/modules/yum.py\", line 1695, in run\r\n  File \"/tmp/ansible_ansible.legacy.yum_payload_wj2O6u/ansible_ansible.legacy.yum_payload.zip/ansible/modules/yum.py\", line 1577, in ensure\r\n  File \"/tmp/ansible_ansible.legacy.yum_payload_wj2O6u/ansible_ansible.legacy.yum_payload.zip/ansible/modules/yum.py\", line 1438, in latest\r\nAttributeError: 'NoneType' object has no attribute 'extend'\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct

Hints from issue discussion:
Files identified in the description:
* [`lib/ansible/modules/yum.py`](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/yum.py)

If these files are incorrect, 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/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Commit message: yum: fix releasever for latest

Fixes #78058

Patch to review:
diff --git a/changelogs/fragments/78058-yum-releasever-latest.yml b/changelogs/fragments/78058-yum-releasever-latest.yml
new file mode 100644
index 00000000000000..3700345d3eae0f
--- /dev/null
+++ b/changelogs/fragments/78058-yum-releasever-latest.yml
@@ -0,0 +1,2 @@
+bugfixes:
+  - "yum - fix traceback when ``releasever`` is specified with ``latest`` (https://github.com/ansible/ansible/issues/78058)"
diff --git a/lib/ansible/modules/yum.py b/lib/ansible/modules/yum.py
index a4955adba04f98..f607c50ed1bdb9 100644
--- a/lib/ansible/modules/yum.py
+++ b/lib/ansible/modules/yum.py
@@ -1322,7 +1322,7 @@ def latest(self, items, repoq):
         updates = {}
         obsoletes = {}
         update_all = False
-        cmd = None
+        cmd = self.yum_basecmd
 
         # determine if we're doing an update all
         if '*' in items:
@@ -1341,7 +1341,7 @@ def latest(self, items, repoq):
             self.module.fail_json(**res)
 
         if update_all:
-            cmd = self.yum_basecmd + ['update']
+            cmd.append('update')
             will_update = set(updates.keys())
             will_update_from_other_package = dict()
         else:
@@ -1523,12 +1523,12 @@ def latest(self, items, repoq):
             cmd.extend(['--releasever=%s' % self.releasever])
 
         # run commands
-        if cmd:     # update all
+        if update_all:
             rc, out, err = self.module.run_command(cmd)
             res['changed'] = True
         elif self.update_only:
             if pkgs['update']:
-                cmd = self.yum_basecmd + ['update'] + pkgs['update']
+                cmd += ['update'] + pkgs['update']
                 locale = get_best_parsable_locale(self.module)
                 lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale)
                 rc, out, err = self.module.run_command(cmd, environ_update=lang_env)
@@ -1539,7 +1539,7 @@ def latest(self, items, repoq):
             else:
                 rc, out, err = [0, '', '']
         elif pkgs['install'] or will_update and not self.update_only:
-            cmd = self.yum_basecmd + ['install'] + pkgs['install'] + pkgs['update']
+            cmd += ['install'] + pkgs['install'] + pkgs['update']
             locale = get_best_parsable_locale(self.module)
             lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale)
             rc, out, err = self.module.run_command(cmd, environ_update=lang_env)
diff --git a/test/integration/targets/yum/tasks/yuminstallroot.yml b/test/integration/targets/yum/tasks/yuminstallroot.yml
index f9bee6f9ae90fa..bb69151ad70898 100644
--- a/test/integration/targets/yum/tasks/yuminstallroot.yml
+++ b/test/integration/targets/yum/tasks/yuminstallroot.yml
@@ -109,6 +109,25 @@
             - "yum_result.rc == 0"
             - "yum_result.changed"
             - "rpm_result.rc == 0"
+    - name: remove python before another test
+      yum:
+        name: 'python'
+        state: absent
+        installroot: "{{ buildah_mount.stdout }}"
+        releasever: "{{ buildah_host_releasever.stdout }}"
+    - name: test yum install of python using releasever with latest
+      yum:
+        name: 'python'
+        state: latest
+        installroot: "{{ buildah_mount.stdout }}"
+        releasever: "{{ buildah_host_releasever.stdout }}"
+      register: yum_result
+    - name: verify installation of python
+      assert:
+        that:
+            - "yum_result.rc == 0"
+            - "yum_result.changed"
+            - "rpm_result.rc == 0"
   always:
     - name: remove buildah container
       command: "buildah rm yum_installroot_releasever_test"


Code review comments:
diff_hunk: @@ -1322,7 +1322,7 @@ def latest(self, items, repoq):
         updates = {}
         obsoletes = {}
         update_all = False
-        cmd = None
+        cmd = self.yum_basecmd
path: lib/ansible/modules/yum.py
text: @user1:
Since this is the last method called by `ensure`, the side effect doesn't matter, but cmd could be a copy of the list so self.yum_basecmd isn't mutated.

@author:
Ah, good point. I'll fix that 👍

diff_hunk: @@ -1322,7 +1322,7 @@ def latest(self, items, repoq):
         updates = {}
         obsoletes = {}
         update_all = False
-        cmd = None
+        cmd = self.yum_basecmd
path: lib/ansible/modules/yum.py
text: @author:
```suggestion
        cmd = self.yum_basecmd.copy()
```