Repository: ansible-collections/community.general

Issue:
opkg update_cache not working after ansible update
### Summary

Hi!

It seems that some recent updates to `opkg` command broke `update_cache`.

When I set `update_cache: true` ansible returns an error. Switch to `update_cache: false` fixes the issue.

Installing `community.general:==6.2.0` fixed the issue.

### Issue Type

Bug Report

### Component Name

community.general.opkg

### Ansible Version

```console (paste below)
ansible --version
ansible [core 2.14.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/garar/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/garar/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True

```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /usr/lib/python3/dist-packages/ansible_collections
Collection        Version
----------------- -------
community.general 6.3.0  

```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

Ubuntu 22.04 and OpenWrt 21.02.3

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- name: Base tasks for openwrt
  hosts: openwrt
  tasks:
    - name: Install vim
      opkg:
        name:
          - vim-fuller
        state: present
        update_cache: true

```


### Expected Results

Ansible should update opkg cache and install vim-fullter.

### Actual Results

Log too long, please see here: https://gist.github.com/garar/9f71392a682f8f16f2645c0dbee091cc

### Code of Conduct

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


Discussion:
Files identified in the description:
* [`plugins/modules/opkg.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/opkg.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 --->
cc @skinp
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
CC @russoz @joergho maybe this is related to #5718?
@russoz 
I think in [opkg.py:161](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/opkg.py#L161) and [opkg.py:179](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/opkg.py#L179) it should be
```
rc, dummy, dummy = self.runner("update_cache").run()
```
instead of
```
dummy, rc, dummy = self.runner("update_cache").run()
```

@joergho you are right on spot! Thanks! I have added a test case for `update_cache=true`, it failed with the current code and works with the new one.

Resolving patch:
diff --git a/plugins/modules/opkg.py b/plugins/modules/opkg.py
--- a/plugins/modules/opkg.py
+++ b/plugins/modules/opkg.py
@@ -148,6 +148,11 @@ def _force(value):
             ),
         )
 
+        if self.vars.update_cache:
+            rc, dummy, dummy = self.runner("update_cache").run()
+            if rc != 0:
+                self.do_raise("could not update package db")
+
     @staticmethod
     def split_name_and_version(package):
         """ Split the name and the version when using the NAME=VERSION syntax """
@@ -164,10 +169,6 @@ def _package_in_desired_state(self, name, want_installed, version=None):
         return want_installed == has_package
 
     def state_present(self):
-        if self.vars.update_cache:
-            dummy, rc, dummy = self.runner("update_cache").run()
-            if rc != 0:
-                self.do_raise("could not update package db")
         with self.runner("state force package") as ctx:
             for package in self.vars.name:
                 pkg_name, pkg_version = self.split_name_and_version(package)
@@ -176,16 +177,14 @@ def state_present(self):
                     if not self._package_in_desired_state(pkg_name, want_installed=True, version=pkg_version):
                         self.do_raise("failed to install %s" % package)
                     self.vars.install_c += 1
+            if self.verbosity >= 4:
+                self.vars.run_info = ctx.run_info
         if self.vars.install_c > 0:
             self.vars.msg = "installed %s package(s)" % (self.vars.install_c)
         else:
             self.vars.msg = "package(s) already present"
 
     def state_absent(self):
-        if self.vars.update_cache:
-            dummy, rc, dummy = self.runner("update_cache").run()
-            if rc != 0:
-                self.do_raise("could not update package db")
         with self.runner("state force package") as ctx:
             for package in self.vars.name:
                 package, dummy = self.split_name_and_version(package)
@@ -194,6 +193,8 @@ def state_absent(self):
                     if not self._package_in_desired_state(package, want_installed=False):
                         self.do_raise("failed to remove %s" % package)
                     self.vars.remove_c += 1
+            if self.verbosity >= 4:
+                self.vars.run_info = ctx.run_info
         if self.vars.remove_c > 0:
             self.vars.msg = "removed %s package(s)" % (self.vars.remove_c)
         else:


Test patch:
diff --git a/tests/unit/plugins/modules/test_opkg.py b/tests/unit/plugins/modules/test_opkg.py
--- a/tests/unit/plugins/modules/test_opkg.py
+++ b/tests/unit/plugins/modules/test_opkg.py
@@ -150,6 +150,54 @@ def patch_opkg(mocker):
             ),
         ],
     ),
+    ModuleTestCase(
+        id="install_vim_updatecache",
+        input={"name": "vim-fuller", "state": "present", "update_cache": True},
+        output={
+            "msg": "installed 1 package(s)"
+        },
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/opkg", "update"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+            RunCmdCall(
+                command=["/testbin/opkg", "list-installed", "vim-fuller"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+            RunCmdCall(
+                command=["/testbin/opkg", "install", "vim-fuller"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out=(
+                    "Multiple packages (libgcc1 and libgcc1) providing same name marked HOLD or PREFER. Using latest.\n"
+                    "Installing vim-fuller (9.0-1) to root...\n"
+                    "Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/packages/vim-fuller_9.0-1_x86_64.ipk\n"
+                    "Installing terminfo (6.4-2) to root...\n"
+                    "Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/terminfo_6.4-2_x86_64.ipk\n"
+                    "Installing libncurses6 (6.4-2) to root...\n"
+                    "Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/libncurses6_6.4-2_x86_64.ipk\n"
+                    "Configuring terminfo.\n"
+                    "Configuring libncurses6.\n"
+                    "Configuring vim-fuller.\n"
+                ),
+                err="",
+            ),
+            RunCmdCall(
+                command=["/testbin/opkg", "list-installed", "vim-fuller"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="vim-fuller - 9.0-1 \n",   # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
+                err="",
+            ),
+        ],
+    ),
 ]
 TEST_CASES_IDS = [item.id for item in TEST_CASES]