AWS SDK for Ruby v2のwaitersの解説


## 何の機能か？
AWSのAPIは、基本的にあるリクエストに対するレスポンスを即座に返してしまい、そのリクエストで意図した結果が必ず起こるとは限りません。
APIリクエストに成功すると、HTTPコード200 とともにインスタンスの情報が返ってきます。

ですが、HTTP 200を受け取ったからといって、このインスタンスがrunningになるとは限りません。
APIレスポンスが返ってきた後、その対象が望むべきStatusになっているかどうかを暫くの間監視しておく必要があります。
このような場合aws-sdk v2から導入されたwaitersを使うと便利です。


## まずv2をインストール 
```Gemfile
gem 'aws-sdk', '2.0.11.pre'
gem 'aws-sdk-core'
```

環境変数をセットしておくこと

```bash
$ export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXX
$ export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$ export AWS_REGION=ap-northeast-1

```

## 使い方の例

### 簡単な使い方

wait_untilメソッドで処理をブロックしています。

```ruby
require 'aws-sdk'

ec2 = Aws::EC2::Client.new
ec2.start_instances(instance_ids: ['i-12345678'])
ec2.wait_until(:instance_running,  instance_ids:['i-12345678'])
```


### waitの細かい制御方法

以下のケースは一時間ec2が起動するまで待つ例です。(15秒おきに240回待つ)

```ruby
ec2 = Aws::EC2::Client.new
ec2.wait_until(:instance_running, instance_ids:['i-12345678']) do |w|
  w.interval = 15
  w.max_attempts = 240
end
```

### RDSでDBがavailableになるまで待つ
```ruby
rds.create_db_instance(db_instance_identifier:'db01')
rds.wait_until(:db_instance_available, db_instance_identifier:'db01')
```
* 与えるパラメータもResourceによって違うので注意



### interval毎に処理を挟む
```ruby
ec2 = Aws::EC2::Client.new
ec2.wait_until(:instance_running, instance_ids:['i-12345678']) do |w|
  w.before_attempt do |attempt|
    puts "before interval..."
  end

  w.before_wait do |attempt, prev_response|
    puts "after interval..."
  end
end
```
* before_waitはintervalの後に処理を挟むことが出来る
* before_attemptはintervalの前に処理を挟むことが出来る


### rescueでエラーを拾う
指定時間待ってもステータスが変わらない場合、Aws::Waiters::Errors::WaiterFailedが発生する。

```ruby
ec2 = Aws::EC2::Client.new
 
begin
  ec2.wait_until(:instance_running,  instance_ids:['i-1234567']) do |w|
    w.interval = 15
    w.max_attempts = 240
  end
rescue Aws::Waiters::Errors::WaiterFailed => error
  puts "failed waiting for instance running: #{error.message}"
end
```



## Resource別statusの確認

主なResourceのstatusを調べてみる。

wait_untilの引数にすればそのステータスになるまで処理をブロックできます。

###	EC2
```ruby
Aws::EC2::Client.new.waiter_names
=> [:system_status_ok,
 :instance_status_ok,
 :image_available,
 :instance_running,
 :instance_stopped,
 :instance_terminated,
 :export_task_completed,
 :export_task_cancelled,
 :snapshot_completed,
 :subnet_available,
 :volume_available,
 :volume_in_use,
 :volume_deleted,
 :vpc_available,
 :vpn_connection_available,
 :vpn_connection_deleted,
 :bundle_task_complete,
 :conversion_task_completed,
 :conversion_task_cancelled,
 :customer_gateway_available,
 :conversion_task_deleted,
 :spot_instance_request_fulfilled]
```

###	S3
```ruby
Aws::S3::Client.new.waiter_names
=> [:bucket_exists, :bucket_not_exists, :object_exists, :object_not_exists]
```

###	RDS
```ruby
Aws::RDS::Client.new.waiter_names
=> [:db_instance_available, :db_instance_deleted]
```

### Redshift
```ruby
Aws::Redshift::Client.new.waiter_names
=> [:cluster_available, :cluster_deleted, :snapshot_available]
```

### DynamoDB
```ruby
Aws::DynamoDB::Client.new.waiter_names
=> [:table_exists, :table_not_exists]
```

###	ステータスが存在しないケース
```ruby
Aws::EMR::Client.new.waiter_names
=> []
```
* ElastiCache、Kinesis、SQSなどもステータスが存在しません。


## まとめ
APIリクエスト後Statusの値をintervalを設けてPollingし、何回以内に成功しなければ異常と判断されるので例外を投げると言った処理を自分で作っていたかと思うのですが、その部分をSDKで吸収してくれるようになりました。