lint
決まったルールにもとづいてソースコードをチェックするツールを「 lint 」と呼びます。lint は実行時にエラーにならないが、バグの原因になりそうな部分などを指摘します。 Ansible の lint ツールに ansible-lint
があります。
ansible-lint をインストール
ansible-lint をインストールします。
pip3 install ansible-lint
ansible-lint --version
y_mrok@ctrl:~$ pip3 install ansible-lint
Collecting ansible-lint
Downloading ansible_lint-5.3.0-py3-none-any.whl (114 kB)
|████████████████████████████████| 114 kB 4.7 MB/s
Collecting tenacity
Downloading tenacity-8.0.1-py3-none-any.whl (24 kB)
Collecting enrich>=1.2.6
Downloading enrich-1.2.6-py3-none-any.whl (8.6 kB)
Requirement already satisfied: pyyaml in /usr/lib/python3/dist-packages (from ansible-lint) (5.3.1)
Requirement already satisfied: packaging in ./.local/lib/python3.8/site-packages (from ansible-lint) (21.0)
Collecting ruamel.yaml<1,>=0.15.37; python_version >= "3.7"
Downloading ruamel.yaml-0.17.17-py3-none-any.whl (109 kB)
|████████████████████████████████| 109 kB 47.9 MB/s
Collecting rich>=9.5.1
Downloading rich-10.15.2-py3-none-any.whl (214 kB)
|████████████████████████████████| 214 kB 10.6 MB/s
Collecting wcmatch>=7.0
Downloading wcmatch-8.3-py3-none-any.whl (42 kB)
|████████████████████████████████| 42 kB 2.6 MB/s
Requirement already satisfied: pyparsing>=2.0.2 in ./.local/lib/python3.8/site-packages (from packaging->ansible-lint) (2.4.7)
Collecting ruamel.yaml.clib>=0.1.2; platform_python_implementation == "CPython" and python_version < "3.10"
Downloading ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl (570 kB)
|████████████████████████████████| 570 kB 12.6 MB/s
Collecting commonmark<0.10.0,>=0.9.0
Downloading commonmark-0.9.1-py2.py3-none-any.whl (51 kB)
|████████████████████████████████| 51 kB 4.5 MB/s
Collecting pygments<3.0.0,>=2.6.0
Downloading Pygments-2.10.0-py3-none-any.whl (1.0 MB)
|████████████████████████████████| 1.0 MB 19.6 MB/s
Requirement already satisfied: colorama<0.5.0,>=0.4.0 in /usr/lib/python3/dist-packages (from rich>=9.5.1->ansible-lint) (0.4.3)
Collecting bracex>=2.1.1
Downloading bracex-2.2.1-py3-none-any.whl (12 kB)
Installing collected packages: tenacity, commonmark, pygments, rich, enrich, ruamel.yaml.clib, ruamel.yaml, bracex, wcmatch, ansible-lint
Successfully installed ansible-lint-5.3.0 bracex-2.2.1 commonmark-0.9.1 enrich-1.2.6 pygments-2.10.0 rich-10.15.2 ruamel.yaml-0.17.17 ruamel.yaml.clib-0.2.6 tenacity-8.0.1 wcmatch-8.3
y_mrok@ctrl:~$ ansible-lint --version
ansible-lint 5.3.0 using ansible 2.12.0
y_mrok@ctrl:~$
ansible-lint コマンド
構文
ansible-lint [-h] [-L] [-f {rich,plain,rst,codeclimate,quiet,pep8}] [-q] [-p]
[--parseable-severity] [--progressive] [--project-dir PROJECT_DIR]
[-r RULESDIR] [-R] [--show-relpath] [-t TAGS] [-T] [-v] [-x SKIP_LIST]
[-w WARN_LIST] [--enable-list ENABLE_LIST] [--nocolor] [--force-color]
[--exclude EXCLUDE_PATHS] [-c CONFIG_FILE] [--offline] [--version]
[lintables [lintables ...]]
チェックルール
command-instead-of-module │ Using command rather than module
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Executing a command when there is an Ansible module is
│ generally a bad idea
version_added │ historic
tags │ command-shell, idiom
severity │ HIGH
╵
╷
command-instead-of-shell │ Use shell only when shell functionality is required
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Shell should only be used when piping, redirecting or
│ chaining commands (and Ansible would be preferred for
│ some of those!)
version_added │ historic
tags │ command-shell, idiom
severity │ HIGH
╵
╷
deprecated-bare-vars │ Using bare variables is deprecated
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Using bare variables is deprecated. Update your playbooks
│ so that the environment value uses the full variable
│ syntax {{ your_variable }}
version_added │ historic
tags │ deprecations
severity │ VERY_HIGH
╵
╷
deprecated-command-syntax │ Using command rather than an argument to e.g. file
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Executing a command when there are arguments to modules
│ is generally a bad idea
version_added │ historic
tags │ command-shell, deprecations
severity │ VERY_HIGH
╵
╷
deprecated-local-action │ Do not use 'local_action', use 'delegate_to: localhost'
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Do not use local_action, use delegate_to: localhost
version_added │ v4.0.0
tags │ deprecations
severity │ MEDIUM
╵
╷
deprecated-module │ Deprecated module
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ These are deprecated modules, some modules are kept
│ temporarily for backwards compatibility but usage is
│ discouraged. For more details see:
│ https://docs.ansible.com/ansible/latest/collections/inde…
version_added │ v4.0.0
tags │ deprecations
severity │ HIGH
╵
╷
empty-string-compare │ Don't compare to empty string
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Use when: var|length > 0 rather than when: var != "" (or
│ conversely when: var|length == 0 rather than when: var ==
│ "")
version_added │ v4.0.0
tags │ idiom
severity │ HIGH
╵
╷
git-latest │ Git checkouts must contain explicit version
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ All version control checkouts must point to an explicit
│ commit or tag, not just latest
version_added │ historic
tags │ idempotency
severity │ MEDIUM
╵
╷
hg-latest │ Mercurial checkouts must contain explicit revision
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ All version control checkouts must point to an explicit
│ commit or tag, not just latest
version_added │ historic
tags │ idempotency
severity │ MEDIUM
╵
╷
ignore-errors │ Use failed_when and specify error conditions instead of
│ using ignore_errors
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Instead of ignoring all errors, ignore the errors only
│ when using {{ ansible_check_mode }}, register the errors
│ using register, or use failed_when: and specify
│ acceptable error conditions to reduce the risk of
│ ignoring important failures.
version_added │ v5.0.7
tags │ unpredictability, experimental
severity │ LOW
╵
╷
inline-env-var │ Command module does not accept setting environment
│ variables inline
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Use environment: to set environment variables or use
│ shell module which accepts both
version_added │ historic
tags │ command-shell, idiom
severity │ VERY_HIGH
╵
╷
internal-error │ Unexpected internal error
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ This error can be caused by internal bugs but also by
│ custom rules. Instead of just stopping linter we generate
│ the errors and continue processing. This allows users to
│ add this rule to their warn list until the root cause is
│ fixed.
version_added │ v5.0.0
tags │ core
severity │ VERY_HIGH
╵
╷
literal-compare │ Don't compare to literal True/False
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Use when: var rather than when: var == True (or
│ conversely when: not var)
version_added │ v4.0.0
tags │ idiom
severity │ HIGH
╵
╷
load-failure │ Failed to load or parse file
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Linter failed to process a YAML file, possible not an
│ Ansible file.
version_added │ v4.3.0
tags │ core
severity │ VERY_HIGH
╵
╷
meta-incorrect │ meta/main.yml default values should be changed
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ meta/main.yml default values should be changed for:
│ author, description, company, license, license
version_added │ v4.0.0
tags │ metadata
severity │ HIGH
╵
╷
meta-no-info │ meta/main.yml should contain relevant info
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ meta/main.yml should contain: author, description,
│ license, min_ansible_version, platforms
version_added │ v4.0.0
tags │ metadata
severity │ HIGH
╵
╷
meta-no-tags │ Tags must contain lowercase letters and digits only
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Tags must contain lowercase letters and digits only, and
│ galaxy_tags is expected to be a list
version_added │ v4.0.0
tags │ metadata
severity │ HIGH
╵
╷
meta-video-links │ meta/main.yml video_links should be formatted correctly
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Items in video_links in meta/main.yml should be
│ dictionaries, and contain only keys url and title, and
│ have a shared link from a supported provider
version_added │ v4.0.0
tags │ metadata
severity │ LOW
╵
╷
no-changed-when │ Commands should not change things if nothing needs doing
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Tasks should tell Ansible when to return changed, unless
│ the task only reads information. To do this, set
│ changed_when, use the creates or removes argument, or use
│ when to run the task only if another check has a
│ particular result.
│
│ For example, this task registers the shell output and
│ uses the return code to define when the task has changed.
│
│ .. code:: yaml
│
│ ┌───────────────────────────────────────────────────────┐
│ │ - name: handle shell output with return code │
│ │ ansible.builtin.shell: cat {{ myfile|quote }} │
│ │ register: myoutput │
│ │ changed_when: myoutput.rc != 0 │
│ └───────────────────────────────────────────────────────┘
│
│ The following example will trigger the rule since the
│ task does not handle the output of the command.
│
│ .. code:: yaml
│
│ ┌───────────────────────────────────────────────────────┐
│ │ - name: does not handle any output or return codes │
│ │ ansible.builtin.command: cat {{ myfile|quote }} │
│ └───────────────────────────────────────────────────────┘
version_added │ historic
tags │ command-shell, idempotency
severity │ HIGH
╵
╷
no-handler │ Tasks that run when changed should likely be handlers
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ If a task has a when: result.changed setting, it is
│ effectively acting as a handler. You could use notify and
│ move that task to handlers. (more)
version_added │ historic
tags │ idiom
severity │ MEDIUM
╵
╷
no-jinja-nesting │ Nested jinja pattern
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ There should not be any nested jinja pattern. Example
│ (bad): {{ list_one + {{ list_two | max }} }}, Example
│ (good): {{ list_one + max(list_two) }}, Example
│ (allowed): --format='{{'{{'}}.Size{{'}}'}}'
version_added │ v4.3.0
tags │ formatting
severity │ VERY_HIGH
╵
╷
no-jinja-when │ No Jinja2 in when
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ when is a raw Jinja2 expression, remove redundant {{ }}
│ from variable(s).
version_added │ historic
tags │ deprecations
severity │ HIGH
╵
╷
no-loop-var-prefix │ Role loop_var should use configured prefix.
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Looping inside roles has the risk of clashing with loops
│ from user-playbooks. (more)
tags │ idiom
severity │ MEDIUM
╵
╷
no-relative-paths │ Doesn't need a relative path in role
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ copy and template do not need to use relative path for
│ src
version_added │ v4.0.0
tags │ idiom
severity │ HIGH
╵
╷
no-tabs │ Most files should not contain tabs
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Tabs can cause unexpected display issues, use spaces
version_added │ v4.0.0
tags │ formatting
severity │ LOW
╵
╷
package-latest │ Package installs should not use latest
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Package installs should use state=present with or without
│ a version
version_added │ historic
tags │ idempotency
severity │ VERY_LOW
╵
╷
parser-error │ AnsibleParserError
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Ansible parser fails; this usually indicates an invalid
│ file.
version_added │ v5.0.0
tags │ core
severity │ VERY_HIGH
╵
╷
partial-become │ become_user requires become to work as expected
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ become_user without become will not actually change user
version_added │ historic
tags │ unpredictability
severity │ VERY_HIGH
╵
╷
playbook-extension │ Use ".yml" or ".yaml" playbook extension
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Playbooks should have the ".yml" or ".yaml" extension
version_added │ v4.0.0
tags │ formatting
severity │ MEDIUM
╵
╷
risky-file-permissions │ File permissions unset or incorrect
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Missing or unsupported mode parameter can cause
│ unexpected file permissions based on version of Ansible
│ being used. Be explicit, like mode: 0644 to avoid hitting
│ this rule. Special preserve value is accepted only by
│ copy, template modules. See
│ https://github.com/ansible/ansible/issues/71200
version_added │ v4.3.0
tags │ unpredictability, experimental
severity │ VERY_HIGH
╵
╷
risky-octal │ Octal file permissions must contain leading zero or be a
│ string
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Numeric file permissions without leading zero can behave
│ in unexpected ways. See
│ https://docs.ansible.com/ansible/latest/collections/ansi…
version_added │ historic
tags │ formatting
severity │ VERY_HIGH
╵
╷
risky-shell-pipe │ Shells that use pipes should set the pipefail option
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Without the pipefail option set, a shell command that
│ implements a pipeline can fail and still return 0. If any
│ part of the pipeline other than the terminal command
│ fails, the whole pipeline will still return 0, which may
│ be considered a success by Ansible. Pipefail is available
│ in the bash shell.
version_added │ v4.1.0
tags │ command-shell
severity │ MEDIUM
╵
╷
role-name │ Role name {0} does not match ^[a-z][a-z0-9_]+$ pattern
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Role names are now limited to contain only lowercase
│ alphanumeric characters, plus '' and start with an alpha
│ character. See developing collections
│ <https://docs.ansible.com/ansible/devel/dev_guide/develo…
version_added │ v4.3.0
tags │ deprecations, metadata
severity │ HIGH
╵
╷
syntax-check │ Ansible syntax check failed
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Running ansible-playbook --syntax-check ... failed.
│
│ This error cannot be disabled due to being a prerequisite
│ for other steps. You can either exclude these files from
│ linting or better assure they can be loaded by Ansible.
│ This is often achieved by editing inventory file and/or
│ ansible.cfg so ansible can load required variables.
│
│ If undefined variables are the failure reason you could
│ use jinja default() filter in order to provide fallback
│ values.
version_added │ v5.0.0
tags │ core, unskippable
severity │ VERY_HIGH
╵
╷
unnamed-task │ All tasks should be named
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ All tasks should have a distinct name for readability and
│ for --start-at-task to work
version_added │ historic
tags │ idiom
severity │ MEDIUM
╵
╷
var-naming │ All variables should be named using only lowercase and
│ underscores
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ All variables should be named using only lowercase and
│ underscores
version_added │ v5.0.10
tags │ idiom, experimental
severity │ MEDIUM
╵
╷
var-spacing │ Variables should have spaces before and after: {{
│ var_name }}
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Variables should have spaces before and after: {{
│ var_name }}
version_added │ v4.0.0
tags │ formatting
severity │ LOW
╵
╷
yaml │ Violations reported by yamllint
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Rule violations reported by YamlLint when this is
│ installed.
│
│ You can fully disable all of them by adding 'yaml' to the
│ 'skip_list'.
│
│ Specific tag identifiers that are printed at the end of
│ rule name, like 'trailing-spaces' or 'indentation' can
│ also be be skipped, allowing you to have a more fine
│ control.
│
│ By default this rule is not used when yamllint library is
│ missing. If you want to make its absence a runtime
│ failure, please add 'yaml' to 'enable_list' inside the
│ configuration file.
version_added │ v5.0.0
tags │ formatting, yaml
severity │ VERY_LOW
╵
プレイブックをチェック
次のプレイブックをチェックします。
sample.yml
---
- name: Apache server installed
hosts: web
become: yes
tasks:
- name: Install Apache
ansible.builtin.dnf:
name: httpd
state: latest
- name: Apache enabled and running
ansible.builtin.systemd:
name: httpd.service
state: started
enabled: yes
- name: copy index.html
ansible.builtin.template:
src: ./index.j2
dest: /var/www/html/index.html
チェック結果です。
y_mrok@ctrl:~/code/exam31$ ansible-lint sample.yml
WARNING Overriding detected file kind 'yaml' with 'playbook' for given positional argument: sample.yml
WARNING Listing 2 violation(s) that are fatal
package-latest: Package installs should not use latest
sample.yml:7 Task/Handler: Install Apache
risky-file-permissions: File permissions unset or incorrect
sample.yml:16 Task/Handler: copy index.html
You can skip specific rules or tags by adding them to your configuration file:
# .ansible-lint
warn_list: # or 'skip_list' to silence them completely
- experimental # all rules tagged as experimental
- package-latest # Package installs should not use latest
Finished with 1 failure(s), 1 warning(s) on 1 files.
y_mrok@ctrl:~/code/exam31$
2 つ指摘されました。
1 つめです。
package-latest: Package installs should not use latest
sample.yml:7 Task/Handler: Install Apache
次のチェックルールに該当しました。
package-latest │ Package installs should not use latest
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Package installs should use state=present with or without
│ a version
version_added │ historic
tags │ idempotency
severity │ VERY_LOW
「パッケージのインストールでは、バージョンの有無にかかわらず、state=presentを使用する必要があります。」なので、パッケージのインストール時に state
パラメーターに指定するのは latest
ではなく present
が良いとのことです。
2 つめです。
risky-file-permissions: File permissions unset or incorrect
sample.yml:16 Task/Handler: copy index.html
次のチェックルールに該当しました。
risky-file-permissions │ File permissions unset or incorrect
╶───────────────────────────┼───────────────────────────────────────────────────────────╴
description │ Missing or unsupported mode parameter can cause
│ unexpected file permissions based on version of Ansible
│ being used. Be explicit, like mode: 0644 to avoid hitting
│ this rule. Special preserve value is accepted only by
│ copy, template modules. See
│ https://github.com/ansible/ansible/issues/71200
version_added │ v4.3.0
tags │ unpredictability, experimental
severity │ VERY_HIGH
「modeパラメータがない、またはサポートされていない場合、使用しているAnsibleのバージョンに応じて、予期しないファイルパーミッションが発生することがあります。modeのように明示してください。0644のように明示することで、このルールにヒットしないようにします。特殊な preserve 値は、copy や template モジュールでのみ受け入れられます。」なので、タスクでファイルをコピーする ansible.builtin.template
モジュールや ansible.builtin.copy
モジュールを使用するときは mode
パラメーターを指定して明示的にパーミッションを指定したほうが良いとのことです。
指摘事項を修正したプレイブックです。
sample1.yml
---
- name: Apache server installed
hosts: web
become: yes
tasks:
- name: Install Apache
ansible.builtin.dnf:
name: httpd
state: present
- name: Apache enabled and running
ansible.builtin.systemd:
name: httpd.service
state: started
enabled: yes
- name: copy index.html
ansible.builtin.template:
src: ./index.j2
dest: /var/www/html/index.html
mode: '0644'
ansible-lint でチェックした結果です。指摘事項を修正したので、今度は指摘されませんでした。
y_mrok@ctrl:~/code/exam31$ ansible-lint sample1.yml
WARNING Overriding detected file kind 'yaml' with 'playbook' for given positional argument: sample1.yml
y_mrok@ctrl:~/code/exam31$