Repository: ansible-collections/community.general

Issue:
define custom path to opkg
### Summary

I am trying to configure my new Odroid N2+ with CoreELEC (based on Just enough OS) via Ansible.

I want to use `community.general.opkg` to install some tools.

Unfortunately the bin file is located at another path than usual.

```
TASK [Install useful tools] ***************************************************************************************************************************************
fatal: [odroid]: FAILED! => {"changed": false, "msg": "Failed to find required executable \"opkg\" in paths: /bin:/usr/bin:/usr/sbin:/sbin"}
```

this is the correct location:

```
odroid:~ # which opkg
/opt/bin/opkg
```

Crating a symbolic link in the expected path is no solution either:

```
TASK [Create a symbolic link for opkg] ***************************************************************************************************************************************
fatal: [odroid]: FAILED! => {"changed": false, "msg": "Error while linking: [Errno 30] Read-only file system: b'/opt/bin/opkg' -> b'/bin/opkg'", "path": "/bin/opkg"}
```

### Issue Type

Feature Idea

### Component Name

opkg

### Additional Information

I would like to define the location of the `opkg` bin file somewhere.

### Code of Conduct

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


Discussion:
Files identified in the description:
* [`plugins/modules/packaging/os/opkg.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/packaging/os/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 --->
As a workaround I use this now:

```yaml
  tasks:
    - name: Update opkg list
      ansible.builtin.command: /opt/bin/opkg update

    - name: Upgrade opkg packages
      ansible.builtin.command: /opt/bin/opkg upgrade

    - name: Install useful tools
      ansible.builtin.command: /opt/bin/opkg install mc htop iftop dstat iperf iotop oping git multitail unzip ncdu qrencode imagemagick
```
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 --->
Hi @CWempe thanks for requesting this feature. I am taking a look at it now.

In the meantime, another thing that could work (not tested) is:

```yaml
- community.general.opkg:
    name: [ mc htop iftop dstat iperf iotop oping git multitail unzip ncdu qrencode imagemagick ]
    update_cache: true
  environment:
    PATH: /opt/bin:{{ ansible_env.PATH }}
```


Resolving patch:
diff --git a/plugins/modules/opkg.py b/plugins/modules/opkg.py
--- a/plugins/modules/opkg.py
+++ b/plugins/modules/opkg.py
@@ -66,6 +66,11 @@
             - Update the package DB first.
         default: false
         type: bool
+    executable:
+        description:
+            - The executable location for C(opkg).
+        type: path
+        version_added: 7.2.0
 requirements:
     - opkg
     - python
@@ -106,6 +111,7 @@
     force: overwrite
 '''
 
+import os
 from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
 from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
 
@@ -118,6 +124,7 @@ class Opkg(StateModuleHelper):
             force=dict(choices=["", "depends", "maintainer", "reinstall", "overwrite", "downgrade", "space",
                                 "postinstall", "remove", "checksum", "removal-of-dependent-packages"]),
             update_cache=dict(default=False, type='bool'),
+            executable=dict(type="path"),
         ),
     )
 
@@ -138,15 +145,18 @@ def _force(value):
                 value = None
             return cmd_runner_fmt.as_optval("--force-")(value, ctx_ignore_none=True)
 
+        dir, cmd = os.path.split(self.vars.executable) if self.vars.executable else (None, "opkg")
+
         self.runner = CmdRunner(
             self.module,
-            command="opkg",
+            command=cmd,
             arg_formats=dict(
                 package=cmd_runner_fmt.as_list(),
                 state=cmd_runner_fmt.as_map(state_map),
                 force=cmd_runner_fmt.as_func(_force),
-                update_cache=cmd_runner_fmt.as_bool("update")
+                update_cache=cmd_runner_fmt.as_bool("update"),
             ),
+            path_prefix=dir,
         )
 
         if self.vars.update_cache:


Test patch: