引き続き第4回目です。インフラCI実践ガイドの第6章に取り組んでみます。
6章はかなりボリュームがあるので前半部分になります。
第6章ではユニットテストについて解説されており、いよいよCIをするために必要となる要素が固まってきた感じがあります。
要点についてまとめています。
6.1 インフラ環境におけるテスト
人力で行うテストは手順書を作成して、それに従って実施するのが基本。手順書のレビューなども実施している。また、手順書に記載されているもの以外のテストも暗黙的に実施している。
この手順書やノウハウというものをコード化する際の注意
明文化されたテストだけでなく暗黙的なテストもコードとして記述する。
6.1.1 テストの体系
V字モデルについての解説とそれをインフラのテスト内容と紐づけていく。
- 要件定義
- 基本設計
- 詳細設計
- コーディング
このフェーズに合わせて以下のテストを実施する。
- コードレビュー(コーディング)
- ユニットテスト(詳細設計)
- インテグレーションテスト(基本定義)
- システムテスト(要件定義)
次から各テストの概要について説明
6.1.2 コードレビュー
作成された手順書(ここではAnsibleのPlaybook)のコードをレビューする。
「コーディング規約の準拠チェック」と「構文チェック」の2つを実施する
コーディング規約の準拠チェック
「ansible-lint」というコーディング規約のチェックツールを使う
構文チェック
実際の処理を行わないが、単純なYAMLの構文ミスやロール名の間違いなどによるPlaybookのエラーなどのケアレスミスを発見する
6.1.3 ユニットテスト
個々に実施される手順や設定に注目し、その作業が確実に実行されたのか、想定通りの設定値になっているのかを確認して、作業の確実性を向上する。
6.1.4 インテグレーションテスト、システムテスト
インテグレーションテストでは、設計どおりの機能を満たしているかをシステムの構成要素を結合して確認します。
システムテストでは、本番環境と同一かほぼ同等の環境で、より広範囲に、性能や可用性などの観点を含めたテストが行われます。
6.2 コードレビューの自動化
実際に演習を行いながらコードレビューの自動化を学習していく
6.2.1 コーディング規約の準拠をテストする
ansible-lintによるテスト
サンプルを入手します。
[root@infraci ~]# cd ~/
[root@infraci ~]# git clone https://github.com/infra-ci-book/lint-rules.git
Cloning into 'lint-rules'...
remote: Enumerating objects: 103, done.
remote: Total 103 (delta 0), reused 0 (delta 0), pack-reused 103
Receiving objects: 100% (103/103), 13.28 KiB | 0 bytes/s, done.
Resolving deltas: 100% (39/39), done.
[root@infraci ~]# cd ~/lint-rules/samples/ansible/
[root@infraci ansible]# ll
-rw-r--r-- 1 root root 239 6月 24 16:54 named_tasks.yml
-rw-r--r-- 1 root root 157 6月 24 16:54 nameless_tasks.yml
次に2つのymlを実行して正しく動作すること、namelessではnameが指定されていないことを確認します。
[root@infraci ansible]# cd ~/lint-rules/samples/ansible/
[root@infraci ansible]# ansible-playbook named_tasks.yml
※nameが表示される
[root@infraci ansible]# ansible-playbook nameless_tasks.yml
※nameがない
この2つのファイルにansible-lintを適用します。
[root@infraci ansible]# cd ~/lint-rules/samples/ansible/
[root@infraci ansible]# ansible-lint --force-color -v named_tasks.yml
Examining named_tasks.yml of type playbook
[root@infraci ansible]# echo $?
0
[root@infraci ansible]#
[root@infraci ansible]# ansible-lint --force-color -v nameless_tasks.yml
Examining nameless_tasks.yml of type playbook
[ANSIBLE0011] All tasks should be named
nameless_tasks.yml:4
Task/Handler: shell ps |grep -v grep
[root@infraci ansible]# echo $0
-bash
エラー時に色が変わっているので画像でもUPしておきます。
ansible-lintのデフォルトルール
デフォルトルールを確認します。
[root@infraci ansible]# ansible-lint -R -L
ANSIBLE0002: Trailing whitespace
There should not be any trailing whitespace
ANSIBLE0004: Git checkouts must contain explicit version
All version control checkouts must point to an explicit commit or tag, not just "latest"
ANSIBLE0005: Mercurial checkouts must contain explicit revision
All version control checkouts must point to an explicit commit or tag, not just "latest"
ANSIBLE0006: Using command rather than module
Executing a command when there is an Ansible module is generally a bad idea
ANSIBLE0007: Using command rather than an argument to e.g. file
Executing a command when there is are arguments to modules is generally a bad idea
ANSIBLE0008: Deprecated sudo
Instead of sudo/sudo_user, use become/become_user.
ANSIBLE0009: Octal file permissions must contain leading zero
Numeric file permissions without leading zero can behave in unexpected ways. See http://docs.ansible.com/ansible/file_module.html
ANSIBLE0010: Package installs should not use latest
Package installs should use state=present with or without a version
ANSIBLE0011: All tasks should be named
All tasks should have a distinct name for readability and for --start-at-task to work
ANSIBLE0012: Commands should not change things if nothing needs doing
Commands should either read information (and thus set changed_when) or not do something if it has already been done (using creates/removes) or only do it if another check has a particular result (when)
ANSIBLE0013: Use shell only when shell functionality is required
Shell should only be used when piping, redirecting or chaining commands (and Ansible would be preferred for some of those!)
ANSIBLE0014: Environment variables don't work as part of command
Environment variables should be passed to shell or command through environment argument
ANSIBLE0015: Using bare variables is deprecated
Using bare variables is deprecated. Update your playbooks so that the environment value uses the full variable syntax ("{{your_variable}}").
ANSIBLE0016: Tasks that run when changed should likely be handlers
If a task has a `when: result.changed` setting, it's effectively acting as a handler
ANSIBLE0017: become_user requires become to work as expected
become_user without become will not actually change user
ANSIBLE0018: Deprecated always_run
Instead of always_run, use check_mode.
適用するルールの選択
デフォルトルールの適用を避けたい場合の除外方法についての説明
ansible-lintでエラーになる場合にどのようにルールを除外する方法について説明。いくつかやり方がある。
①全体ルールとして除外する方法 → ルールファイルでskip_listで指定する
0006のルールの適用を除外したファイルがある
[root@infraci ketchup-vagrant-ansible]# cat ~/lint-rules/rules/ansible/.ansible-lint
parseable: true
quiet: false
rulesdir:
- lint-rules/rules/ansible/rules
skip_list:
- ANSIBLE0006
use_default_rules: true
verbosity: 1
0006を除外してルールファイルを指定して、結果OKとなる
この方法は全体のチェックから除外する方法。
ほかに以下の除外のやり方もある(詳細は割愛)
②特定のルールをその時だけ除外して実行する方法(-xを利用)
③特定のタスクに対して、特定のルールを除外して実行する方法(–excludeを利用)
独自のルールを定義する
shellまたはcommandを使ったときにrmが含まれているとエラーにする独自ルールを記載します。ルールはpythonで記載します
[root@infraci ketchup-vagrant-ansible]# cd ~/lint-rules/rules/ansible/rules/
[root@infraci rules]# vim ShellRestrictRm.py
from ansiblelint import AnsibleLintRule
class ShellRestrictRm(AnsibleLintRule):
id = 'MYRULE0001'
shortdesc = 'rm command is not allowd in shell module'
description = 'This is a sample rule for ansible-lint'
tags = ['shell']
check_command = 'rm'
def matchtask(self, file, task):
if task['action']['__ansible_module__'] not in ['shell', 'command']:
return False
if self.check_command in task['action']['__ansible_arguments__']:
return True
return False
Pythonの構文チェックを行って問題ないことを確認してから、ルールとして登録されるかをチェックします。
[root@infraci rules]# python -m py_compile ShellRestrictRm.py
[root@infraci rules]# ansible-lint -r ~/lint-rules/rules/ansible/rules -L
MYRULE0001: rm command is not allowd in shell module
This is a sample rule for ansible-lint
もう1つのルールを作成します。Ciscoのios_commandを使っている場合にshow running-configを実行しようとするとエラーにするルールです。
[root@infraci ansible]# cd ~/lint-rules/rules/ansible/rules/
[root@infraci rules]# vim IosCommandRestrictShowRunningConfig.py
from ansiblelint import AnsibleLintRule
class IosCommandRestrictShowRunningConfig(AnsibleLintRule):
id = 'MYRULE0002'
shortdesc = 'show running-config is not allowed in ios_command'
description = 'This is a sample rule for ansible-lint'
tags = ['ios']
check_command = 'show running-config'
def matchtask(self, file, task):
if task['action']['__ansible_module__'] not in ['ios_command']:
return False
if self.check_command in task['action']['commands']:
return True
return False
[root@infraci rules]# ansible-lint -r ~/lint-rules/rules/ansible/rules -L
MYRULE0001: rm command is not allowd in shell module
This is a sample rule for ansible-lint
MYRULE0002: show running-config is not allowed in ios_command
This is a sample rule for ansible-lint
次にルールをチェックするためのsampleのPlaybookを作ります。
[root@infraci rules]# cd ~/lint-rules/samples/ansible/
[root@infraci ansible]# vim lint-sample.yml
- hosts: localhost
tasks:
- shell: rm -rf /hoge
- ios_command:
commands: show running-config
最後にルールが適用されるか試してみます。
うまくいきました。
私はルールやPlaybookファイルを記載しているときにスペルミスが結構あって少しはまりました。
長くなりましが、ここまででansible-lintをつかったコーディング規約の準拠をテストする方法が理解できました。
今回は以上となります。
コメント