koh’s blog

Sys Admin who loves automation

ansible-lintの設定

ansible-lintというAnsibleのPlaybookの問題点を指摘してくれるツールがあります。

github.com

無駄なスペースを検知してくれたり、Playbookのベストプラクティスに沿わないような記載を指摘してくれます。

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[201] Trailing whitespace
site.yml:6
        msg: hello

[403] Package installs should not use latest
site.yml:8
Task/Handler: install httpd

[koh@kohs-MBP] ~/work/linttest
%

デフォルトルールの一覧はこちらです。

docs.ansible.com

Playbookの書き方や運用の仕方によっては特定のルールを無視したり別途ルールを追加したりしたい場合もあると思います。
そのための設定方法についてまとめました。
※ ansible-lintのバージョンは 4.1.0 をもとに説明しています。

コマンドラインでの設定

コマンドラインのオプションでも各ルールを設定できます。

オプション一覧(クリックで展開)

[koh@kohs-MBP] ~/work/linttest
% ansible-lint --help
Usage: ansible-lint [options] playbook.yml [playbook2 ...]

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -L                    list all the rules
  -q                    quieter, although not silent output
  -p                    parseable output in the format of pep8
  --parseable-severity  parseable output including severity of rule
  -r RULESDIR           specify one or more rules directories using one or
                        more -r arguments. Any -r flags override the default
                        rules in
                        /Users/koh/.pyenv/versions/3.7.3/lib/python3.7/site-
                        packages/ansiblelint/rules, unless -R is also used.
  -R                    Use default rules in
                        /Users/koh/.pyenv/versions/3.7.3/lib/python3.7/site-
                        packages/ansiblelint/rules in addition to any extra
                        rules directories specified with -r. There is no need
                        to specify this if no -r flags are used
  -t TAGS               only check rules whose id/tags match these values
  -T                    list all the tags
  -v                    Increase verbosity level
  -x SKIP_LIST          only check rules whose id/tags do not match these
                        values
  --nocolor             disable colored output
  --force-color         Try force colored output (relying on ansible's code)
  --exclude=EXCLUDE_PATHS
                        path to directories or files to skip. This option is
                        repeatable.
  -c C                  Specify configuration file to use.  Defaults to
                        ".ansible-lint"
[koh@kohs-MBP] ~/work/linttest
%

基本的にはコマンドラインでのオプションですべて設定できるのですが、チームでルールを共有したりCIに組み込んだりする際には設定ファイルに指定するケースが多いと思います。
設定ファイルでの記載方法について書いていきます。

設定ファイル

デフォルトでは ./.ansible-lint 、もしくはオプション -c で指定したファイルになります。
公式の例はこのような形になっています。

exclude_paths:
  - ./my/excluded/directory/
  - ./my/other/excluded/directory/
  - ./last/excluded/directory/
parseable: true
quiet: true
rulesdir:
  - ./rule/directory/
skip_list:
  - skip_this_tag
  - and_this_one_too
  - skip_this_id
  - '401'
tags:
  - run_this_tag
use_default_rules: true
verbosity: 1

https://docs.ansible.com/ansible-lint/configuring/configuring.html#configuration-file

それぞれの設定項目について説明していきます。

exclude_paths (--exclude)

チェックから除外するパス/ファイルを指定します。

parseable (-p)

出力の形式を変更できます。
複数行に分かれているものが1行にまとまります。

# parseable: false

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[201] Trailing whitespace
/Users/koh/work/linttest/roles/201/tasks/main.yml:4
    msg: hello

[202] Octal file permissions must contain leading zero or be a string
/Users/koh/work/linttest/roles/202/tasks/main.yml:2
Task/Handler: error 202

[203] Most files should not contain tabs
/Users/koh/work/linttest/roles/203/tasks/main.yml:4
    msg: "   tab"

[koh@kohs-MBP] ~/work/linttest
%
# parseable: true

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
/Users/koh/work/linttest/roles/201/tasks/main.yml:4: [E201] Trailing whitespace
/Users/koh/work/linttest/roles/202/tasks/main.yml:2: [E202] Octal file permissions must contain leading zero or be a string
/Users/koh/work/linttest/roles/203/tasks/main.yml:4: [E203] Most files should not contain tabs
[koh@kohs-MBP] ~/work/linttest
%

quiet (-q)

出力形式を変更できます。
0にはなりませんが少し減ります。

# quiet: true

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[201] /Users/koh/work/linttest/roles/201/tasks/main.yml:4
[202] /Users/koh/work/linttest/roles/202/tasks/main.yml:2
[203] /Users/koh/work/linttest/roles/203/tasks/main.yml:4
[koh@kohs-MBP] ~/work/linttest
%

rulesdir (-r)

自作のruleを格納したファイル/ディレクトリを指定できます。
自作ruleについては後述します。

skip_list (-x)

スキップするタグやエラーIDを指定できます。
タグについては -T オプションで一覧を出力できます。

タグ一覧(クリックで展開)

[koh@kohs-MBP] ~/work/linttest
% ansible-lint -T
ANSIBLE0002 ['[201]']
ANSIBLE0004 ['[401]']
ANSIBLE0005 ['[402]']
ANSIBLE0006 ['[303]']
ANSIBLE0007 ['[302]']
ANSIBLE0008 ['[103]']
ANSIBLE0009 ['[202]']
ANSIBLE0010 ['[403]']
ANSIBLE0011 ['[502]']
ANSIBLE0012 ['[301]']
ANSIBLE0013 ['[305]']
ANSIBLE0014 ['[304]']
ANSIBLE0015 ['[104]']
ANSIBLE0016 ['[503]']
ANSIBLE0017 ['[501]']
ANSIBLE0018 ['[101]']
ANSIBLE0019 ['[102]']
behaviour ['[503]']
bug ['[304]']
command-shell ['[305]', '[302]', '[304]', '[306]', '[301]', '[303]']
deprecated ['[105]', '[104]', '[103]', '[101]', '[102]']
formatting ['[104]', '[203]', '[201]', '[204]', '[206]', '[205]', '[202]']
idempotency ['[301]']
idiom ['[601]', '[602]']
metadata ['[701]', '[704]', '[703]', '[702]']
module ['[404]', '[401]', '[403]', '[402]']
oddity ['[501]']
readability ['[502]']
repeatability ['[401]', '[403]', '[402]']
resources ['[302]', '[303]']
safety ['[305]']
task ['[502]', '[503]', '[504]', '[501]']
[koh@kohs-MBP] ~/work/linttest
%

tags (-t)

skip_listの逆でこの値が指定された場合は指定されたタグのみチェックします。
ちなみにskip_listとtags両方に同じ値を設定するとスキップされるみたいです。

use_default_rules (-R)

trueの場合デフォルトのルールが適用されます。
自作のルールのみチェックしたい場合にはfalseに指定します。

verbosity (-v)

出力の詳細度を指定できます。
数値で出力量が変わりそうに見えますが現状0か1以上かでのみ挙動が変わりそうです。

        for file in files:
            if self.verbosity > 0:
                print("Examining %s of type %s" % (file['path'], file['type']))
            matches.extend(self.rules.run(file, tags=set(self.tags),
                           skip_list=self.skip_list))

https://github.com/ansible/ansible-lint/blob/5170c04201e71400421b27255280902c211f8548/lib/ansiblelint/__init__.py#L281

Playbook内での設定

Playbook内でコメントを入れることにより行単位でチェック対象から外すことができます。

例として下記のようなPlaybookを作成します。

[koh@kohs-MBP] ~/work/linttest
% cat site.yml
---
- hosts: all
  tasks:
    - name: install latest httpd
      yum:
        name: httpd
        state: latest

    - name: install mysql
      yum:
        name: mysql
        state: installed
[koh@kohs-MBP] ~/work/linttest
%

このPlaybookに対してansible-lintを実行すると403のエラーが返されます。

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[403] Package installs should not use latest
site.yml:4
Task/Handler: install latest httpd

[koh@kohs-MBP] ~/work/linttest
%

どうしてもhttpdは常に最新にしておきたいけど他のパッケージはlatestにはしたくないといったとき(多分無い)には下記のように記載できます。

[koh@kohs-MBP] ~/work/linttest
% cat site.yml
---
- hosts: all
  tasks:
    - name: install latest httpd
      yum:
        name: httpd
        state: latest # noqa 403

    - name: install mysql
      yum:
        name: mysql
        state: installed
[koh@kohs-MBP] ~/work/linttest
%

実際にエラーを返す行に # noqa ID とコメントを入れるとチェック対象から除外されます。

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[koh@kohs-MBP] ~/work/linttest
%

自作ルール

ansible-lint用のルールを自作することができます。
READMEにも作成の仕方が記載されていますが、デフォルトルールのスクリプトを参考にするとわかりやすいと思います。

https://github.com/ansible/ansible-lint#creating-custom-rules

https://github.com/ansible/ansible-lint/tree/master/lib/ansiblelint/rules

試しに一つ自作ルールを作成してみます。

適当なディレクトリを作成してルール用のスクリプトを作成します。

[koh@kohs-MBP] ~/work/linttest
% cat origrules/True4BooleanRule.py
from ansiblelint import AnsibleLintRule


class True4BooleanRule(AnsibleLintRule):
    id = '99'
    shortdesc = 'Use "True" for boolean'
    description = 'We should "True" for boolean not "yes" or "true".'
    tags = ['formatting']

    def match(self, file, line):
        return ': yes' in line or ': true' in line
[koh@kohs-MBP] ~/work/linttest
%

PlaybookではbooleanのTrueを指定する際に True 以外にも yes , true なども使用できます。
このルールでは先頭大文字の True に統一するべく yes , true を検知してエラーを出します。
※例のためのスクリプトなのでかなり雑に書いています。使用する際には適宜修正してください。

.ansible-lint に下記のように記載します。

[koh@kohs-MBP] ~/work/linttest
% cat .ansible-lint
rulesdir:
  - ./origrules/
[koh@kohs-MBP] ~/work/linttest
%

site.ymlを下記のようにすると3つのタスクのうち上2つが目論見通りエラーになります。

[koh@kohs-MBP] ~/work/linttest
% cat site.yml
---
- hosts: all
  tasks:
    - name: this should be error
      service:
        name: httpd
        state: started
        enabled: yes

    - name: this should be error too
      service:
        name: mysqld
        state: started
        enabled: true

    - name: this is ok
      service:
        name: haproxy
        state: started
        enabled: True
[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[99] Use "True" for boolean
site.yml:8
        enabled: yes

[99] Use "True" for boolean
site.yml:14
        enabled: true

[koh@kohs-MBP] ~/work/linttest
%

まとめ

設定ファイルでの各パラメータの説明等が公式ドキュメントに見当たらなかったのでまとめてみました。
自作ルールの作成が思ったよりも簡単だったので色々試してみたいですね。