Test Kitchenを稼働中のサーバに使う

What

  • すでに動いているサーバをTestKitchenの管理下に置く方法
  • TestKitchenの力を借りて、AnsibleとServerspecを連携する方法

Why

TestKitchenを使うとAnsibleとServerspecがシームレスに連携します。それぞれのツールのインベントリファイルを用意する必要はありません。各ツールが連携してくれることにより、kitchen create → kitchen converge → kitchen verify と打つだけで、テスト環境の構築と設定、テストが完了します。この気軽さになれてしまうとAnsibleやServerspecの生コマンドを打つのが面倒になります。

気軽なものになれてしまうと、TestKitchenを利用してテストしたAnsibleとServerspecのコードを、TestKitchenで本番環境にデプロイしたくなります。この思い付きをTestKitchenが用意しているProxyドライバを使うことで実現できそうだったので試しました。

How

TestKitchenの設定

.kitchen.ymlは次のとおりです。テスト対象サーバのdriverをProxyで設定するのがポイントです。

---
platforms:
  - name: windows2012R2
    os_type: windows
    driver:
      name: proxy
      host: ec2-54-238-188-116.ap-northeast-1.compute.amazonaws.com
      reset_command: "ping localhost"
      username: administrator
      password: $2V=bWYEiPr
      port: 5986
    transport: 
      name: winrm
      username: administrator
      password: $2V=bWYEiPr
      port: 5986
      winrm_transport: ssl
    provisioner:
      name: ansible_push
      chef_bootstrap_url: nil
      ansible_port: 5986
      verbose: v
      ansible_connection: winrm
      playbook: test/integration/default/site.yml
    verifier:
      name: shell
      command: 'cd test/integration/default/ && rake spec'

suites:
  - name: default

ディレクトリ構成は次のとおりです。

.
├── .kitchen.yml
├── chefignore
└── test
    └── integration
        └── default
            ├── Rakefile
            ├── roles
            │   └── test
            │       └── tasks
            │           └── main.yml
            ├── site.yml
            └── spec
                ├── localhost
                │   └── hostname_spec.rb
                └── spec_helper.rb

既存サーバをTestKitchenの管理下に置く

まずは対象サーバをTestKitchenで管理下するためにkitchen createします。kitchen createが成功すると、kitchen listにplatformsで定義したサーバが出てきます。

$ kitchen create
-----> Starting Kitchen (v1.16.0)
-----> Creating <default-windows2012R2>...
       Resetting instance state with command: ping localhost
       
       WIN-G9B17NT9AD7 [127.0.0.1]に ping を送信しています 32 バイトのデータ:
       127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128
       127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128
       127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128
       127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128
       
       127.0.0.1 の ping 統計:
           パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
       ラウンド トリップの概算時間 (ミリ秒):
           最小 = 0ms、最大 = 0ms、平均 = 0ms
       Finished creating <default-windows2012R2> (0m3.82s).
-----> Kitchen is finished. (0m4.14s)
$ kitchen list
Instance               Driver  Provisioner  Verifier  Transport  Last Action  Last Error
default-windows2012R2  Proxy   AnsiblePush  Shell     Winrm      Created      <None>

.kitchenディレクトリの配下に、管理下に置かれているサーバの情報がymlで格納されます。

├── .kitchen
│   ├── default-windows2012R2.yml
│   └── logs
│       ├── default-windows2012R2.log
│       └── kitchen.log

既存サーバをAnsibleで構築する

次にansibleでサーバを構築するためにkitchen convergeします。今回利用するplaybookはwin_pingモジュールのみです。

 $ kitchen converge 
-----> Starting Kitchen (v1.16.0)
-----> Converging <default-windows2012R2>...
$$$$$$ Running legacy converge for 'Proxy' Driver
       Preparing files for transfer
       *************** AnsiblePush install_command ***************
       Ansible push config validated
       True
       Transferring files to <default-windows2012R2>
       True
       *************** AnsiblePush run ***************
No config file found; using defaults

PLAY [all] **********************************************************************************************************************************************

TASK [test : ping] **************************************************************************************************************************************
ok: [windows2012R2] => {"changed": false, "ping": "pong"}

PLAY RECAP **********************************************************************************************************************************************
windows2012R2              : ok=1    changed=0    unreachable=0    failed=0   

       *************** AnsiblePush end run *******************
       True
       Finished converging <default-windows2012R2> (0m4.70s).
-----> Kitchen is finished. (0m5.33s)

ansiblepushの内部では次のコマンドが実施されています。

ansible-playbook --inventory-file=`which kitchen-ansible-inventory` -v --limit=windows2012R2 test/integration/default/site.yml

Inventoryとして指定されているkitchen-ansible-inventoryがポイントです。kitchen-ansible-inventoryは、provisionerで指定されている値と.kitchenディレクトリ配下に保存されているサーバの情報を利用してansibleのインベントリを生成します。この機能のおかげで、ansibleのインベントリファイルを作らなくて済みます。

 $ cat .kitchen/default-windows2012R2.yml 
---
hostname: ec2-54-238-188-116.ap-northeast-1.compute.amazonaws.com
last_action: converge
last_error: 
 $  kitchen-ansible-inventory 
{
  "all": [
    "windows2012R2"
  ],
  "_meta": {
    "hostvars": {
      "windows2012R2": {
        "ansible_ssh_host": "ec2-54-238-188-116.ap-northeast-1.compute.amazonaws.com",
        "ansible_ssh_user": "administrator",
        "ansible_ssh_pass": "$2V=bWYEiPr",
        "ansible_ssh_port": 5986,
        "ansible_winrm_server_cert_validation": "ignore",
        "ansible_winrm_transport": "ssl",
        "ansible_connection": "winrm"
      }
    }
  }
}

既存サーバをServerspecでテストする

最後に、Serverspecでサーバを構築するためにkitchen verifyを実施します。今回のテスト項目はホスト名だけです。

 $  cat test/integration/default/spec/localhost/hostname_spec.rb 
require 'spec_helper'

describe command('hostname') do
  its(:stdout) { should match /WIN-G9B17NT9AD7/ }
end
 $ kitchen verify
-----> Starting Kitchen (v1.16.0)
-----> Setting up <default-windows2012R2>...
$$$$$$ Running legacy setup for 'Proxy' Driver
       Finished setting up <default-windows2012R2> (0m0.00s).
-----> Verifying <default-windows2012R2>...
       [Shell] Verify on instance=#<Kitchen::Instance:0x000000021beef8> with state={:username=>"administrator", :password=>"$2V=bWYEiPr", :port=>5986, :hostname=>"ec2-54-238-188-116.ap-northeast-1.compute.amazonaws.com", :last_action=>"setup", :last_error=>nil}
/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby -I/usr/local/rvm/gems/ruby-2.3.0@global/gems/rspec-support-3.5.0/lib:/usr/local/rvm/gems/ruby-2.3.0@global/gems/rspec-core-3.5.4/lib /usr/local/rvm/gems/ruby-2.3.0@global/gems/rspec-core-3.5.4/exe/rspec --pattern spec/localhost/\*_spec.rb

Command "hostname"
  stdout
    should match /WIN-G9B17NT9AD7/

Finished in 0.93135 seconds (files took 1.12 seconds to load)
1 example, 0 failures

       Finished verifying <default-windows2012R2> (0m2.33s).
-----> Kitchen is finished. (0m2.79s)

テストが成功しました。Serverspecは、TestKitchenが設定した環境変数をインベントリに利用しています。このおかげでServerSpec用のインベントリを用意しなくて済みます。

 $  cat test/integration/default/spec/spec_helper.rb 
require 'serverspec'
require 'winrm'

set :backend, :winrm

opts = {
  user: ENV['KITCHEN_USERNAME'],
  password: ENV['KITCHEN_PASSWORD'],
  endpoint: 'https://' + ENV['KITCHEN_HOSTNAME'] + ':5986/wsman',
  operation_timeout: 300,
  no_ssl_peer_verification: true
} 

winrm = WinRM::Connection.new(opts)
Specinfra.configuration.winrm = winrm

Summary

  • 気軽。コマンドを悩まないので、作業のリズム感がよい。
  • 間違ったTestKitchenの使い方な気がする。