2014年3月17日月曜日

LoadBalancerもRSpecで定義: Lbspec

LoadBalancerのspecは、あるリクエストに対して、期待するレスポンスを返すか という表現では不十分である。実際にnodeにリクエストが到達するか ということを表現しなければいけない。そこで、LbspecというRSpecのMatcherを作った。これを使うと、LoadBalancerがnodeにリクエストを転送することをRSpecの書式で表現できる。

これでLoadBalancerもRSpecで定義できるはず。。

リンク

Installation

Gemfileに書く:

gem 'lbspec'

bundlerを実行する:

$ bundle

もしくはgem installする:

$ gem install lbspec

Requires

  • nodeにsshで入れる(~/.ssh/configなどで定義されている)

Usage

こんな感じで spec_helper.rbで require lbspec する:

# spec/spec_helper.rb
require 'rspec'
require 'lbspec'

あとはSpecを記述する:

require_relative 'spec_helper'

describe 'vhost_a' do
  it { should transfer('node_a') }
end

describe 'vhost_b' do
  it { should transfer(['node_b','node_c']) }
end

describe 'vhost_c:80' do
  it { should transfer(['node_b','node_c']).port(80) }
end

describe 'vhost_c:80' do
  it { should transfer(['node_b','node_c']).port(53).udp }
end

describe 'vhost_c:80' do
  it { should transfer('node_c').http.path('/test/') }
end

describe 'vhost_c:443' do
  it { should transfer(['node_b','node_c']).port(80).https.path('/test/') }
end

How it works

#transfer

  1. nodeにsshでログインする
  2. nodeでprobeをキャプチャーする
  3. probeを使ってリクエストを投げる(or パケットを投げる)
  4. nodeにprobeが来ているかで判定する

ここで、nodeではコマンドを実行しているだけなので、nodeに特別なプログラムを入れる必要はない。(ngrepとかtcpdumpが簡単かもしれないが、access.logをgrepするのほうが簡単かもしれない。)

#tranfer works

Configuration

#transfer

キャプチャとリクエストを投げる方法はカスタマイズできる。なので、キャプチャ方法を定義して、自分で投げたいリクエストを生成できる。

RSpec.configuration.lbspec_capture_command =
  lambda do |port, prove|
  port_str = port > 0 ? "port #{port}" : ''
  "sudo ngrep #{prove} #{port_str} | grep -v \"match:\""
end

RSpec.configuration.lbspec_https_request_command =
  lambda do |addr, port, path, prove|
  uri = 'https://' + "#{addr}:#{port}#{path}?#{prove}"
  system("curl -o /dev/null -sk #{uri}")
end

カスタマイズできるのは次のコマンド。

  • lbspec_capture_command with |port, prove|
  • lbspec_udp_request_command with |addr, port, prove|
  • lbspec_tcp_request_command with |addr, port, prove|
  • lbspec_http_request_command with |addr, port, path, prove|
  • lbspec_https_request_command with |addr, port, path, prove|