route53 0.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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in route53.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ route53 (0.1.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ builder (3.0.0)
10
+ hpricot (0.8.3)
11
+ ruby-hmac (0.4.0)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ builder
18
+ bundler (>= 1.0.0)
19
+ hpricot
20
+ route53!
21
+ ruby-hmac
data/README.markdown ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/lib/route53.rb ADDED
@@ -0,0 +1,301 @@
1
+ module Route53
2
+ require 'hmac'
3
+ require 'hmac-sha2'
4
+ require 'base64'
5
+ require 'time'
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'hpricot'
9
+ require 'builder'
10
+ require 'digest/md5'
11
+
12
+ class Connection
13
+ attr_reader :base_url
14
+ attr_reader :api
15
+ attr_reader :endpoint
16
+
17
+ def initialize(accesskey,secret,api='2010-10-01',endpoint='https://route53.amazonaws.com/')
18
+ @accesskey = accesskey
19
+ @secret = secret
20
+ @api = api
21
+ @endpoint = endpoint
22
+ @base_url = endpoint+@api
23
+ end
24
+
25
+ def request(url,type = "GET",data = nil)
26
+ puts "URL: #{url}"
27
+ uri = URI(url)
28
+ http = Net::HTTP.new(uri.host, uri.port)
29
+ http.use_ssl = true
30
+ time = get_date
31
+ hmac = HMAC::SHA256.new(@secret)
32
+ hmac.update(time)
33
+ signature = Base64.encode64(hmac.digest).chomp
34
+ headers = {
35
+ 'Date' => time,
36
+ 'X-Amzn-Authorization' => "AWS3-HTTPS AWSAccessKeyId=#{@accesskey},Algorithm=HmacSHA256,Signature=#{signature}",
37
+ 'Content-Type' => 'text/xml; charset=UTF-8'
38
+ }
39
+ resp, data = http.send_request(type,uri.path,data,headers)
40
+ puts "Resp:"+resp.to_s
41
+ #puts "Data:"+data
42
+ return AWSResponse.new(data,self)
43
+ end
44
+
45
+ def get_zones(name = nil)
46
+ resp = request("#{@base_url}/hostedzone")
47
+ if resp.error?
48
+ return nil
49
+ end
50
+ zone_list = Hpricot::XML(resp.raw_data)
51
+ zones = []
52
+ elements = zone_list.search("HostedZone")
53
+ elements.each do |e|
54
+ zones.push(Zone.new(e.search("Name").first.innerText,
55
+ e.search("Id").first.innerText,
56
+ self))
57
+ end
58
+ unless name.nil?
59
+ name_arr = name.split('.')
60
+ (0 ... name_arr.size).each do |i|
61
+ search_domain = name_arr.last(name_arr.size-i).join('.')+"."
62
+ zone_select = zones.select { |z| z.name == search_domain }
63
+ return zone_select if zone_select.size == 1
64
+ end
65
+ return nil
66
+ end
67
+ return zones
68
+ end
69
+
70
+ def get_date
71
+ #return Time.now.utc.rfc2822
72
+ uri = URI(@endpoint)
73
+ http = Net::HTTP.new(uri.host, uri.port)
74
+ http.use_ssl = true
75
+ resp = nil
76
+ http.start { |http| resp = http.head('/date') }
77
+ return resp['Date']
78
+ end
79
+
80
+ end
81
+
82
+
83
+ class Zone
84
+ attr_reader :host_url
85
+ attr_reader :name
86
+ attr_reader :records
87
+ attr_reader :conn
88
+
89
+ def initialize(name,host_url,conn)
90
+ @name = name
91
+ unless @name.end_with?(".")
92
+ @name += "."
93
+ end
94
+ @host_url = host_url
95
+ @conn = conn
96
+ end
97
+
98
+ def exists?
99
+
100
+ end
101
+
102
+ def delete_zone
103
+ @conn.request(@conn.base_url + @host_url,"DELETE")
104
+ end
105
+
106
+ def create_zone(comment = nil)
107
+ xml_str = ""
108
+ xml = Builder::XmlMarkup.new(:target=>xml_str, :indent=>2)
109
+ xml.instruct!
110
+ xml.CreateHostedZoneRequest(:xmlns => @endpoint+'doc/'+@api+'/') { |create|
111
+ create.Name(@name)
112
+ # AWS lists this as required
113
+ # "unique string that identifies the request and that
114
+ # allows failed CreateHostedZone requests to be retried without the risk of executing the operation twice."
115
+ # Just going to pass a random string instead.
116
+ create.CallerReference(rand().to_s)
117
+ create.HostedZoneConfig { |conf|
118
+ conf.Comment(comment)
119
+ }
120
+ }
121
+ puts "XML:\n#{xml_str}"
122
+ @conn.request(@conn.base_url + "/hostedzone","POST",xml_str)
123
+ end
124
+
125
+ def get_records(type="ANY")
126
+ return nil if host_url.nil?
127
+ resp = @conn.request(@conn.base_url+@host_url+"/rrset")
128
+ if resp.error?
129
+ return nil
130
+ end
131
+ zone_file = Hpricot::XML(resp.raw_data)
132
+ records = zone_file.search("ResourceRecordSet")
133
+
134
+ dom_records = []
135
+ records.each do |record|
136
+ puts "Name:"+record.search("Name").first.innerText
137
+ puts "Type:"+record.search("Type").first.innerText
138
+ puts "TTL:"+record.search("TTL").first.innerText
139
+ record.search("Value").each do |val|
140
+ puts "Val:"+val.innerText
141
+ end
142
+ dom_records.push(DNSRecord.new(record.search("Name").first.innerText,
143
+ record.search("Type").first.innerText,
144
+ record.search("TTL").first.innerText,
145
+ record.search("Value").map { |val| val.innerText },
146
+ self))
147
+ end
148
+ @records = dom_records
149
+ if type != 'ANY'
150
+ return dom_records.select { |r| r.type == type }
151
+ end
152
+ return dom_records
153
+ end
154
+
155
+ #When deleting a record an optional value is available to specify just a single value within a recordset like an MX record
156
+ #Takes an array of [:action => , :record => ] where action is either CREATE or DELETE and record is a DNSRecord
157
+ def gen_change_xml(change_list,comment=nil)
158
+ #Get zone list and pick zone that matches most ending chars
159
+
160
+ xml_str = ""
161
+ xml = Builder::XmlMarkup.new(:target=>xml_str, :indent=>2)
162
+ xml.instruct!
163
+ xml.ChangeResourceRecordSetsRequest(:xmlns => @conn.endpoint+'doc/'+@conn.api+'/') { |req|
164
+ req.ChangeBatch { |batch|
165
+ batch.Comment(comment) unless comment.nil?
166
+ batch.Changes { |changes|
167
+ change_list.each { |change_item|
168
+ change_item[:record].gen_change_xml(changes,change_item[:action])
169
+ }
170
+ }
171
+ }
172
+ }
173
+ puts "XML:\n#{xml_str}"
174
+ return xml_str
175
+ end
176
+
177
+ #For modifying multiple or single records within a single transaction
178
+ def perform_actions(change_list,comment=nil)
179
+ xml_str = gen_change_xml(change_list,comment)
180
+ @conn.request(@conn.base_url + @host_url+"/rrset","POST",xml_str)
181
+ end
182
+
183
+
184
+ def to_s
185
+ return "#{@name} #{@host_url}"
186
+ end
187
+ end
188
+
189
+ class AWSResponse
190
+ attr_reader :raw_data
191
+ def initialize(resp,conn)
192
+ @raw_data = resp
193
+ if error?
194
+ $stderr.puts "An Error has occured"
195
+ $stderr.puts @raw_data
196
+ end
197
+ @conn = conn
198
+ end
199
+
200
+ def error?
201
+ return Hpricot::XML(@raw_data).search("ErrorResponse").size > 0
202
+ end
203
+
204
+ def complete?
205
+ if @change_url.nil?
206
+ change = Hpricot::XML(@raw_data).search("ChangeInfo")
207
+ if change.size > 0
208
+ @change_url = change.first.search("Id").first.innerText
209
+ else
210
+ return false
211
+ end
212
+ end
213
+ if @complete.nil? || @complete == false
214
+ status = Hpricot::XML(@conn.request(@conn.base_url+@change_url).raw_data).search("Status")
215
+ @complete = status.size > 0 && status.first.innerText == "INSYNC" ? true : false
216
+ end
217
+ return @complete
218
+ end
219
+
220
+ def pending?
221
+ #Return opposite of complete via XOR
222
+ return complete? ^ true
223
+ end
224
+
225
+ def to_s
226
+ return @raw_data
227
+ end
228
+ end
229
+
230
+ class DNSRecord
231
+ attr_reader :name
232
+ attr_reader :type
233
+ attr_reader :ttl
234
+ attr_reader :values
235
+
236
+ def initialize(name,type,ttl,values,zone)
237
+ @name = name
238
+ @type = type
239
+ @ttl = ttl
240
+ @values = values
241
+ @zone = zone
242
+ end
243
+
244
+ def gen_change_xml(xml,action)
245
+ xml.Change { |change|
246
+ change.Action(action.upcase)
247
+ change.ResourceRecordSet { |record|
248
+ record.Name(@name)
249
+ record.Type(@type)
250
+ record.TTL(@ttl)
251
+ record.ResourceRecords { |resources|
252
+ @values.each { |val|
253
+ resources.ResourceRecord { |record|
254
+ record.Value(val)
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ end
261
+
262
+ def delete(comment=nil)
263
+ @zone.perform_actions([{:action => "DELETE", :record => self}],comment)
264
+ end
265
+
266
+ def create(comment=nil)
267
+ @zone.perform_actions([{:action => "CREATE", :record => self}],comment)
268
+ end
269
+
270
+ #Need to modify to a param hash
271
+ def update(name,type,ttl,values,comment=nil)
272
+ prev = self.clone
273
+ @name = name unless name.nil?
274
+ @type = type unless type.nil?
275
+ @ttl = ttl unless ttl.nil?
276
+ @values = values unless values.nil?
277
+ @zone.perform_actions(self.name,[
278
+ {:action => "DELETE", :record => prev},
279
+ {:action => "CREATE", :record => self},
280
+ ],comment)
281
+ end
282
+
283
+ #Returns the raw array so the developer can update large batches manually
284
+ #Need to modify to a param hash
285
+ def update_dirty(name,type,ttl,values)
286
+ prev = self.clone
287
+ @name = name unless name.nil?
288
+ @type = type unless type.nil?
289
+ @ttl = ttl unless ttl.nil?
290
+ @values = values unless values.nil?
291
+ return [{:action => "DELETE", :record => prev},
292
+ {:action => "CREATE", :record => self}]
293
+ end
294
+
295
+ def to_s
296
+ return "#{@name} #{@type} #{@ttl} #{@values}"
297
+ end
298
+ end
299
+ end
300
+
301
+
@@ -0,0 +1,3 @@
1
+ module Route53
2
+ VERSION = "0.1.0"
3
+ end
data/route53.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/route53/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "route53"
6
+ s.version = Route53::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = "Philip Corliss"
9
+ s.email = 'pcorlis@50projects.com'
10
+ s.homepage = 'http://github.com/pcorliss/ruby_route_53'
11
+ s.summary = "Library for Amazon's Route 53 service"
12
+ s.description = "Provides CRUD and list operations for records and zones as part of Amazon's Route 53 service."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+
16
+ s.add_development_dependency "bundler", ">= 1.0.0"
17
+ s.add_development_dependency "ruby-hmac"
18
+ s.add_development_dependency "hpricot"
19
+ s.add_development_dependency "builder"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
23
+ s.require_path = 'lib'
24
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: route53
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Philip Corliss
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-08 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bundler
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 0
31
+ - 0
32
+ version: 1.0.0
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: ruby-hmac
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: hpricot
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :development
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: builder
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ type: :development
73
+ version_requirements: *id004
74
+ description: Provides CRUD and list operations for records and zones as part of Amazon's Route 53 service.
75
+ email: pcorlis@50projects.com
76
+ executables: []
77
+
78
+ extensions: []
79
+
80
+ extra_rdoc_files: []
81
+
82
+ files:
83
+ - .gitignore
84
+ - Gemfile
85
+ - Gemfile.lock
86
+ - README.markdown
87
+ - Rakefile
88
+ - lib/route53.rb
89
+ - lib/route53/version.rb
90
+ - route53.gemspec
91
+ has_rdoc: true
92
+ homepage: http://github.com/pcorliss/ruby_route_53
93
+ licenses: []
94
+
95
+ post_install_message:
96
+ rdoc_options: []
97
+
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ segments:
114
+ - 1
115
+ - 3
116
+ - 6
117
+ version: 1.3.6
118
+ requirements: []
119
+
120
+ rubyforge_project:
121
+ rubygems_version: 1.3.7
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Library for Amazon's Route 53 service
125
+ test_files: []
126
+