inifileを使ってServerspecのテストを書いてみる part3
気づいたらしばらくぶりの更新になってしまいました。。。
今日は、inifileを使ってテストを記述した場合と、should matchを使ってテストを記載した場合を比較していみたいと思います。
個人的にinifileの方が優れていると思う点は、以下の通りです。
- 正規表現の考慮が減るのでテストを記述しやすい
- 揺らぎやすい表記を無視してテストできる(
=
前後の空白など) - どこがNGなのかわかりやすい
正規表現の考慮が減るのでテストを記述しやすい
php.ini
のパラメータの一つである、session.save_path
を例にとります。
テストコードで比較するとこんな感じです。
should match
の場合
its(:content) { should match %r{^session.save_path = "\/var\/lib\/php\/session"$} }
inifile
の場合
expect(conf['Session']['session.save_path']).to eq "/var/lib/php/session"
正規表現を使って記述する必要があるので、パスを表す/
をエスケープしたり、テストしたい記述の先頭に^
末尾に$
をつけて、記述の前後に余計な文字が含まれている場合に誤検知しないようにするなど、考慮するポイントが様々あります。
正規表現の方がいい場面もあるかもしれませんが、inifile
の場合は正規表現で表す必要は無くなります。
直感的にあるべき姿をイメージしやすいのはinifile
を使った書き方ではないかと思います。
揺らぎやすい表記を無視してテストできる
例えばphp.ini
の設定がこのようになっていたとします。
short_open_tag=Off
should matchだと=
の前後の空白も考慮して描かないとNGになってしまいますが(本来はOKであってほしい)、inifileならOKで検出してくれます。
Package "php" should be installed php.ini File "/etc/php.ini" should be file configures check php.ini Finished in 1.84 seconds (files took 3.82 seconds to load) 3 examples, 0 failures
こういったケースもあるので、inifile
をテストに活用するのはアリだと思っています。
(コーディング規約の統一という意味では、NG
で拾いたいという考え方もあるかもしれません。)
どこがNGなのかわかりやすい
例えば、php.ini
の設定で行末に余計な文字を入れてしまったとしましょう。
short_open_tag = Offf
should match
を使った場合
1) php.ini File "/etc/php.ini" content should match /^short_open_tag = Off$/ On host `WEB01' Failure/Error: its(:content) { should match %r{^short_open_tag = Off$} } expected "[PHP]\n\n;;;;;;;;;;;;;;;;;;;\n; About php.ini ;\n;;;;;;;;;;;;;;;;;;;\n; PHP's initialization file,... shared memory segment\n;sysvshm.init_mem = 10000\n\n\n; Local Variables:\n; tab-width: 4\n; End:\n" to match /^short_open_tag = Off$/ Diff: @@ -1,2 +1,1660 @@ -/^short_open_tag = Off$/ ... php.iniのファイルの内容がずらっと出る ...
間違っているテストケースはわかりますが、どこがどう間違っているのか一見わかりません。
inifile
の場合
1) files File "/etc/php.ini" configures check On host `WEB01' Failure/Error: expect(conf['PHP']['short_open_tag']).to eq 'Offf' expected: "Off" got: "Offf" (compared using ==) sudo -p 'Password: ' env PATH="/sbin:/usr/sbin:$PATH" /bin/sh -c cat\ /etc/php.ini\ 2\>\ /dev/null\ \|\|\ echo\ -n [PHP]
expected
とgot
の部分を見れば、期待される値(Off
)に対してどんな値が得られたか(Offf
)がすぐわかるので、NGの原因を掴みやすいと思います。
逆にinifileを使わないほうが良い場面
inifileは、セクションとキーで構成されるファイルを扱うものなので、そうでない設定ファイルには適用できないと思います。
例えばhttpd.conf
などが挙げられます。
この辺は未検証なので断言できませんが、多分できないでしょう。
テスト対象の設定ファイルに応じて、inifile
とshould match
を使い分けたいところです。
inifileを使ってServerspecのテストを書いてみる part2
今回は、inifile
のインストール&テストの記述をしていきます。
GitHub - TwP/inifile: Native Ruby package for reading and writing INI files
インストール
インストールはいたって簡単で、gemパッケージを追加するだけです。
なので、Gemfile
にinifile
を記述してインストールしましょう。
$ cat Gemfile # frozen_string_literal: true source "https://rubygems.org" gem "rake" gem "serverspec" gem "highline" gem "inifile" $ bundle install ... $ bundle exec gem list | grep inifile inifile (3.0.0)
テストの記述
ではテストを記述していきます。
今回は、php.ini
に記述されたパラメータshort_open_tag = Off
とmysql.connect_timeout = 60
を例に書いていきます。
早速、実際のコードを示します。
1 require 'spec_helper' 2 require 'inifile' 3 4 # パッケージがインストールされているか 5 describe package('php') do 6 it { should be_installed } 7 end 8 9 # php.iniの設定は正しいか 10 describe 'php.ini' do 11 describe file('/etc/php.ini') do 12 # ファイルは存在するか 13 it { should be_file } 14 15 # 各パラメータの設定は正しいか 16 it 'configures check' do 17 conf = IniFile.new.parse(subject.content) 18 expect(conf['PHP']['short_open_tag']).to eq 'Off' 19 expect(conf['MySQL']['mysql.connect_timeout']).to eq 60 20 end 21 end 22 end
ポイントを解説します。
point1:requireでinifile
を呼び出す
2行目のrequire 'inifile'
で、inifileモジュールを使えるようにします。
point2:expect
を使ってセクション・キーのペアで設定値をテスト
15〜20行目が、テスト部分に該当します。
17行目で、php.ini
を構文解析した結果を変数conf
に格納しています。
18行目と19行目で各パラメータの値をテストしています。
例えば、expect(conf['PHP']['short_open_tag']).to eq 'Off'
は、セクション[PHP]
のキーshort_open_tag
の値がOff
であることを確認します。
実際のphp.ini
ファイルと照らし合わせてみると良いでしょう。
以下、抜粋。
$ cat modules/php/templates/etc/php.ini.erb ... [PHP] ... short_open_tag = Off ... [MySQL] ... mysql.connect_timeout = 60 ...
今回示した部分以外のテストしたい場合も、同様に記述すればOKです。
テストしてみる
実際にテストするとこのような結果になります。
configures check
の部分がinifile
を使ってテストした部分に該当します。
ちなみに、configures check
という表示は、テストコードの16行目でit 'configures check' do
と記述した部分が該当します。
$ TARGET_HOST=WEB01 ASK_SUDO_PASSWORD=1 bundle exec rspec spec/WEB01/php_spec.rb Enter sudo password: Package "php" should be installed php.ini File "/etc/php.ini" should be file configures check Finished in 2 seconds (files took 2.99 seconds to load) 3 examples, 0 failures
こんな感じで、テストすることが可能です。
次回は、should match
を使ってテストを記述した場合との比較をしてみたいと思います。
inifileを使ってServerspecのテストを書いてみる part1
前回記事で、「phpの設定ファイルであるphp.ini
ファイルのテストは、inifileを使うことをお勧めします。」と述べました。
今日から数回に分けて、その辺を深掘りします。
GitHub - TwP/inifile: Native Ruby package for reading and writing INI files
inifileとは
Rubyパッケージの一種で、php.ini
などセクションとキーで構成された設定ファイル(INI file)をRubyで扱えるようにできるモジュールです。
ここで、セクション・キーとはこんなものを指します。
[section] ; これはセクション key_a=1 ; これはキー key_b=2 ; これはキー
私はServerspecのテストで使うことが多いですが、Serverspecに限らずRubyでINI fileを扱いたい場面であればどこでも活用できると思います。
テストで利用すると何が良いのか
前回記事で説明した通り、単純にshould match
を使っただけでは誤検知する恐れのある、以下のようなケースを回避できます。
- コメント行に、テストしたい文字列が入っている
- 行末・行頭の誤字があるが、それを除くとテストしたい文字列となる
厳密にはshould match
でも正規表現を使えば回避できないことはないです。
ですが、inifileならキーの名前とその値をrubyのObjectクラスとして利用できるので、純粋に設定だけを意識すれば良くなります(コメント行とか余計なことは考えなくても良い)。
そういった意味でも、inifileの方をお勧めします。
今回はここまで
次回以降、inifileのインストール・コードの記述・テストの実行をやっていこうと思います。
puppetでLAMP構成を構築する part4
前回puppetizeしたphpをServerspecでテストしてみたいと思います。
はじめに
前回・今回の記事では「puppetize」→「Serverspec」の順で作業してしまってますが、本当は、テスト駆動開発の考えかた的に「Serverspecでテストケースを記述」→「puppetize」の順に進めたほうが望ましいです。
少しだけ具体的に言うと、「先にあるべき姿を持った上でテストケースを記述(Serverspec)し、それを満たすように実装(puppetize)をする」という流れですね。
次回以降(mysqlなど)は、この順序で進めたいと思います。
テストの記述
前回のpuppetizeでは、以下をpuppetizeしています。
なので、各々をServerspecでテストしましょう。
テストコードはこんな感じです。
特にクセのある記述はないので、解説は省略します。
$ cat spec/WEB01/php_spec.rb require 'spec_helper' # 1. epelリポジトリは存在するか describe yumrepo('epel') do it { should exist } it { should be_enabled } end # 2. remiリポジトリは存在するか describe yumrepo('remi') do it { should exist } it { should be_enabled } end # 3. パッケージがインストールされているか describe package('php') do it { should be_installed } end # 4. php.iniは配置されているか describe 'php.ini' do describe file('/etc/php.ini') do it { should be_file } end end
あとはテストを実行してみましょう。
$ ASK_SUDO_PASSWORD=1 bundle exec rake spec:WEB01 .... Yumrepo "epel" should exist should be enabled Yumrepo "remi" should exist should be enabled Package "php" should be installed php.ini File "/etc/php.ini" should be file .... Finished in 6.09 seconds (files took 2.55 seconds to load) ○○ examples, 0 failures
php.iniの記述内容をテストしたい場合
php.iniの変更は行ってないので、今回はphp.ini
の記述内容のテストは省略しています。
もしテストしたい場合、inifileを使ってテストすることをお勧めします。
GitHub - TwP/inifile: Native Ruby package for reading and writing INI files
its(:content) { should match(/****/) }
でも記述はできますが、コメント行に同じ記述があるなどした場合、それを拾ってテストが通ってしまう場合などがあります。
例)its(:content) { should match(/short_open_tag = On/) }
の場合
以下のような記述もOKになってしまう。
... ; short_open_tag = On short_open_tag = Off ...
正規表現の^
や$
を活用すればshould match
でも記述できるといえばできますが、=
の間の空白有無も考慮して書くなど少々面倒です。
そういった意味でもinifileの方がオススメです。
詳しくは次回解説したいと思います。
puppetでLAMP構成を構築する part3
前回の続きで、phpのpuppetizeをしていきたいと思います。
phpのモジュール化
せっかくなのでphpもモジュール化しようと思います。
モジュール化の方針は、以前のapacheのpuppetizeとおおよそ同じ構造にしますので、そちらを参照ください。
PuppetでApacheを管理してみる part2 - ressyのナレッジ的なブログ
manifestの記述
早速manifestを記述しましょう。
前回の話を踏まえ、リポジトリの追加をしてから、phpをインストールします。
ちなみにインストールの流れは、以下を参考にしています。
CentOSにPHP5.6をインストール - Qiita
$ cat modules/php/manifests/init.pp class php( $enabled = 'false', ) { case $enabled { 'true': { # epelリポジトリの追加(remiを追加するために必要) package { 'epel-release': ensure => installed, source => 'http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm' , provider => rpm, } # remiリポジトリの追加 package { 'remi-release': ensure => installed, source => 'http://rpms.famillecollet.com/enterprise/remi-release-6.rpm', provider => rpm, require => Package['epel-release'], } # phpのインストール package { [ 'php', 'php-devel', 'php-mysql', ]: ensure => present, install_options => [ '--enablerepo=remi', '--enablerepo=remi-php56' ], require => Package['remi-release'], } # phpの設定ファイル file { '/etc/php.ini': ensure => present, content => file('/etc/puppet/modules/php/files/etc/php.ini'), require => Package['php'], } } 'false': { package { 'php': ensure => absent, } } } }
ポイントを解説します。
epel/remiリポジトリの追加
epel、remiリポジトリの追加は、package
リソースで記述すればOKです。
その際に、source
でインストール元URL、provider
でパッケージマネージャを指定します。
ちなみにphp5.6を入れる目的でremiを追加しますが、remiを入れるにはepelリポジトリが必要なので、同様にpuppetizeします。
phpのインストール
remiリポジトリからパッケージをインストールする場合、オプションでremiリポジトリを有効にする必要があります。
yumコマンドでインストールする例を挙げると以下のようになり、これをpuppetizeする必要があります。
$ sudo yum install --enablerepo=remi --enablerepo=remi-php56 php php-devel php-mysql
ポイントは--enablerepo=remi --enablerepo=remi-php56
をどうpuppetizeするかです。
具体的には、pacakge
リソースでinstall_options
を使って記載すればOKです。
phpの設定ファイル
ここは特記事項はないかなと思います。
あえて言えば、php.iniで変数を使ってpuppetizeするような箇所は設けてないので、files
で管理していることくらいでしょうか。
moduleの呼び出し
こちらの記事で作成した、site.pp
にphpモジュール分を追加します。
# cat /etc/puppet/manifests/site.pp node 'WEB01' { # WEB01ではApache/phpは運用する class { 'http': enabled => 'true', port => '80' } # phpを使用する class { 'php': enabled => 'true', } } node default { # 他のサーバではApache/phpは運用しない class { 'http': enabled => 'false', } # phpは使用しない class { 'php': enabled => 'true', } }
ざっとこんなところでしょうか。
phpのpuppetizeについて、最低限の実装はできたかと思います。
puppetでLAMP構成を構築する part2
今回から、LAMP構成の一要素であるphpのpuppetize(puppetで構成管理すること)を取り上げたいと思います。
今日の記事では、puppetize前の注意事項を取り上げます。
環境
phpインストールをpuppetizeする前に
シンプルにpuppetizeする場合、pacakge
リソースを使用すれば良いのは容易に想像がつくと思います。
っが、phpのバージョンによっては少しだけ工夫が必要になります。
例えば、CentOS6系にphp5.6をインストールしたいとします。
しかし、yum provides
を実行するとphp 5.3系しかリポジトリから取得できない状態となっています。
$ cat /etc/redhat-release CentOS release 6.8 (Final) $ $ yum provides php 読み込んだプラグイン:fastestmirror, security Loading mirror speeds from cached hostfile * base: ftp.iij.ad.jp * epel: ftp.riken.jp * extras: ftp.iij.ad.jp * remi-safe: repo1.sea.innoscale.net * updates: ftp.iij.ad.jp extras | 3.4 kB 00:00 puppetlabs-deps | 2.5 kB 00:00 puppetlabs-products | 2.5 kB 00:00 remi-safe | 2.9 kB 00:00 remi-safe/primary_db | 705 kB 00:00 updates | 3.4 kB 00:00 updates/primary_db | 3.7 MB 00:00 php-5.3.3-47.el6.x86_64 : PHP scripting language for creating dynamic web sites リポジトリー : base 一致 : php-5.3.3-48.el6_8.x86_64 : PHP scripting language for creating dynamic web sites リポジトリー : updates 一致 :
ですので、例えば以下のようにpuppetizeすると、php5.3系がインストールされてしまいます。
仮にensure => latest
としても一緒です。
package { 'php': ensure => present, }
CentOS6系にphp5.6を入れるには、事前にremiをyumリポジトリに追加しておく必要があります。
remiとはリポジトリの一種で、これを追加しておくことで、CentOS標準のリポジトリだけでは提供されていないパッケージもインストールできるようになります。
似たものとしてepelやrpmforgeなどがあります。
php5.6はremiで提供されているので、/etc/yum.repo.d/
にremiを追加しておくことでyumでインストールできるようになります。
puppetizeする際も、このことを考慮する必要があります。
次回の記事で、このことを踏まえてpuppetizeを始めたいと思います。