ansible-lint icon indicating copy to clipboard operation
ansible-lint copied to clipboard

Local collection install conflict between ansible-lint and molecule

Open aldehir opened this issue 3 years ago • 3 comments

Summary

It appears there is a conflict with how molecule and ansible-lint install a project if it is a collection.

Molecule uses ansible-galaxy to install the collection in the ansible-compat cache directory, while ansible-lint attempts to create a symlink to the collection in the same cache directory. If you run molecule first, ansible-lint will fail and conversely if you run ansible-lint first, molecule will fail.

Issue Type
  • Bug Report
Ansible and Ansible Lint details
$ ansible --version
ansible [core 2.13.2]
  config file = None
  configured module search path = ['/home/alde/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/alde/example/venv/lib/python3.10/site-packages/ansible
  ansible collection location = /home/alde/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/alde/example/venv/bin/ansible
  python version = 3.10.0rc2 (default, May  1 2022, 08:45:48) [GCC 10.3.1 20210422 (Red Hat 10.3.1-1)]
  jinja version = 3.1.2
  libyaml = True

$ ansible-lint --version
ansible-lint 6.4.1.dev5 using ansible 2.13.2

$ molecule --version
molecule 4.0.1 using python 3.10
    ansible:2.13.2
    delegated:4.0.1 from molecule
    podman:2.0.2 from molecule_podman requiring collections: containers.podman>=1.7.0 ansible.posix>=1.3.0
  • ansible installation method: pip
  • ansible-lint installation method: source, main branch
OS / ENVIRONMENT
  • Fedora 33
  • Python 3.10
STEPS TO REPRODUCE
$ ansible-galaxy collection init dummy.example
$ cd dummy/example
$ molecule init scenario
$ molecule create
$ ansible-lint
Traceback (most recent call last):
  File "/home/alde/example/venv/bin/ansible-lint", line 8, in <module>
    sys.exit(_run_cli_entrypoint())
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/__main__.py", line 266, in _run_cli_entrypoint
    sys.exit(main(sys.argv))
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/__main__.py", line 166, in main
    app = get_app(offline=options.offline)
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/app.py", line 260, in get_app
    _perform_mockings()
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/_mockings.py", line 92, in _perform_mockings
    while os.readlink(link_path) != target:
OSError: [Errno 22] Invalid argument: '/home/alde/.cache/ansible-compat/50d858/collections/ansible_collections/dummy/example'
Desired Behavior

I expect ansible-lint to provide lint results.

Actual Behavior

Ansible lint fails because /home/alde/.cache/ansible-compat/50d858/collections/ansible_collections/dummy/example exists from prior molecule run and is not a symlink.

$ ansible-lint
Traceback (most recent call last):
  File "/home/alde/example/venv/bin/ansible-lint", line 8, in <module>
    sys.exit(_run_cli_entrypoint())
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/__main__.py", line 266, in _run_cli_entrypoint
    sys.exit(main(sys.argv))
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/__main__.py", line 166, in main
    app = get_app(offline=options.offline)
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/app.py", line 260, in get_app
    _perform_mockings()
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/_mockings.py", line 92, in _perform_mockings
    while os.readlink(link_path) != target:
OSError: [Errno 22] Invalid argument: '/home/alde/.cache/ansible-compat/50d858/collections/ansible_collections/dummy/example'

aldehir avatar Aug 07 '22 22:08 aldehir

Erroring in this case is expected but I agree that a stack trace is far from ideal.

The linter would not override symlinks with mocks in order to prevent accidents. If someone created a folder there, it means that you don't need mocking. Mocking will work only when there is nobody else creating overlapping data.

There are clear indications regarding dangers of using mocking and we discourage it, the entire feature was added more as a temporary measure for those that are unable to gather their dependencies. Mocks are removed after run, but we never want to remove non-mocks, so you can probably guess how use of mocks could end-up removing data from disk.

I will make a change to avoid a stack trace, but we will not be able to make it work. Best direction for you is to remove the mocks.

ssbarnea avatar Aug 08 '22 10:08 ssbarnea

@aldehir I just merged a change that might sort the issue for you. Can you please try the code from main branch and let me know?

ssbarnea avatar Aug 08 '22 14:08 ssbarnea

@ssbarnea Unfortunately I am still getting an error, different one this time however.

$ ansible-lint
Traceback (most recent call last):
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/_mockings.py", line 98, in _perform_mockings
    if os.readlink(link_path) != target:
OSError: [Errno 22] Invalid argument: '/home/alde/.cache/ansible-compat/50d858/collections/ansible_collections/dummy/example'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/alde/example/venv/bin/ansible-lint", line 8, in <module>
    sys.exit(_run_cli_entrypoint())
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/__main__.py", line 266, in _run_cli_entrypoint
    sys.exit(main(sys.argv))
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/__main__.py", line 166, in main
    app = get_app(offline=options.offline)
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/app.py", line 260, in get_app
    _perform_mockings()
  File "/home/alde/example/venv/lib/python3.10/site-packages/ansiblelint/_mockings.py", line 102, in _perform_mockings
    link_path.unlink(missing_ok=True)
  File "/home/alde/.pyenv/versions/3.10.0rc2/lib/python3.10/pathlib.py", line 1204, in unlink
    self._accessor.unlink(self)
IsADirectoryError: [Errno 21] Is a directory: '/home/alde/.cache/ansible-compat/50d858/collections/ansible_collections/dummy/example'

Molecule installs the collection to ~/.cache/ansible-compat/<hash>/collections/ansible_collections/dummy/example, so the directory for the collection exists but since it is an entire directory structure and not a symlink, os.readlink() fails followed by unlink() failing for the same reason.

Regarding your advice to remove mocks, I am using a minimal example without any mocks that I configured myself.

aldehir avatar Aug 08 '22 15:08 aldehir

I am going to close this due to lack of reproducer on it. If you can please create a small repository with a script at the root that reproduces that error we can work on addressing it.

ssbarnea avatar Aug 14 '22 08:08 ssbarnea

@ssbarnea I've been struggling with the same issue, so I've created a small repo with an example collection and a Makefile with targets to reproduce the bug. The repo is here: https://github.com/heindsight/ansible-lint-vs-molecule.git

The bug can be reproduced by doing

git clone https://github.com/heindsight/ansible-lint-vs-molecule.git
cd ansible-lint-vs-molecule
make venv
. venv/bin/activate
make molecule-then-lint

To demonstrate that a similar issue also occurs when running ansible-lint first, followed by molecule you can also run

make lint-then-molecule

The make targets will override the XDG_CACHE_HOME env var to avoid polluting your ~/.cache (and to avoid any interference between the two reproduction scenarios). Incidentally, this is also how I currently work around the bug - I set XDG_CACHE_HOME to different values when running molecule or ansible-lint.

heindsight avatar Sep 08 '22 22:09 heindsight