amazon-ec2 0.0.2 → 0.0.3

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.txt CHANGED
@@ -1,9 +1,31 @@
1
1
  =CHANGELOG.txt : Amazon Elastic Compute Cloud (EC2) Ruby Gem
2
2
 
3
- ==2006.12.14
4
- * Fixed bug in .run_instances method. Patch submitted by Stephen Caudill on AWS forums. Thanks!
3
+ ===0.0.3 (12/16/2006)
4
+ * API CHANGE : Changed method name 'authorize' to 'authorize_security_group_ingress' to ensure consistent
5
+ naming of Ruby library methods to match AWS EC2 API actions. Alias to 'authorize' for backwards compatibility.
6
+ * API CHANGE : Changed method name 'revoke' to 'revoke_security_group_ingress' to ensure consistent
7
+ naming of Ruby library methods to match AWS EC2 API actions. Alias to 'revoke' for backwards compatibility.
8
+ * API CHANGE : Changed method name 'delete_securitygroup' to 'delete_security_group' to ensure consistent
9
+ naming of Ruby library methods to match AWS EC2 API actions. Alias to 'delete_securitygroup' for backwards compatibility.
10
+ * API CHANGE : Changed method name 'describe_securitygroups' to 'describe_security_group' to ensure consistent
11
+ naming of Ruby library methods to match AWS EC2 API actions. Alias to 'describe_securitygroups' for backwards compatibility.
12
+ * API CHANGE : Changed method name 'create_securitygroup' to 'create_security_group' to ensure consistent
13
+ naming of Ruby library methods to match AWS EC2 API actions. Alias to 'create_securitygroup' for backwards compatibility.
14
+ * Added many API rdoc's, some method descriptions copied from Amazon Query API Developer Guide.
15
+ * Extracted some parts of the formerly monolithic EC2 library out into separate files for manageability.
16
+ * Changed the HTTP 'User-Agent' string used for each request so that we have our own user agent
17
+ to identify this library's calls. Now set the version # in the user agent string based on the
18
+ master version number for this library which is stored in lib/EC2/version.rb and should only
19
+ be defined in one place.
20
+ * Set @http.verify_mode = OpenSSL::SSL::VERIFY_NONE to avoid seeing SSL Cert warning
21
+ "warning: peer certificate won't be verified in this SSL session". File EC2.rb:96
22
+ * Make 'pathlist' utility method a private method (EC2.rb:111). No reason I can see for this to be exposed.
5
23
 
24
+ ===0.0.2 (12/14/2006)
25
+ * Bugfix in run_instances method. Method works now. Patch submitted by Stephen Caudill on AWS forums. Thanks!
6
26
 
7
- ==2006.12.13
8
- * Original sample library released by Amazon Web Services, LLC
9
- * Library packaged as a gem and RubyForge project 'amazon-ec2' setup by Glenn Rempe
27
+ ===0.0.1 (12/13/2006)
28
+ * Initial release of the Ruby Gem. This includes the version of the library exactly as provided by
29
+ Amazon Web Services as example code. No changes or enhancements to that code were made other than
30
+ packaging it as a Ruby Gem.
31
+ * RubyForge project created. http://amazon-ec2.rubyforge.org
data/History.txt CHANGED
@@ -1,11 +1,3 @@
1
1
  =History.txt : Amazon Elastic Compute Cloud (EC2) Ruby Gem
2
2
 
3
3
  ==Version History
4
-
5
- ===0.0.2 (12/14/2006)
6
- * Bugfix in run_instances method. Method works now.
7
-
8
- ===0.0.1 (12/13/2006)
9
- * Initial release of the Ruby Gem. This includes the version of the library exactly as provided by
10
- Amazon Web Services as example code. No changes or enhancements to that code were made other than
11
- packaging it as a Ruby Gem.
data/Manifest.txt CHANGED
@@ -6,6 +6,13 @@ Rakefile
6
6
  setup.rb
7
7
  lib/EC2.rb
8
8
  lib/EC2/version.rb
9
+ lib/EC2/responses.rb
10
+ lib/EC2/images.rb
11
+ lib/EC2/instances.rb
12
+ lib/EC2/keypairs.rb
13
+ lib/EC2/image_attributes.rb
14
+ lib/EC2/security_groups.rb
15
+
9
16
  test/test_helper.rb
10
17
  test/EC2_test.rb
11
18
 
data/README.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  AWS EC2 is an interface library that can be used to interact
4
4
  with the Amazon EC2 system. The library exposes one main interface class,
5
- 'AWSAuthConnection'. This class performs all the operations for using using
5
+ 'AWSAuthConnection'. This class performs all the operations for using
6
6
  the EC2 service including header signing.
7
7
 
8
8
  ==Important note about this project:
data/lib/EC2.rb CHANGED
@@ -1,11 +1,20 @@
1
- # This software code is made available "AS IS" without warranties of any
2
- # kind. You may copy, display, modify and redistribute the software
3
- # code either by itself or as incorporated into your code; provided that
4
- # you do not remove any proprietary notices. Your use of this software
5
- # code is at your own risk and you waive any claim against Amazon Web
6
- # Services LLC or its affiliates with respect to your use of this software
7
- # code. (c) 2006 Amazon Web Services LLC or its affiliates. All rights
8
- # reserved.
1
+ # Amazon Web Services EC2 Query API Ruby Library
2
+ # This library has been packaged as a Ruby Gem
3
+ # by Glenn Rempe ( glenn @nospam@ elasticworkbench.com ).
4
+ #
5
+ # Source code and gem hosted on RubyForge
6
+ # under the Ruby License as of 12/14/2006:
7
+ # http://amazon-ec2.rubyforge.org
8
+
9
+ # Original Amazon Web Services Notice
10
+ # This software code is made available "AS IS" without warranties of any
11
+ # kind. You may copy, display, modify and redistribute the software
12
+ # code either by itself or as incorporated into your code; provided that
13
+ # you do not remove any proprietary notices. Your use of this software
14
+ # code is at your own risk and you waive any claim against Amazon Web
15
+ # Services LLC or its affiliates with respect to your use of this software
16
+ # code. (c) 2006 Amazon Web Services LLC or its affiliates. All rights
17
+ # reserved.
9
18
 
10
19
  require 'base64'
11
20
  require 'cgi'
@@ -15,24 +24,40 @@ require 'net/https'
15
24
  require 'rexml/document'
16
25
  require 'time'
17
26
 
18
- # Require any lib files that we have bundled with this Ruby Gem
19
- Dir[File.join(File.dirname(__FILE__), 'EC2/**/*.rb')].sort.each { |lib| require lib }
20
-
21
27
  include REXML
22
28
 
29
+ # Require any lib files that we have bundled with this Ruby Gem in the lib/EC2 directory.
30
+ # Parts of the EC2 module and AWSAuthConnection class are broken out into separate
31
+ # files for maintainability and are organized by the functional groupings defined
32
+ # in the EC2 API developers guide.
33
+ Dir[File.join(File.dirname(__FILE__), 'EC2/**/*.rb')].sort.each { |lib| require lib }
34
+
23
35
  module EC2
36
+
37
+ # Which host FQDN will we connect to for all API calls to AWS?
24
38
  DEFAULT_HOST = 'ec2.amazonaws.com'
39
+
40
+ # Define the ports to use for SSL(true) or Non-SSL(false) connections.
25
41
  PORTS_BY_SECURITY = { true => 443, false => 80 }
42
+
43
+ # This is the version of the API as defined by Amazon Web Services
26
44
  API_VERSION = '2006-10-01'
27
- RELEASE_VERSION = "7813"
28
45
 
29
- # Builds the canonical string for signing.
30
- # Note: The parameters in the path passed in must already be sorted in
31
- # case-insensitive alphabetical order and must not be url encoded.
46
+ # This release version is passed in with each request as part
47
+ # of the HTTP 'User-Agent' header. Set this be the same value
48
+ # as what is stored in the lib/EC2/version.rb module constant instead.
49
+ # This way we keep it nice and DRY and only have to define the
50
+ # version number in a single place.
51
+ RELEASE_VERSION = EC2::VERSION::STRING
52
+
53
+ # Builds the canonical string for signing. This strips out all '&', '?', and '='
54
+ # from the query string to be signed.
55
+ # Note: The parameters in the path passed in must already be sorted in
56
+ # case-insensitive alphabetical order and must not be url encoded.
32
57
  def EC2.canonical_string(path)
33
58
  buf = path.gsub(/\&|\?|=/,"")
34
59
  end
35
-
60
+
36
61
  # Encodes the given string with the aws_secret_access_key, by taking the
37
62
  # hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
38
63
  # url encode the result of that to protect the string if it's going to
@@ -42,514 +67,100 @@ module EC2
42
67
  b64_hmac =
43
68
  Base64.encode64(
44
69
  OpenSSL::HMAC.digest(digest, aws_secret_access_key, str)).strip
45
-
70
+
46
71
  if urlencode
47
72
  return CGI::escape(b64_hmac)
48
73
  else
49
74
  return b64_hmac
50
75
  end
51
76
  end
52
-
53
-
54
- # uses Net::HTTP to interface with EC2.
77
+
78
+
79
+ # The library exposes one main interface class, 'AWSAuthConnection'.
80
+ # This class performs all the operations for using the EC2 service
81
+ # including header signing. This class uses Net::HTTP to interface
82
+ # with EC2 Query API interface.
55
83
  class AWSAuthConnection
56
-
84
+
85
+ # Allow viewing, or turning on and off, the verbose mode of the connection class.
86
+ # If 'true' some 'puts' are done to view variable contents.
57
87
  attr_accessor :verbose
58
-
88
+
59
89
  def initialize(aws_access_key_id, aws_secret_access_key, is_secure=true,
60
90
  server=DEFAULT_HOST, port=PORTS_BY_SECURITY[is_secure])
91
+
61
92
  @aws_access_key_id = aws_access_key_id
62
93
  @aws_secret_access_key = aws_secret_access_key
63
94
  @http = Net::HTTP.new(server, port)
64
95
  @http.use_ssl = is_secure
65
- @verbose = false
66
- end
67
-
68
- def pathlist(key, arr)
69
- params = {}
70
- arr.each_with_index do |value, i|
71
- params["#{key}.#{i+1}"] = value
72
- end
73
- params
74
- end
75
-
76
- def register_image(imageLocation)
77
- params = { "ImageLocation" => imageLocation }
78
- RegisterImageResponse.new(make_request("RegisterImage", params))
79
- end
80
-
81
- def describe_images(imageIds=[], owners=[], executableBy=[])
82
- params = pathlist("ImageId", imageIds)
83
- params.merge!(pathlist("Owner", owners))
84
- params.merge!(pathlist("ExecutableBy", executableBy))
85
- DescribeImagesResponse.new(make_request("DescribeImages", params))
86
- end
87
-
88
- def deregister_image(imageId)
89
- params = { "ImageId" => imageId }
90
- DeregisterImageResponse.new(make_request("DeregisterImage", params))
91
- end
92
-
93
- def create_keypair(keyName)
94
- params = { "KeyName" => keyName }
95
- CreateKeyPairResponse.new(make_request("CreateKeyPair", params))
96
- end
97
-
98
- def describe_keypairs(keyNames=[])
99
- params = pathlist("KeyName", keyNames)
100
- DescribeKeyPairsResponse.new(make_request("DescribeKeyPairs", params))
101
- end
102
-
103
- def delete_keypair(keyName)
104
- params = { "KeyName" => keyName }
105
- DeleteKeyPairResponse.new(make_request("DeleteKeyPair", params))
106
- end
107
-
108
- def run_instances(imageId, kwargs={})
109
- in_params = { :minCount=>1, :maxCount=>1, :keyname=>nil, :groupIds=>[], :userData=>nil, :base64Encoded=>false }
110
- in_params.merge!(kwargs)
111
- userData = Base64.encode64(userData) if userData and not base64Encoded
96
+ # Don't verify the SSL certificates. Avoids SSL Cert warning on every GET.
97
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
112
98
 
113
- params = {
114
- "ImageId" => imageId,
115
- "MinCount" => in_params[:minCount].to_s,
116
- "MaxCount" => in_params[:maxCount].to_s,
117
- }.merge(pathlist("SecurityGroup", in_params[:groupIds]))
118
-
119
- params["KeyName"] = in_params[:keyname] unless in_params[:keyname].nil?
120
-
121
- RunInstancesResponse.new(make_request("RunInstances", params))
122
- end
123
-
124
- def describe_instances(instanceIds=[])
125
- params = pathlist("InstanceId", instanceIds)
126
- DescribeInstancesResponse.new(make_request("DescribeInstances", params))
127
- end
128
-
129
- def terminate_instances(instanceIds)
130
- params = pathlist("InstanceId", instanceIds)
131
- TerminateInstancesResponse.new(make_request("TerminateInstances", params))
132
- end
133
-
134
- def create_securitygroup(groupName, groupDescription)
135
- params = {
136
- "GroupName" => groupName,
137
- "GroupDescription" => groupDescription
138
- }
139
- CreateSecurityGroupResponse.new(make_request("CreateSecurityGroup", params))
140
- end
141
-
142
- def describe_securitygroups(groupNames=[])
143
- params = pathlist("GroupName", groupNames)
144
- DescribeSecurityGroupsResponse.new(make_request("DescribeSecurityGroups", params))
145
- end
146
-
147
- def delete_securitygroup(groupName)
148
- params = { "GroupName" => groupName }
149
- DeleteSecurityGroupResponse.new(make_request("DeleteSecurityGroup", params))
150
- end
151
-
152
- def authorize(*args)
153
- params = auth_revoke_impl(*args)
154
- AuthorizeSecurityGroupIngressResponse.new(make_request("AuthorizeSecurityGroupIngress", params))
155
- end
156
-
157
- def revoke(*args)
158
- params = auth_revoke_impl(*args)
159
- RevokeSecurityGroupIngressResponse.new(make_request("RevokeSecurityGroupIngress", params))
160
- end
161
-
162
- def modify_image_attribute(imageId, attribute, operationType, attributeValueHash)
163
- params = {
164
- "ImageId" => imageId,
165
- "Attribute" => attribute,
166
- "OperationType" => operationType
167
- }
168
- if attribute == "launchPermission"
169
- params.merge!(pathlist("UserGroup", attributeValueHash[:userGroups])) if attributeValueHash.has_key? :userGroups
170
- params.merge!(pathlist("UserId", attributeValueHash[:userIds])) if attributeValueHash.has_key? :userIds
171
- end
172
- ModifyImageAttributeResponse.new(make_request("ModifyImageAttribute", params))
173
- end
174
-
175
- def reset_image_attribute(imageId, attribute)
176
- params = { "ImageId" => imageId, "Attribute" => attribute }
177
- ResetImageAttributeResponse.new(make_request("ResetImageAttribute", params))
178
- end
179
-
180
- def describe_image_attribute(imageId, attribute)
181
- params = { "ImageId" => imageId, "Attribute" => attribute }
182
- DescribeImageAttributeResponse.new(make_request("DescribeImageAttribute", params))
183
- end
184
-
185
- private
186
-
187
- def auth_revoke_impl(groupName, kwargs={})
188
- in_params = { :ipProtocol=>nil, :fromPort=>nil, :toPort=>nil, :cidrIp=>nil, :sourceSecurityGroupName=>nil,
189
- :sourceSecurityGroupOwnerId=>nil}
190
- in_params.merge! kwargs
99
+ @verbose = false
191
100
 
192
- { "GroupName" => in_params[:groupName] ,
193
- "IpProtocol" => in_params[:ipProtocol],
194
- "FromPort" => in_params[:fromPort].to_s,
195
- "ToPort" => in_params[:toPort].to_s,
196
- "CidrIp" => in_params[:cidrIp],
197
- "SourceSecurityGroupName" => in_params[:sourceSecurityGroupName],
198
- "SourceSecurityGroupOwnerId" => in_params[:sourceSecurityGroupOwnerId],
199
- }.reject { |key, value| value.nil? or value.empty?}
200
-
201
101
  end
202
102
 
203
- private
204
-
205
-
206
- def make_request(action, params, data='')
207
-
208
- @http.start do
209
-
210
- params.merge!( {"Action"=>action, "SignatureVersion"=>"1", "AWSAccessKeyId"=>@aws_access_key_id,
211
- "Version"=>API_VERSION, "Timestamp"=>Time.now.getutc.iso8601,} )
212
- p params if @verbose
213
-
214
- sigpath = "?" + params.sort_by { |param| param[0].downcase }.collect { |param| param.join("=") }.join("&")
215
-
216
- sig = get_aws_auth_param(sigpath, @aws_secret_access_key)
217
-
218
- path = "?" + params.sort.collect do |param|
219
- CGI::escape(param[0]) + "=" + CGI::escape(param[1])
220
- end.join("&") + "&Signature=" + sig
221
-
222
- puts path if @verbose
223
-
224
- req = Net::HTTP::Get.new("/#{path}")
225
-
226
- # ruby will automatically add a random content-type on some verbs, so
227
- # here we add a dummy one to 'supress' it. change this logic if having
228
- # an empty content-type header becomes semantically meaningful for any
229
- # other verb.
230
- req['Content-Type'] ||= ''
231
- req['User-Agent'] = 'ec2-ruby-query 1.2-#{RELEASE_VERSION}'
232
-
233
- data = nil unless req.request_body_permitted?
234
- @http.request(req, data)
235
-
236
- end
237
- end
238
-
239
- # set the Authorization header using AWS signed header authentication
240
- def get_aws_auth_param(path, aws_secret_access_key)
241
- canonical_string = EC2.canonical_string(path)
242
- encoded_canonical = EC2.encode(aws_secret_access_key, canonical_string)
243
- end
244
- end
245
-
246
- class Response
247
- attr_reader :http_response
248
- attr_reader :http_xml
249
- attr_reader :structure
250
-
251
- ERROR_XPATH = "Response/Errors/Error"
252
103
 
253
- def initialize(http_response)
254
- @http_response = http_response
255
- @http_xml = http_response.body
256
- @is_error = false
257
- if http_response.is_a? Net::HTTPSuccess
258
- @structure = parse
259
- else
260
- @is_error = true
261
- @structure = parse_error
262
- end
263
- end
264
-
265
- def is_error?
266
- @is_error
267
- end
268
-
269
- def parse_error
270
- doc = Document.new(@http_xml)
271
- element = XPath.first(doc, ERROR_XPATH)
272
-
273
- errorCode = XPath.first(element, "Code").text
274
- errorMessage = XPath.first(element, "Message").text
275
-
276
- [["#{errorCode}: #{errorMessage}"]]
277
- end
278
-
279
- def parse
280
- # Placeholder -- this method should be overridden in child classes.
281
- nil
282
- end
283
-
284
- def to_s
285
- @structure.collect do |line|
286
- line.join("\t")
287
- end.join("\n")
288
- end
289
- end
290
-
291
- class DescribeImagesResponse < Response
292
- ELEMENT_XPATH = "DescribeImagesResponse/imagesSet/item"
293
- def parse
294
- doc = Document.new(@http_xml)
295
- lines = []
296
-
297
- doc.elements.each(ELEMENT_XPATH) do |element|
298
- imageId = XPath.first(element, "imageId").text
299
- imageLocation = XPath.first(element, "imageLocation").text
300
- imageOwnerId = XPath.first(element, "imageOwnerId").text
301
- imageState = XPath.first(element, "imageState").text
302
- isPublic = XPath.first(element, "isPublic").text
303
- lines << ["IMAGE", imageId, imageLocation, imageOwnerId, imageState, isPublic]
304
- end
305
- lines
306
- end
307
- end
308
-
309
- class RegisterImageResponse < Response
310
- ELEMENT_XPATH = "RegisterImageResponse/imageId"
311
- def parse
312
- doc = Document.new(@http_xml)
313
- lines = [["IMAGE", XPath.first(doc, ELEMENT_XPATH).text]]
314
- end
315
- end
316
-
317
- class DeregisterImageResponse < Response
318
- def parse
319
- # If we don't get an error, the deregistration succeeded.
320
- [["Image deregistered."]]
321
- end
322
- end
323
-
324
- class CreateKeyPairResponse < Response
325
- ELEMENT_XPATH = "CreateKeyPairResponse"
326
- def parse
327
- doc = Document.new(@http_xml)
328
- element = XPath.first(doc, ELEMENT_XPATH)
329
-
330
- keyName = XPath.first(element, "keyName").text
331
- keyFingerprint = XPath.first(element, "keyFingerprint").text
332
- keyMaterial = XPath.first(element, "keyMaterial").text
104
+ private
333
105
 
334
- line = [["KEYPAIR", keyName, keyFingerprint], [keyMaterial]]
335
- end
336
- end
337
-
338
- class DescribeKeyPairsResponse < Response
339
- ELEMENT_XPATH = "DescribeKeyPairsResponse/keySet/item"
340
- def parse
341
- doc = Document.new(@http_xml)
342
- lines = []
343
-
344
- doc.elements.each(ELEMENT_XPATH) do |element|
345
- keyName = XPath.first(element, "keyName").text
346
- keyFingerprint = XPath.first(element, "keyFingerprint").text
347
- lines << ["KEYPAIR", keyName, keyFingerprint]
348
- end
349
- lines
350
- end
351
- end
352
-
353
- class DeleteKeyPairResponse < Response
354
- def parse
355
- # If we don't get an error, the deletion succeeded.
356
- [["Keypair deleted."]]
357
- end
358
- end
359
-
360
- class RunInstancesResponse < Response
361
- ELEMENT_XPATH = "RunInstancesResponse"
362
- def parse
363
- doc = Document.new(@http_xml)
364
- lines = []
365
-
366
- rootelement = XPath.first(doc, ELEMENT_XPATH)
367
-
368
- reservationId = XPath.first(rootelement, "reservationId").text
369
- ownerId = XPath.first(rootelement, "ownerId").text
370
- groups = nil
371
- rootelement.elements.each("groupSet/item/groupId") do |element|
372
- if not groups
373
- groups = element.text
374
- else
375
- groups += "," + element.text
106
+ # pathlist is a utility method which takes a key string and and array as input.
107
+ # It converts the array into a Hash with the hash key being 'Key.n' where
108
+ # 'n' increments by 1 for each iteration. So if you pass in args
109
+ # ("ImageId", ["123", "456"]) you should get
110
+ # {"ImageId.1"=>"123", "ImageId.2"=>"456"} returned.
111
+ def pathlist(key, arr)
112
+ params = {}
113
+ arr.each_with_index do |value, i|
114
+ params["#{key}.#{i+1}"] = value
376
115
  end
116
+ params
377
117
  end
378
- lines << ["RESERVATION", reservationId, ownerId, groups]
379
-
380
- # rootelement = XPath.first(doc, ELEMENT_XPATH)
381
- rootelement.elements.each("instancesSet/item") do |element|
382
- instanceId = XPath.first(element, "instanceId").text
383
- imageId = XPath.first(element, "imageId").text
384
- instanceState = XPath.first(element, "instanceState/name").text
385
- # Only for debug mode, which we don't support yet:
386
- instanceStateCode = XPath.first(element, "instanceState/code").text
387
- dnsName = XPath.first(element, "dnsName").text
388
- # We don't return this, but still:
389
- reason = XPath.first(element, "reason").text
390
- lines << ["INSTANCE", instanceId, imageId, dnsName, instanceState]
391
- end
392
- lines
393
- end
394
- end
395
-
396
- class DescribeInstancesResponse < Response
397
- ELEMENT_XPATH = "DescribeInstancesResponse/reservationSet/item"
398
- def parse
399
- doc = Document.new(@http_xml)
400
- lines = []
401
-
402
- doc.elements.each(ELEMENT_XPATH) do |rootelement|
403
- reservationId = XPath.first(rootelement, "reservationId").text
404
- ownerId = XPath.first(rootelement, "ownerId").text
405
- groups = nil
406
- rootelement.elements.each("groupSet/item/groupId") do |element|
407
- if not groups
408
- groups = element.text
409
- else
410
- groups += "," + element.text
411
- end
412
- end
413
- lines << ["RESERVATION", reservationId, ownerId, groups]
414
-
415
- rootelement.elements.each("instancesSet/item") do |element|
416
- instanceId = XPath.first(element, "instanceId").text
417
- imageId = XPath.first(element, "imageId").text
418
- instanceState = XPath.first(element, "instanceState/name").text
419
- # Only for debug mode, which we don't support yet:
420
- instanceStateCode = XPath.first(element, "instanceState/code").text
421
- dnsName = XPath.first(element, "dnsName").text
422
- # We don't return this, but still:
423
- reason = XPath.first(element, "reason").text
424
- lines << ["INSTANCE", instanceId, imageId, dnsName, instanceState]
425
- end
426
- end
427
- lines
428
- end
429
- end
430
-
431
- class TerminateInstancesResponse < Response
432
- ELEMENT_XPATH = "TerminateInstancesResponse/instancesSet/item"
433
- def parse
434
- doc = Document.new(@http_xml)
435
- lines = []
436
118
 
437
- doc.elements.each(ELEMENT_XPATH) do |element|
438
- instanceId = XPath.first(element, "instanceId").text
439
- shutdownState = XPath.first(element, "shutdownState/name").text
440
- # Only for debug mode, which we don't support yet:
441
- shutdownStateCode = XPath.first(element, "shutdownState/code").text
442
- previousState = XPath.first(element, "previousState/name").text
443
- # Only for debug mode, which we don't support yet:
444
- previousStateCode = XPath.first(element, "previousState/code").text
445
- lines << ["INSTANCE", instanceId, previousState, shutdownState]
446
- end
447
- lines
448
- end
449
- end
450
-
451
- class CreateSecurityGroupResponse < Response
452
- def parse
453
- # If we don't get an error, the creation succeeded.
454
- [["Security Group created."]]
455
- end
456
- end
457
-
458
- class DescribeSecurityGroupsResponse < Response
459
- ELEMENT_XPATH = "DescribeSecurityGroupsResponse/securityGroupInfo/item"
460
- def parse
461
- doc = Document.new(@http_xml)
462
- lines = []
463
119
 
464
- doc.elements.each(ELEMENT_XPATH) do |rootelement|
465
- groupName = XPath.first(rootelement, "groupName").text
466
- ownerId = XPath.first(rootelement, "ownerId").text
467
- groupDescription = XPath.first(rootelement, "groupDescription").text
468
- lines << ["GROUP", ownerId, groupName, groupDescription]
469
- rootelement.elements.each("ipPermissions/item") do |element|
470
- ipProtocol = XPath.first(element, "ipProtocol").text
471
- fromPort = XPath.first(element, "fromPort").text
472
- toPort = XPath.first(element, "toPort").text
473
- permArr = [
474
- "PERMISSION",
475
- ownerId,
476
- groupName,
477
- "ALLOWS",
478
- ipProtocol,
479
- fromPort,
480
- toPort,
481
- "FROM"
482
- ]
483
- element.elements.each("groups/item") do |subelement|
484
- userId = XPath.first(subelement, "userId").text
485
- targetGroupName = XPath.first(subelement, "groupName").text
486
- lines << permArr + ["USER", userId, "GRPNAME", targetGroupName]
487
- end
488
- element.elements.each("ipRanges/item") do |subelement|
489
- cidrIp = XPath.first(subelement, "cidrIp").text
490
- lines << permArr + ["CIDR", cidrIp]
491
- end
120
+ # Make the connection to AWS EC2 passing in our request. This is generally called from
121
+ # within a 'Response' class object or one of its sub-classes so the response is interpreted
122
+ # in its proper context. See lib/EC2/responses.rb
123
+ def make_request(action, params, data='')
124
+
125
+ @http.start do
126
+
127
+ params.merge!( {"Action"=>action, "SignatureVersion"=>"1", "AWSAccessKeyId"=>@aws_access_key_id,
128
+ "Version"=>API_VERSION, "Timestamp"=>Time.now.getutc.iso8601,} )
129
+ p params if @verbose
130
+
131
+ sigpath = "?" + params.sort_by { |param| param[0].downcase }.collect { |param| param.join("=") }.join("&")
132
+
133
+ sig = get_aws_auth_param(sigpath, @aws_secret_access_key)
134
+
135
+ path = "?" + params.sort.collect do |param|
136
+ CGI::escape(param[0]) + "=" + CGI::escape(param[1])
137
+ end.join("&") + "&Signature=" + sig
138
+
139
+ puts path if @verbose
140
+
141
+ req = Net::HTTP::Get.new("/#{path}")
142
+
143
+ # Ruby will automatically add a random content-type on some verbs, so
144
+ # here we add a dummy one to 'supress' it. Change this logic if having
145
+ # an empty content-type header becomes semantically meaningful for any
146
+ # other verb.
147
+ req['Content-Type'] ||= ''
148
+ req['User-Agent'] = "rubyforge-amazon-ec2-ruby-gem-query-api v-#{RELEASE_VERSION}"
149
+
150
+ data = nil unless req.request_body_permitted?
151
+ @http.request(req, data)
152
+
492
153
  end
154
+
493
155
  end
494
- lines
495
- end
496
- end
497
-
498
- class DeleteSecurityGroupResponse < Response
499
- def parse
500
- # If we don't get an error, the deletion succeeded.
501
- [["Security Group deleted."]]
502
- end
503
- end
504
-
505
- class AuthorizeSecurityGroupIngressResponse < Response
506
- def parse
507
- # If we don't get an error, the authorization succeeded.
508
- [["Ingress authorized."]]
509
- end
510
- end
511
-
512
- class RevokeSecurityGroupIngressResponse < Response
513
- def parse
514
- # If we don't get an error, the revocation succeeded.
515
- [["Ingress revoked."]]
516
- end
517
- end
518
-
519
- class ModifyImageAttributeResponse < Response
520
- def parse
521
- # If we don't get an error, modification succeeded.
522
- [["Image attribute modified."]]
523
- end
524
- end
525
-
526
- class ResetImageAttributeResponse < Response
527
- def parse
528
- # If we don't get an error, reset succeeded.
529
- [["Image attribute reset."]]
530
- end
531
- end
532
-
533
- class DescribeImageAttributeResponse < Response
534
- ELEMENT_XPATH = "DescribeImageAttributeResponse"
535
- def parse
536
- doc = Document.new(@http_xml)
537
- lines = []
538
-
539
- rootelement = XPath.first(doc, ELEMENT_XPATH)
540
- imageId = XPath.first(rootelement, "imageId").text
541
156
 
542
- # Handle launchPermission attributes:
543
- rootelement.elements.each("launchPermission/item/*") do |element|
544
- lines << [
545
- "launchPermission",
546
- imageId,
547
- element.name,
548
- element.text
549
- ]
157
+
158
+ # Set the Authorization header using AWS signed header authentication
159
+ def get_aws_auth_param(path, aws_secret_access_key)
160
+ canonical_string = EC2.canonical_string(path)
161
+ encoded_canonical = EC2.encode(aws_secret_access_key, canonical_string)
550
162
  end
551
- lines
552
- end
163
+
553
164
  end
554
-
165
+
555
166
  end