route53 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +674 -0
- data/README.markdown +181 -0
- data/lib/route53.rb +97 -39
- data/lib/route53/cli.rb +96 -30
- data/lib/route53/version.rb +1 -1
- metadata +4 -3
data/README.markdown
CHANGED
@@ -0,0 +1,181 @@
|
|
1
|
+
Ruby Interface for Amazon's Route 53
|
2
|
+
====================================
|
3
|
+
|
4
|
+
This interface can either be used as a command line tool or as a library from within your existing ruby project. It provides a way to interact with Amazon's Route 53 service.
|
5
|
+
|
6
|
+
Costs & Impact
|
7
|
+
--------------
|
8
|
+
|
9
|
+
At the time of this writing Amazon charges $1/zone/month. This includes zones that have been created and deleted and then recreated. The creator of this gem is not responsible for costs incurred while using this interface or unexpected oepration or bugs which may incur a cost for the user. The creator is also not responsible for any downtime incurred or disruption in service from the usage of this tool. DNS can be a tricky thing, be careful and always make sure you have a backup of your zone prior to mucking around with it. (route53 -l example.com.)
|
10
|
+
|
11
|
+
Latest Version
|
12
|
+
--------------
|
13
|
+
|
14
|
+
The latest source should be available on my [github account](https://github.com/pcorliss/ruby_route_53) and if you can't obtain it using the gem command you can go directly to the [gem page](https://rubygems.org/gems/route53) hosted on rubygems.
|
15
|
+
|
16
|
+
Installation
|
17
|
+
------------
|
18
|
+
|
19
|
+
Installing the Gem
|
20
|
+
|
21
|
+
sudo gem install route53
|
22
|
+
|
23
|
+
Ubuntu with precompiled dependencies
|
24
|
+
|
25
|
+
sudo apt-get update
|
26
|
+
sudo apt-get install ruby rubygems libopenssl-ruby libhmac-ruby libbuilder-ruby libhpricot-ruby
|
27
|
+
sudo gem install route53 --ignore-dependencies
|
28
|
+
/var/lib/gems/1.X/gems/route53-W.Y.Z/bin/route53
|
29
|
+
|
30
|
+
#When working with the library and using this method you may need to require the library manually
|
31
|
+
require '/var/lib/gems/1.X/gems/route53-W.Y.Z/lib/route53'
|
32
|
+
|
33
|
+
Ubuntu with building dependencies
|
34
|
+
|
35
|
+
sudo apt-get update
|
36
|
+
sudo apt-get install ruby rubygems ruby-dev build-essential libopenssl-ruby
|
37
|
+
sudo gem install route53
|
38
|
+
/var/lib/gems/1.X/bin/route53
|
39
|
+
|
40
|
+
The first time you run the gem in command line mode you'll be prompted to setup. You'll want to have your Amazon access and secret key ready.
|
41
|
+
|
42
|
+
You've either elected to run the setup or a configuration file could not be found.
|
43
|
+
Please answer the following prompts.
|
44
|
+
Amazon Access Key: []
|
45
|
+
Amazon Secret Key: []
|
46
|
+
Amazon Route 53 API Version: [2010-10-01]
|
47
|
+
Amazon Route 53 Endpoint: [https://route53.amazonaws.com/]
|
48
|
+
Default TTL: [3600]
|
49
|
+
Save the configuration file to "~/.route53"?: [Y]
|
50
|
+
|
51
|
+
Command Line Options
|
52
|
+
--------------------
|
53
|
+
|
54
|
+
Usage: route53 [options]
|
55
|
+
|
56
|
+
-v, --version Print Version Information
|
57
|
+
-h, --help Show this message
|
58
|
+
-V, --verbose Verbose Output
|
59
|
+
-l, --list [ZONE] Receive a list of all zones or specify a zone to view
|
60
|
+
-n, --new [ZONE] Create a new Zone
|
61
|
+
-d, --delete [ZONE] Delete a Zone
|
62
|
+
-z, --zone [ZONE] Specify a zone to perform an operation on. Either in 'example.com.' or '/hostedzone/XXX' format
|
63
|
+
-c, --create Create a new record
|
64
|
+
-r, --remove Remove a record
|
65
|
+
-g, --change Change a record
|
66
|
+
--name [NAME] Specify a name for a record
|
67
|
+
--type [TYPE] Specify a type for a record
|
68
|
+
--ttl [TTL] Specify a TTL for a record
|
69
|
+
--values [VALUE1],[VALUE2],[VALUE3]
|
70
|
+
Specify one or multiple values for a record
|
71
|
+
-m, --comment [COMMENT] Provide a comment for this operation
|
72
|
+
--no-wait Do not wait for actions to finish syncing.
|
73
|
+
-s, --setup Run the setup ptogram to create your configuration file.
|
74
|
+
-f, --file [CONFIGFILE] Specify a configuration file to use
|
75
|
+
--access [ACCESSKEY] Specify an access key on the command line.
|
76
|
+
--secret [SECRETKEY] Specify a secret key on the command line. WARNING: Not a good idea
|
77
|
+
|
78
|
+
|
79
|
+
Command Line Usage
|
80
|
+
------------------
|
81
|
+
|
82
|
+
Once route53 is installed, started and has been setup you're ready to start. You can use the following examples to get started.
|
83
|
+
|
84
|
+
#Creating a new zone
|
85
|
+
route53 -n example.com.
|
86
|
+
|
87
|
+
#List Operations
|
88
|
+
route53 -l #Get all zones for this account
|
89
|
+
route53 -l example.com. #Get all records for this account within the zone example.com.
|
90
|
+
|
91
|
+
#Create a new record within our newly created zone.
|
92
|
+
route53 --zone example.com. -c --name foo.example.com. --type CNAME --ttl 3600 --values example.com.
|
93
|
+
|
94
|
+
#New MX Record for a Google Apps hosted domain
|
95
|
+
route53 --zone example.com. -c --name example.com. --type MX --ttl 3600 \
|
96
|
+
--values "10 ASPMX.L.GOOGLE.com.","20 ALT1.ASPMX.L.GOOGLE.com.","30 ALT2.ASPMX.L.GOOGLE.com.","40 ASPMX2.GOOGLEMAIL.com.","50 ASPMX3.GOOGLEMAIL.com."
|
97
|
+
|
98
|
+
#Update the TTL of a record (Leave values nil to leave them alone)
|
99
|
+
#You'll be prompted to select the record from a list.
|
100
|
+
#If updating values for a record, make sure to includ all other values. Otherwise they will be dropped
|
101
|
+
route53 --zone example.com. -g --ttl 600
|
102
|
+
|
103
|
+
#Deleting a zone - First remove all records except the NS and SOA record. Then delete the zone.
|
104
|
+
route53 --zone example.com. -r
|
105
|
+
route53 -d example.com.
|
106
|
+
|
107
|
+
Library Usage
|
108
|
+
-------------
|
109
|
+
|
110
|
+
If you're using this as a library for your own ruby project you can load it and perform operations by using the following examples.
|
111
|
+
|
112
|
+
require 'route53'
|
113
|
+
|
114
|
+
#Creating a Connection object
|
115
|
+
conn = Route53::Connection.new(my_access_key,my_secret_key) #opens connection
|
116
|
+
|
117
|
+
#Creating a new zone and working with responses
|
118
|
+
new_zone = Route53::Zone.new("example.com.",nil,conn) #Create a new zone "example.com."
|
119
|
+
resp = new_zone.create_zone #Creates a new zone
|
120
|
+
exit 1 if resp.error? #Exit if there was an error. The AWSResponse Class automatically prints out error messages to STDERR.
|
121
|
+
while resp.pending? #Waits for response to sync on Amazon's servers.
|
122
|
+
sleep 1 #If you'll be performing operations on this newly created zone you'll probably want to wait.
|
123
|
+
end
|
124
|
+
|
125
|
+
#List Operations
|
126
|
+
zones = conn.get_zones #Requests list of all zones for this account
|
127
|
+
records = zones.first.get_records #Gets list of all records for a specific zone
|
128
|
+
|
129
|
+
#Create a new record within our newly created zone.
|
130
|
+
new_record = Route53::DNSRecord.new("foo.example.com.","CNAME","3600",["example.com."],new_zone)
|
131
|
+
resp = new_record.create
|
132
|
+
|
133
|
+
#New MX Record for a Google Apps hosted domain
|
134
|
+
new_mx_record = Route53::DNSRecord.new("example.com.","MX","3600",
|
135
|
+
["10 ASPMX.L.GOOGLE.com.",
|
136
|
+
"20 ALT1.ASPMX.L.GOOGLE.com.",
|
137
|
+
"30 ALT2.ASPMX.L.GOOGLE.com.",
|
138
|
+
"40 ASPMX2.GOOGLEMAIL.com.",
|
139
|
+
"50 ASPMX3.GOOGLEMAIL.com."],
|
140
|
+
new_zone)
|
141
|
+
resp = new_mx_record.create
|
142
|
+
|
143
|
+
#Update the TTL of a record (Leave values nil to leave them alone)
|
144
|
+
#If updating values for a record, make sure to includ all other values. Otherwise they will be dropped
|
145
|
+
resp = new_record.update(nil,nil,"600",nil)
|
146
|
+
|
147
|
+
#Deleting a zone
|
148
|
+
#A zone can't be deleted until all of it's records have been deleted (Except for 1 NS record and 1 SOA record)
|
149
|
+
new_zone.get_records.each do |record|
|
150
|
+
unless record.type == 'NS' || record.type == 'SOA'
|
151
|
+
record.delete
|
152
|
+
end
|
153
|
+
end
|
154
|
+
new_zone.delete_zone
|
155
|
+
|
156
|
+
|
157
|
+
Requirements
|
158
|
+
------------
|
159
|
+
|
160
|
+
Written with Ruby 1.9.2 on an Ubuntu Linux machine. Smoke tested on an Ubuntu Linux machine with Ruby 1.8.7.
|
161
|
+
|
162
|
+
Amazon AWS account with Route 53 opted into - See the signup link at [http://aws.amazon.com/route53/](http://aws.amazon.com/route53/)
|
163
|
+
ruby openssl support
|
164
|
+
ruby-hmac
|
165
|
+
hpricot
|
166
|
+
builder
|
167
|
+
|
168
|
+
Support and Bugs
|
169
|
+
----------------
|
170
|
+
|
171
|
+
Bug reports are appreciated and encouraged. Please either file a detailed issue on [Github](https://github.com/pcorliss/ruby_route_53/issues) or send an email to support@50projects.com.
|
172
|
+
|
173
|
+
Contact
|
174
|
+
-------
|
175
|
+
|
176
|
+
This ruby interface for Amazon's Route 53 service was created by Philip Corliss (pcorliss@50projects.com) [50projects.com](http://50projects.com). You can find more information on him and 50projects at [http://blog.50projects.com](http://blog.50projects.com)
|
177
|
+
|
178
|
+
License
|
179
|
+
-------
|
180
|
+
|
181
|
+
LazyRaid is licensed under the GPL. See the LICENSE file for details.
|
data/lib/route53.rb
CHANGED
@@ -45,31 +45,40 @@ module Route53
|
|
45
45
|
'X-Amzn-Authorization' => "AWS3-HTTPS AWSAccessKeyId=#{@accesskey},Algorithm=HmacSHA256,Signature=#{signature}",
|
46
46
|
'Content-Type' => 'text/xml; charset=UTF-8'
|
47
47
|
}
|
48
|
-
resp, raw_resp = http.send_request(type,uri.path,data,headers)
|
48
|
+
resp, raw_resp = http.send_request(type,uri.path+"?"+(uri.query.nil? ? "" : uri.query),data,headers)
|
49
49
|
#puts "Resp:"+resp.to_s if @verbose
|
50
50
|
#puts "XML_RESP:"+raw_resp if @verbose
|
51
51
|
return AWSResponse.new(raw_resp,self)
|
52
52
|
end
|
53
53
|
|
54
54
|
def get_zones(name = nil)
|
55
|
-
|
56
|
-
|
57
|
-
return nil
|
58
|
-
end
|
59
|
-
zone_list = Hpricot::XML(resp.raw_data)
|
55
|
+
truncated = true
|
56
|
+
query = []
|
60
57
|
zones = []
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
58
|
+
while truncated
|
59
|
+
if !name.nil? && name.start_with?("/hostedzone/")
|
60
|
+
resp = request("#{@base_url}#{name}")
|
61
|
+
truncated = false
|
62
|
+
else
|
63
|
+
resp = request("#{@base_url}/hostedzone?"+query.join("&"))
|
64
|
+
end
|
65
|
+
return nil if resp.error?
|
66
|
+
zone_list = Hpricot::XML(resp.raw_data)
|
67
|
+
elements = zone_list.search("HostedZone")
|
68
|
+
elements.each do |e|
|
69
|
+
zones.push(Zone.new(e.search("Name").first.innerText,
|
70
|
+
e.search("Id").first.innerText,
|
71
|
+
self))
|
72
|
+
end
|
73
|
+
truncated = (zone_list.search("IsTruncated").first.innerText == "true") if truncated
|
74
|
+
query = ["marker="+zone_list.search("NextMarker").first.innerText] if truncated
|
66
75
|
end
|
67
|
-
unless name.nil?
|
76
|
+
unless name.nil? || name.start_with?("/hostedzone/")
|
68
77
|
name_arr = name.split('.')
|
69
78
|
(0 ... name_arr.size).each do |i|
|
70
79
|
search_domain = name_arr.last(name_arr.size-i).join('.')+"."
|
71
80
|
zone_select = zones.select { |z| z.name == search_domain }
|
72
|
-
return zone_select
|
81
|
+
return zone_select
|
73
82
|
end
|
74
83
|
return nil
|
75
84
|
end
|
@@ -112,10 +121,6 @@ module Route53
|
|
112
121
|
@conn = conn
|
113
122
|
end
|
114
123
|
|
115
|
-
def exists?
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
124
|
def delete_zone
|
120
125
|
@conn.request(@conn.base_url + @host_url,"DELETE")
|
121
126
|
end
|
@@ -130,37 +135,52 @@ module Route53
|
|
130
135
|
# "unique string that identifies the request and that
|
131
136
|
# allows failed CreateHostedZone requests to be retried without the risk of executing the operation twice."
|
132
137
|
# Just going to pass a random string instead.
|
133
|
-
create.CallerReference(rand().to_s)
|
138
|
+
create.CallerReference(rand(2**32).to_s(16))
|
134
139
|
create.HostedZoneConfig { |conf|
|
135
140
|
conf.Comment(comment)
|
136
141
|
}
|
137
142
|
}
|
138
143
|
#puts "XML:\n#{xml_str}" if @conn.verbose
|
139
|
-
@conn.request(@conn.base_url + "/hostedzone","POST",xml_str)
|
144
|
+
resp = @conn.request(@conn.base_url + "/hostedzone","POST",xml_str)
|
145
|
+
resp_xml = Hpricot::XML(resp.raw_data)
|
146
|
+
@host_url = resp_xml.search("HostedZone").first.search("Id").first.innerText
|
147
|
+
return resp
|
140
148
|
end
|
141
149
|
|
142
150
|
def get_records(type="ANY")
|
143
151
|
return nil if host_url.nil?
|
144
|
-
resp = @conn.request(@conn.base_url+@host_url+"/rrset")
|
145
|
-
if resp.error?
|
146
|
-
return nil
|
147
|
-
end
|
148
|
-
zone_file = Hpricot::XML(resp.raw_data)
|
149
|
-
records = zone_file.search("ResourceRecordSet")
|
150
152
|
|
153
|
+
truncated = true
|
154
|
+
query = []
|
151
155
|
dom_records = []
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
156
|
+
while truncated
|
157
|
+
resp = @conn.request(@conn.base_url+@host_url+"/rrset?"+query.join("&"))
|
158
|
+
if resp.error?
|
159
|
+
return nil
|
160
|
+
end
|
161
|
+
zone_file = Hpricot::XML(resp.raw_data)
|
162
|
+
records = zone_file.search("ResourceRecordSet")
|
163
|
+
|
164
|
+
records.each do |record|
|
165
|
+
#puts "Name:"+record.search("Name").first.innerText if @conn.verbose
|
166
|
+
#puts "Type:"+record.search("Type").first.innerText if @conn.verbose
|
167
|
+
#puts "TTL:"+record.search("TTL").first.innerText if @conn.verbose
|
168
|
+
record.search("Value").each do |val|
|
169
|
+
#puts "Val:"+val.innerText if @conn.verbose
|
170
|
+
end
|
171
|
+
dom_records.push(DNSRecord.new(record.search("Name").first.innerText,
|
172
|
+
record.search("Type").first.innerText,
|
173
|
+
record.search("TTL").first.innerText,
|
174
|
+
record.search("Value").map { |val| val.innerText },
|
175
|
+
self))
|
176
|
+
end
|
177
|
+
|
178
|
+
truncated = (zone_file.search("IsTruncated").first.innerText == "true")
|
179
|
+
if truncated
|
180
|
+
next_name = zone_file.search("NextRecordName").first.innerText
|
181
|
+
next_type = zone_file.search("NextRecordType").first.innerText
|
182
|
+
query = ["name="+next_name,"type="+next_type]
|
158
183
|
end
|
159
|
-
dom_records.push(DNSRecord.new(record.search("Name").first.innerText,
|
160
|
-
record.search("Type").first.innerText,
|
161
|
-
record.search("TTL").first.innerText,
|
162
|
-
record.search("Value").map { |val| val.innerText },
|
163
|
-
self))
|
164
184
|
end
|
165
185
|
@records = dom_records
|
166
186
|
if type != 'ANY'
|
@@ -205,19 +225,42 @@ module Route53
|
|
205
225
|
|
206
226
|
class AWSResponse
|
207
227
|
attr_reader :raw_data
|
228
|
+
|
229
|
+
#I wanted to put this in a seprate file but ruby's method of determinign the root of the gem is a pain in the butt and I was in a hurry. Sorry. -PC
|
230
|
+
|
231
|
+
|
208
232
|
def initialize(resp,conn)
|
209
233
|
@raw_data = resp
|
210
234
|
if error?
|
211
|
-
$stderr.puts "
|
212
|
-
$stderr.puts
|
235
|
+
$stderr.puts "ERROR: Amazon returned an error for the request."
|
236
|
+
$stderr.puts "ERROR: RAW_XML: "+@raw_data
|
237
|
+
$stderr.puts "ERROR: "+error_message
|
238
|
+
$stderr.puts ""
|
239
|
+
$stderr.puts "What now? "+helpful_message
|
240
|
+
#exit 1
|
213
241
|
end
|
214
242
|
@conn = conn
|
243
|
+
@created = Time.now
|
215
244
|
puts "Raw: #{@raw_data}" if @conn.verbose
|
216
245
|
end
|
217
246
|
|
218
247
|
def error?
|
219
248
|
return Hpricot::XML(@raw_data).search("ErrorResponse").size > 0
|
220
249
|
end
|
250
|
+
|
251
|
+
def error_message
|
252
|
+
xml = Hpricot::XML(@raw_data)
|
253
|
+
msg_code = xml.search("Code")
|
254
|
+
msg_text = xml.search("Message")
|
255
|
+
return (msg_code.size > 0 ? msg_code.first.inner_text : "") + (msg_text.size > 0 ? ': ' + msg_text.first.innerText : "")
|
256
|
+
end
|
257
|
+
|
258
|
+
def helpful_message
|
259
|
+
xml = Hpricot::XML(@raw_data)
|
260
|
+
msg_code = xml.search("Code").first.innerText
|
261
|
+
return $messages[msg_code] if $messages[msg_code]
|
262
|
+
return $messages["Other"]
|
263
|
+
end
|
221
264
|
|
222
265
|
def complete?
|
223
266
|
return true if error?
|
@@ -232,6 +275,10 @@ module Route53
|
|
232
275
|
if @complete.nil? || @complete == false
|
233
276
|
status = Hpricot::XML(@conn.request(@conn.base_url+@change_url).raw_data).search("Status")
|
234
277
|
@complete = status.size > 0 && status.first.innerText == "INSYNC" ? true : false
|
278
|
+
if !@complete && @created - Time.now > 60
|
279
|
+
$stderr.puts "WARNING: Amazon Route53 Change timed out on Sync. This may not be an issue as it may just be Amazon being assy. Then again your request may not have completed.'"
|
280
|
+
@complete = true
|
281
|
+
end
|
235
282
|
end
|
236
283
|
return @complete
|
237
284
|
end
|
@@ -254,6 +301,9 @@ module Route53
|
|
254
301
|
|
255
302
|
def initialize(name,type,ttl,values,zone)
|
256
303
|
@name = name
|
304
|
+
unless @name.end_with?(".")
|
305
|
+
@name += "."
|
306
|
+
end
|
257
307
|
@type = type
|
258
308
|
@ttl = ttl
|
259
309
|
@values = values
|
@@ -312,9 +362,17 @@ module Route53
|
|
312
362
|
end
|
313
363
|
|
314
364
|
def to_s
|
315
|
-
return "#{@name} #{@type} #{@ttl} #{@values}"
|
365
|
+
return "#{@name} #{@type} #{@ttl} #{@values.join(",")}"
|
316
366
|
end
|
317
367
|
end
|
318
368
|
end
|
319
369
|
|
370
|
+
$messages = { "InvalidClientTokenId" => "You may have a missing or incorrect secret or access key. Please double check your configuration files and amazon account",
|
371
|
+
"MissingAuthenticationToken" => "You may have a missing or incorrect secret or access key. Please double check your configuration files and amazon account",
|
372
|
+
"OptInRequired" => "In order to use Amazon's Route 53 service you first need to signup for it. Please see http://aws.amazon.com/route53/ for your account information and use the associated access key and secret.",
|
373
|
+
"Other" => "It looks like you've run into an unhandled error. Please send a detailed bug report with the entire input and output from the program to support@50projects.com or to https://github.com/pcorliss/ruby_route_53/issues and we'll do out best to help you.",
|
374
|
+
"SignatureDoesNotMatch" => "It looks like your secret key is incorrect or no longer valid. Please check your amazon account information for the proper key.",
|
375
|
+
"HostedZoneNotEmpty" => "You'll need to first delete the contents of this zone. You can do so using the '--remove' option as part of the command line interface.",
|
376
|
+
"InvalidChangeBatch" => "You may have tried to delete a NS or SOA record. This error is safe to ignore if you're just trying to delete all records as part of a zone prior to deleting the zone. Otherwise please file a bug by sending a detailed bug report with the entire input and output from the program to support@50projects.com or to https://github.com/pcorliss/ruby_route_53/issues and we'll do out best to help you."}
|
377
|
+
|
320
378
|
|
data/lib/route53/cli.rb
CHANGED
@@ -49,10 +49,10 @@ module Route53
|
|
49
49
|
opts.on('-V', '--verbose',"Verbose Output") { @options.verbose = true }
|
50
50
|
#opts.on('-q', '--quiet',"Quiet Output") { @options.quiet = true }
|
51
51
|
|
52
|
-
opts.on('-l', '--list [ZONE]', String, "Receive a list of all zones or specify a zone to view") { |zone| @options.zone = zone
|
53
|
-
opts.on('-n', '--new [ZONE]', String, "Create a new Zone") { |zone| @options.zone = zone
|
54
|
-
opts.on('-d', '--delete [ZONE]', String, "Delete a Zone") { |zone| @options.zone = zone
|
55
|
-
opts.on('-z', '--zone [ZONE]', String, "Specify a zone to perform an operation on") { |zone| @options.zone = zone }
|
52
|
+
opts.on('-l', '--list [ZONE]', String, "Receive a list of all zones or specify a zone to view") { |zone| @options.zone = zone unless zone.nil?; @options.list = true }
|
53
|
+
opts.on('-n', '--new [ZONE]', String, "Create a new Zone") { |zone| @options.zone = zone unless zone.nil?; @options.new_zone = true }
|
54
|
+
opts.on('-d', '--delete [ZONE]', String, "Delete a Zone") { |zone| @options.zone = zone unless zone.nil?; @options.delete_zone = true }
|
55
|
+
opts.on('-z', '--zone [ZONE]', String, "Specify a zone to perform an operation on. Either in 'example.com.' or '/hostedzone/XXX' format") { |zone| @options.zone = zone }
|
56
56
|
|
57
57
|
opts.on('-c', '--create', "Create a new record") { @options.create_record = true }
|
58
58
|
opts.on('-r', '--remove', String, "Remove a record") { |record| @options.remove_record = true }
|
@@ -89,6 +89,11 @@ module Route53
|
|
89
89
|
load_config
|
90
90
|
@config['access_key'] = @options.access unless @options.access.nil?
|
91
91
|
@config['secret_key'] = @options.secret unless @options.secret.nil?
|
92
|
+
|
93
|
+
|
94
|
+
required_options("",["--access-key"]) if @config['access_key'].nil? || @config['access_key'] == ""
|
95
|
+
required_options("",["--secret_key"]) if @config['secret_key'].nil? || @config['secret_key'] == ""
|
96
|
+
|
92
97
|
end
|
93
98
|
|
94
99
|
def output_options
|
@@ -100,14 +105,47 @@ module Route53
|
|
100
105
|
end
|
101
106
|
|
102
107
|
def arguments_valid?
|
103
|
-
true
|
108
|
+
return true if @arguments.length == 0
|
109
|
+
$stderr.puts "Received extra arguments. that couldn't be handled:#{@arguments}"
|
110
|
+
return false
|
104
111
|
end
|
105
112
|
|
106
113
|
# Setup the arguments
|
107
114
|
def process_arguments
|
108
|
-
|
109
|
-
|
110
115
|
if @options.new_zone
|
116
|
+
new_zone
|
117
|
+
elsif @options.delete_zone
|
118
|
+
delete_zone
|
119
|
+
elsif @options.create_record
|
120
|
+
create_record
|
121
|
+
elsif @options.remove_record
|
122
|
+
remove_record
|
123
|
+
elsif @options.change_record
|
124
|
+
change_record
|
125
|
+
elsif @options.list || @options.zone.nil?
|
126
|
+
list
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def list
|
131
|
+
zones = conn.get_zones(@options.zone)
|
132
|
+
unless zones.nil?
|
133
|
+
zones.each do |z|
|
134
|
+
puts z
|
135
|
+
if @options.zone
|
136
|
+
records = z.get_records(@options.dnstype.nil? ? "ANY" : @options.dnstype)
|
137
|
+
records.each do |r|
|
138
|
+
puts r
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
else
|
143
|
+
$stderr.puts "ERROR: No Records found for #{@options.zone}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def new_zone
|
148
|
+
if @options.zone
|
111
149
|
new_zone = Route53::Zone.new(@options.zone,nil,conn)
|
112
150
|
puts "Creating New Zone #{@options.zone}"
|
113
151
|
resp = new_zone.create_zone(@options.comment)
|
@@ -117,9 +155,13 @@ module Route53
|
|
117
155
|
pending_wait(resp)
|
118
156
|
puts "Zone Created."
|
119
157
|
end
|
158
|
+
else
|
159
|
+
required_options("new zone",["--zone"])
|
120
160
|
end
|
121
|
-
|
122
|
-
|
161
|
+
end
|
162
|
+
|
163
|
+
def delete_zone
|
164
|
+
if @options.zone
|
123
165
|
records = conn.get_zones(@options.zone)
|
124
166
|
if records.size > 0
|
125
167
|
if records.size > 1
|
@@ -134,14 +176,21 @@ module Route53
|
|
134
176
|
else
|
135
177
|
$stderr.puts "ERROR: Couldn't Find Record for @options.zone."
|
136
178
|
end
|
179
|
+
else
|
180
|
+
required_options("delete zone",["--zone"])
|
137
181
|
end
|
138
|
-
|
139
|
-
|
182
|
+
end
|
183
|
+
|
184
|
+
def create_record
|
185
|
+
if @options.zone && @options.name &&
|
186
|
+
@options.dnstype && @options.values &&
|
187
|
+
(@options.ttl || @config['default_ttl'])
|
140
188
|
zones = conn.get_zones(@options.zone)
|
141
189
|
if zones.size > 0
|
142
190
|
resps = []
|
143
191
|
zones.each do |z|
|
144
192
|
puts "Creating Record"
|
193
|
+
@options.ttl = @config['default_ttl'] if @options.ttl.nil?
|
145
194
|
record = Route53::DNSRecord.new(@options.name,@options.dnstype,@options.ttl,@options.values,z)
|
146
195
|
puts "Creating Record #{record}"
|
147
196
|
resps.push(record.create)
|
@@ -153,10 +202,16 @@ module Route53
|
|
153
202
|
else
|
154
203
|
$stderr.puts "ERROR: Couldn't Find Record for @options.zone."
|
155
204
|
end
|
156
|
-
|
205
|
+
else
|
206
|
+
#$stderr.puts "ERROR: The following arguments are required for a create record operation."
|
207
|
+
#$stderr.puts "ERROR: --zone and at least one of --name, --type, --ttl or --values"
|
208
|
+
#exit 1
|
209
|
+
required_options("create record",["--zone","--name","--type","--ttl","--values"])
|
157
210
|
end
|
158
|
-
|
159
|
-
|
211
|
+
end
|
212
|
+
|
213
|
+
def remove_record
|
214
|
+
if @options.zone
|
160
215
|
zones = conn.get_zones(@options.zone)
|
161
216
|
if zones.size > 0
|
162
217
|
zones.each do |z|
|
@@ -178,9 +233,16 @@ module Route53
|
|
178
233
|
else
|
179
234
|
$stderr.puts "ERROR: Couldn't Find Record for @options.zone."
|
180
235
|
end
|
236
|
+
else
|
237
|
+
#$stderr.puts "ERROR: The following arguments are required for a record removal operation."
|
238
|
+
#$stderr.puts "ERROR: --zone"
|
239
|
+
#exit 1
|
240
|
+
required_options("record removal",["--zone"])
|
181
241
|
end
|
182
|
-
|
183
|
-
|
242
|
+
end
|
243
|
+
|
244
|
+
def change_record
|
245
|
+
if @options.zone && (@options.name || @options.dnstype || @options.ttl || @options.values)
|
184
246
|
zones = conn.get_zones(@options.zone)
|
185
247
|
if zones.size > 0
|
186
248
|
zones.each do |z|
|
@@ -202,20 +264,21 @@ module Route53
|
|
202
264
|
else
|
203
265
|
$stderr.puts "ERROR: Couldn't Find Record for @options.zone."
|
204
266
|
end
|
267
|
+
else
|
268
|
+
#$stderr.puts "ERROR: The following arguments are required for a record change operation."
|
269
|
+
#$stderr.puts "ERROR: --zone and at least one of --name, --type, --ttl or --values"
|
270
|
+
#exit 1
|
271
|
+
required_options("record change",["--zone"],["--name","--type","--ttl","--values"])
|
205
272
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def required_options(operation,required = [],at_least_one = [],optional = [])
|
276
|
+
operation == "" ? operation += " " : operation = " "+operation+" "
|
277
|
+
$stderr.puts "ERROR: The following arguments are required for a#{operation}operation."
|
278
|
+
$stderr.puts "ERROR: #{required.join(", ")} #{ (required.size > 1 ? "are" : "is") } required." if required.size > 0
|
279
|
+
$stderr.puts "ERROR: At least one of #{at_least_one.join(", ")} are required." if at_least_one.size > 0
|
280
|
+
$stderr.puts "ERROR: #{optional.join(", ")}are optional." if optional.size > 0
|
281
|
+
exit 1
|
219
282
|
end
|
220
283
|
|
221
284
|
def setup
|
@@ -226,10 +289,12 @@ module Route53
|
|
226
289
|
new_config['secret_key'] = get_input(String,"Amazon Secret Key")
|
227
290
|
new_config['api'] = get_input(String,"Amazon Route 53 API Version","2010-10-01")
|
228
291
|
new_config['endpoint'] = get_input(String,"Amazon Route 53 Endpoint","https://route53.amazonaws.com/")
|
292
|
+
new_config['default_ttl'] = get_input(String,"Default TTL","3600")
|
229
293
|
if get_input(true.class,"Save the configuration file to \"~/.route53\"?","Y")
|
230
294
|
File.open(@options.file,'w') do |out|
|
231
295
|
YAML.dump(new_config,out)
|
232
296
|
end
|
297
|
+
File.chmod(0600,@options.file)
|
233
298
|
load_config
|
234
299
|
else
|
235
300
|
puts "Not Saving File. Dumping Config instead."
|
@@ -271,7 +336,6 @@ module Route53
|
|
271
336
|
exit 1
|
272
337
|
end
|
273
338
|
selection = selection.to_i
|
274
|
-
puts "Received #{selection}"
|
275
339
|
if selection == records.size && allowall
|
276
340
|
return records
|
277
341
|
elsif selection == records.size + 1
|
@@ -287,6 +351,8 @@ module Route53
|
|
287
351
|
def pending_wait(resp)
|
288
352
|
while !@options.nowait && resp.pending?
|
289
353
|
print '.'
|
354
|
+
|
355
|
+
STDOUT.flush
|
290
356
|
sleep 1
|
291
357
|
end
|
292
358
|
end
|