ansible / ansible-lint

ansible-lint checks playbooks for practices and behavior that could potentially be improved and can fix some of the most common ones for you
https://ansible.readthedocs.io/projects/lint/
GNU General Public License v3.0
3.46k stars 654 forks source link

Variable groups: 'dict object' has no attribute '{group_name}' - syntax-check[specific] #4241

Open dcasella opened 3 months ago

dcasella commented 3 months ago
Summary

Using groups['{group_name}'] in serial gets linted as an error:

The field 'serial' has an invalid value, which includes an undefined variable. The error was: 'dict object' has no attribute '{group_name}' ansible-lint(syntax-check[specific])
Issue Type
OS / ENVIRONMENT
$ ansible-lint --version
ansible-lint 24.6.1 using ansible-core:2.17.1 ansible-compat:24.6.1 ruamel-yaml:0.18.6 ruamel-yaml-clib:0.2.7
STEPS TO REPRODUCE

ansible.cfg:

[defaults]
inventory = inventories/D

inventories/D/on-prem.yaml:

group_name:
  hosts:
    host1:
    host2:

playbooks/test.yaml:

---
- name: Test
  hosts:
    - group_name
  serial: "{{ batch | default(groups['group_name'] | length) }}"
  gather_facts: false
  tasks:
    - name: Debug
      delegate_to: localhost
      ansible.builtin.debug:
        msg: "{{ batch | default(groups['group_name'] | length) }}"

Run commands (both work):

$ ansible-playbook playbooks/test.yaml
$ ansible-playbook playbooks/test.yaml -e batch=1

Linter error:

$ ansible-lint playbooks/test.yaml
WARNING  Listing 1 violation(s) that are fatal
syntax-check[specific]: The field 'serial' has an invalid value, which includes an undefined variable. The error was: 'dict object' has no attribute 'group_name'
playbooks/test.yaml:2:3

                  Rule Violation Summary
 count tag                    profile rule associated tags
     1 syntax-check[specific] min     core, unskippable

Failed: 1 failure(s), 0 warning(s) on 1 files.
Desired Behavior

No linter error.

Actual Behavior

Linter error:

└ ansible-lint -vvvv playbooks/test.yaml
DEBUG    Logging initialized to level 10
INFO     Identified /home/dcasella/{REDACTED_PATH} as project root due .git directory.
DEBUG    Options: Options(_skip_ansible_syntax_check=False, cache_dir=PosixPath('/home/dcasella/.cache/ansible-compat/5db8df'), colored=True, configured=True, cwd=PosixPath('/home/dcasella/{REDACTED_PATH}/test'), display_relative_path=True, exclude_paths=['.cache', '.git', '.hg', '.svn', '.tox'], format=None, lintables=['playbooks/test.yaml'], list_rules=False, list_tags=False, write_list=[], parseable=False, quiet=0, rulesdirs=[PosixPath('/home/dcasella/{REDACTED_PATH}/venv/lib/python3.11/site-packages/ansiblelint/rules')], skip_list=[], tags=[], verbosity=4, warn_list=['experimental', 'jinja', 'fqcn'], mock_filters=[], mock_modules=[], mock_roles=[], loop_var_prefix=None, only_builtins_allow_collections=[], only_builtins_allow_modules=[], var_naming_pattern=None, offline=None, project_dir='/home/dcasella/{REDACTED_PATH}', extra_vars=None, enable_list=[], skip_action_validation=True, strict=False, rules={}, profile=None, task_name_prefix='{stem} | ', sarif_file=None, config_file=None, generate_ignore=False, rulesdir=[], use_default_rules=False, version=False, list_profiles=False, ignore_file=None, max_tasks=100, max_block_depth=20, supported_ansible_also=[])
DEBUG    CWD: /home/dcasella/{REDACTED_PATH}/test
DEBUG    Logging initialized to level 10
DEBUG    Effective yamllint rules used: {'anchors': {'level': 'error', 'forbid-undeclared-aliases': True, 'forbid-duplicated-anchors': False}, 'braces': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 1, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'brackets': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'colons': {'level': 'error', 'max-spaces-before': 0, 'max-spaces-after': 1}, 'commas': {'level': 'error', 'max-spaces-before': 0, 'min-spaces-after': 1, 'max-spaces-after': 1}, 'comments': {'level': 'warning', 'require-starting-space': True, 'ignore-shebangs': True, 'min-spaces-from-content': 1}, 'comments-indentation': False, 'document-end': False, 'document-start': False, 'empty-lines': {'level': 'error', 'max': 2, 'max-start': 0, 'max-end': 0}, 'empty-values': False, 'float-values': False, 'hyphens': {'level': 'error', 'max-spaces-after': 1}, 'indentation': {'level': 'error', 'spaces': 'consistent', 'indent-sequences': True, 'check-multi-line-strings': False}, 'key-duplicates': {'level': 'error'}, 'key-ordering': False, 'line-length': {'level': 'error', 'max': 160, 'allow-non-breakable-words': True, 'allow-non-breakable-inline-mappings': False}, 'new-line-at-end-of-file': {'level': 'error'}, 'new-lines': {'level': 'error', 'type': 'unix'}, 'octal-values': {'forbid-implicit-octal': True, 'forbid-explicit-octal': True, 'level': 'error'}, 'quoted-strings': False, 'trailing-spaces': {'level': 'error'}, 'truthy': {'level': 'warning', 'allowed-values': ['true', 'false'], 'check-keys': True}}
INFO     Set ANSIBLE_LIBRARY=/home/dcasella/.cache/ansible-compat/9f86d0/modules:/home/dcasella/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/dcasella/.cache/ansible-compat/9f86d0/collections:/home/dcasella/.ansible/collections:/usr/share/ansible/collections:/home/dcasella/{REDACTED_PATH}/venv/lib/python3.11/site-packages:/home/dcasella/.cache/ansible-compat/9f86d0/collections:/home/dcasella/.ansible/collections:/usr/share/ansible/collections:/home/dcasella/{REDACTED_PATH}/venv/lib/python3.11/site-packages
INFO     Set ANSIBLE_ROLES_PATH=/home/dcasella/.cache/ansible-compat/9f86d0/roles:/home/dcasella/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
DEBUG    Effective yamllint rules used: {'anchors': {'level': 'error', 'forbid-undeclared-aliases': True, 'forbid-duplicated-anchors': False}, 'braces': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 1, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'brackets': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'colons': {'level': 'error', 'max-spaces-before': 0, 'max-spaces-after': 1}, 'commas': {'level': 'error', 'max-spaces-before': 0, 'min-spaces-after': 1, 'max-spaces-after': 1}, 'comments': {'level': 'warning', 'require-starting-space': True, 'ignore-shebangs': True, 'min-spaces-from-content': 1}, 'comments-indentation': False, 'document-end': False, 'document-start': False, 'empty-lines': {'level': 'error', 'max': 2, 'max-start': 0, 'max-end': 0}, 'empty-values': False, 'float-values': False, 'hyphens': {'level': 'error', 'max-spaces-after': 1}, 'indentation': {'level': 'error', 'spaces': 'consistent', 'indent-sequences': True, 'check-multi-line-strings': False}, 'key-duplicates': {'level': 'error'}, 'key-ordering': False, 'line-length': {'level': 'error', 'max': 160, 'allow-non-breakable-words': True, 'allow-non-breakable-inline-mappings': False}, 'new-line-at-end-of-file': {'level': 'error'}, 'new-lines': {'level': 'error', 'type': 'unix'}, 'octal-values': {'forbid-implicit-octal': True, 'forbid-explicit-octal': True, 'level': 'error'}, 'quoted-strings': False, 'trailing-spaces': {'level': 'error'}, 'truthy': {'level': 'warning', 'allowed-values': ['true', 'false'], 'check-keys': True}}
DEBUG    Logging initialized to level 10
DEBUG    Effective yamllint rules used: {'anchors': {'level': 'error', 'forbid-undeclared-aliases': True, 'forbid-duplicated-anchors': False}, 'braces': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 1, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'brackets': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'colons': {'level': 'error', 'max-spaces-before': 0, 'max-spaces-after': 1}, 'commas': {'level': 'error', 'max-spaces-before': 0, 'min-spaces-after': 1, 'max-spaces-after': 1}, 'comments': {'level': 'warning', 'require-starting-space': True, 'ignore-shebangs': True, 'min-spaces-from-content': 1}, 'comments-indentation': False, 'document-end': False, 'document-start': False, 'empty-lines': {'level': 'error', 'max': 2, 'max-start': 0, 'max-end': 0}, 'empty-values': False, 'float-values': False, 'hyphens': {'level': 'error', 'max-spaces-after': 1}, 'indentation': {'level': 'error', 'spaces': 'consistent', 'indent-sequences': True, 'check-multi-line-strings': False}, 'key-duplicates': {'level': 'error'}, 'key-ordering': False, 'line-length': {'level': 'error', 'max': 160, 'allow-non-breakable-words': True, 'allow-non-breakable-inline-mappings': False}, 'new-line-at-end-of-file': {'level': 'error'}, 'new-lines': {'level': 'error', 'type': 'unix'}, 'octal-values': {'forbid-implicit-octal': True, 'forbid-explicit-octal': True, 'level': 'error'}, 'quoted-strings': False, 'trailing-spaces': {'level': 'error'}, 'truthy': {'level': 'warning', 'allowed-values': ['true', 'false'], 'check-keys': True}}
INFO     Set ANSIBLE_LIBRARY=/home/dcasella/.cache/ansible-compat/9f86d0/modules:/home/dcasella/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/dcasella/.cache/ansible-compat/9f86d0/collections:/home/dcasella/.ansible/collections:/usr/share/ansible/collections:/home/dcasella/{REDACTED_PATH}/venv/lib/python3.11/site-packages:/home/dcasella/.cache/ansible-compat/9f86d0/collections:/home/dcasella/.ansible/collections:/usr/share/ansible/collections:/home/dcasella/{REDACTED_PATH}/venv/lib/python3.11/site-packages
INFO     Set ANSIBLE_ROLES_PATH=/home/dcasella/.cache/ansible-compat/9f86d0/roles:/home/dcasella/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Executing syntax check on playbook playbooks/test.yaml (0.63s)
WARNING  Listing 1 violation(s) that are fatal
syntax-check[specific]: The field 'serial' has an invalid value, which includes an undefined variable. The error was: 'dict object' has no attribute 'group_name'
playbooks/test.yaml:2:3

DEBUG    Attempting to release lock 140220984488464 on /home/dcasella/.cache/ansible-compat/5db8df/.lock
DEBUG    Lock 140220984488464 released on /home/dcasella/.cache/ansible-compat/5db8df/.lock

DEBUG    Determined rule-profile order: {'internal-error': (0, 'min'), 'load-failure': (1, 'min'), 'parser-error': (2, 'min'), 'syntax-check': (3, 'min'), 'command-instead-of-module': (4, 'basic'), 'command-instead-of-shell': (5, 'basic'), 'deprecated-bare-vars': (6, 'basic'), 'deprecated-local-action': (7, 'basic'), 'deprecated-module': (8, 'basic'), 'inline-env-var': (9, 'basic'), 'key-order': (10, 'basic'), 'literal-compare': (11, 'basic'), 'jinja': (12, 'basic'), 'no-free-form': (13, 'basic'), 'no-jinja-when': (14, 'basic'), 'no-tabs': (15, 'basic'), 'partial-become': (16, 'basic'), 'playbook-extension': (17, 'basic'), 'role-name': (18, 'basic'), 'schema': (19, 'basic'), 'name': (20, 'basic'), 'var-naming': (21, 'basic'), 'yaml': (22, 'basic'), 'name': (23, 'moderate'), 'name': (24, 'moderate'), 'name': (25, 'moderate'), 'spell-var-name': (26, 'moderate'), 'avoid-implicit': (27, 'safety'), 'latest': (28, 'safety'), 'package-latest': (29, 'safety'), 'risky-file-permissions': (30, 'safety'), 'risky-octal': (31, 'safety'), 'risky-shell-pipe': (32, 'safety'), 'galaxy': (33, 'shared'), 'ignore-errors': (34, 'shared'), 'layout': (35, 'shared'), 'meta-incorrect': (36, 'shared'), 'meta-no-tags': (37, 'shared'), 'meta-video-links': (38, 'shared'), 'meta-version': (39, 'shared'), 'meta-runtime': (40, 'shared'), 'no-changed-when': (41, 'shared'), 'no-changelog': (42, 'shared'), 'no-handler': (43, 'shared'), 'no-relative-paths': (44, 'shared'), 'max-block-depth': (45, 'shared'), 'max-tasks': (46, 'shared'), 'unsafe-loop': (47, 'shared'), 'avoid-dot-notation': (48, 'production'), 'sanity': (49, 'production'), 'fqcn': (50, 'production'), 'import-task-no-when': (51, 'production'), 'meta-no-dependencies': (52, 'production'), 'single-entry-point': (53, 'production'), 'use-loop': (54, 'production')}
                  Rule Violation Summary
 count tag                    profile rule associated tags
     1 syntax-check[specific] min     core, unskippable

DEBUG    Found virtualenv, assuming `pip3 install` will work.
DEBUG    Registered VCS backend: bzr
DEBUG    Registered VCS backend: git
DEBUG    Registered VCS backend: hg
DEBUG    Registered VCS backend: svn
DEBUG    Found ansible-lint 24.6.1 dist
Failed: 1 failure(s), 0 warning(s) on 1 files.

N.B. I get the same error in the root repository (/home/dcasella/{REDACTED_PATH} from the log above). The example provided was tested inside a /test directory.

cavcrosby commented 3 months ago

I believe this occurs because underneath the covers, the syntax-check performed is done using localhost as the inventory. This is despite what the documentation says on syntax-check violations.

Our linter runs ansible-playbook --syntax-check on all playbooks, and if any of these reports a syntax error, this stops any further processing of these files.

This error cannot be disabled due to being a prerequisite for other steps. You can exclude these files from linting, but it is better to make sure they can be loaded by Ansible. This is often achieved by editing the inventory file and/or ansible.cfg so ansible can load required variables.

Also, I do think #4225 is related.