amazon-ec2 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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