How to contribute - AWX (2)

めりーくりすます。Red Hatのさいとうです。この記事はAnsible 2 Advent Calendarの12月25日のエントリです。 前回の記事に引き続き、2回めの今回は、実際にAWXのコードを修正して規定のテストを実施し、アップストリームのAWXプロジェクトにPull Requestを作成するまでの流れを紹介します。

※前回同様、開発環境上での作業はプロンプトに [host] を、開発環境上に起動させた開発用のコンテナ上で作業する作業は [container] と明記しますので、実際に作業する環境に注意して読んでください。

AWXの開発用コンテナ群が起動していることを確認する

How to contribute - AWX(1)にしたがって、すでに開発用のコンテナ群が起動しており、管理ユーザ(awx)が作成されていることを確認します。

tools_awx_1, tools_rabbitmq_1, tools_postgres_1 そして tools_memcached_1 という4つのコンテナが起動しているはずです:

[host]$ docker ps --format "table {{.ID}}\t{{.Names}}\t{{ .Ports }}"
CONTAINER ID        NAMES               PORTS
210c423c473c        tools_awx_1         0.0.0.0:6899->6899/tcp, 0.0.0.0:7899-7999->7899-7999/tcp, 0.0.0.0:8013->8013/tcp, 0.0.0.0:8043->8043/tcp, 0.0.0.0:8080->8080/tcp, 22/tcp, 0.0.0.0:8888->8888/tcp
5226b93ab835        tools_rabbitmq_1    4369/tcp, 5671-5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp
82c29aab2d47        tools_postgres_1    0.0.0.0:5432->5432/tcp
7cb708df7506        tools_memcached_1   0.0.0.0:11211->11211/tcp

AWXのダッシュボードやジョブ実行の機能を提供しているのが tools_awx_1 です。このコンテナは、開発環境にチェックアウトしたawxリポジトリをマウントしています。

[host]$ docker inspect tools_awx_1 -f "{{ .Mounts }}"
[{bind  /home/ansible/work/src/awx /awx_devel  rw true rprivate} {bind  /home/ansible/work/src/awx/awx/projects /var/lib/awx/projects  rw true rprivate}]

つまり、コードの修正は開発環境上で実施すればOKです。ただし、フロントエンドのJavaScriptのコードを修正する場合は、修正を反映するために:

  1. tools_awx_1コンテナの停止(make docker-composeで起動しているはずなのでCTRL+Cで停止)
  2. make clean && make languages && make ui-devel の実施
  3. make docker-composeで再度起動

が必要になるので注意してください。

コードの修正

それでは、実際にコードを修正してから、規定のテストを実施してみましょう。サンプルとしてダッシュボードへのログイン時に出力されるログを修正してみます。

[修正前]
awx_1        | 2019-12-21 02:51:51,354 INFO     awx.api.generics User awx logged in from N.N.N.N

[修正後]
awx_1        | 2019-12-21 02:51:51,354 INFO     awx.api.generics Welcome to Ansible AWX! User awx logged in from N.N.N.N

以下のようにログ出力メッセージを修正してみました。

[host]$  git diff awx/api/generics.py
diff --git a/awx/api/generics.py b/awx/api/generics.py
index 37ca8bef4..eb8de518c 100644
--- a/awx/api/generics.py
+++ b/awx/api/generics.py
@@ -92,7 +92,7 @@ class LoggedLoginView(auth_views.LoginView):
         ret = super(LoggedLoginView, self).post(request, *args, **kwargs)
         current_user = getattr(request, 'user', None)
         if request.user.is_authenticated:
-            logger.info(smart_text(u"User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None))))
+            logger.info(smart_text(u"Welcome to Ansible AWX! User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None))))
             ret.set_cookie('userLoggedIn', 'true')
             current_user = UserSerializer(self.request.user)
             current_user = smart_text(JSONRenderer().render(current_user.data))

DjangoアプリケーションのPythonコードに対する修正ですから、コンテナをリスタートすることなく make docker-compose を実行しているコンソールのログには、ダッシュボードからのログイン時に修正されたログが出力されるはずです。

規定のテストを実施する

実際に動作確認して、動作確認できたらcommit&pushしてPRを作成し、あとはアップストリーム側のCIテストにおまかせしてしまう...という手もありますが、本家のお作法にしたがって、手元でテストを実施してからPull Requestを作成します。

まずはflake8(make flake8)でPythonコードの書式のチェックです:

[container]$ make flake8
./awx/api/generics.py:95:161: E501 line too long (162 > 160 characters)
make: *** [Makefile:342: flake8] Error 1

長い...しまった...修正した行が長すぎた...では怒られないような書式に修正します:

[host]$ git diff awx/api/generics.py
diff --git a/awx/api/generics.py b/awx/api/generics.py
index 37ca8bef4..642c761b3 100644
--- a/awx/api/generics.py
+++ b/awx/api/generics.py
@@ -92,7 +92,8 @@ class LoggedLoginView(auth_views.LoginView):
         ret = super(LoggedLoginView, self).post(request, *args, **kwargs)
         current_user = getattr(request, 'user', None)
         if request.user.is_authenticated:
-            logger.info(smart_text(u"User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None))))
+            logger.info( smart_text(
+                u"Welcome to Ansible AWX! User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None))))
             ret.set_cookie('userLoggedIn', 'true')
             current_user = UserSerializer(self.request.user)
             current_user = smart_text(JSONRenderer().render(current_user.data))

これでよかろう:

[container]$ make flake8
[container]$ 

問題が報告されることなく無事に通過しました。続いてJavaScriptコードは修正していませんが、念の為 JSHint(make jshint)も実施しておきます:

[container]$ make jshint
...
Running "jshint:source" (jshint) task

✔ No problems
...

コードの書式的には、これで問題ないようです。次に、テストフレームワーク(pytestとJasmine)を利用したテストを実施します。まずはpytest(make test)から:

[container]$ make test
====================================== test session starts =======================================
platform linux -- Python 3.6.8, pytest-3.6.0, py-1.8.0, pluggy-0.6.0
Django settings: awx.settings.development (from ini file)
rootdir: /awx_devel, inifile: pytest.ini
plugins: xdist-1.27.0, timeout-1.3.3, pythonpath-0.7.3, mock-1.11.1, forked-1.1.3, django-3.7.0, cov-2.8.1, celery-4.3.0
gw0 [2572] / gw1 [2572] / gw2 [2572] / gw3 [2572]
.....................................................................................................................................................s [  5%]
...
....................                                                                                                                                   [100%]
========================================================= 2562 passed, 10 skipped in 388.78 seconds ==========================================================
cd awxkit && /venv/awx/bin/tox -re py2,py3
GLOB sdist-make: /awx_devel/awxkit/setup.py
py2 create: /awx_devel/awxkit/.tox/py2
...
  py2: commands succeeded
  py3: commands succeeded
  congratulations :)
awx-manage check_migrations --dry-run --check  -n 'vNNN_missing_migration_file'
No changes detected

エラーで失敗するようであれば、コードに問題がある可能性が高い(テスト自体の問題である場合ありますが、経験上その可能性は非常に低いです)ので、修正したコードを見直してからmake testを再実行しましょう。

続いてUIのテストです。make ui-test-ciは、Chromeをヘッドレスモードで起動してUIテストを実施してくれます。

[container]$ make ui-test-ci
npm --prefix awx/ui run test:ci
...
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 4 of 41 (skipped 1) SUCCESS (0 secs / 0.254 secs)
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 6 of 41 (skipped 1) SUCCESS (0 secs / 0.45 secs)
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 8 of 41 (skipped 1) SUCCESS (0 secs / 0.656 secs)
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 11 of 41 (skipped 1) SUCCESS (0 secs / 0.885 secs)
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 13 of 41 (skipped 1) SUCCESS (0 secs / 1.008 secs)
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 14 of 41 (skipped 1) SUCCESS (0 secs / 1.185 secs)
HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 38 of 41 (skipped 3) SUCCESS (1.967 secs / 1.753 secs)

無事にパスすればテスト完了です。

commit & push そしてPull Request

git commit --signoff でサインして適切なコメントをつけて修正コードをcommit & pushしましょう。

[host]$ git add awx/api/generics.py
[host]$ git commit --signoff
[host]$ git push --set-upstream origin PR/adc20191204

最後に、GithubサイトでPull Requestを作成してください。

以上、AWXのアップストリームプロジェクトにコードをコントリビュートするためのオーソドックスな方法について紹介しました。AWXプロジェクトにコードをコントリビュートしたいと考えている「みなさん」の参考になりますように :)

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。