Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal-error caused by "no hosts matched the subscripted pattern" #2422

Closed
tumbl3w33d opened this issue Sep 15, 2022 · 6 comments · Fixed by #2481
Closed

internal-error caused by "no hosts matched the subscripted pattern" #2422

tumbl3w33d opened this issue Sep 15, 2022 · 6 comments · Fixed by #2481
Assignees
Labels

Comments

@tumbl3w33d
Copy link

Summary

When I address a host that ansible-lint does not know about, it throws an internal-error.

Issue Type
  • Bug Report
Ansible and Ansible Lint details
ansible [core 2.13.4]
  config file = None
  configured module search path = ['/home/tumble/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/tumble/projects/ansible-playground/venv/lib/python3.10/site-packages/ansible
  ansible collection location = /home/tumble/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/tumble/projects/ansible-playground/venv/bin/ansible
  python version = 3.10.6 (main, Aug  3 2022, 17:39:45) [GCC 12.1.1 20220730]
  jinja version = 3.1.1
  libyaml = True

ansible-lint 6.5.2 using ansible 2.13.4
  • ansible installation method: pip
  • ansible-lint installation method: pip
OS / ENVIRONMENT

5.15.65-1-MANJARO #1 SMP PREEMPT Mon Sep 5 10:15:47 UTC 2022

STEPS TO REPRODUCE

foo.yml

---
- name: Do important things
  hosts: all[0]
  tasks:
    - name: Say hello
      ansible.builtin.debug:
        msg: hey there

- name: Do other stuff
  hosts: all[1]
  tasks:
    - name: Say bye
      ansible.builtin.debug:
        msg: bye bye

ansible-lint --exclude venv -v foo.yml

Desired Behavior

No linting errors reported.

Actual Behavior
ansible-lint --exclude venv -v foo.yml 
INFO     Set ANSIBLE_LIBRARY=/home/tumble/.cache/ansible-compat/0935d5/modules:/home/tumble/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/tumble/.cache/ansible-compat/0935d5/collections:/home/tumble/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/tumble/.cache/ansible-compat/0935d5/roles:/home/tumble/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Set ANSIBLE_LIBRARY=/home/tumble/.cache/ansible-compat/0935d5/modules:/home/tumble/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/tumble/.cache/ansible-compat/0935d5/collections:/home/tumble/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/tumble/.cache/ansible-compat/0935d5/roles:/home/tumble/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
WARNING  Overriding detected file kind 'yaml' with 'playbook' for given positional argument: foo.yml
INFO     Executing syntax check on foo.yml (0.43s)
WARNING  Listing 1 violation(s) that are fatal
internal-error: Unexpected error code 1 from execution of: ansible-playbook --syntax-check -i localhost, foo.yml
foo.yml:1 ERROR! No hosts matched the subscripted pattern 'all[1]'


You can skip specific rules or tags by adding them to your configuration file:
# .config/ansible-lint.yml
warn_list:  # or 'skip_list' to silence them completely
  - internal-error  # Unexpected internal error.

Finished with 1 failure(s), 0 warning(s) on 1 files.

But the following (not specifying the playbook by name) works fine:

ansible-lint --exclude venv -v
INFO     Set ANSIBLE_LIBRARY=/home/tumble/.cache/ansible-compat/0935d5/modules:/home/tumble/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/tumble/.cache/ansible-compat/0935d5/collections:/home/tumble/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/tumble/.cache/ansible-compat/0935d5/roles:/home/tumble/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Set ANSIBLE_LIBRARY=/home/tumble/.cache/ansible-compat/0935d5/modules:/home/tumble/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/tumble/.cache/ansible-compat/0935d5/collections:/home/tumble/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/tumble/.cache/ansible-compat/0935d5/roles:/home/tumble/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Looking up for files, excluding /home/tumble/projects/ansible-playground/venv ...

Why do I care? The first is what the vscode plugin seems to be doing
image

@tumbl3w33d tumbl3w33d added bug new Triage required labels Sep 15, 2022
@ssbarnea
Copy link
Member

I checked and this error happens only when you use all[1], as all happens to be of size 1:

$ ansible-playbook --syntax-check -i localhost, playbook.yml
ERROR! No hosts matched the subscripted pattern 'all[1]'
FAIL: 1

This comes from ansible itself, so we have little control over it but I suspect that you could use jinja to avoid this error. Basically the linter found a flaw in your logic, as your code does not work well with host groups of size 0 or 1 (all in your case).

@tumbl3w33d
Copy link
Author

I should have added that my scenario is molecule where it's rather hard to address certain vms by name, as they are dynamically generated on parallel runs (which I'm doing). Logically the test works fine, it's just the linter that is unhappy. I'll try to figure out some workaround with jinja, good hint, thanks.

@ssbarnea
Copy link
Member

ssbarnea commented Sep 15, 2022

I checked and hosts: "{{ all[1] | default([]) }}" does not trigger any errors.

TBH, I do really see this as a bug, more of a feature! ;) -- Still I will consult with other teammates.

Molecule scenarios are just playbooks, no reason to treat them different.

Because the ansible originating error is not really giving a direct escape solution, I would likely keep the bug open as we might be able to spot it and return an improved error message that includes an example of "resilient way to selecting a host" (example above).

@tumbl3w33d
Copy link
Author

Yeah, that seems to be working, thanks. Maybe there's a way in molecule to address hosts differently that I don't know about. What I do is creating 2 (Hetzner) VMs like this:

platforms:
  - name: "${VM_PREFIX:-$USER}-wireguard-default-1"
    name_length_limit: 64
    server_type: cpx11
    image: debian-10
    location: hel1
    networks:
      wireguard-network:
        ip_range: 10.0.0.0/16
        subnet:
          ip: 10.0.66.42/24
  - name: "${VM_PREFIX:-$USER}-wireguard-default-2"
    name_length_limit: 64
    server_type: cpx11
    image: debian-11
    location: hel1
    networks:
      wireguard-network:
        ip_range: 10.0.0.0/16
        subnet:
          ip: 10.0.66.43/24

As you know, --parallel attaches a UUID to the name and I wouldn't be aware of any other way than all[x] to do the following:

- name: Converge
  hosts: "{{ 'all[1]' }}"
  tasks:
    - name: Set preshared key for client
      ansible.builtin.set_fact:
        wg_psk: somecrazysecrethere

- name: Converge
  hosts: all[0]
  tasks:
    - name: Set private_network_ip for server
      ansible.builtin.set_fact:
        private_network_ip: 10.0.66.42

- name: Converge
  hosts: all
  tasks:
    - name: Import wireguard
      ansible.builtin.import_role:
        name: wireguard
      vars:
        wg_peers:
          - "{{ groups['all'][0] }}"
          - "{{ groups['all'][1] }}"
        wg_net_cidr: 10.0.187.1/24

So to sum it up, I need to do certain things on one host and other things on the other.

But I can really live with the given workaround. Thanks!

@ssbarnea ssbarnea removed the new Triage required label Sep 15, 2022
@ssbarnea ssbarnea self-assigned this Sep 15, 2022
@ssbarnea
Copy link
Member

This bug is basically caused by a pelicular behavior of ansible when you feed it -i localhost, which is the documented way to enforce it to ignore a potentially present inventory in config. Let me exemplify running syntax check with two different options on a minimal playbook made to reproduce the bug:

- name: Reproduce bug
  hosts: all[1]
$ ansible-playbook -i localhost, --syntax-check playbook.yml
ERROR! No hosts matched the subscripted pattern 'all[1000]'
FAIL: 1

$ ansible-playbook --syntax-check playbook.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

playbook: playbook.yml

That is bit hilarious because during linting we explicitly fed ansible -i localhost, just to avoid the useless warnings about inventory. Still, a side effect of this is that now all[1] becomes a runtime error, while all[0] still works.

Running without any inventory allows both to work but you get the extra warnings.

In general, I do not find indexing of a host group without fallback as a good practice as it assumes a group has multiple hosts inside, when in fact it can even have none!

That is why I would recommend anyone using [] on hosts to add a fallback, such foo[2] | default([]) so you never get a runtime error, regardless what inventory you have.

I am not sure what we can do to improve the experience here.

@tumbl3w33d
Copy link
Author

tumbl3w33d commented Sep 21, 2022

Since it's a rare edge case it's probably good enough to have it documented in this issue so people find the workaround. Best you could do would be adding log output like "if you're sure this is a false-positive, use jinja to work around it: {{ 'all[1]' }}.

Edit: follow the documentation that has been added after my comment. It shows a cleaner solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants