Chef InSpecに入門してみる。
いつもはChefのaudit-modeを使ってインフラのテストをしていますが、Chef 15になってaudit-modeが廃止されました...
なのでChef InSpecに入門してこのやるせない気持ちをどうにかしようと思います。
概要
- Chefのバージョンが15以上になると
audit-mode
が利用できなくなる。 - いつもは
audit-mode
での動作検証をリリース時に実施しているため、InSpecで代用できないか試してみる。
InSpecとは?
インフラのテストなどを実施するための機能を提供しているOSSです。
Chef InSpec is a free and open-source framework for testing and auditing your applications and infrastructure. Chef InSpec works by comparing the actual state of your system with the desired state that you express in easy-to-read and easy-to-write Chef InSpec code. Chef InSpec detects violations and displays findings in the form of a report, but puts you in control of remediation.
簡単なテストを実装してみる
InSpecの実行環境整備
以下のリンクからダウンロードして、適宜インストールすればOKです。
ちなみにgem install
でも入ります。
$ sudo gem install inspec
インストールが正常に完了したら適当にコマンド叩いて、パスなどに問題がなく、正常に動くことを確認。
$ inspec -v 4.18.85
Skeltonの作成
以下のコマンドを実行するとinspec
という名前のSkeletonをカレントディレクトリに作成できます。
$ inspec init profile inspec ─────────────────────────── InSpec Code Generator ─────────────────────────── Creating new profile at /Users/kazono/Workspace/chef/chef-repo/inspec • Creating file README.md • Creating directory controls • Creating file controls/example.rb • Creating file inspec.yml
ディレクトリ構成は↓のような感じ。
$ tree inspec inspec ├── README.md ├── controls │ └── example.rb └── inspec.yml 1 directory, 3 files
テストの実行
サンプルとして生成されたテストを試しに実行してみます。テスト実行時はinspec exec <PATH/TO/TEST_FILE>
をすればOKです。
$ inspec exec inspec/controls/example.rb Profile: tests from inspec/controls/example.rb (tests from inspec.controls.example.rb) Version: (not specified) Target: local:// ✔ tmp-1.0: Create /tmp directory ✔ File /tmp is expected to be directory File /tmp ✔ is expected to be directory Profile Summary: 1 successful control, 0 control failures, 0 controls skipped Test Summary: 2 successful, 0 failures, 0 skipped
テスト用のスクリプトが複数あり、全てを同時に実行したい場合には、inspec exec .
を実行すればOKです。
$ cd inspec $ inspec exec . Profile: InSpec Profile (inspec) Version: 0.1.0 Target: local:// ✔ tmp-1.0: Create /tmp directory ✔ File /tmp is expected to be directory File /tmp ✔ is expected to be directory Profile Summary: 1 successful control, 0 control failures, 0 controls skipped Test Summary: 2 successful, 0 failures, 0 skipped
サーバを指定したテストの実施
指定したサーバに対してテストを実施したい場合は、-t
オプションを指定すればOKです。
今回テスト用に作成したスクリプトは以下(単純にKafkaのポートLISTENを確認するだけのテスト)。
control 'kafka' do impact 1.0 title 'Checks for Kafka Broker' describe port(9092) do it { should be_listening } end end
これをkafka001.ponteru.co.jp
に対してテストしてみるとこんな感じ。
$ inspec exec controls/kafka.rb -t ssh://$USER@kafka001.ponteru.co.jp Profile: tests from controls/kafka.rb (tests from controls.kafka.rb) Version: (not specified) Target: ssh://kazono@kafka001.ponteru.co.jp:22 ✔ kafka: Checks for Kafka Broker ✔ Port 9092 is expected to be listening Profile Summary: 1 successful control, 0 control failures, 0 controls skipped Test Summary: 1 successful, 0 failures, 0 skipped
テストコードの書き方
Skeletonを作成したときに作成されるexample.rb
自体が良い例なのでこれを読めば基本的には問題なく理解できるかと思われます。
# copyright: 2018, The Authors title "sample section" # you can also use plain tests describe file("/tmp") do it { should be_directory } end # you add controls here control "tmp-1.0" do # A unique ID for this control impact 0.7 # The criticality, if this control fails. title "Create /tmp directory" # A human-readable title desc "An optional description..." describe file("/tmp") do # The actual test it { should be_directory } end end
title
でテストブロックやテスト自体にタイトルをつけることができ、impact
でテストの実行に失敗したときの深刻度を定義できます。
軽く調べた感じ、impactについてはこんな感じで指定しておけば良さそうです。
- impact が 0 ~ 0.4 ... low
- impact が 04 ~ 0.7 ... mid
- impact が 0.7 ~ 1 ... high
テスト自体は↓のようなブロックで記述します。この際、使用可能なリソースについては、以下のドキュメントにまとめられているので一読しておくと良いかも。
describe file("/tmp") do it { should be_directory } end
ちなみにdescribe
のブロックを1ファイルに複数書くこともできるし、control
ブロックの中に複数のdescribe
ブロックを作成してテストを実施することもできます。
commandリソースを使ってsudoを実行したい
以下のようにcommand
リソース内でsudo
を使うと、sudo: no tty present and no askpass program specified
と怒られてしまいます。
そのため、command
リソース内でsudo
を実行したい場合は以下のようにして--sudo-password
オプションにsudoパスワードを渡しましょう。
$ read -s password # sudoパスワードを入力 $ inspec exec controls/kafka.rb -t ssh://$USER@kafka001.ponteru.co.jp --sudo --sudo-password=${password}
テスト失敗時にリトライ処理をしたい
ローカルホストであれば、以下のようにテストを記述することで失敗時にリトライさせることができます。
control 'kafka' do impact 1.0 title 'Checks for Kafka Broker' describe port(9092) do before do 60.times do unless port(9092).listening? puts "Port 9092 isn't ready, retrying.." sleep 10 end end end it { should be_listening } end end
ただ、これをリモートホストに対して流すと、延々とPort 9092 isn't ready, retrying..
を表示し続けてしまいました。
Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. # 本当はここらへんでKafka Brokerを再起動しているので、LISTENになっているはず... Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. Port 9092 isn't ready, retrying.. ...
調べてみると、どうやらリモートホストの状態は最初の一回しか確認せず、以降はその状態を保持してしまうため、ポートが正常にLISTENになってもループを抜けられない模様。
リモートホストのテストで、リトライ処理を正常に行いたい場合は--no-backend-cach
を使えば、リモートホストの状態をキャッシュしなくなるので意図した挙動になるようです。
$ inspec exec controls/kafka.rb -t ssh://$USER@kafka001.ponteru.co.jp --no-backend-cach