right_aws 1.9.0 → 3.1.0

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.
Files changed (70) hide show
  1. data/History.txt +164 -13
  2. data/Manifest.txt +28 -1
  3. data/README.txt +12 -10
  4. data/Rakefile +56 -29
  5. data/lib/acf/right_acf_interface.rb +343 -172
  6. data/lib/acf/right_acf_invalidations.rb +144 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +229 -0
  9. data/lib/acw/right_acw_interface.rb +248 -0
  10. data/lib/as/right_as_interface.rb +698 -0
  11. data/lib/awsbase/right_awsbase.rb +755 -115
  12. data/lib/awsbase/support.rb +2 -78
  13. data/lib/awsbase/version.rb +9 -0
  14. data/lib/ec2/right_ec2.rb +274 -1294
  15. data/lib/ec2/right_ec2_ebs.rb +514 -0
  16. data/lib/ec2/right_ec2_images.rb +444 -0
  17. data/lib/ec2/right_ec2_instances.rb +797 -0
  18. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  19. data/lib/ec2/right_ec2_placement_groups.rb +108 -0
  20. data/lib/ec2/right_ec2_reserved_instances.rb +243 -0
  21. data/lib/ec2/right_ec2_security_groups.rb +496 -0
  22. data/lib/ec2/right_ec2_spot_instances.rb +422 -0
  23. data/lib/ec2/right_ec2_tags.rb +139 -0
  24. data/lib/ec2/right_ec2_vpc.rb +598 -0
  25. data/lib/ec2/right_ec2_vpc2.rb +382 -0
  26. data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
  27. data/lib/elb/right_elb_interface.rb +573 -0
  28. data/lib/emr/right_emr_interface.rb +728 -0
  29. data/lib/iam/right_iam_access_keys.rb +71 -0
  30. data/lib/iam/right_iam_groups.rb +195 -0
  31. data/lib/iam/right_iam_interface.rb +341 -0
  32. data/lib/iam/right_iam_mfa_devices.rb +67 -0
  33. data/lib/iam/right_iam_users.rb +251 -0
  34. data/lib/rds/right_rds_interface.rb +1657 -0
  35. data/lib/right_aws.rb +30 -13
  36. data/lib/route_53/right_route_53_interface.rb +641 -0
  37. data/lib/s3/right_s3.rb +108 -41
  38. data/lib/s3/right_s3_interface.rb +349 -118
  39. data/lib/sdb/active_sdb.rb +388 -54
  40. data/lib/sdb/right_sdb_interface.rb +323 -64
  41. data/lib/sns/right_sns_interface.rb +286 -0
  42. data/lib/sqs/right_sqs.rb +1 -2
  43. data/lib/sqs/right_sqs_gen2.rb +73 -17
  44. data/lib/sqs/right_sqs_gen2_interface.rb +146 -73
  45. data/lib/sqs/right_sqs_interface.rb +12 -22
  46. data/right_aws.gemspec +91 -0
  47. data/test/README.mdown +39 -0
  48. data/test/acf/test_right_acf.rb +11 -19
  49. data/test/awsbase/test_helper.rb +2 -0
  50. data/test/awsbase/test_right_awsbase.rb +11 -0
  51. data/test/ec2/test_right_ec2.rb +32 -1
  52. data/test/elb/test_helper.rb +2 -0
  53. data/test/elb/test_right_elb.rb +43 -0
  54. data/test/rds/test_helper.rb +2 -0
  55. data/test/rds/test_right_rds.rb +120 -0
  56. data/test/route_53/fixtures/a_record.xml +18 -0
  57. data/test/route_53/fixtures/alias_record.xml +18 -0
  58. data/test/route_53/test_helper.rb +2 -0
  59. data/test/route_53/test_right_route_53.rb +141 -0
  60. data/test/s3/test_right_s3.rb +176 -42
  61. data/test/s3/test_right_s3_stubbed.rb +6 -4
  62. data/test/sdb/test_active_sdb.rb +120 -19
  63. data/test/sdb/test_batch_put_attributes.rb +54 -0
  64. data/test/sdb/test_right_sdb.rb +71 -16
  65. data/test/sns/test_helper.rb +2 -0
  66. data/test/sns/test_right_sns.rb +153 -0
  67. data/test/sqs/test_right_sqs.rb +0 -6
  68. data/test/sqs/test_right_sqs_gen2.rb +104 -49
  69. data/test/ts_right_aws.rb +1 -0
  70. metadata +181 -22
data/lib/right_aws.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2007-2008 RightScale Inc
2
+ # Copyright (c) 2007-2011 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -35,10 +35,27 @@ require 'rubygems'
35
35
  require 'right_http_connection'
36
36
 
37
37
  $:.unshift(File.dirname(__FILE__))
38
- require 'awsbase/benchmark_fix'
38
+ require 'awsbase/version'
39
39
  require 'awsbase/support'
40
+ require 'awsbase/benchmark_fix'
40
41
  require 'awsbase/right_awsbase'
41
42
  require 'ec2/right_ec2'
43
+ require 'ec2/right_ec2_images'
44
+ require 'ec2/right_ec2_instances'
45
+ require 'ec2/right_ec2_security_groups'
46
+ require 'ec2/right_ec2_spot_instances'
47
+ require 'ec2/right_ec2_ebs'
48
+ require 'ec2/right_ec2_reserved_instances'
49
+ require 'ec2/right_ec2_vpc'
50
+ require 'ec2/right_ec2_vpc2'
51
+ require 'ec2/right_ec2_monitoring'
52
+ require 'ec2/right_ec2_placement_groups'
53
+ require 'ec2/right_ec2_windows_mobility'
54
+ require 'ec2/right_ec2_tags'
55
+ require 'elb/right_elb_interface'
56
+ require 'emr/right_emr_interface'
57
+ require 'acw/right_acw_interface'
58
+ require 'as/right_as_interface'
42
59
  require 's3/right_s3_interface'
43
60
  require 's3/right_s3'
44
61
  require 'sqs/right_sqs_interface'
@@ -47,17 +64,17 @@ require 'sqs/right_sqs_gen2_interface'
47
64
  require 'sqs/right_sqs_gen2'
48
65
  require 'sdb/right_sdb_interface'
49
66
  require 'acf/right_acf_interface'
50
-
51
-
52
- module RightAws #:nodoc:
53
- module VERSION #:nodoc:
54
- MAJOR = 1
55
- MINOR = 9
56
- TINY = 0
57
-
58
- STRING = [MAJOR, MINOR, TINY].join('.')
59
- end
60
- end
67
+ require 'acf/right_acf_streaming_interface'
68
+ require 'acf/right_acf_origin_access_identities'
69
+ require 'acf/right_acf_invalidations'
70
+ require 'rds/right_rds_interface'
71
+ require 'iam/right_iam_interface'
72
+ require 'iam/right_iam_groups'
73
+ require 'iam/right_iam_users'
74
+ require 'iam/right_iam_access_keys'
75
+ require 'iam/right_iam_mfa_devices'
76
+ require 'route_53/right_route_53_interface'
77
+ require 'sns/right_sns_interface'
61
78
 
62
79
  #-
63
80
 
@@ -0,0 +1,641 @@
1
+ # Copyright (c) 2007-2011 RightScale Inc
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #
22
+
23
+ module RightAws
24
+
25
+ # = RightAws::Route53Interface -- Amazon Route 53 web service interface.
26
+ #
27
+ # The RightAws::Route53Interface class provides a complete interface to Amazon Route 53: a web
28
+ # service that enables you to manage your DNS service.
29
+ #
30
+ # For explanations of the semantics of each call, please refer to Amazon's documentation at
31
+ # http://aws.amazon.com/documentation/route53/
32
+ #
33
+ # Examples:
34
+ #
35
+ # # Create Route53 handler
36
+ # r53 = RightAws::Route53Interface.new(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
37
+ #
38
+ # #------------------------
39
+ # # Create Hosted Zone
40
+ # #------------------------
41
+ #
42
+ # hosted_zone_config = {
43
+ # :name => 'my-awesome-site.com.',
44
+ # :config => {
45
+ # :comment => 'My test site!'
46
+ # }
47
+ # }
48
+ # r53.create_hosted_zone(hosted_zone_config) #=>
49
+ # {:name_servers=>
50
+ # ["ns-1115.awsdns-11.org",
51
+ # "ns-696.awsdns-23.net",
52
+ # "ns-1963.awsdns-53.co.uk",
53
+ # "ns-362.awsdns-45.com"],
54
+ # :aws_id=>"/hostedzone/Z1K6NCF0EB26FB",
55
+ # :caller_reference=>"1295424990-710392-gqMuw-KcY8F-LFlrB-SQhp9",
56
+ # :config=>{:comment=>"My test site!"},
57
+ # :change_info=>
58
+ # {:status=>"PENDING",
59
+ # :aws_id=>"/change/C23QGMT8XTCAJY",
60
+ # :submitted_at=>"2011-01-19T08:16:31.046Z"},
61
+ # :name=>"my-awesome-site.com."}
62
+ #
63
+ # # List Hosted Zones
64
+ # r53.list_hosted_zones #=> []
65
+ # [{:aws_id=>"/hostedzone/Z1K6NCF0EB26FB",
66
+ # :caller_reference=>"1295424990-710392-gqMuw-KcY8F-LFlrB-SQhp9",
67
+ # :config=>{:comment=>"My test site!"},
68
+ # :name=>"my-awesome-site.com."}]
69
+ #
70
+ # #--------------------------------
71
+ # # Manage DNS Records and Changes
72
+ # #--------------------------------
73
+ #
74
+ # # Create DNS Records
75
+ # resource_record_sets = [ { :name => 'www1.my-awesome-site.com.',
76
+ # :type => 'NS',
77
+ # :ttl => 600,
78
+ # :resource_records => 'www.mysite.com' },
79
+ # { :name => 'www2.my-awesome-site.com.',
80
+ # :type => 'A',
81
+ # :ttl => 600,
82
+ # :resource_records => ['10.0.0.1'] } ]
83
+ # r53.create_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'my first set of records') #=>
84
+ # { :status=>"PENDING",
85
+ # :aws_id=>"/change/C2C6IGNRTKA0AY",
86
+ # :submitted_at=>"2011-01-19T08:29:26.160Z" }
87
+ #
88
+ # # Delete DNS records
89
+ # r53.delete_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'I dont need them any more') #=>
90
+ # { :status=>"PENDING",
91
+ # :aws_id=>"/change/C1CYJ10EZBFLO7",
92
+ # :submitted_at=>"2011-01-19T08:26:41.220Z" }
93
+ #
94
+ # # Create or delete DNS records (:action key must be provided):
95
+ # resource_record_sets = [ { :action => :create,
96
+ # :name => 'www1.my-awesome-site.com.',
97
+ # :type => 'NS',
98
+ # :ttl => 600,
99
+ # :resource_records => 'www.mysite.com' },
100
+ # { :action => :delete,
101
+ # :name => 'www2.my-awesome-site.com.',
102
+ # :type => 'A',
103
+ # :ttl => 600,
104
+ # :resource_records => ['10.0.0.1'] } ]
105
+ # r53.change_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'do change records')
106
+ # { :status=>"PENDING",
107
+ # :aws_id=>"/change/C2PWXVECN794LK",
108
+ # :submitted_at=>"2011-01-19T08:31:33.301Z" }
109
+ #
110
+ # # List DNS Records
111
+ # r53.list_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB") #=>
112
+ # [{:type=>"NS",
113
+ # :ttl=>172800,
114
+ # :resource_records=>
115
+ # ["ns-1115.awsdns-11.org.",
116
+ # "ns-696.awsdns-23.net.",
117
+ # "ns-1963.awsdns-53.co.uk.",
118
+ # "ns-362.awsdns-45.com."],
119
+ # :name=>"my-awesome-site.com."},
120
+ # {:type=>"SOA",
121
+ # :ttl=>900,
122
+ # :resource_records=>
123
+ # ["ns-1115.awsdns-11.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"],
124
+ # :name=>"my-awesome-site.com."},
125
+ # {:type=>"NS",
126
+ # :ttl=>600,
127
+ # :resource_records=>["www.mysite.com"],
128
+ # :name=>"www1.my-awesome-site.com."}]
129
+ #
130
+ # # Get Change info
131
+ # r53.get_change("/change/C2C6IGNRTKA0AY")
132
+ # {:status=>"INSYNC",
133
+ # :aws_id=>"/change/C2C6IGNRTKA0AY",
134
+ # :submitted_at=>"2011-01-19T08:29:26.160Z"}
135
+ #
136
+ # #------------------------
137
+ # # Delete Hosted Zone
138
+ # #------------------------
139
+ #
140
+ # # Get a list of DNS records I have created (the first 2 records were added by Amazon and cannot be deleted)
141
+ # resource_record_sets = r53.list_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB")
142
+ # resource_record_sets.shift
143
+ # resource_record_sets.shift
144
+ #
145
+ # # Delete them all
146
+ # delete_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'kill all records I have created') #=>
147
+ # { :status=>"PENDING",
148
+ # :aws_id=>"/change/C6NCO8Z50MHXV",
149
+ # :submitted_at=>"2011-01-19T08:46:37.307Z" }
150
+ #
151
+ # # Delete Hosted Zone
152
+ # r53.delete_hosted_zone("/hostedzone/Z1K6NCF0EB26FB") #=>
153
+ # { :status=>"PENDING",
154
+ # :aws_id=>"/change/C3OJ31D4V5P2LU",
155
+ # :submitted_at=>"2011-01-19T08:46:37.530Z" }
156
+ #
157
+ class Route53Interface < RightAwsBase
158
+
159
+ include RightAwsBaseInterface
160
+
161
+ API_VERSION = "2011-05-05"
162
+ DEFAULT_HOST = "route53.amazonaws.com"
163
+ DEFAULT_PATH = '/'
164
+ DEFAULT_PROTOCOL = 'https'
165
+ DEFAULT_PORT = 443
166
+
167
+ @@bench = AwsBenchmarkingBlock.new
168
+ def self.bench_xml
169
+ @@bench.xml
170
+ end
171
+ def self.bench_service
172
+ @@bench.service
173
+ end
174
+
175
+ # Create a new handle to an Route53 account. All handles share the same per process or per thread
176
+ # HTTP connection to Amazon Route53. Each handle is for a specific account. The params have the
177
+ # following options:
178
+ # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol).
179
+ # * <tt>:server</tt>: Route53 service host, default: DEFAULT_HOST
180
+ # * <tt>:port</tt>: Route53 service port, default: DEFAULT_PORT
181
+ # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL
182
+ # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
183
+ # * <tt>:signature_version</tt>: The signature version : '0','1' or '2'(default)
184
+ #
185
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
186
+ init({ :name => 'ROUTE_53',
187
+ :default_host => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).host : DEFAULT_HOST,
188
+ :default_port => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).port : DEFAULT_PORT,
189
+ :default_service => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).path : DEFAULT_PATH,
190
+ :default_protocol => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).scheme : DEFAULT_PROTOCOL,
191
+ :default_api_version => ENV['ROUTE_53_API_VERSION'] || API_VERSION },
192
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'] ,
193
+ aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],
194
+ params)
195
+ end
196
+
197
+ #-----------------------------------------------------------------
198
+ # Requests
199
+ #-----------------------------------------------------------------
200
+
201
+ # Generates request hash for REST API.
202
+ def generate_request(method, path, params={}, body=nil, headers={}) # :nodoc:
203
+ # Params
204
+ params.delete_if{ |key, val| val.right_blank? }
205
+ unless params.right_blank?
206
+ path += "?" + params.to_a.collect{ |key,val| "#{AwsUtils::amz_escape(key)}=#{AwsUtils::amz_escape(val.to_s)}" }.join("&")
207
+ end
208
+ # Headers
209
+ headers = AwsUtils::fix_headers(headers)
210
+ headers['content-type'] ||= 'text/xml' if body
211
+ headers['date'] = Time.now.httpdate
212
+ # Auth
213
+ signature = AwsUtils::sign(@aws_secret_access_key, headers['date'])
214
+ headers['X-Amzn-Authorization'] = "AWS3-HTTPS AWSAccessKeyId=#{@aws_access_key_id},Algorithm=HmacSHA1,Signature=#{signature}"
215
+ # Request
216
+ path = "#{@params[:service]}#{@params[:api_version]}/#{path}"
217
+ request = "Net::HTTP::#{method.capitalize}".right_constantize.new(path)
218
+ request.body = body if body
219
+ # Set request headers
220
+ headers.each { |key, value| request[key.to_s] = value }
221
+ # prepare output hash
222
+ { :request => request,
223
+ :server => @params[:server],
224
+ :port => @params[:port],
225
+ :protocol => @params[:protocol] }
226
+ end
227
+
228
+ # Sends request to Amazon and parses the response.
229
+ # Raises AwsError if any banana happened.
230
+ def request_info(request, parser, &block) # :nodoc:
231
+ request_info_impl(:acf_connection, @@bench, request, parser, &block)
232
+ end
233
+
234
+ def incrementally_list_hosted_zones(path, parser, params={}, &block) # :nodoc:
235
+ opts = {}
236
+ opts['MaxItems'] = params[:max_items] if params[:max_items]
237
+ opts['Marker'] = params[:marker] if params[:marker]
238
+ last_response = nil
239
+ loop do
240
+ link = generate_request('GET', path, opts)
241
+ last_response = request_info(link, parser.new(:logger => @logger))
242
+ opts['Marker'] = last_response[:next_marker]
243
+ break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?
244
+ end
245
+ last_response
246
+ end
247
+
248
+ def incrementally_list_resource_records(path, parser, params={}, &block) # :nodoc:
249
+ opts = {}
250
+ opts[:maxitems] = params.delete(:max_items) if params[:max_items]
251
+ last_response = nil
252
+ loop do
253
+ link = generate_request('GET', path, opts)
254
+ last_response = request_info(link, parser.new(:logger => @logger))
255
+ opts[:maxitems] = last_response[:max_items]
256
+ opts[:name] = last_response[:next_record_name]
257
+ opts[:type] = last_response[:next_record_type]
258
+ break unless block && block.call(last_response) && last_response[:is_truncated]
259
+ end
260
+ last_response
261
+ end
262
+
263
+ def expand_hosted_zone_id(aws_id) # :nodoc:
264
+ aws_id[%r{^/hostedzone/}] ? aws_id : "/hostedzone/#{aws_id}"
265
+ end
266
+
267
+ def expand_change_id(aws_id) # :nodoc:
268
+ aws_id[%r{^/change/}] ? aws_id : "/change/#{aws_id}"
269
+ end
270
+
271
+ def hosted_zone_config_to_xml(config) # :nodoc:
272
+ config[:caller_reference] ||= AwsUtils::generate_call_reference
273
+ hosted_zone_config = ''
274
+ unless config[:config].right_blank?
275
+ hosted_zone_config = " <HostedZoneConfig>\n" +
276
+ " <Comment>#{AwsUtils::xml_escape config[:config][:comment]}</Comment>\n" +
277
+ " </HostedZoneConfig>\n"
278
+ end
279
+ # XML
280
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
281
+ "<CreateHostedZoneRequest xmlns=\"https://#{@params[:server]}/doc/#{API_VERSION}/\">\n" +
282
+ " <Name>#{config[:name]}</Name>\n" +
283
+ " <CallerReference>#{config[:caller_reference]}</CallerReference>\n" +
284
+ hosted_zone_config +
285
+ "</CreateHostedZoneRequest>\n"
286
+ end
287
+
288
+ def resource_record_sets_to_xml(resource_record_changes, comment) # :nodoc:
289
+ # Comment
290
+ xml_comment = comment.right_blank? ? '' : " <Comment>#{AwsUtils::xml_escape(comment)}</Comment>\n"
291
+ # Changes
292
+ xml_changes = ''
293
+ resource_record_changes.each do |change|
294
+ xml_resource_records = Array(change[:resource_records]).map{|record| " <ResourceRecord><Value>#{AwsUtils::xml_escape(record)}</Value></ResourceRecord>\n" }.join('')
295
+ xml_changes += " <Change>\n" +
296
+ " <Action>#{AwsUtils::xml_escape(change[:action].to_s.upcase)}</Action>\n" +
297
+ " <ResourceRecordSet>\n" +
298
+ " <Name>#{AwsUtils::xml_escape(change[:name])}</Name>\n" +
299
+ " <Type>#{AwsUtils::xml_escape(change[:type].to_s.upcase)}</Type>\n"
300
+ if change[:alias_target]
301
+ alias_target = change[:alias_target]
302
+ xml_changes +=
303
+ " <AliasTarget>\n" +
304
+ " <HostedZoneId>#{AwsUtils::xml_escape(alias_target[:hosted_zone_id].to_s)}</HostedZoneId>\n" +
305
+ " <DNSName>#{AwsUtils::xml_escape(alias_target[:dns_name].to_s)}</DNSName>\n" +
306
+ " </AliasTarget>\n"
307
+ else
308
+ xml_changes +=
309
+ " <TTL>#{AwsUtils::xml_escape(change[:ttl].to_s)}</TTL>\n" +
310
+ " <ResourceRecords>\n" +
311
+ xml_resource_records +
312
+ " </ResourceRecords>\n"
313
+ end
314
+ xml_changes +=
315
+ " </ResourceRecordSet>\n" +
316
+ " </Change>\n"
317
+ end
318
+ # XML
319
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
320
+ "<ChangeResourceRecordSetsRequest xmlns=\"https://#{@params[:server]}/doc/#{API_VERSION}/\">\n" +
321
+ " <ChangeBatch>\n" +
322
+ xml_comment +
323
+ " <Changes>\n" +
324
+ xml_changes +
325
+ " </Changes>\n" +
326
+ " </ChangeBatch>\n" +
327
+ "</ChangeResourceRecordSetsRequest>\n"
328
+ end
329
+
330
+
331
+ #-----------------------------------------------------------------
332
+ # Hosted Zones
333
+ #-----------------------------------------------------------------
334
+
335
+ # List your hosted zones.
336
+ #
337
+ # r53.list_hosted_zones #=>
338
+ # [{:config=>{:comment=>"KD1, description"},
339
+ # :aws_id=>"/hostedzone/Z2P714ENJN23PN",
340
+ # :caller_reference=>"1295424990-710392-gqMuw-KcY8F-LFlrB-SQhp9",
341
+ # :name=>"patch-island.com."},
342
+ # {:config=>{:comment=>"My awesome site!"},
343
+ # :aws_id=>"/hostedzone/ZWEC7PPVACGQ4",
344
+ # :caller_reference=>"1295422234-657482-hfkeo-JFKid-Ldfle-Sdrty",
345
+ # :name=>"mysite.patch-island.com."}, ...]
346
+ #
347
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ListHostedZones.html
348
+ #
349
+ def list_hosted_zones
350
+ result = []
351
+ incrementally_list_hosted_zones('hostedzone', ListHostedZonesParser) do |response|
352
+ result += response[:items]
353
+ true
354
+ end
355
+ result
356
+ end
357
+
358
+ # Create new hosted zone
359
+ #
360
+ # config = {
361
+ # :name => 'mysite.patch-island.com.',
362
+ # :config => {
363
+ # :comment => 'My awesome site!'
364
+ # }
365
+ # }
366
+ # r53.create_hosted_zone(config) #=>
367
+ # {:config=>{:comment=>"My awesome site!"},
368
+ # :change_info=>
369
+ # {:status=>"PENDING",
370
+ # :aws_id=>"/change/C2NOTVGL7IOFFF",
371
+ # :submitted_at=>"2011-01-18T15:34:18.086Z"},
372
+ # :aws_id=>"/hostedzone/ZWEC7PPVACGQ4",
373
+ # :caller_reference=>"1295365357-227168-NfZ4P-VGHWi-Yq0p7-nuN6q",
374
+ # :name_servers=>
375
+ # ["ns-794.awsdns-35.net",
376
+ # "ns-459.awsdns-57.com",
377
+ # "ns-1537.awsdns-00.co.uk",
378
+ # "ns-1165.awsdns-17.org"],
379
+ # :name=>"mysite.patch-island.com."}
380
+ #
381
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/index.html?API_CreateHostedZone.html
382
+ #
383
+ def create_hosted_zone(config)
384
+ config[:caller_reference] ||= AwsUtils::generate_unique_token
385
+ link = generate_request('POST', 'hostedzone', {}, hosted_zone_config_to_xml(config))
386
+ request_info(link, GetHostedZoneParser.new(:logger => @logger))
387
+ end
388
+
389
+ # Get your hosted zone.
390
+ #
391
+ # r53.get_hosted_zone("ZWEC7PPVACGQ4") #=>
392
+ # {:config=>{:comment=>"My awesome site!"},
393
+ # :aws_id=>"/hostedzone/ZWEC7PPVACGQ4",
394
+ # :caller_reference=>"1295422234-657482-hfkeo-JFKid-Ldfle-Sdrty",
395
+ # :name_servers=>
396
+ # ["ns-794.awsdns-35.net",
397
+ # "ns-459.awsdns-57.com",
398
+ # "ns-1537.awsdns-00.co.uk",
399
+ # "ns-1165.awsdns-17.org"],
400
+ # :name=>"mysite.patch-island.com."}
401
+ #
402
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_GetHostedZone.html
403
+ #
404
+ def get_hosted_zone(hosted_zone_aws_id)
405
+ link = generate_request('GET', expand_hosted_zone_id(hosted_zone_aws_id))
406
+ request_info(link, GetHostedZoneParser.new(:logger => @logger))
407
+ end
408
+
409
+ # Delete hosted zone.
410
+ #
411
+ # r53.delete_hosted_zone("/hostedzone/Z2P714ENJN23PN") #=>
412
+ # {:status=>"PENDING",
413
+ # :submitted_at=>"2011-01-18T15:45:45.060Z",
414
+ # :aws_id=>"/change/C1PN1SDWZKPTAC"}
415
+ #
416
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_DeleteHostedZone.html
417
+ #
418
+ def delete_hosted_zone(hosted_zone_aws_id)
419
+ link = generate_request('DELETE', expand_hosted_zone_id(hosted_zone_aws_id))
420
+ request_info(link, GetChangeParser.new(:logger => @logger))
421
+ end
422
+
423
+ #-----------------------------------------------------------------
424
+ # Resource Records Set
425
+ #-----------------------------------------------------------------
426
+
427
+ # List your resource record sets.
428
+ # Options: :type, :name, :max_items
429
+ #
430
+ # r53.list_resource_record_sets("/hostedzone/ZWEC7PPVACGQ4") #=>
431
+ # [{:type=>"NS",
432
+ # :ttl=>172800,
433
+ # :name=>"mysite.patch-island.com.",
434
+ # :resource_records=>
435
+ # ["ns-459.awsdns-57.com.",
436
+ # "ns-794.awsdns-35.net.",
437
+ # "ns-1165.awsdns-17.org.",
438
+ # "ns-1537.awsdns-00.co.uk."]},
439
+ # {:type=>"SOA",
440
+ # :ttl=>900,
441
+ # :name=>"mysite.patch-island.com.",
442
+ # :resource_records=>
443
+ # ["ns-794.awsdns-35.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"]},
444
+ # {:type=>"NS",
445
+ # :ttl=>600,
446
+ # :resource_records=>["xxx.mysite.com"],
447
+ # :name=>"m1.mysite.patch-island.com."}]
448
+ #
449
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ListResourceRecordSets.html
450
+ #
451
+ def list_resource_record_sets(hosted_zone_aws_id, options={})
452
+ options = options.dup
453
+ result = []
454
+ incrementally_list_resource_records("#{expand_hosted_zone_id(hosted_zone_aws_id)}/rrset", ListResourceRecordSetsParser, options) do |response|
455
+ result += response[:items]
456
+ true
457
+ end
458
+ result
459
+ end
460
+
461
+ # Create or delete DNS records.
462
+ #
463
+ # resource_record_sets = [{ :action => :create,
464
+ # :name => 'm3.mysite.patch-island.com',
465
+ # :type => 'NS',
466
+ # :ttl => 600,
467
+ # :resource_records => 'xxx.mysite.com' },
468
+ # { :action => :delete,
469
+ # :name => 'm2.mysite.patch-island.com',
470
+ # :type => 'A',
471
+ # :ttl => 600,
472
+ # :resource_records => ['10.0.0.1'] }]
473
+ # r53.change_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'KD: Comment#1') #=>
474
+ # {:status=>"PENDING",
475
+ # :submitted_at=>"2011-01-18T20:21:56.828Z",
476
+ # :aws_id=>"/change/C394PNLM1B2P08"}
477
+ #
478
+ # PS: resource_record_sets must have an :action key set (== :create or :delete)
479
+ # PPS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html
480
+ #
481
+ def change_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment = '')
482
+ link = generate_request('POST', "#{expand_hosted_zone_id(hosted_zone_aws_id)}/rrset", {}, resource_record_sets_to_xml(resource_record_sets, comment))
483
+ request_info(link, GetChangeParser.new(:logger => @logger))
484
+ end
485
+
486
+ # Create DNS records.
487
+ #
488
+ # resource_record_sets = [{ :name => 'm3.mysite.patch-island.com',
489
+ # :type => 'NS',
490
+ # :ttl => 600,
491
+ # :resource_records => 'xxx.mysite.com' },
492
+ # { :name => 'm2.mysite.patch-island.com',
493
+ # :type => 'A',
494
+ # :ttl => 600,
495
+ # :resource_records => ['10.0.0.1'] }]
496
+ # r53.create_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'KD: Comment#1') #=>
497
+ # {:status=>"PENDING",
498
+ # :submitted_at=>"2011-01-18T20:21:56.828Z",
499
+ # :aws_id=>"/change/C394PNLM1B2P08"}
500
+ #
501
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html
502
+ #
503
+ def create_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment = '')
504
+ resource_record_sets.each{|rrs| rrs[:action] = :create}
505
+ change_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment)
506
+ end
507
+
508
+ # Delete DNS records.
509
+ #
510
+ # resource_record_sets = [{ :name => 'm3.mysite.patch-island.com',
511
+ # :type => 'NS',
512
+ # :ttl => 600,
513
+ # :resource_records => 'xxx.mysite.com' },
514
+ # { :name => 'm2.mysite.patch-island.com',
515
+ # :type => 'A',
516
+ # :ttl => 600,
517
+ # :resource_records => ['10.0.0.1'] }]
518
+ # r53.create_resource_record_sets("/hostedzone/Z1K6NCF0EB26FB", resource_record_sets, 'KD: Comment#1') #=>
519
+ # {:status=>"PENDING",
520
+ # :submitted_at=>"2011-01-18T20:21:56.828Z",
521
+ # :aws_id=>"/change/C394PNLM1B2P08"}
522
+ #
523
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html
524
+ #
525
+ def delete_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment = '')
526
+ resource_record_sets.each{|rrs| rrs[:action] = :delete}
527
+ change_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment)
528
+ end
529
+
530
+
531
+ # Get the current state of a change request.
532
+ #
533
+ # r53.get_change("/change/C1PN1SDWZKPTAC") #=>
534
+ # {:status=>"INSYNC",
535
+ # :aws_id=>"/change/C1PN1SDWZKPTAC",
536
+ # :submitted_at=>"2011-01-18T15:45:45.060Z"}
537
+ #
538
+ # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_GetChange.html
539
+ #
540
+ def get_change(change_aws_id)
541
+ link = generate_request('GET', expand_change_id(change_aws_id))
542
+ request_info(link, GetChangeParser.new(:logger => @logger))
543
+ end
544
+
545
+ #-----------------------------------------------------------------
546
+ # Hosted Zones
547
+ #-----------------------------------------------------------------
548
+
549
+ class ListHostedZonesParser < RightAWSParser # :nodoc:
550
+ def reset
551
+ @result = { :items => [] }
552
+ end
553
+ def tagstart(name, attributes)
554
+ case name
555
+ when 'HostedZone' then @item = { :config => {} }
556
+ end
557
+ end
558
+ def tagend(name)
559
+ case name
560
+ when 'IsTruncated' then @result[:is_truncated] = @text == 'true'
561
+ when 'NextMarker' then @result[:next_marker] = @text
562
+ when 'MaxItems' then @result[:max_items] = @text.to_i
563
+ when 'Id' then @item[:aws_id] = @text
564
+ when 'Name' then @item[:name] = @text
565
+ when 'CallerReference' then @item[:caller_reference] = @text
566
+ when 'HostedZone' then @result[:items] << @item
567
+ else
568
+ case full_tag_name
569
+ when %r{/Config/Comment$} then @item[:config][:comment] = @text
570
+ end
571
+ end
572
+ end
573
+ end
574
+
575
+ class GetHostedZoneParser < RightAWSParser # :nodoc:
576
+ def reset
577
+ @result = {}
578
+ end
579
+ def tagend(name)
580
+ case full_tag_name
581
+ when %r{/HostedZone/Id} then @result[:aws_id] = @text
582
+ when %r{/HostedZone/Name} then @result[:name] = @text
583
+ when %r{/HostedZone/CallerReference} then @result[:caller_reference] = @text
584
+ when %r{/Config/Comment$} then (@result[:config] ||= {})[:comment] = AwsUtils::xml_unescape(@text)
585
+ when %r{/ChangeInfo/Id$} then (@result[:change_info] ||= {})[:aws_id] = @text
586
+ when %r{/ChangeInfo/Status$} then (@result[:change_info] ||= {})[:status] = @text
587
+ when %r{/ChangeInfo/SubmittedAt$} then (@result[:change_info] ||= {})[:submitted_at] = @text
588
+ when %r{/DelegationSet/NameServers/NameServer$} then (@result[:name_servers] ||= []) << @text
589
+ end
590
+ end
591
+ end
592
+
593
+ #-----------------------------------------------------------------
594
+ # Resource Records Set
595
+ #-----------------------------------------------------------------
596
+
597
+ class ListResourceRecordSetsParser < RightAWSParser # :nodoc:
598
+ def reset
599
+ @result = { :items => [] }
600
+ end
601
+ def tagstart(name, attributes)
602
+ case name
603
+ when 'ResourceRecordSet' then @item = {}
604
+ end
605
+ end
606
+ def tagend(name)
607
+ case name
608
+ when 'IsTruncated' then @result[:is_truncated] = @text == 'true'
609
+ when 'NextRecordName' then @result[:next_record_name] = @text
610
+ when 'NextRecordType' then @result[:next_record_type] = @text
611
+ when 'MaxItems' then @result[:max_items] = @text.to_i
612
+ when 'Type' then @item[:type] = @text
613
+ when 'Name' then @item[:name] = @text
614
+ when 'TTL' then @item[:ttl] = @text.to_i
615
+ when 'ResourceRecordSet' then @result[:items] << @item
616
+ else
617
+ case full_tag_name
618
+ when %r{/ResourceRecord/Value} then (@item[:resource_records] ||= []) << @text
619
+ when %r{/AliasTarget/DNSName} then (@item[:alias_target] ||= {})[:dns_name] = @text
620
+ when %r{/AliasTarget/HostedZoneId} then (@item[:alias_target] ||= {})[:hosted_zone_id] = @text
621
+ end
622
+ end
623
+ end
624
+ end
625
+
626
+ class GetChangeParser < RightAWSParser # :nodoc:
627
+ def reset
628
+ @result = { }
629
+ end
630
+ def tagend(name)
631
+ case name
632
+ when 'Id' then @result[:aws_id] = @text
633
+ when 'Status' then @result[:status] = @text
634
+ when 'SubmittedAt' then @result[:submitted_at] = @text
635
+ end
636
+ end
637
+ end
638
+
639
+ end
640
+
641
+ end