Repository: ansible-collections/community.general

Issue:
Ooops! Snap installation failed while executing
### Summary

This is related to #7130 . This issue #7130 is still not completely fixed. The following code fails on Ubuntu 22.04. I tested this with 7.3.0, 7.4.0. It fails with those versions. The code below does however work when using 6.6.0.

```yaml
    - name: Install packages
      ansible.builtin.apt:
        name:
          - snapd
          - fuse
          - udev
        state: present
        update_cache: true

    - name: Install Microk8s
      community.general.snap:
        name: microk8s
        classic: true
        channel: 1.26/stable
```

```bash
TASK [Install Microk8s] ********************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible_collections.community.general.plugins.module_utils.mh.exceptions.ModuleHelperException
fatal: [c2d-galaxy1]: FAILED! => changed=false 
  channel: 1.26/stable
  classic: true
  cmd: '[''state'', ''classic'', ''channel'', ''dangerous'', ''microk8s'']'
  msg: Ooops! Snap installation failed while executing '['state', 'classic', 'channel', 'dangerous', 'microk8s']', please examine logs and error output for more details.
  output:
    channel: 1.26/stable
    classic: true
    cmd: '[''state'', ''classic'', ''channel'', ''dangerous'', ''microk8s'']'
    snap_names:
    - microk8s
    snaps_installed:
    - microk8s
  snap_names:
  - microk8s
  snaps_installed:
  - microk8s
  vars:
    channel: 1.26/stable
    classic: true
    cmd: '[''state'', ''classic'', ''channel'', ''dangerous'', ''microk8s'']'
    snap_names:
    - microk8s
    snaps_installed:
    - microk8s
```

### Issue Type

Bug Report

### Component Name

snap

### Ansible Version

```console (paste below)
θ97° [:ansible-dev]└2 master(+104/-29,-1)* 130 ± ansible --version
ansible [core 2.15.0]
  config file = /home/onknows/git/gitlab/c2/ansible-dev/ansible-dev.cfg
  configured module search path = ['/home/onknows/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/onknows/.virtualenv/c2d/lib/python3.10/site-packages/ansible
  ansible collection location = /home/onknows/git/gitlab/c2/ansible-dev-collections:/home/onknows/git/gitlab/c2/ansible-dev
  executable location = /home/onknows/.virtualenv/c2d/bin/ansible
  python version = 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] (/home/onknows/.virtualenv/c2d/bin/python)
  jinja version = 3.1.2
  libyaml = True
```


### Community.general Version

```console (paste below)
# /home/onknows/git/gitlab/c2/ansible-dev/ansible_collections
Collection        Version
----------------- -------
community.general 7.4.0 

```


### Configuration

```yaml
    - name: Install packages
      ansible.builtin.apt:
        name:
          - snapd
          - fuse
          - udev
        state: present
        update_cache: true

    - name: Install Microk8s
      community.general.snap:
        name: microk8s
        classic: true
        channel: 1.26/stable

```


### OS / Environment

Ubuntu 22.04

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
---
- name: galaxy.yml
  hosts: galaxy
  become: yes

  tasks:

    - name: Install packages
      ansible.builtin.apt:
        name:
          - snapd
          - fuse
          - udev
        state: present
        update_cache: true

    - name: Install Microk8s
      community.general.snap:
        name: microk8s
        classic: true
        channel: 1.26/stable
```


### Expected Results

Snap install like on community.general 6.6.0

### Actual Results

```console (paste below)
TASK [Install Microk8s] ********************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible_collections.community.general.plugins.module_utils.mh.exceptions.ModuleHelperException
fatal: [c2d-galaxy1]: FAILED! => changed=false 
  channel: 1.26/stable
  classic: true
  cmd: '[''state'', ''classic'', ''channel'', ''dangerous'', ''microk8s'']'
  msg: Ooops! Snap installation failed while executing '['state', 'classic', 'channel', 'dangerous', 'microk8s']', please examine logs and error output for more details.
  output:
    channel: 1.26/stable
    classic: true
    cmd: '[''state'', ''classic'', ''channel'', ''dangerous'', ''microk8s'']'
    snap_names:
    - microk8s
    snaps_installed:
    - microk8s
  snap_names:
  - microk8s
  snaps_installed:
  - microk8s
  vars:
    channel: 1.26/stable
    classic: true
    cmd: '[''state'', ''classic'', ''channel'', ''dangerous'', ''microk8s'']'
    snap_names:
    - microk8s
    snaps_installed:
    - microk8s

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/snap.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/snap.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 @angristan @vcarceler
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
CC @russoz
hi @onknows, thanks for reporting the issue.

Could you please paste the output of the playbook when running with `-vvvv`? TIA
Also, could you please inform:
- the version of snap you are using?
- the OS distro and version in both the target node and the controller?
- the full output of `ansible-galaxy collection list community.general`?
For the record, the problem in #7130 was that the wrong version of the collection was being used. As soon as the user made sure the correct version (7.3.0 at the time), the problem was gone.
> hi @onknows, thanks for reporting the issue.
> 
> Could you please paste the output of the playbook when running with `-vvvv`? TIA

See [provision.log](https://github.com/ansible-collections/community.general/files/12645211/provision.log)

```bash
vagrant@c2d-galaxy1:~$ snap --version
snap    2.60.3
snapd   2.60.3
series  16
ubuntu  22.04
kernel  6.2.0-32-generic
```

Controller node

```bash
onknows@io3:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.3 LTS"
onknows@io3:~$ 
```


Target node:

```bash
vagrant@c2d-galaxy1:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"
vagrant@c2d-galaxy1:~$ 
```
Full list:

```bash
le-dev]└2 master(+13/-11)* ± ansible-galaxy collection list community.general

# /home/onknows/git/gitlab/c2/ansible-dev/ansible_collections
Collection        Version
----------------- -------
community.general 7.3.0  
θ100° [:ansible-dev]└2 master(+29/-8)* ± 
```
Hi @onknows , thanks for the extra info. However, in the output file you attached, I do not see in the output of the module a variable `run_info` that is only set when using `-vvvv` (4 times). If you indeed used `-vvvv`, that would be an indication that maybe we have a different version of the module being used somehow. 

Could you please double check and ensure that we have `run_info` in the output? As an example you can see in  https://github.com/ansible-collections/community.general/issues/7130#issuecomment-1686957124 how the output should look like.
Hi @russoz, my bad, I did not see four `v`, below is the log with `-vvvv`
[provision.log](https://github.com/ansible-collections/community.general/files/12655568/provision.log)

Encountered the same problem
@russoz Hi, is there any news on that?
Hi @Voldemat , sorry, haven't had time to work on it these last days. Hopefully on the weekend. 
@russoz is there any awy I can help? I'm also hitting this issue
Got same issue @russoz 
[provision.log](https://github.com/ansible-collections/community.general/files/12774145/provision.log)

Ok, the errors that we have been seeing are coming up during the installation of the snaps. The way the module works today is:
1. Detect the installed snaps
2. Install (if needed)

Parsing the installation output to detect whether it was successful or not. However, that seems to be proving less straightforward than expected, so I am changing the logic to:

1. Detect the installed snaps
2. Install (if needed)
3. Detect the final state (and compare with the initial one)

The snap command is not fast (it takes a couple of seconds just to start) and that extra call will be noticed, but it is more reliable that way.

Resolving patch:
diff --git a/plugins/modules/snap.py b/plugins/modules/snap.py
--- a/plugins/modules/snap.py
+++ b/plugins/modules/snap.py
@@ -183,7 +183,6 @@ class Snap(StateModuleHelper):
     __disable_re = re.compile(r'(?:\S+\s+){5}(?P<notes>\S+)')
     __set_param_re = re.compile(r'(?P<snap_prefix>\S+:)?(?P<key>\S+)\s*=\s*(?P<value>.+)')
     __list_re = re.compile(r'^(?P<name>\S+)\s+\S+\s+\S+\s+(?P<channel>\S+)')
-    __install_re = re.compile(r'(?P<name>\S+)\s.+\s(installed|refreshed)')
     module = dict(
         argument_spec={
             'name': dict(type='list', elements='str', required=True),
@@ -209,14 +208,24 @@ def __init_module__(self):
         # if state=present there might be file names passed in 'name', in
         # which case they must be converted to their actual snap names, which
         # is done using the names_from_snaps() method calling 'snap info'.
+        self.vars.set("snapinfo_run_info", [], output=(self.verbosity >= 4))
+        self.vars.set("status_run_info", [], output=(self.verbosity >= 4))
+        self.vars.set("status_out", None, output=(self.verbosity >= 4))
+        self.vars.set("run_info", [], output=(self.verbosity >= 4))
+
         if self.vars.state == "present":
-            self.vars.set("snapinfo_run_info", [], output=(self.verbosity >= 4))
             self.vars.set("snap_names", self.names_from_snaps(self.vars.name))
             status_var = "snap_names"
         else:
             status_var = "name"
-        self.vars.set("snap_status", self.snap_status(self.vars[status_var], self.vars.channel), output=False)
-        self.vars.set("snap_status_map", dict(zip(self.vars.name, self.vars.snap_status)), output=False)
+        self.vars.set("status_var", status_var, output=False)
+        self.vars.set("snap_status", self.snap_status(self.vars[self.vars.status_var], self.vars.channel), output=False, change=True)
+        self.vars.set("snap_status_map", dict(zip(self.vars.name, self.vars.snap_status)), output=False, change=True)
+
+    def __quit_module__(self):
+        self.vars.snap_status = self.snap_status(self.vars[self.vars.status_var], self.vars.channel)
+        if self.vars.channel is None:
+            self.vars.channel = "stable"
 
     def _run_multiple_commands(self, commands, actionable_names, bundle=True, refresh=False):
         results_cmd = []
@@ -252,10 +261,6 @@ def _run_multiple_commands(self, commands, actionable_names, bundle=True, refres
             results_run_info,
         )
 
-    def __quit_module__(self):
-        if self.vars.channel is None:
-            self.vars.channel = "stable"
-
     def convert_json_subtree_to_map(self, json_subtree, prefix=None):
         option_map = {}
 
@@ -350,9 +355,8 @@ def _status_check(name, channel, installed):
         list_out = out.split('\n')[1:]
         list_out = [self.__list_re.match(x) for x in list_out]
         list_out = [(m.group('name'), m.group('channel')) for m in list_out if m]
-        if self.verbosity >= 4:
-            self.vars.status_out = list_out
-            self.vars.status_run_info = ctx.run_info
+        self.vars.status_out = list_out
+        self.vars.status_run_info = ctx.run_info
 
         return [_status_check(n, channel, list_out) for n in snap_name]
 
@@ -383,14 +387,10 @@ def _present(self, actionable_snaps, refresh=False):
             self.vars.cmd, rc, out, err, run_info = self._run_multiple_commands(params, actionable_snaps, bundle=False, refresh=refresh)
         else:
             self.vars.cmd, rc, out, err, run_info = self._run_multiple_commands(params, actionable_snaps, refresh=refresh)
-        if self.verbosity >= 4:
-            self.vars.run_info = run_info
+        self.vars.run_info = run_info
 
         if rc == 0:
-            match_install2 = [self.__install_re.match(line) for line in out.split('\n')]
-            match_install = [m.group('name') in actionable_snaps for m in match_install2 if m]
-            if len(match_install) == len(actionable_snaps):
-                return
+            return
 
         classic_snap_pattern = re.compile(r'^error: This revision of snap "(?P<package_name>\w+)"'
                                           r' was published using classic confinement')
@@ -480,8 +480,7 @@ def _generic_state_action(self, actionable_func, actionable_var, params):
         if self.check_mode:
             return
         self.vars.cmd, rc, out, err, run_info = self._run_multiple_commands(params, actionable_snaps)
-        if self.verbosity >= 4:
-            self.vars.run_info = run_info
+        self.vars.run_info = run_info
         if rc == 0:
             return
         msg = "Ooops! Snap operation failed while executing '{cmd}', please examine logs and " \


Test patch:
diff --git a/tests/unit/plugins/modules/test_snap.py b/tests/unit/plugins/modules/test_snap.py
--- a/tests/unit/plugins/modules/test_snap.py
+++ b/tests/unit/plugins/modules/test_snap.py
@@ -402,6 +402,19 @@
                 out="hello-world (12345/stable) v12345 from Canonical** installed\n",
                 err="",
             ),
+            RunCmdCall(
+                command=['/testbin/snap', 'list'],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out=(
+                    "Name    Version      Rev    Tracking         Publisher    Notes"
+                    "core20  20220826     1623   latest/stable    canonical**  base"
+                    "lxd     5.6-794016a  23680  latest/stable/…  canonical**  -"
+                    "hello-world     5.6-794016a  23680  latest/stable/…  canonical**  -"
+                    "snapd   2.57.4       17336  latest/stable    canonical**  snapd"
+                    ""),
+                err="",
+            ),
         ]
     ),
     ModuleTestCase(
@@ -438,6 +451,20 @@
                 out=issue_6803_kubectl_out,
                 err="",
             ),
+            RunCmdCall(
+                command=['/testbin/snap', 'list'],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out=(
+                    "Name    Version      Rev    Tracking         Publisher    Notes"
+                    "core20  20220826     1623   latest/stable    canonical**  base"
+                    "lxd     5.6-794016a  23680  latest/stable/…  canonical**  -"
+                    "microk8s     5.6-794016a  23680  latest/stable/…  canonical**  -"
+                    "kubectl     5.6-794016a  23680  latest/stable/…  canonical**  -"
+                    "snapd   2.57.4       17336  latest/stable    canonical**  snapd"
+                    ""),
+                err="",
+            ),
         ]
     ),
 ]