序章

Ansible での単体テストは、役割が意図したとおりに機能することを確認するための鍵です。 Molecular は、さまざまな環境に対して役割をテストするシナリオを指定できるようにすることで、このプロセスを容易にします。 Moleculeは、内部でAnsibleを使用して、構成された環境に役割をデプロイするプロビジョナーに役割をオフロードし、ベリファイア( Testinfra など)を呼び出して構成のドリフトをチェックします。 これにより、あなたの役割がその特定のシナリオで環境に予想されるすべての変更を加えたことを確認できます。

このガイドでは、 Apache をホストにデプロイし、CentOS7でfirewalldを構成するAnsibleロールを構築します。 この役割が意図したとおりに機能することをテストするには、 Docker をドライバーとして使用し、サーバーの状態をテストするためのPythonライブラリであるTestinfraを使用してMoleculeでテストを作成します。 MoleculeはDockerコンテナーをプロビジョニングして役割をテストし、Testinfraはサーバーが意図したとおりに構成されていることを確認します。 終了すると、環境間でビルドするための複数のテストケースを作成し、Moleculeを使用してこれらのテストを実行できるようになります。

前提条件

このガイドを開始する前に、次のものが必要です。

ステップ1—環境の準備

前提条件を満たしている場合は、Python3が必要です。 venv、およびDockerがインストールされ、正しく構成されている。 まず、AnsiblewithMoleculeをテストするための仮想環境を作成しましょう。

root以外のユーザーとしてログインし、新しい仮想環境を作成することから始めます。

  1. python3 -m venv my_env

これをアクティブにして、アクションがその環境に制限されていることを確認します。

  1. source my_env/bin/activate

次に、アクティブ化された環境で、 wheel パッケージは、 bdist_wheel setuptools その拡張 pip Ansibleのインストールに使用します:

  1. python3 -m pip install wheel

これでインストールできます moleculedockerpip. Ansibleは、Moleculeの依存関係として自動的にインストールされます。

  1. python3 -m pip install molecule docker

これらの各パッケージの機能は次のとおりです。

  • molecule:これは、ロールのテストに使用するメインのMoleculeパッケージです。 インストール molecule Ansibleを他の依存関係とともに自動的にインストールし、Ansibleプレイブックを使用して役割とテストを実行できるようにします。
  • docker:このPythonライブラリは、MoleculeがDockerとのインターフェイスに使用します。 Dockerをドライバーとして使用しているため、これが必要になります。

次に、Moleculeでロールを作成しましょう。

ステップ2—分子での役割の作成

環境を設定したら、Moleculeを使用して、Apacheのインストールをテストするために使用する基本的な役割を作成できます。 この役割は、ディレクトリ構造といくつかの初期テストを作成し、Dockerをドライバーとして指定して、MoleculeがDockerを使用してテストを実行するようにします。

と呼ばれる新しい役割を作成します ansible-apache:

  1. molecule init role -r ansible-apache -d docker

The -r フラグは役割の名前を指定します -d テストで使用するMoleculeのホストをプロビジョニングするドライバーを指定します。

新しく作成されたロールのディレクトリに移動します。

  1. cd ansible-apache

デフォルトの役割をテストして、Moleculeが正しく設定されているかどうかを確認します。

  1. molecule test

デフォルトの各テストアクションを一覧表示する出力が表示されます。 テストを開始する前に、Moleculeは構成ファイルを検証します molecule.yml すべてが正常であることを確認します。 また、テストアクションの順序を指定するこのテストマトリックスも出力します。

Output
--> Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml. Validation completed successfully. --> Test matrix └── default ├── lint ├── destroy ├── dependency ├── syntax ├── create ├── prepare ├── converge ├── idempotence ├── side_effect ├── verify └── destroy ...

役割を作成してテストをカスタマイズしたら、各テストアクションについて詳しく説明します。 今のところ、注意を払ってください PLAY_RECAP テストごとに、デフォルトのアクションのいずれもが failed 状態。 たとえば、 PLAY_RECAP デフォルトの場合 'create' アクションは次のようになります。

Output
... PLAY RECAP ********************************************************************* localhost : ok=5 changed=4 unreachable=0 failed=0

次に、Apacheとfirewalldを構成するための役割の変更に移りましょう。

ステップ3—ApacheとFirewalldの設定

Apacheとfirewalldを設定するには、ロールのタスクファイルを作成し、インストールするパッケージと有効にするサービスを指定します。 これらの詳細は、デフォルトのApacheインデックスページを置き換えるために使用する変数ファイルとテンプレートから抽出されます。

まだ ansible-apache ディレクトリ、を使用してロールのタスクファイルを作成します nano またはお気に入りのテキストエディタ:

  1. nano tasks/main.yml

ファイルがすでに存在していることがわかります。 そこにあるものを削除し、次のコードに置き換えて、必要なパッケージをインストールし、正しいサービス、HTMLのデフォルト、およびファイアウォール設定を有効にします。

〜/ ansible-apache / tasks / main.yml
---
- name: "Ensure required packages are present"
  yum:
    name: "{{ pkg_list }}"
    state: present

- name: "Ensure latest index.html is present"
  template:
    src: index.html.j2
    dest: /var/www/html/index.html

- name: "Ensure httpd service is started and enabled"
  service:
    name: "{{ item }}"
    state: started
    enabled: true
  with_items: "{{ svc_list }}"

- name: "Whitelist http in firewalld"
  firewalld:
    service: http
    state: enabled
    permanent: true
    immediate: true

このプレイブックには、次の4つのタスクが含まれています。

  • "Ensure required packages are present":このタスクは、下の変数ファイルにリストされているパッケージをインストールします pkg_list. 変数ファイルは次の場所にあります ~/ansible-apache/vars/main.yml このステップの最後に作成します。
  • "Ensure latest index.html is present":このタスクはテンプレートページをコピーします、 index.html.j2、デフォルトのインデックスファイルに貼り付けます。 /var/www/html/index.html、Apacheによって生成されます。 このステップでは、新しいテンプレートも作成します。
  • "Ensure httpd service is started and enabled":このタスクは、にリストされているサービスを開始して有効にします svc_list 変数ファイル内。
  • "Whitelist http in firewalld":このタスクはホワイトリストに登録されます http でのサービス firewalld. Firewalldは、CentOSサーバーにデフォルトで存在する完全なファイアウォールソリューションです。 のために http サービスを機能させるには、必要なポートを公開する必要があります。 指示する firewalld サービスをホワイトリストに登録すると、サービスに必要なすべてのポートがホワイトリストに登録されます。

終了したら、ファイルを保存して閉じます。

次に、を作成しましょう templates のディレクトリ index.html.j2 テンプレートページ:

  1. mkdir templates

ページ自体を作成します。

  1. nano templates/index.html.j2

次の定型コードを貼り付けます。

〜/ ansible-apache / templates / index.html.j2
<div style="text-align: center">
    <h2>Managed by Ansible</h2>
</div>

ファイルを保存して閉じます。

ロールを完了するための最後のステップは、変数ファイルを作成することです。このファイルは、メインのロールプレイブックにパッケージとサービスの名前を提供します。

  1. nano vars/main.yml

次のコードを使用してデフォルトのコンテンツを貼り付けます。 pkg_listsvc_list:

〜/ ansible-apache / vars / main.yml
---
pkg_list:
  - httpd
  - firewalld
svc_list:
  - httpd
  - firewalld

これらのリストには、次の情報が含まれています。

  • pkg_list:これには、ロールがインストールするパッケージの名前が含まれています。 httpdfirewalld.
  • svc_list:これには、ロールが開始して有効にするサービスの名前が含まれます。 httpdfirewalld.

注:変数ファイルに空白行がないことを確認してください。空白行がない場合、リンティング中にテストが失敗します。

役割の作成が完了したので、意図したとおりに機能するかどうかをテストするようにMoleculeを構成しましょう。

ステップ4—テストを実行するための役割を変更する

この場合、Moleculeの構成には、Molecule構成ファイルの変更が含まれます。 molecule.yml プラットフォーム仕様を追加します。 構成して開始する役割をテストしているため httpd systemdサービスでは、systemdが構成され、特権モードが有効になっているイメージを使用する必要があります。 このチュートリアルでは、 milcom/centos7-systemd イメージはDockerHubで利用できます。 特権モードでは、コンテナーをホストマシンのほぼすべての機能で実行できます。

編集しましょう molecule.yml これらの変更を反映するには:

  1. nano molecule/default/molecule.yml

強調表示されたプラットフォーム情報を追加します。

〜/ ansible-apache /molecule / default /molecule.yml
---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: centos7
    image: milcom/centos7-systemd
    privileged: true
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8

完了したら、ファイルを保存して閉じます。

テスト環境の構成が正常に完了したので、役割の実行後にMoleculeがコンテナーに対して実行するテストケースの作成に移りましょう。

ステップ5—テストケースの作成

この役割のテストでは、次の条件を確認します。

  • それは httpdfirewalld パッケージがインストールされます。
  • それは httpdfirewalld サービスが実行され、有効になっています。
  • それは http サービスはファイアウォール設定で有効になっています。
  • それか index.html テンプレートファイルで指定されたものと同じデータが含まれています。

これらのテストがすべて合格した場合、役割は意図したとおりに機能します。

これらの条件のテストケースを作成するには、デフォルトのテストを編集してみましょう。 ~/ansible-apache/molecule/default/tests/test_default.py. Testinfraを使用して、Moleculeクラスを使用するPython関数としてテストケースを記述します。

開ける test_default.py:

  1. nano molecule/default/tests/test_default.py

テストを最初から作成できるように、ファイルの内容を削除します。

注:テストを作成するときは、テストが2行で区切られていることを確認してください。そうしないと、失敗します。

必要なPythonモジュールをインポートすることから始めます。

〜/ ansible-apache /molecule / default / tests / test_default.py
import os
import pytest

import testinfra.utils.ansible_runner

これらのモジュールには次のものが含まれます。

  • os:この組み込みのPythonモジュールは、オペレーティングシステムに依存する機能を有効にし、Pythonが基盤となるオペレーティングシステムとインターフェイスできるようにします。
  • pytest pytest モジュールは、テスト書き込みを有効にします。
  • testinfra.utils.ansible_runner:このTestinfraモジュールは、コマンド実行のバックエンドとしてAnsibleを使用します。

モジュールのインポートの下に、Ansibleバックエンドを使用して現在のホストインスタンスを返す次のコードを追加します。

〜/ ansible-apache /molecule / default / tests / test_default.py
...
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')

Ansibleバックエンドを使用するようにテストファイルを構成したら、ホストの状態をテストする単体テストを作成しましょう。

最初のテストでは、 httpdfirewalld インストールされています:

〜/ ansible-apache /molecule / default / tests / test_default.py
...

@pytest.mark.parametrize('pkg', [
  'httpd',
  'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed

テストはpytest.mark.parametrizeデコレータで始まります。これにより、テストの引数をパラメータ化できます。 この最初のテストには test_pkg の存在をテストするためのパラメータとして httpdfirewalld パッケージ。

次のテストでは、 httpdfirewalld 実行中であり、有効になっています。 それはとります test_svc パラメータとして:

〜/ ansible-apache /molecule / default / tests / test_default.py
...

@pytest.mark.parametrize('svc', [
  'httpd',
  'firewalld'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled

最後のテストでは、ファイルとコンテンツがに渡されたことを確認します parametrize() 存在。 ファイルが自分の役割によって作成されておらず、コンテンツが適切に設定されていない場合は、 assert 戻ります False:

〜/ ansible-apache /molecule / default / tests / test_default.py
...

@pytest.mark.parametrize('file, content', [
  ("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
  ("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)

各テストで、 assert 戻ります True また False テスト結果によって異なります。

完成したファイルは次のようになります。

〜/ ansible-apache /molecule / default / tests / test_default.py
import os
import pytest

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')


@pytest.mark.parametrize('pkg', [
  'httpd',
  'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed


@pytest.mark.parametrize('svc', [
  'httpd',
  'firewalld'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled


@pytest.mark.parametrize('file, content', [
  ("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
  ("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)

テストケースを指定したので、役割をテストしましょう。

ステップ6—Moleculeを使用して役割をテストする

テストを開始すると、Moleculeはシナリオで定義したアクションを実行します。 デフォルトを実行してみましょう molecule シナリオをもう一度、それぞれを詳しく調べながら、デフォルトのテストシーケンスでアクションを実行します。

デフォルトシナリオのテストを再度実行します。

  1. molecule test

これにより、テスト実行が開始されます。 初期出力は、デフォルトのテストマトリックスを出力します。

Output
--> Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml. Validation completed successfully. --> Test matrix └── default ├── lint ├── destroy ├── dependency ├── syntax ├── create ├── prepare ├── converge ├── idempotence ├── side_effect ├── verify └── destroy

リンティングから始めて、各テストアクションと期待される出力を見ていきましょう。

lintingアクションが実行されます yamllint, flake8、 と ansible-lint:

  • yamllint:このリンターは、ロールディレクトリに存在するすべてのYAMLファイルで実行されます。
  • flake8:このPythonコードリンターは、Testinfra用に作成されたテストをチェックします。
  • ansible-lint:Ansibleプレイブック用のこのリンターは、すべてのシナリオで実行されます。
Output
... --> Scenario: 'default' --> Action: 'lint' --> Executing Yamllint on files found in /home/sammy/ansible-apache/... Lint completed successfully. --> Executing Flake8 on files found in /home/sammy/ansible-apache/molecule/default/tests/... Lint completed successfully. --> Executing Ansible Lint on /home/sammy/ansible-apache/molecule/default/playbook.yml... Lint completed successfully.

次のアクションdestroyは、 destroy.yml ファイル。 これは、新しく作成されたコンテナでの役割をテストするために行われます。

デフォルトでは、destroyは2回呼び出されます。テスト実行の開始時に既存のコンテナを削除し、最後に新しく作成されたコンテナを削除します。

Output
... --> Scenario: 'default' --> Action: 'destroy' PLAY [Destroy] ***************************************************************** TASK [Destroy molecule instance(s)] ******************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) deletion to complete] ******************************* ok: [localhost] => (item=None) ok: [localhost] TASK [Delete docker network(s)] ************************************************ skipping: [localhost] PLAY RECAP ********************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0

破棄アクションが完了すると、テストは依存関係に進みます。 このアクションにより、役割で必要な場合にansible-galaxyから依存関係をプルできます。 この場合、私たちの役割は次のことを行いません。

Output
... --> Scenario: 'default' --> Action: 'dependency' Skipping, missing the requirements file.

次のテストアクションは構文チェックで、デフォルトで実行されます playbook.yml プレイブック。 と同じように機能します --syntax-check コマンドのフラグ ansible-playbook --syntax-check playbook.yml:

Output
... --> Scenario: 'default' --> Action: 'syntax' playbook: /home/sammy/ansible-apache/molecule/default/playbook.yml

次に、テストはcreateアクションに進みます。 これは create.yml ロールのMoleculeディレクトリにファイルを作成して、仕様を使用してDockerコンテナを作成します。

Output
... --> Scenario: 'default' --> Action: 'create' PLAY [Create] ****************************************************************** TASK [Log into a Docker registry] ********************************************** skipping: [localhost] => (item=None) skipping: [localhost] TASK [Create Dockerfiles from image names] ************************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Discover local Docker images] ******************************************** ok: [localhost] => (item=None) ok: [localhost] TASK [Build an Ansible compatible image] *************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Create docker network(s)] ************************************************ skipping: [localhost] TASK [Create molecule instance(s)] ********************************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) creation to complete] ******************************* changed: [localhost] => (item=None) changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=5 changed=4 unreachable=0 failed=0

作成後、テストはprepareアクションに進みます。 このアクションは、コンバージを実行する前にホストを特定の状態にする準備プレイブックを実行します。 これは、役割を実行する前に、役割でシステムの事前構成が必要な場合に役立ちます。 繰り返しますが、これは私たちの役割には適用されません。

Output
... --> Scenario: 'default' --> Action: 'prepare' Skipping, prepare playbook not configured.

準備後、converge アクションは、コンテナでロールを実行します。 playbook.yml プレイブック。 複数のプラットフォームがで構成されている場合 molecule.yml ファイル、Moleculeはこれらすべてに収束します:

Output
... --> Scenario: 'default' --> Action: 'converge' PLAY [Converge] **************************************************************** TASK [Gathering Facts] ********************************************************* ok: [centos7] TASK [ansible-apache : Ensure required packages are present] ******************* changed: [centos7] TASK [ansible-apache : Ensure latest index.html is present] ******************** changed: [centos7] TASK [ansible-apache : Ensure httpd service is started and enabled] ************ changed: [centos7] => (item=httpd) changed: [centos7] => (item=firewalld) TASK [ansible-apache : Whitelist http in firewalld] **************************** changed: [centos7] PLAY RECAP ********************************************************************* centos7 : ok=5 changed=4 unreachable=0 failed=0

カバーした後、テストはべき等に進みます。 このアクションは、べき等性についてプレイブックをテストし、複数の実行で予期しない変更が行われないことを確認します。

Output
... --> Scenario: 'default' --> Action: 'idempotence' Idempotence completed successfully.

次のテストアクションは、副作用アクションです。 これにより、HAフェイルオーバーなど、より多くのことをテストできる状況を作り出すことができます。 デフォルトでは、Moleculeは副作用のプレイブックを構成せず、タスクはスキップされます。

Output
... --> Scenario: 'default' --> Action: 'side_effect' Skipping, side effect playbook not configured.

次に、Moleculeは、デフォルトのベリファイアであるTestinfraを使用してverifierアクションを実行します。 このアクションは、前に書いたテストを実行します test_default.py. すべてのテストに合格すると、成功メッセージが表示され、Moleculeは次のステップに進みます。

Output
... --> Scenario: 'default' --> Action: 'verify' --> Executing Testinfra tests found in /home/sammy/ansible-apache/molecule/default/tests/... ============================= test session starts ============================== platform linux -- Python 3.6.5, pytest-3.7.3, py-1.5.4, pluggy-0.7.1 rootdir: /home/sammy/ansible-apache/molecule/default, inifile: plugins: testinfra-1.14.1 collected 6 items tests/test_default.py ...... [100%] ========================== 6 passed in 41.05 seconds =========================== Verifier completed successfully.

最後に、Molecule destroy は、テスト中に完了したインスタンスを破棄し、それらのインスタンスに割り当てられたネットワークを削除します。

Output
... --> Scenario: 'default' --> Action: 'destroy' PLAY [Destroy] ***************************************************************** TASK [Destroy molecule instance(s)] ******************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) deletion to complete] ******************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Delete docker network(s)] ************************************************ skipping: [localhost] PLAY RECAP ********************************************************************* localhost : ok=2 changed=2 unreachable=0 failed=0

これでテストアクションが完了し、役割が意図したとおりに機能したことを確認します。

結論

この記事では、Apacheとfirewalldをインストールして構成するためのAnsibleロールを作成しました。 次に、Moleculeが役割が正常に実行されたことを表明するために使用したTestinfraを使用して単体テストを作成しました。

非常に複雑な役割にも同じ基本的な方法を使用でき、CIパイプラインを使用してテストを自動化することもできます。 Moleculeは高度に構成可能なツールであり、Dockerだけでなく、Ansibleがサポートする任意のプロバイダーで役割をテストするために使用できます。 独自のインフラストラクチャに対するテストを自動化して、役割が常に最新で機能していることを確認することもできます。 Ubuntu 18.04チュートリアルでMoleculeとTravisCIを使用してAnsibleロールの継続的テストを実装する方法を使用して、MoleculeとTravisCIを使用して継続的テストをワークフローに統合できます。

公式のMoleculeドキュメントは、Moleculeの使用方法を学ぶための最良のリソースです。