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/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
- resp = request("#{@base_url}/hostedzone")
56
- if resp.error?
57
- return nil
58
- end
59
- zone_list = Hpricot::XML(resp.raw_data)
55
+ truncated = true
56
+ query = []
60
57
  zones = []
61
- elements = zone_list.search("HostedZone")
62
- elements.each do |e|
63
- zones.push(Zone.new(e.search("Name").first.innerText,
64
- e.search("Id").first.innerText,
65
- self))
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 if zone_select.size == 1
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
- records.each do |record|
153
- #puts "Name:"+record.search("Name").first.innerText if @conn.verbose
154
- #puts "Type:"+record.search("Type").first.innerText if @conn.verbose
155
- #puts "TTL:"+record.search("TTL").first.innerText if @conn.verbose
156
- record.search("Value").each do |val|
157
- #puts "Val:"+val.innerText if @conn.verbose
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 "An Error has occured"
212
- $stderr.puts @raw_data
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; @options.list = true }
53
- opts.on('-n', '--new [ZONE]', String, "Create a new Zone") { |zone| @options.zone = zone; @options.new_zone = true }
54
- opts.on('-d', '--delete [ZONE]', String, "Delete a Zone") { |zone| @options.zone = zone; @options.delete_zone = true }
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 #if @arguments.length == 1
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
- if @options.delete_zone
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
- if @options.create_record
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
- if @options.remove_record
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
- if @options.change_record
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
- if @options.list || @options.zone.nil?
208
- zones = conn.get_zones(@options.zone)
209
- zones.each do |z|
210
- puts z
211
- if @options.zone
212
- records = z.get_records(@options.dnstype.nil? ? "ANY" : @options.dnstype)
213
- records.each do |r|
214
- puts r
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