Repository: ansible/molecule

Issue:
3.6.0 login to podman fails '/usr/bin/env /bin/podman' No such file
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Do not report bugs before reproducing them with the code of the main branch! -->
<!--- Please also check https://molecule.readthedocs.io/en/latest/faq.html --->
<!--- Please use https://groups.google.com/forum/#!forum/molecule-users for usage questions -->

# Issue Type

- Bug report

# Molecule and Ansible details

```
ansible --version
```
```
ansible [core 2.11.4] 
  config file = /home/indermue/src/ansible-mysql/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/ansible
  ansible collection location = /home/indermue/src/ansible-mysql/collections:/root/python-venv/ansible-4.2.0/lib/python3.8/site-packages:/var/lib/awx/projects/ansible-mysql-manual/collections:/opt/my-tower-venv/ansible-4.2.0/lib/python3.8/site-packages
  executable location = /root/python-venv/ansible-4.4.0/bin/ansible
  python version = 3.8.8 (default, Aug 11 2021, 06:52:42) [GCC 8.5.0 20210514 (Red Hat 8.5.0-3)]
  jinja version = 3.0.1
  libyaml = True
```

```
molecule --version
```
```
molecule 3.6.0 using python 3.8
    ansible:2.11.4
    delegated:3.6.0 from molecule
    podman:1.1.0 from molecule_podman requiring collections: containers.podman>=1.7.0 ansible.posix>=1.3.0
```

Molecule installation method (one of):

- pip

Ansible installation method (one of):

- pip

# Desired Behavior

`molecule login -h node -s scenario` should open bash from within the podman container.

# Actual Behaviour

Molecule fails to execute with the following error:

```
INFO     Inventory /home/indermue/src/ansible-mysql/molecule/mariadb/inventory/static.yml linked to /root/.cache/molecule/ansible-mysql/mariadb/inventory/hosts
INFO     Inventory /home/indermue/src/ansible-mysql/molecule/mariadb/inventory/group_vars linked to /root/.cache/molecule/ansible-mysql/mariadb/inventory/group_vars
INFO     Inventory /home/indermue/src/ansible-mysql/molecule/mariadb/inventory/host_vars linked to /root/.cache/molecule/ansible-mysql/mariadb/inventory/host_vars
INFO     Running mariadb > login 
Traceback (most recent call last):
  File "/root/python-venv/ansible-4.4.0/bin/molecule", line 8, in <module>
    sys.exit(main())
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/click/core.py", line 1137, in __call__
    return self.main(*args, **kwargs)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/click/core.py", line 1062, in main
    rv = self.invoke(ctx)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/click/core.py", line 1668, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/click/core.py", line 763, in invoke
    return __callback(*args, **kwargs)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/molecule/command/login.py", line 166, in login
    base.execute_subcommand(scenario.config, subcommand)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/molecule/command/base.py", line 149, in execute_subcommand
    return command(config).execute()
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/molecule/logger.py", line 188, in wrapper
    rt = func(*args, **kwargs)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/molecule/command/login.py", line 101, in execute
    self._get_login(hostname)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/molecule/command/login.py", line 146, in _get_login
    app.runtime.exec(cmd)
  File "/root/python-venv/ansible-4.4.0/lib/python3.8/site-packages/ansible_compat/runtime.py", line 138, in exec
    result = run_func(
  File "/usr/lib64/python3.8/subprocess.py", line 493, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib64/python3.8/subprocess.py", line 858, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib64/python3.8/subprocess.py", line 1706, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/env /bin/podman exec -e COLUMNS=254 -e LINES=60 -e TERM=bash -e TERM=xterm -ti mariadb_104 bash'  
```



Discussion:
@laurent-indermuehle Is login the only feature broken or other commands are also broken?

Login is bit tricky as it requires a tty to function and we do not have a proper test for it.

It would also be very useful to know if the command can be executed well outside molecule. Is `/usr/bin/env` really missing? Which platform?
@ssbarnea I've successfully used `molecule test` and `molecule destroy`. What other feature can I test?
I am having the same issue with the Docker driver:

```
> molecule --version
molecule 3.6.0 using python 3.9
    ansible:2.12.2
    delegated:3.6.0 from molecule
    docker:1.1.0 from molecule_docker requiring collections: community.docker>=1.9.1
    podman:1.1.0 from molecule_podman requiring collections: containers.podman>=1.7.0 ansible.posix>=1.3.0

> molecule login
INFO     Running default > login
Traceback (most recent call last):
  File "/home/iinuwa/.local/bin/molecule", line 8, in <module>
    sys.exit(main())
  File "/home/iinuwa/.local/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/iinuwa/.local/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/molecule/command/login.py", line 166, in login
    base.execute_subcommand(scenario.config, subcommand)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/molecule/command/base.py", line 149, in execute_subcommand
    return command(config).execute()
  File "/home/iinuwa/.local/lib/python3.9/site-packages/molecule/logger.py", line 188, in wrapper
    rt = func(*args, **kwargs)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/molecule/command/login.py", line 101, in execute
    self._get_login(hostname)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/molecule/command/login.py", line 146, in _get_login
    app.runtime.exec(cmd)
  File "/home/iinuwa/.local/lib/python3.9/site-packages/ansible_compat/runtime.py", line 138, in exec
    result = run_func(
  File "/usr/lib/python3.9/subprocess.py", line 505, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.9/subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.9/subprocess.py", line 1821, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/env docker exec -e COLUMNS=106 -e LINES=54 -e TERM=bash -e TERM=xterm -ti instance bash'
```

I think that the problem is the type of `args`. I can reproduce with just this:

```python
import subprocess

args = '/usr/bin/env docker exec -e COLUMNS=106 -e LINES=54 -e TERM=bash -e TERM=xterm -ti instance bash'

run_func = subprocess.run

result = run_func(args)
```


```
Traceback (most recent call last):
  File "/tmp/repro.py", line 7, in <module>
    result = run_func(
  File "/usr/lib/python3.9/subprocess.py", line 505, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.9/subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.9/subprocess.py", line 1821, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/env docker exec -e COLUMNS=106 -e LINES=54 -e TERM=bash -e TERM=xterm -ti instance bash'
```

If I change `args` to a list, it works properly:

```python
import subprocess

args = ['/usr/bin/env', 'docker', 'exec', '-e', 'COLUMNS=106', '-e', 'LINES=54', '-e', 'TERM=bash', '-e', 'TERM=xterm', '-ti', 'instance', 'bash']

run_func = subprocess.run

result = run_func(args)
```
```
[root@instance /]#
```

For some reason, when I try to patch this in `ansible_compat/runtime.py:138`, it just hangs:
```diff
+        args = args.split(' ')
        for _ in range(self.max_retries + 1 if retry else 1):
            result = run_func(
                args,
                universal_newlines=True,
                check=False,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                env=env or self.environ,
                cwd=cwd,
            )
# ...
```
Tested with 3.5.2, and it works OK there. Could have been a change ~in `ansible-compat` between 0.5.0 or~ in `molecule` 3.5.2 -> 3.6.0.

Scratch that: `ansible-compat` resolves to 1.0.0 on my machine with `molecule` 3.5.2 and 3.6.0. So it is probably in `molecule.

The real issue is that `shell=True` is not set and `stdout` and `stderr` are redirected to `subprocess.PIPE` in `ansible-compat/runtime.py`. Regressed in eb045aea6a219d/#3237.

I think that either another `is_interactive` option or `exec_interactive` command needs to be added for interactive commands.
@ssbarnea, I edited the above post a couple of times, so I don't know if it sent notifications. I think I've narrowed down the issue in the comment above though.
@iinuwa @ssbarnea I give a manually rollback with https://github.com/ansible-community/molecule/commit/eb045aea6a219d0dce3544d1ff91ce70a00eb458, and repackage it as .deb/.rpm:
* https://github.com/alvistack/ansible-community-molecule/commit/3e782d63cecce3afdacbb35008aea3b06ff44777
* https://build.opensuse.org/package/show/home:alvistack/ansible-community-molecule-3.6.0

I could confirm that after rollback with `patch -p1 -R`, now `molecule login` functioning as normal ;-)
I am aware that this regression is genuine and I was planning to work on fixing it but sadly I need to focus on getting some important changes to the linter first. If anyone can takeover this and sort it I will be very happy to help with reviews. It may be needed to patch ansible-compat dependency to fix it, but that is not a big issue.
I'm actually seeing this on molecule against a Vagrant VM:

```
molecule 3.6.1 using python 3.9
    ansible:2.12.2
    delegated:3.6.1 from molecule
    ec2:0.4 from molecule_ec2
    vagrant:1.0.0 from molecule_vagrant
```

The command issued:

```
$ molecule  login --scenario-name vagrant-centos-7
INFO     Inventory /Users/isuftin/Development/CTek/automation/ansible/roles/ssl_filtering/molecule/vagrant-centos-7/../common/group_vars linked to /Users/isuftin/.cache/molecule/ssl_filtering/vagrant-centos-7/inventory/group_vars
INFO     Running vagrant-centos-7 > login
Traceback (most recent call last):
  File "/Users/isuftin/.pyenv/versions/ctek-development/bin/molecule", line 8, in <module>
    sys.exit(main())
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/molecule/command/login.py", line 166, in login
    base.execute_subcommand(scenario.config, subcommand)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/molecule/command/base.py", line 149, in execute_subcommand
    return command(config).execute()
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/molecule/logger.py", line 188, in wrapper
    rt = func(*args, **kwargs)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/molecule/command/login.py", line 101, in execute
    self._get_login(hostname)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/molecule/command/login.py", line 146, in _get_login
    app.runtime.exec(cmd)
  File "/Users/isuftin/.pyenv/versions/3.9.10/envs/ctek-development/lib/python3.9/site-packages/ansible_compat/runtime.py", line 138, in exec
    result = run_func(
  File "/Users/isuftin/.pyenv/versions/3.9.10/lib/python3.9/subprocess.py", line 505, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/Users/isuftin/.pyenv/versions/3.9.10/lib/python3.9/subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/Users/isuftin/.pyenv/versions/3.9.10/lib/python3.9/subprocess.py", line 1821, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/usr/bin/env ssh 127.0.0.1 -l vagrant -p 2222 -i /Users/isuftin/.cache/molecule/ssl_filtering/vagrant-centos-7/.vagrant/machines/centos7-vagrant-centos-7-ssl-filtering/virtualbox/private_key -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o ForwardX11=no -o LogLevel=ERROR -o IdentitiesOnly=yes -o StrictHostKeyChecking=no'
```

Actually running the command it's trying to run works:

```
/usr/bin/env ssh 127.0.0.1 -l vagrant -p 2222 -i /Users/isuftin/.cache/molecule/ssl_filtering/vagrant-centos-7/.vagrant/machines/centos7-vagrant-centos-7-ssl-filtering/virtualbox/private_key -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o ForwardX11=no -o LogLevel=ERROR -o IdentitiesOnly=yes -o StrictHostKeyChecking=no

Last login: Tue Feb 15 19:18:50 2022 from 10.0.2.2

This system is built by the Bento project by Chef Software
More information can be found at https://github.com/chef/bento
[vagrant@centos7-vagrant-centos-7-ssl-filtering ~]
```

molecule.yml:

```
---
dependency:
  name: galaxy
  options:
    role-file: molecule/common/requirements.yml
    requirements-file: molecule/common/requirements.yml
    force: False

driver:
  name: vagrant
  provider:
    name: virtualbox

lint: |
  set -e
  yamllint .
  ansible-lint --project-dir . -vv
  flake8 --exclude "molecule/**/tests/*"

platforms:
  - name: "centos7-${MOLECULE_SCENARIO_NAME}-ssl-filtering"
    box: bento/centos-7
    memory: 512
    cpus: 1
    config_options:
      synced_folder: true
      ssh.keep_alive: true
      "vm.box_download_insecure": "true"
    groups:
      - os_centos_7
      - vm_vagrant

provisioner:
  name: ansible
  log: True
  config_options:
    defaults:
      timeout: 60
      stdout_callback: yaml
      bin_ansible_callbacks: True
      callback_whitelist: profile_tasks, timer, yaml
    ssh_connection:
      pipelining: True
      ssh_args: -C -o ControlMaster=auto -o ControlPersist=60s -o IdentitiesOnly=yes -o PreferredAuthentications=publickey
      retries: 3
    galaxy:
      display_progress: False
  inventory:
    links:
      group_vars: ../common/group_vars
  playbooks:
    converge: ../common/converge.yml

verifier:
  name: testinfra
  directory: ../common/tests
  lint:
    name: flake8
  options:
    v: 1

```
Downgrading to Molecule 3.5.2 works as expected

```
$ molecule --version
molecule 3.5.2 using python 3.9
    ansible:2.12.2
    delegated:3.5.2 from molecule
    ec2:0.4 from molecule_ec2
    vagrant:1.0.0 from molecule_vagrant

$ molecule --env-file molecule/common/molecule.env.yml  login --scenario-name vagrant-centos-7
INFO     Inventory /Users/isuftin/Development/CTek/automation/ansible/roles/ssl_filtering/molecule/vagrant-centos-7/../common/group_vars linked to /Users/isuftin/.cache/molecule/ssl_filtering/vagrant-centos-7/inventory/group_vars
INFO     Running vagrant-centos-7 > login
Last login: Tue Feb 15 19:19:35 2022 from 10.0.2.2

This system is built by the Bento project by Chef Software
More information can be found at https://github.com/chef/bento
```
Same issue here but with docker.
Verified 3.5.2 is working indeed. Running the command worked fine too so it must be an issue with the subprocess call.


Resolving patch:
diff --git a/src/molecule/command/login.py b/src/molecule/command/login.py
--- a/src/molecule/command/login.py
+++ b/src/molecule/command/login.py
@@ -21,11 +21,12 @@
 
 import logging
 import os
+import shlex
+import subprocess
 
 import click
 
 from molecule import scenarios, util
-from molecule.app import app
 from molecule.command import base
 
 LOG = logging.getLogger(__name__)
@@ -142,8 +143,8 @@ def _get_login(self, hostname):  # pragma: no cover
         login_options["lines"] = lines
         login_cmd = self._config.driver.login_cmd_template.format(**login_options)
 
-        cmd = f"/usr/bin/env {login_cmd}"
-        app.runtime.exec(cmd)
+        cmd = shlex.split(f"/usr/bin/env {login_cmd}")
+        subprocess.run(cmd)
 
 
 @base.click_command_ex()


Test patch: