🏸

ansibleのcollectionにPRを送る流れ

10 min read

はじめに

この記事は、ansible公式のcollectionにPull Requestを送る流れをまとめたものです。
基本的にはドキュメントが豊富でその流れに沿って進める事ができますが、躓いた部分も多くありましたので、記事にしてみました。

実際に送ったPull Requestは下記になります。

https://github.com/ansible-collections/junipernetworks.junos/pull/171

環境準備

下記にて動作確認をしています。

項目 バージョン
macOS 11.2.3(BigSur)
python(system) 3.9.4
ansible 2.10.9
docker 20.10.6
pyenv 1.2.26

リポジトリのfork/clone

Pull Requestを送る対象のリポジトリをfork/cloneしてきますが、注意点があります。
後述するテストを行う際に、ansible-testコマンドを使用しますが、下記ディレクトリ構成にしないと上手く動かないようです。

{任意のディレクトリ}/ansible_collections/{namespace}/{collection}/

namespaceとcollectionはリポジトリ名から確認できます(namespace.collectionという形になっています)。
ansible-collections/junipernetworks.junosをcloneする例は下記になります。

> mkdir -p ~/dev/ansible_collections/junipernetworks
> git clone https://github.com/minefuto/junipernetworks.junos ~/dev/ansible_collections/junipernetworks/junos

今回は~/dev配下にansible_collectionsディレクトリを作成していますが、どこを指定しても問題ありません。

ansibleのインストール

ansibleをvenv環境にインストールします。
venvのファイルが邪魔にならないように、とりあえず~/dev配下にvenv環境を作成します。

> cd ~/dev
> python3 -m venv venv
> source venv/bin/activate
venv> pip install ansible
venv> ansible --version       
ansible 2.10.9
  config file = None
  configured module search path = ['/Users/minefuto/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.9.4 (default, Apr  5 2021, 01:50:46) [Clang 12.0.0 (clang-1200.0.32.29)]

pythonパッケージのインストール

使用するcollectionに必要なpythonパッケージをインストールします。
また、toxもテストの際に必要になるためインストールしておきます。

venv> cd ~/dev/ansible_collections/junipernetworks/junos
venv> pip install tox
venv> pip install -r requirements.txt
venv> pip install -r test-requirements.txt

docker/pyenvのインストール

後述するansible-testコマンドを使用する際にdocker/pyenvを使用する場合があるため、必要に応じて下記を参考にそれぞれのツールをインストールします。

https://docs.docker.jp/docker-for-mac/install.html
https://github.com/pyenv/pyenv

collectionの動作確認

cloneしたcollectionのコードを使用してansibleを動かせるかを確認します。
ansibleはデフォルトではconfigured module search path配下のモジュールを読み込むため、~/dev/ansible_collections配下にcloneしたものは読み込んでくれません。

venv> ansible --version       
ansible 2.10.9
  config file = None
  configured module search path = ['/Users/minefuto/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
-snip-

configured module search pathは、環境変数ANSIBLE_LIBRARYで変更する事ができるため、~/dev/ansible_collections/{namespace}/{collection}を指定し、上手く反映されているか確認します。

venv> export ANSIBLE_LIBRARY=/Users/minefuto/dev/ansible_collections/junipernetworks/junos
venv> ansible --version                  
ansible 2.10.9
  config file = None
  configured module search path = ['/Users/minefuto/dev/ansible_collections/junipernetworks/junos']
-snip-

指定ができたら、ansible-docコマンドを使用して、実際に~/dev/ansible_collections配下のモジュールを呼び出してみます。
正常に呼び出せていたら、呼び出し元のpythonファイルがcloneしてきたファイルとなります。

venv> ansible-doc -t module junos_command
> JUNOS_COMMAND (/Users/minefuto/dev/ansible_collections/junipernetworks/junos/plugins/modules/junos_command.py)
-snip-

この状態になれば、その他のコマンド(ansible-playbook等)でもcloneしてきたcollection内のモジュールを呼び出せるようになっているはずです。

テストの動作確認

コードに変更を加える前にテストを正しく実行できるかを確認します。
ansible公式のcollectionでは、主に下記4つのテストを通す必要があります。
各collection毎に必要なテストは異なりますので、詳細はドキュメントを確認して下さい。

  • tox
  • Sanityテスト
  • Unitテスト
  • Integrationテスト

ansible-testコマンドについて

Sanity/Unit/Integrationテストを行うためのコマンドとなります。
いくつかの実行方法がありますが、今回は下記の2つ紹介します。

  • docker環境での実行
  • ローカル環境での実行

docker環境での実行

docker環境での実行方法は、自動的にコンテナを作成し、複数のpythonバージョンでテストを行ってくれます。
dockerさえインストールしていれば、その他環境を準備する必要がないため、特に理由がなければこちらを使う方が簡単かと思います。
下記コマンドにてdocker環境で各種テストを実行できます。
特定のpythonバージョンでのみテストを行いたい場合は、--pythonオプションにより指定可能です。

venv> ansible-test <testの種類> --docker -v
venv> ansible-test <testの種類> --docker -v --python 3.8

テストに使用するdockerイメージは下記が使用され、デフォルトでは2.9.0が使用されるようです。

https://quay.io/repository/ansible/default-test-container?tab=tags
collectionが2.9.0をサポートしていない場合等、使用するdockerイメージを変更したい場合は、下記のようにバージョン指定する事ができます。
venv> ansible-test <testの種類> --docker quay.io/ansible/default-test-container:2.10.0

ローカル環境での実行

ローカル環境での実行方法は、複数のpythonバージョンでテストを行う場合、pyenvでテストに必要な複数のpythonバージョンをインストールしておく必要があります。
また、各pythonバージョン毎に個別でvenv環境構築や依存パッケージのインストール等が必要となるため、docker環境で上手く動かない場合等に使用すれば良いかと思います。

下記コマンドにてローカル環境で各種テストを実行できます。
venv環境では--python-interpriterを使用して明示的にvenvのpythonを指定します。
Import Errorが発生した場合は、必要に応じて不足しているpythonパッケージをpipコマンドでインストールして下さい。

ansible-test <testの種類> --python 3.9 -v --python-interpreter $(which python)

他のpythonバージョンでansible-testコマンドを実行する場合は、下記のようにpyenvでバージョン変更し、venvの作成からやり直して下さい。

venv> deactivate
> cd ~/dev
> pyenv global 3.8.7
> python3 -m venv venv38
> source venv/bin/activate
> cd ~/dev/ansible_collections/junipernetworks/junos
venv38> pip install ansible
venv38> pip install -r requirements.txt
venv38> pip install -r test-requirements.txt
venv38> ansible-test <testの種類> --python 3.8 -v --python-interpreter $(which python)

tox

toxは複数のpythonバージョンでテストやコードフォーマットの確認を行う事ができるツールですが、ansible公式のcollectionではblack/flake8等によるコードフォーマットの確認のみに使用しているようです。
下記コマンドにてtoxによるチェックを実行し、エラーがない事を確認します。

venv> tox -elinters
-snip-
_________________________________________________________ summary __________________________________________________________
  linters: commands succeeded
  congratulations :)

Sanityテスト

Sanityテストは、docに記載されている型と実際のコードの型が一致しているかや、必要なモジュールがimportできるか等、様々な静的解析を行います。
テスト内容の詳細はドキュメントを確認して下さい。
下記コマンドにてdocker環境又はローカル環境でSanityテストを実行し、エラーがない事を確認します。

venv> ansible-test sanity --docker -v
venv> ansible-test sanity --python 3.9 -v --python-interpreter $(which python)

注意点として依存するcollectionがある場合、下記のようなエラーが発生して失敗します。

Running sanity test 'import' with Python 3.9
ERROR: Found 76 import issue(s) on python 3.9 which need to be resolved:
-snip-

依存するcollectionについては、galaxy.ymlのdependenciesに記載されています。

galaxy.yml
---
authors:
  - Ansible Network Community (ansible-network)
dependencies:
  "ansible.netcommon": ">=2.0.0"
-snip-

解決するには、依存するcollectionを下記形式で配置する必要があります。

{任意のディレクトリ}/ansible_collections/{namespace}/{collection}/

ansible-collections/junipernetworks.junosが依存している、ansible-collections/ansible.netcommonを配置する場合は、下記のように配置します。

> mkdir -p ~/dev/ansible_collections/ansible
> git clone https://github.com/ansible-collections/ansible.netcommon ~/dev/ansible_collections/ansible/netcommon

Unitテスト

Unitテストは、test/unit配下のテストコードを実行します。
下記コマンドにてdocker環境又はローカル環境でUnitテストを実行し、エラーがない事を確認します。

venv> ansible-test units --docker -v
venv> ansible-test units --python 3.9 -v --python-interpreter $(which python)

Integrationテスト

Integrationテストは、実際に操作する対象のネットワーク機器を用意し、test/integration/targets/{module名}配下に準備されたplaybookを実行し、正常に動作するかを確認するテストとなります。

Integrationテストは各collectionによって異なるため、詳細はドキュメント確認して下さい。
今回は、ネットワーク機器を操作するcollectionに関するテストの流れを記載しています。

ネットワーク機器の準備

テスト対象のネットワーク機器を準備する必要があります。
準備するネットワーク機器については、公式が実際にCIを回している機器と一致させると良いかと思います。
詳細はGithubのPull Requestページから連携しているCIツール(zuul)のログから確認する事ができます。
ansible-collections/junipernetworks.junosは、vsrx3 18.4R1を使用しているようです。

inventoryファイルの準備

テストに使用するinventoryファイルを準備する必要があります。
こちらも公式が実際にCIに使用しているinventoryファイルをCIツール(zuul)のログから確認してきて、必要な項目のみ自分の環境用に変更する形が良いかと思います。
ansible-collections/junipernetworks.junosは、下記のinventoryファイルを使用しているようです。

inventory
# This file is generated by Ansible
#  DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
#
[junos]
vsrx3-18.4R1 ansible_host=38.108.68.235 ansible_user=zuul

[yang:children]
junos

[junos:vars]
ansible_network_os=junipernetworks.junos.junos
ansible_python_interpreter=/home/zuul/venv/bin/python
netconf={'transport': 'netconf'}

[netconf]
vsrx3-18.4R1 ansible_host=38.108.68.235 ansible_user=zuul

[netconf:vars]
ansible_network_os=junipernetworks.junos.junos
ansible_python_interpreter=/home/zuul/venv/bin/python
netconf={'transport': 'netconf'}

自分の環境用に修正したinventoryファイルを適当な場所(今回は~/inventory)に保存します。

テストの実行

下記コマンドにてIntegrationテストを実行し、エラーがない事を確認します。
(docker環境ではなぜか上手く動かなかったため、ローカル環境にて実行しています)

venv> ansible-test network-integration --inventory ~/inventory

コード変更作業

テストが正常に動作する事を確認できたら、実際にPull Requestを送るための変更やテストコードを追加していきます。
変更が完了したら、再度テストを実行しエラーが発生しない事を確認します。

また、変更作業を行った後は、changelogを追加する必要があります。
changelogは、changelogs/fragments/配下にyamlファイルを下記フォーマットで新しく作成します。

---
minor_changes:
  - Change src element from str to path for junos_scp.

過去のPull Requestのchangelogを確認すれば、どのような形で記載するのかイメージしやすいかと思います。
changelogの詳細なルールについては、ドキュメントを確認して下さい。

Pull Request

ここまできたら、実際にPull Requestを送ります(下記は実際に送ってみたものです)。
送った後はCIが無事完了したかを確認したり、メンテナの方のレビューを待ちましょう。
問題がなければ無事マージされるはずです。

CIでエラーが発生した場合は、下記のようにPull RequestページのChecksタブからログを確認し、なぜ失敗したのかを確認してみましょう。
CIツール(zuul)側の内部エラーでテストが失敗する可能性もあるようなので、その場合はメンテナの方が再度CIを回してくれます。

あとがき

ここまでがansible-collections/junipernetworks.junosにPull Requestを送り、マージされるまでの流れとなります。
個人的には、ansible-testコマンドを正常に動作させるのが難しく、環境構築等に手間取りました。
試行錯誤しながらなんとかやってみた結果になるため、誤っている点やもっと良い方法があればご指摘いただければ幸いです。