CodeBuild でデータベースを利用したテストを実行する

eyecatch-codebuild

こんにちは、藤本です。

CodeBuild でテストする時に DBアクセスするのってどうするんだろう、と気になったので、方法を考えてみました。

概要

AWS re:Invent 2016 で CodeBuild がリリースされました。AWS が提供するビルドサービスがです。CodeBuild に関しては下記エントリをご参照ください。

【速報】フルマネージドのビルドサービスCodeBuild爆誕 #reinvent

私はビルドサービスをTravis CIしか利用したことがなかったのですが、Travis CI ではビルド環境に手軽にデータベースを利用することができます。設定ファイル(.travis.yml)にservices: mysqlを追加するだけで簡単に利用できます。CodeBuild の buildspec.yml にはデータベースを利用する設定がありません。と思っていたのですが、よくよく考えたら、CodeBuild はビルド環境の root 権限があるので自由に設定できます。ビルド環境である Ubuntu にコマンドベースで MySQL をセットアップすることで利用することができました。

最初は独自の Docker Image を作成して、MySQL を起動しようと思ったのですが、バックグラウンドで MySQL が起動させることができず、断念しました。。

試してみた

今回は Django アプリケーションを例に MySQL へ CRUD するテストコードを実行します。

CodeBuild の処理の流れは以下のようになります。

  1. ビルド環境の起動
  2. MySQL のインストール
  3. MySQL の起動
  4. データベースの作成
  5. アプリケーションのテストテーブル作成
  6. アプリケーションのテストコード実行

上記処理を実行するbuildspec.ymlのサンプル設定を以下に記載します。

buildspec.yml
version: 0.1

environment_variables:
  plaintext:
    DB_ROOT_PASSWORD: password
    DB_HOST: localhost
    DB_USER: mysql
    DB_PASSWORD: password
phases:
  install:
    commands:
      - echo "mysql-server mysql-server/root_password password ${DB_ROOT_PASSWORD}" |debconf-set-selections
      - echo "mysql-server mysql-server/root_password_again password ${DB_ROOT_PASSWORD}" |debconf-set-selections
      - apt-get update -y
      - apt-get install -y mysql-server-5.6
      - service mysql start
      - mysql -p${DB_ROOT_PASSWORD} -e "create database project;"
      - mysql -p${DB_ROOT_PASSWORD} -e "GRANT ALL ON *.* TO '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';"
      - pip install -r requirements.txt
      - python project/manage.py migrate
  build:
    commands:
      - python project/manage.py test app.tests

ポイントとしては以下となります。

  • MySQL Server はインストール時に root パスワードをインタラクティブに設定する必要があるため、debconf-set-selections でパスワードを設定する(CodeBuild 関係なく、Ubuntu の注意点)
  • ビルド環境用にデータベース情報を設定ファイルに定義するのはイケていないので、アプリケーションは環境変数から設定情報を取得
  • CodeBuild のbuildspec.yml、もしくはビルドプロジェクト設定から環境変数にデータベース設定情報を注入
  • データベースユーザー作成は同じく環境変数を使って作成

それでは、早速試してみましょう.

CodeBuild のビルドプロジェクト作成

CodeBuild のビルドプロジェクト作成は下記エントリをご参照ください。

今回は Django アプリケーションで試したので Runtime に Python、Version に 2.7 を指定しています。また Source Provider には CodeCommit を利用しています。

動作確認

それでは作成したテストコードを CodeCommit にプッシュして、CodeBuild のビルドでテストを実行しましょう。

ソースコードのプッシュ

$ git commit -am "add root password"
[master b995a2a] add root password
 2 files changed, 7 insertions(+), 12 deletions(-)

$ git push origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 563 bytes | 0 bytes/s, done.
Total 6 (delta 5), reused 0 (delta 0)
To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/django-fujimoto
   f729356..b995a2a  master -> master

ビルド実行

CodeBuild のビルドプロジェクトの画面を表示します。

Start build をクリックします。

AWS_CodeBuild_Management

ビルド設定画面へ遷移します。CodeCommit をソースプロバイダとした場合、まずビルドする Git リポジトリの Branch を選択します。Branch を選択すると、最新の Commit ID が取得されます。別リビジョンの Commit ID を選択することも可能です。Start build をクリックします。

AWS_CodeBuild_Management 2

ビルドが開始されます。

ログから動作確認

CodeBuild のビルド処理は CloudWatch Logs に記録されます。ログから動作を確認してみましょう。awscliでログを出力できます。ポイントに絞って解説します。

$ aws --region us-east-1 logs get-log-events --log-group-name /aws/codebuild/django-fujimoto --log-stream-name 85014b59-5081-4ace-b491-e7c07a82ced3 |jq -r '.events[].message'

buildspec.ymlに記載したパラメータが環境変数にセットされます。今回は、MySQL セットアップ用の root パスワード、アプリケーションのテスト用のDBホスト名、ユーザー名、パスワードを設定しています。

[Container] 2017/03/05 02:49:47 Processing plaintext environment variables

[Container] 2017/03/05 02:49:47 DB_ROOT_PASSWORD = password

[Container] 2017/03/05 02:49:47 DB_HOST = localhost

[Container] 2017/03/05 02:49:47 DB_USER = mysql

[Container] 2017/03/05 02:49:47 DB_PASSWORD = password

MySQL のインストールが開始され、パスワード入力を求められることなく、インストールされます。

[Container] 2017/03/05 02:49:56 Running command apt-get install -y mysql-server-5.6

<<snip>>

[Container] 2017/03/05 02:50:11 Setting up mysql-server-5.6 (5.6.33-0ubuntu0.14.04.1) ...

[Container] 2017/03/05 02:50:12 debconf: (TERM is not set, so the dialog front

[Container] 2017/03/05 02:50:12 debconf: (TERM is not set, so the dialog frontend is not usable.)

[Container] 2017/03/05 02:50:12 debconf: falling back to frontend: Readline

[Container] 2017/03/05 02:50:12 debconf: (This frontend requires a controlling t

[Container] 2017/03/05 02:50:12 debconf: (This frontend requires a controlling tty.)

[Container] 2017/03/05 02:50:12 debconf: falling back to frontend: Teletype

[Container] 2017/03/05 02:50:12 invoke-rc.d: policy-rc.d denied execution of stop.

[Container] 2017/03/05 02:50:15 2017-03-05 02:50:15 0 [Note] /usr/sbin/mysqld (mysqld 5.6.33-0ubuntu0.14.04.1) starting as process 338 ...

defaults_for_timestamp server option (see documentation for more details).

ad.

[Container] 2017/03/05 02:50:15 2017-03-05 02:50:15 0 [Note] /usr/sbin/mysqld (mysqld 5.6.33-0ubuntu0.14.04.1) starting as process 338 ...

defaults_for_timestamp server option (see documentation for more details).

[Container] 2017/03/05 02:50:15 2017-03-05 02:50:15 0 [Note] /usr/sbin/mysqld (mysqld 5.6.33-0ubuntu0.14.04.1) starting as process 338 ...

[Container] 2017/03/05 02:50:21 invoke-rc.d: policy-rc.d denied execution of start.

MySQL を起動します。

[Container] 2017/03/05 02:50:28 Running command service mysql start

[Container] 2017/03/05 02:50:28  * Starting MySQL database server mysqld

[Container] 2017/03/05 02:50:29    ...done.

テストの前準備として、データベース、および DB接続ユーザーを作成します。

[Container] 2017/03/05 02:50:30 Running command mysql -p${DB_ROOT_PASSWORD} -e "create database project;"

[Container] 2017/03/05 02:50:30 Warning: Using a password on the command line interface can be insecure.

[Container] 2017/03/05 02:50:30 Running command mysql -p${DB_ROOT_PASSWORD} -e "GRANT ALL ON *.* TO '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';"

[Container] 2017/03/05 02:50:30 Warning: Using a password on the command line interface can be insecure.

Django の DBマイグレーションにより、DBスキーマをセットアップします。

[Container] 2017/03/05 02:50:47 Operations to perform:

[Container] 2017/03/05 02:50:47 .0001_initial...ations: admin, app, auth, contenttypes, sessions

[Container] 2017/03/05 02:50:47 Running migrations:

[Container] 2017/03/05 02:50:47   Applying contenttypes.0001_initial... OK

[Container] 2017/03/05 02:50:48   Applying admin.0001_initial...OK

[Container] 2017/03/05 02:50:48   Applying admin.0002_logentry_remo

[Container] 2017/03/05 02:50:48   Applying app.0001_initial..._remove_auto_add... OK

[Container] 2017/03/05 02:50:48   Applying app.0002_user...... OK

[Container] 2017/03/05 02:50:48   Applying contenttypes.0002_r

[Container] 2017/03/05 02:50:48   Applying contenttypes.0002_remove_content_type_name... OK

[Container] 2017/03/05 02:50:48   Applying auth.0003_alter_user_email_max_length...th... OK

[Container] 2017/03/05 02:50:48   Applying auth.0004_alter_user_username_opts...... OK

[Container] 2017/03/05 02:50:48   Applying auth.0005_alter_user_last_login_null...K

[Container] 2017/03/05 02:50:48   Applying auth.0006_require_contenttypes_0002.... OK

[Container] 2017/03/05 02:50:48   Applying auth.0007_alter_validators_add_error_mess

[Container] 2017/03/05 02:50:48   Applying auth.0008_alter_user_username_max_length...es... OK

[Container] 2017/03/05 02:50:48   Applying sessions.0001_initial...rname_max_length... OK

[Container] 2017/03/05 02:50:48   Applying sessions.0001_initial... OK

テストを実行します。ちょっと結果が分かりづらいですが、OK ステータスが返ってきています。

[Container] 2017/03/05 02:50:53 --

[Container] 2017/03/05 02:50:53 Ran 2 tests in 2.054s-------------------------------------------------

[Container] 2017/03/05 02:50:53 OK

 2 tests in 2.054s

[Container] 2017/03/05 02:50:53

[Container] 2017/03/05 02:50:53 OK

まとめ

いかがでしょうか? CodeBuild のビルド環境は root 権限を利用できますのでお好みのビルド環境をセットアップできます。今回は MySQL をセットアップしましたが、その他 DB環境や、DBに限らず色々な環境を準備できそうですね。

AWS Cloud Roadshow 2017 福岡