nifty-cloud-sdk 1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +112 -0
- data/Gemfile +4 -0
- data/INSTALL +30 -0
- data/LICENSE.txt +58 -0
- data/README.rdoc +375 -0
- data/Rakefile +61 -0
- data/lib/NIFTY.rb +405 -0
- data/lib/NIFTY/Cloud.rb +24 -0
- data/lib/NIFTY/Cloud/availability_zones.rb +23 -0
- data/lib/NIFTY/Cloud/certificates.rb +283 -0
- data/lib/NIFTY/Cloud/images.rb +134 -0
- data/lib/NIFTY/Cloud/instances.rb +372 -0
- data/lib/NIFTY/Cloud/keypairs.rb +76 -0
- data/lib/NIFTY/Cloud/load_balancers.rb +433 -0
- data/lib/NIFTY/Cloud/security_groups.rb +357 -0
- data/lib/NIFTY/Cloud/volumes.rb +140 -0
- data/lib/NIFTY/config.rb +41 -0
- data/lib/NIFTY/exceptions.rb +32 -0
- data/lib/NIFTY/responses.rb +19 -0
- data/lib/NIFTY/version.rb +3 -0
- data/nifty-cloud-sdk.gemspec +28 -0
- data/sample/availability_zones/describe-availability-zones.rb +37 -0
- data/sample/certificate/create-ssl-certificate.rb +41 -0
- data/sample/certificate/delete-ssl-certificate.rb +28 -0
- data/sample/certificate/describe-ssl-certificate-attribute.rb +58 -0
- data/sample/certificate/describe-ssl-certificates.rb +49 -0
- data/sample/certificate/download-ssl-certificate.rb +31 -0
- data/sample/certificate/modify-ssl-certificate-attribute.rb +29 -0
- data/sample/certificate/register-corporate-info-for-certificate.rb +57 -0
- data/sample/certificate/upload-ssl-certificate.rb +32 -0
- data/sample/images/create-image.rb +33 -0
- data/sample/images/delete-image.rb +26 -0
- data/sample/images/describe-images.rb +70 -0
- data/sample/images/modify-image-attribute.rb +33 -0
- data/sample/instances/cancel_copy_instances.rb +26 -0
- data/sample/instances/copy-instances.rb +41 -0
- data/sample/instances/describe-instance-attribute.rb +78 -0
- data/sample/instances/describe-instances.rb +100 -0
- data/sample/instances/modify-instance-attribute.rb +30 -0
- data/sample/instances/reboot-instances.rb +29 -0
- data/sample/instances/run-instances.rb +104 -0
- data/sample/instances/start-instances.rb +36 -0
- data/sample/instances/stop-instances.rb +35 -0
- data/sample/instances/terminate-instances.rb +34 -0
- data/sample/keypairs/create-key-pair.rb +31 -0
- data/sample/keypairs/delete-key-pair.rb +28 -0
- data/sample/keypairs/describe-key-pairs.rb +31 -0
- data/sample/load_balancers/configure-health-check.rb +41 -0
- data/sample/load_balancers/create-load-balancer.rb +35 -0
- data/sample/load_balancers/delete-load-balancer.rb +28 -0
- data/sample/load_balancers/deregister-instances-from-load-balancer.rb +35 -0
- data/sample/load_balancers/describe-instance-health.rb +36 -0
- data/sample/load_balancers/describe-load-balancers.rb +89 -0
- data/sample/load_balancers/register-instances-with-load-balancer.rb +33 -0
- data/sample/load_balancers/register-port-with-load-balancer.rb +37 -0
- data/sample/load_balancers/set-filter-for-load-balancer.rb +38 -0
- data/sample/load_balancers/update-load-balancer.rb +33 -0
- data/sample/security_groups/authorize-security-group-ingress.rb +36 -0
- data/sample/security_groups/create-security-group.rb +29 -0
- data/sample/security_groups/delete-security-group.rb +28 -0
- data/sample/security_groups/deregister-instances-from-security-group.rb +31 -0
- data/sample/security_groups/describe-security-activities.rb +33 -0
- data/sample/security_groups/describe-security-groups.rb +59 -0
- data/sample/security_groups/register-instances-with-security-group.rb +31 -0
- data/sample/security_groups/revoke-security-group-ingress.rb +36 -0
- data/sample/security_groups/update-security-group.rb +30 -0
- data/sample/volumes/attach-volumes.rb +34 -0
- data/sample/volumes/create-volume.rb +38 -0
- data/sample/volumes/delete-volume.rb +28 -0
- data/sample/volumes/describe-volumes.rb +44 -0
- data/sample/volumes/detach-volume.rb +35 -0
- data/test/test_Cloud.rb +186 -0
- data/test/test_Cloud_availability_zones.rb +50 -0
- data/test/test_Cloud_certificates.rb +627 -0
- data/test/test_Cloud_images.rb +284 -0
- data/test/test_Cloud_instances.rb +941 -0
- data/test/test_Cloud_keypairs.rb +177 -0
- data/test/test_Cloud_load_balancers.rb +1183 -0
- data/test/test_Cloud_responses.rb +43 -0
- data/test/test_Cloud_security_groups.rb +699 -0
- data/test/test_Cloud_volumes.rb +325 -0
- data/test/test_helper.rb +23 -0
- metadata +275 -0
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
Rake::TestTask.new(:test) do |test|
|
6
|
+
test.libs << 'lib' << 'test'
|
7
|
+
test.pattern = 'test/test_*.rb'
|
8
|
+
test.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'rcov/rcovtask'
|
13
|
+
Rcov::RcovTask.new do |test|
|
14
|
+
test.libs << 'test'
|
15
|
+
test.pattern = 'test/test_*.rb'
|
16
|
+
test.verbose = true
|
17
|
+
test.rcov_opts << "--exclude /gems/,/Library/,spec"
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
task :rcov do
|
21
|
+
abort "RCov is not available. In order to run rcov, you must: [sudo] gem install rcov"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => :test
|
26
|
+
|
27
|
+
require 'rake/rdoctask'
|
28
|
+
Rake::RDocTask.new do |rdoc|
|
29
|
+
if File.exist?('VERSION.yml')
|
30
|
+
config = YAML.load(File.read('VERSION.yml'))
|
31
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
32
|
+
else
|
33
|
+
version = ""
|
34
|
+
end
|
35
|
+
|
36
|
+
rdoc.rdoc_dir = 'rdoc'
|
37
|
+
rdoc.title = "NIFTY Cloud SDK documentation"
|
38
|
+
rdoc.main = "README.rdoc"
|
39
|
+
rdoc.rdoc_files.include('README*')
|
40
|
+
rdoc.rdoc_files.include('LICENSE*')
|
41
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
42
|
+
rdoc.options << '--line-numbers'
|
43
|
+
rdoc.options << '-c UTF-8'
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
require 'yard'
|
48
|
+
YARD::Rake::YardocTask.new do |t|
|
49
|
+
#t.files = ['lib/**/*.rb']
|
50
|
+
end
|
51
|
+
rescue LoadError
|
52
|
+
puts "YARD (or a dependency) not available. Install it with: [sudo] gem install yard"
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Generate a perftools.rb profile"
|
56
|
+
task :profile do
|
57
|
+
system("CPUPROFILE=perftools/ec2prof RUBYOPT=\"-r`gem which perftools | tail -1`\" ruby sample/describe-instances.rb")
|
58
|
+
system("pprof.rb --text --ignore=Gem perftools/ec2prof > perftools/ec2prof-results.txt")
|
59
|
+
system("pprof.rb --dot --ignore=Gem perftools/ec2prof > perftools/ec2prof-results.dot")
|
60
|
+
end
|
61
|
+
|
data/lib/NIFTY.rb
ADDED
@@ -0,0 +1,405 @@
|
|
1
|
+
#--
|
2
|
+
# ニフティクラウドSDK for Ruby
|
3
|
+
#
|
4
|
+
# Ruby Gem Name:: nifty-cloud-sdk
|
5
|
+
# Author:: NIFTY Corporation
|
6
|
+
# Copyright:: Copyright 2011 NIFTY Corporation All Rights Reserved.
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
8
|
+
# Home:: http://cloud.nifty.com/api/
|
9
|
+
#++
|
10
|
+
|
11
|
+
%w[ base64 cgi openssl digest/sha1 net/https net/http rexml/document time ostruct ipaddr logger ].each { |f| require f }
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'URI' unless defined? URI
|
15
|
+
rescue Exception => e
|
16
|
+
# nothing
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
require 'xmlsimple' unless defined? XmlSimple
|
21
|
+
rescue Exception => e
|
22
|
+
require 'xml-simple' unless defined? XmlSimple
|
23
|
+
end
|
24
|
+
|
25
|
+
# Hashの値をドット表記で取得可能とするための、Hashクラスに独自のメソッド定義を行う
|
26
|
+
#
|
27
|
+
# @example 次のような形でHashの値を取得することができる
|
28
|
+
# foo[:bar] => "baz"
|
29
|
+
# foo.bar => "baz"
|
30
|
+
class Hash
|
31
|
+
def method_missing(meth, *args, &block)
|
32
|
+
if args.size == 0
|
33
|
+
self[meth.to_s] || self[meth.to_sym]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def type
|
38
|
+
self['type']
|
39
|
+
end
|
40
|
+
|
41
|
+
def has?(key)
|
42
|
+
self[key] && !self[key].to_s.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def does_not_have?(key)
|
46
|
+
self[key].nil? || self[key].to_s.empty?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
module NIFTY
|
52
|
+
LOG = Logger.new(STDOUT)
|
53
|
+
#LOG.level = Logger::DEBUG
|
54
|
+
LOG.level = Logger::INFO
|
55
|
+
|
56
|
+
# 署名パラメータ文字列生成
|
57
|
+
# urlencodeが真の場合はURLエンコードを行い、偽の場合はエンコードしない
|
58
|
+
#
|
59
|
+
def NIFTY.encode(secret_key, str, key_type, urlencode=true)
|
60
|
+
digest = OpenSSL::Digest::Digest.new(key_type)
|
61
|
+
b64_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, secret_key, str)).strip
|
62
|
+
|
63
|
+
if urlencode
|
64
|
+
return CGI::escape(b64_hmac)
|
65
|
+
else
|
66
|
+
return b64_hmac
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# HTTPリクエストの生成、実行、レスポンス解析を行う共通クラス
|
71
|
+
class Base
|
72
|
+
DEFAULT_CONNECTION_TIMEOUT = 30
|
73
|
+
DEFAULT_SOCKET_TIMEOUT = 30
|
74
|
+
DEFAULT_PORT = 443
|
75
|
+
DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=UTF-8'
|
76
|
+
PROTOCOL_HTTPS = 'https'
|
77
|
+
SIGNATURE_METHOD_SHA1 = 'HmacSHA1'
|
78
|
+
SIGNATURE_METHOD_SHA256 = 'HmacSHA256'
|
79
|
+
PORT_RANGE = (0..65535)
|
80
|
+
SIGNATURE_VERSION = ["0", "1", "2"]
|
81
|
+
|
82
|
+
attr_reader :access_key, :secret_key, :use_ssl, :server, :path, :proxy_server, :port, :connection_timeout, :socket_timeout,
|
83
|
+
:user_agent, :max_retry, :signature_version, :signature_method
|
84
|
+
|
85
|
+
# オプションを指定してニフティクラウドAPIクライアントを生成します。
|
86
|
+
#
|
87
|
+
# @option options [String] :access_key 公開キー
|
88
|
+
# @option options [String] :secret_key 秘密キー
|
89
|
+
# @option options [Boolean] :use_ssl SSL暗号化(true or false)
|
90
|
+
# @option options [String] :server APIホスト
|
91
|
+
# @option options [String] :path APIパス
|
92
|
+
# @option options [String] :proxy_server プロキシサーバーのURL
|
93
|
+
# @option options [String] :port 接続先ポート番号
|
94
|
+
# @option options [Integer] :connection_timeout 接続タイムアウト(秒)
|
95
|
+
# @option options [Integer] :socket_timeout ソケットタイムアウト(秒)
|
96
|
+
# @option options [String] :user_agent ユーザーエージェント
|
97
|
+
# @option options [Integer] :max_retry 最大リトライ回数
|
98
|
+
# @option options [String] :signature_version 認証バージョン
|
99
|
+
# @option options [String] :signature_method APIの認証ロジック
|
100
|
+
#
|
101
|
+
# @return [Base] ニフティクラウドAPIクライアントオブジェクト
|
102
|
+
#
|
103
|
+
def initialize( options = {} )
|
104
|
+
@default_host = @default_path = @default_port = nil
|
105
|
+
|
106
|
+
if blank?(options[:server])
|
107
|
+
# options[:server]の指定がない場合はデフォルトのエンドポイントを使用する
|
108
|
+
raise ConfigurationError, "No ENDPOINT_URL provided." if blank?(@default_endpoint)
|
109
|
+
uri = URI.parse(@default_endpoint)
|
110
|
+
raise ConfigurationError, "Invalid ENDPOINT_URL provided." unless PROTOCOL_HTTPS == uri.scheme
|
111
|
+
@default_host = uri.host || (raise ConfigurationError, "Invalid ENDPOINT_URL provided.")
|
112
|
+
@default_path = uri.path
|
113
|
+
@default_port = uri.port
|
114
|
+
end
|
115
|
+
|
116
|
+
options = {
|
117
|
+
:access_key => @default_access_key,
|
118
|
+
:secret_key => @default_secret_key,
|
119
|
+
:use_ssl => true,
|
120
|
+
:server => @default_host,
|
121
|
+
:path => @default_path,
|
122
|
+
:proxy_server => @default_proxy_server,
|
123
|
+
:port => @default_port,
|
124
|
+
:connection_timeout => @default_connection_timeout,
|
125
|
+
:socket_timeout => @default_socket_timeout,
|
126
|
+
:user_agent => @default_user_agent,
|
127
|
+
:max_retry => @default_max_retry,
|
128
|
+
:signature_version => @default_signature_version,
|
129
|
+
:signature_method => @default_signature_method
|
130
|
+
}.merge(options)
|
131
|
+
|
132
|
+
options[:port] = DEFAULT_PORT if blank?(options[:port])
|
133
|
+
|
134
|
+
raise ArgumentError, "No :access_key provided." if blank?(options[:access_key])
|
135
|
+
raise ArgumentError, "No :secret_key provided." if blank?(options[:secret_key])
|
136
|
+
raise ArgumentError, "No :use_ssl provided." if blank?(options[:use_ssl])
|
137
|
+
raise ArgumentError, "Invalid :use_ssl provided. only 'true' allowed." unless true == options[:use_ssl]
|
138
|
+
raise ArgumentError, "No :server provided." if blank?(options[:server])
|
139
|
+
raise ArgumentError, "No :path provided." if blank?(options[:path])
|
140
|
+
raise ArgumentError, "Invalid :port provided." unless valid_port?(options[:port])
|
141
|
+
raise ArgumentError, "Invalid :signature_version provided." unless SIGNATURE_VERSION.include?(options[:signature_version].to_s)
|
142
|
+
raise ArgumentError, "Invalid :signature_method provided." unless [SIGNATURE_METHOD_SHA1, SIGNATURE_METHOD_SHA256].include?(options[:signature_method].to_s)
|
143
|
+
|
144
|
+
@access_key = options[:access_key].to_s
|
145
|
+
@secret_key = options[:secret_key].to_s
|
146
|
+
@use_ssl = options[:use_ssl]
|
147
|
+
@server = options[:server].to_s
|
148
|
+
@path = options[:path].to_s
|
149
|
+
@proxy_server = options[:proxy_server].to_s
|
150
|
+
@port = options[:port].to_i
|
151
|
+
@connection_timeout = options[:connection_timeout].to_s.to_i
|
152
|
+
@socket_timeout = options[:socket_timeout].to_s.to_i
|
153
|
+
@user_agent = options[:user_agent].to_s
|
154
|
+
@max_retry = options[:max_retry].to_s.to_i
|
155
|
+
@signature_version = options[:signature_version].to_s
|
156
|
+
@signature_method = options[:signature_method].to_s
|
157
|
+
|
158
|
+
make_http
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
def make_http
|
163
|
+
proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
|
164
|
+
@http = Net::HTTP::Proxy( proxy.host,
|
165
|
+
proxy.port,
|
166
|
+
proxy.user,
|
167
|
+
proxy.password ).new(@server, @port)
|
168
|
+
|
169
|
+
LOG.debug("[URL]https://#{@server}#{":" + @port.to_s unless @port == 443}#{@path}")
|
170
|
+
LOG.debug("[PROXY]#{@proxy_server}") unless blank?(@proxy_server)
|
171
|
+
|
172
|
+
@http.use_ssl = @use_ssl
|
173
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
174
|
+
@http.open_timeout = (@connection_timeout >= 0) ? @connection_timeout : DEFAULT_CONNECTION_TIMEOUT
|
175
|
+
@http.read_timeout = (@socket_timeout > 0) ? @socket_timeout : DEFAULT_SOCKET_TIMEOUT
|
176
|
+
end
|
177
|
+
|
178
|
+
# :user_dataオプションを指定された際、:base64_encodedオプションの値がtrueならば
|
179
|
+
# :user_dataに対しBase64エンコードを行う
|
180
|
+
private
|
181
|
+
def extract_user_data( options = {} )
|
182
|
+
return unless options[:user_data]
|
183
|
+
if options[:user_data]
|
184
|
+
if options[:base64_encoded]
|
185
|
+
return Base64.encode64(options[:user_data]).gsub(/\n/, "").strip
|
186
|
+
else
|
187
|
+
return options[:user_data]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# パラメータ名のリストを生成したい場合に使用する
|
193
|
+
#
|
194
|
+
# @example
|
195
|
+
# 入力: key = 'ImageId',
|
196
|
+
# arr = ['123', '456'])
|
197
|
+
# 出力: {"ImageId.1"=>"123", "ImageId.2"=>"456"}
|
198
|
+
#
|
199
|
+
private
|
200
|
+
def pathlist(key, arr)
|
201
|
+
raise ArgumentError, "expected a key that is a String" unless key.is_a?(String)
|
202
|
+
raise ArgumentError, "unexpected a key that is empty" if key.empty?
|
203
|
+
|
204
|
+
params = {}
|
205
|
+
[arr].flatten.delete_if{ |value| blank?(value) }.each_with_index{ |value, i| params["#{key}.#{i+1}"] = value.to_s }
|
206
|
+
|
207
|
+
return params
|
208
|
+
end
|
209
|
+
|
210
|
+
# パラメータグループのリストを生成したい場合に使用する
|
211
|
+
#
|
212
|
+
# @example
|
213
|
+
# 入力: key = 'People',
|
214
|
+
# arr_of_hashes = [{:name=>'jon', :age=>'22'}, {:name=>'chris'}],
|
215
|
+
# mappings = {:name => 'Name', :age => 'Age'}
|
216
|
+
# 出力: {"People.1.Name"=>"jon", "People.1.Age"=>'22', 'People.2.Name'=>'chris'}
|
217
|
+
#
|
218
|
+
private
|
219
|
+
def pathhashlist(key, arr_of_hashes, mappings, key_name_mappings={})
|
220
|
+
arr_of_hashes = [arr_of_hashes].flatten
|
221
|
+
raise ArgumentError, "expected a key that is a String" unless key.is_a?(String)
|
222
|
+
raise ArgumentError, "unexpected a key that is empty" if key.empty?
|
223
|
+
arr_of_hashes.each{|h| raise ArgumentError, "expected each element of arr_of_hashes to be a Hash" unless h.is_a?(Hash)}
|
224
|
+
raise ArgumentError, "expected a mappings that is an Hash" unless mappings.is_a?(Hash)
|
225
|
+
raise ArgumentError, "expected a key_name_mappings that is an Hash" unless blank?(key_name_mappings) || key_name_mappings.is_a?(Hash)
|
226
|
+
|
227
|
+
params = {}
|
228
|
+
i = 0
|
229
|
+
arr_of_hashes.each do |hash|
|
230
|
+
param_size = params.size
|
231
|
+
hash.each do |attribute, value|
|
232
|
+
next if blank?(attribute) || blank?(mappings[attribute]) || blank?(value)
|
233
|
+
if value.is_a? Array
|
234
|
+
j = 0
|
235
|
+
value.each do |item|
|
236
|
+
next if blank?(item)
|
237
|
+
param_name = "#{key}.#{i+1}.#{mappings[attribute]}.#{j+1}"
|
238
|
+
param_name << ".#{key_name_mappings[attribute]}" unless blank?(key_name_mappings[attribute])
|
239
|
+
params[param_name] = item.to_s
|
240
|
+
j += 1
|
241
|
+
end
|
242
|
+
else
|
243
|
+
params["#{key}.#{i+1}.#{mappings[attribute]}"] = value.to_s
|
244
|
+
end
|
245
|
+
end
|
246
|
+
i += 1 if params.size > param_size
|
247
|
+
end
|
248
|
+
|
249
|
+
return params
|
250
|
+
end
|
251
|
+
|
252
|
+
# オプションで指定されたキー名を送信用パラメータのキー名に変換し、パラメータを生成する
|
253
|
+
# prefixが指定されている場合はキー名の先頭に付加する
|
254
|
+
#
|
255
|
+
# @example
|
256
|
+
# 入力: options = {:opt1 => "val1", :opt2 => "val2", :opt3 => "val3"},
|
257
|
+
# arr = [:opt1, :opt2],
|
258
|
+
# prefix = "Param"
|
259
|
+
# 出力: {"Param.Opt1 => "val1", "Param.Opt2" => "val2"}
|
260
|
+
#
|
261
|
+
private
|
262
|
+
def opts_to_prms( options={}, arr=nil, prefix=nil)
|
263
|
+
params = {}
|
264
|
+
arr = options.keys if arr.nil?
|
265
|
+
arr.each do |o|
|
266
|
+
key = o.to_s.split('_').collect { |s| s.capitalize }.join
|
267
|
+
key.insert(0, "#{prefix}.") unless blank?(prefix)
|
268
|
+
params[key] = options[o].to_s unless blank?(options[o])
|
269
|
+
end
|
270
|
+
|
271
|
+
return params
|
272
|
+
end
|
273
|
+
|
274
|
+
# オブジェクトの文字列表現が空の場合は
|
275
|
+
# trueを返却する
|
276
|
+
private
|
277
|
+
def blank?( obj )
|
278
|
+
obj.to_s.empty?
|
279
|
+
end
|
280
|
+
|
281
|
+
# ポートチェック
|
282
|
+
# ポート番号が正しい場合はtrue、そうでなければfalseを返却する
|
283
|
+
private
|
284
|
+
def valid_port?( port )
|
285
|
+
return false unless /^\d+$/ =~ port.to_s
|
286
|
+
return PORT_RANGE.include?(port.to_s.to_i)
|
287
|
+
end
|
288
|
+
|
289
|
+
# HTTPリクエスト生成
|
290
|
+
private
|
291
|
+
def make_request(params)
|
292
|
+
params.reject! { |key, value| blank?(value) }
|
293
|
+
|
294
|
+
params.merge!( {'SignatureVersion' => @signature_version,
|
295
|
+
'SignatureMethod' => @signature_method,
|
296
|
+
'AccessKeyId' => @access_key,
|
297
|
+
'Version' => VERSION,
|
298
|
+
'Timestamp' => Time.now.getutc.iso8601.sub(/Z/, sprintf(".%03dZ",(Time.now.getutc.usec/1000)))} )
|
299
|
+
|
300
|
+
@encoded_params = params.sort.collect { |param|
|
301
|
+
"#{CGI::escape(param[0])}=#{CGI::escape(param[1])}"
|
302
|
+
}.join("&").gsub('+', '%20').gsub('%7E', '~')
|
303
|
+
|
304
|
+
sig = get_auth_param(params, @secret_key)
|
305
|
+
|
306
|
+
query = "#{@encoded_params}&Signature=#{sig}"
|
307
|
+
|
308
|
+
LOG.debug("[QUERY]#{query}")
|
309
|
+
|
310
|
+
req = Net::HTTP::Post.new(@path)
|
311
|
+
req.content_type = DEFAULT_CONTENT_TYPE
|
312
|
+
req['User-Agent'] = @user_agent unless blank?(@user_agent)
|
313
|
+
req.body = query
|
314
|
+
|
315
|
+
return req
|
316
|
+
end
|
317
|
+
|
318
|
+
# HTTPリクエスト実行
|
319
|
+
private
|
320
|
+
def exec_request(req)
|
321
|
+
retry_cnt = 0
|
322
|
+
sleep_time = 300 / 1000.0
|
323
|
+
|
324
|
+
begin
|
325
|
+
@http.start do
|
326
|
+
return @http.request(req)
|
327
|
+
end
|
328
|
+
rescue TimeoutError => e
|
329
|
+
# タイムアウトが発生した場合はリトライする
|
330
|
+
raise e if retry_cnt >= @max_retry
|
331
|
+
sleep(sleep_time)
|
332
|
+
LOG.warn("HTTP retry. #{e.message}")
|
333
|
+
retry_cnt += 1
|
334
|
+
retry
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# 署名パラメータ設定
|
339
|
+
private
|
340
|
+
def get_auth_param(params, secret_key)
|
341
|
+
if params["SignatureVersion"].to_i < 2
|
342
|
+
key_type = SIGNATURE_METHOD_SHA1
|
343
|
+
else
|
344
|
+
key_type = params["SignatureMethod"]
|
345
|
+
end
|
346
|
+
|
347
|
+
canonical_string = make_canonical(params)
|
348
|
+
LOG.debug("[SIGN]" + canonical_string.gsub(/\n/, "(\\n)"))
|
349
|
+
|
350
|
+
return NIFTY.encode(secret_key, canonical_string, key_type.downcase.gsub(/hmac/, ''))
|
351
|
+
end
|
352
|
+
|
353
|
+
# 署名パラメータ生成用文字列の生成
|
354
|
+
private
|
355
|
+
def make_canonical(params=[])
|
356
|
+
case params["SignatureVersion"]
|
357
|
+
when '0'
|
358
|
+
return "#{params['Action']}#{params['Timestamp']}"
|
359
|
+
when '1'
|
360
|
+
return params.sort.join
|
361
|
+
when '2'
|
362
|
+
return ['POST', @server, @path, @encoded_params].join("\n")
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# レスポンスXMLの解析を行う
|
367
|
+
# エラーレスポンスの場合は例外を発生させる
|
368
|
+
private
|
369
|
+
def response_generator( params={} )
|
370
|
+
|
371
|
+
LOG.debug('[ACTION]' + params['Action'])
|
372
|
+
http_response = exec_request(make_request(params))
|
373
|
+
|
374
|
+
return Response.parse(:xml => http_response.body) unless response_error?(http_response)
|
375
|
+
end
|
376
|
+
|
377
|
+
# レスポンスのエラーチェック
|
378
|
+
private
|
379
|
+
def response_error?(response)
|
380
|
+
|
381
|
+
#LOG.debug('[BODY]' + response.body)
|
382
|
+
|
383
|
+
# 正常レスポンスの場合はfalseを返却する
|
384
|
+
return false if response.is_a?(Net::HTTPSuccess)
|
385
|
+
|
386
|
+
# XML形式チェック
|
387
|
+
# 形式が不正な場合は例外を発生させる
|
388
|
+
begin
|
389
|
+
doc = REXML::Document.new(response.body)
|
390
|
+
|
391
|
+
# エラーXML形式:
|
392
|
+
# <?xml version="1.0"?><Response><Errors><Error><Code>Client.InvalidInstanceID.NotFound</Code><Message>The instance ID 'foo' does not exist.</Message>
|
393
|
+
# </Error></Errors><RequestID>291cef62-3e86-414b-900e-17246eccfae8</RequestID></Response>
|
394
|
+
error_code = doc.root.elements['Errors'].elements['Error'].elements['Code'].text
|
395
|
+
error_message = doc.root.elements['Errors'].elements['Error'].elements['Message'].text
|
396
|
+
rescue
|
397
|
+
raise ResponseFormatError, "Unexpected error format. response.body is: #{response.body}"
|
398
|
+
end
|
399
|
+
|
400
|
+
raise ResponseError.new(error_code, error_message)
|
401
|
+
end
|
402
|
+
end # end of Base class
|
403
|
+
end # end of NIFTY module
|
404
|
+
|
405
|
+
Dir[File.join(File.dirname(__FILE__), 'NIFTY/**/*.rb')].sort.each { |lib| require lib }
|