route53 0.1.0

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