Permalink
Browse files

add setup of arbitrary dns records via nettica or zerigo apis

  • Loading branch information...
1 parent d984451 commit 61ca87e4a79d584a2e20b072f98856e259ddea1e Matt Conway committed Oct 6, 2009
View
79 generators/vulcanize/templates/base/config/rubber/rubber-dns.yml
@@ -0,0 +1,79 @@
+# OPTIONAL: The dns provider to use. Need to exist in dns_providers below
+# dns_provider: nettica
+
+# OPTIONAL: The configuration for each dns provider (nettica|zerigo|dyndns)
+# This lets rubber update a dynamic dns service with the instance alias and ip
+#
+dns_providers:
+ nettica:
+ user: joe
+ password: sekret
+ type: A
+ ttl: 300
+ zerigo:
+ customer_id: 1234
+ email: foo@bar.com
+ token: hexxy
+ type: A
+ ttl: 300
+ dyndns:
+ user: joe
+ password: sekret
+ update_url: https://members.dyndns.org/nic/update?hostname=%host%&myip=%ip%'
+
+# OPTIONAL: Lets you configure your dns service, for example to add other CNAMES
+# or setup dns round robin, etc. Run "cap rubber:setup_dns_records"
+# to apply them as rubber only sets up instance aliases as part of
+# the standard lifecycle
+#
+# dns_records:
+# # simple A record
+# - host: bar
+# data: 1.1.1.1
+#
+# # more detailed A record
+# - host: bar
+# domain: otherdomain.com
+# data: 1.1.1.1
+# type: A
+# ttl: 300
+#
+# # tld A record
+# - host: ''
+# data: 1.1.1.1
+# type: A
+#
+# # simple CNAME record
+# - host: otherbar
+# domain: foo.com
+# data: bar.foo.com
+# type: CNAME
+# ttl: 300
+#
+# # 2 of the same A records is a round robin dns
+# - host: rr
+# domain: foo.com
+# data: 1.1.1.1
+# type: A
+# ttl: 300
+# - host: rr
+# domain: foo.com
+# data: 1.1.1.2
+# type: A
+# ttl: 300
+#
+# # A record, grabbing ip from instance config
+# - host: baz
+# domain: foo.com
+# data: "#{rubber_instances.for_role('web').first.external_ip}"
+# type: A
+# ttl: 300
+#
+# # MX record
+# - host: ''
+# domain: foo.com
+# data: mail.foo.com
+# type: MX
+# ttl: 300
+# priority: 10
+
View
81 generators/vulcanize/templates/base/config/rubber/rubber.yml
@@ -32,83 +32,10 @@ timezone: US/Eastern
#
domain: foo.com
-# OPTIONAL: The configuration for each dns provider (nettica|zerigo|dyndns)
-# This lets rubber updatea dynamic dns service with the instance alias and ip
-#
-dns_providers:
- nettica:
- user: joe
- password: sekret
- type: A
- ttl: 300
- zerigo:
- customer_id: 1234
- email: foo@bar.com
- token: hexxy
- type: A
- ttl: 300
- dyndns:
- user: joe
- password: sekret
- update_url: https://members.dyndns.org/nic/update?hostname=%host%&myip=%ip%'
-
-# OPTIONAL: The dns provider to use
-# dns_provider: nettica
-
-# OPTIONAL: Lets you configure your dns service, for example to add other CNAMES
-# or setup dns round robin, etc.
-#
-# dns_records:
-# # simple A record
-# - host: bar
-# data: 1.1.1.1
-#
-# # more detailed A record
-# - host: bar
-# domain: otherdomain.com
-# data: 1.1.1.1
-# type: A
-# ttl: 300
-#
-# # tld A record
-# - host: ''
-# data: 1.1.1.1
-# type: A
-#
-# # simple CNAME record
-# - host: otherbar
-# domain: foo.com
-# data: bar.foo.com
-# type: CNAME
-# ttl: 300
-#
-# # 2 of the same A records is a round robin dns
-# - host: rr
-# domain: foo.com
-# data: 1.1.1.1
-# type: A
-# ttl: 300
-# - host: rr
-# domain: foo.com
-# data: 1.1.1.2
-# type: A
-# ttl: 300
-#
-# # A record, grabbing ip from instance config
-# - host: baz
-# domain: foo.com
-# data: "#{rubber_instances.for_role('web').first.external_ip}"
-# type: A
-# ttl: 300
-#
-# # MX record
-# - host: mail
-# domain: foo.com
-# data: 1.1.1.1
-# type: MX
-# ttl: 300
-# priority: 10
-
+# OPTIONAL: See rubber-dns.yml for dns configuration
+# This lets rubber update a dynamic dns service with the instance alias
+# and ip when they are created. It also allows setting up arbitrary
+# dns records (CNAME, MX, Round Robin DNS, etc)
# OPTIONAL: Additional rubber file to pull config from if it exists. This file will
# also be pushed to remote host at RUBBER_ROOT/config/rubber/rubber-secret.yml
View
24 lib/rubber/dns/base.rb
@@ -52,16 +52,28 @@ def destroy_host_record(opts = {})
raise "destroy_host_record not implemented"
end
- protected
+ def host_records_equal?(lhs_opts = {}, rhs_opts = {})
+ lhs = setup_opts(lhs_opts)
+ rhs = setup_opts(rhs_opts)
+ [lhs, rhs].each {|h| h.delete(:id); h.delete(:priority) if h[:priority] == 0}
+ lhs == rhs
+ end
+
+ def setup_opts(opts, required=[])
+ default_opts = {:domain => @env.domain,
+ :type => @provider_env['type'] || @provider_env.record_type || 'A',
+ :ttl => @provider_env.ttl || 300}
+
+ if opts.delete(:no_defaults)
+ actual_opts = Rubber::Util::symbolize_keys(opts)
+ else
+ actual_opts = default_opts.merge(Rubber::Util::symbolize_keys(opts))
+ end
- def setup_opts(opts, required =[])
- default_opts = {:domain => @provider_env.domain,
- :type => @provider_env['type'] || @provider_env.record_type,
- :ttl => @provider_env.ttl}
- actual_opts = default_opts.merge(Rubber::Util::symbolize_keys(opts))
required.each do |r|
raise "Missing required options: #{r}" unless actual_opts[r]
end
+
return actual_opts
end
View
25 lib/rubber/dns/nettica.rb
@@ -34,8 +34,7 @@ def find_host_records(opts = {})
ht = opts[:type]
hd = opts[:data]
- domain_info = check_status @client.list_domain(opts[:domain])
- raise "Domain needs to exist in nettica before records can be updated" unless domain_info.record
+ domain_info = find_or_create_zone(opts[:domain])
domain_info.record.each do |h|
keep = true
@@ -56,6 +55,7 @@ def find_host_records(opts = {})
def create_host_record(opts = {})
opts = setup_opts(opts, [:host, :data, :domain, :type, :ttl])
+ find_or_create_zone(opts[:domain])
record = opts_to_record(opts)
check_status @client.add_record(record)
end
@@ -69,6 +69,7 @@ def destroy_host_record(opts = {})
def update_host_record(old_opts = {}, new_opts = {})
old_opts = setup_opts(old_opts, [:host, :domain])
+ new_opts = setup_opts(new_opts.merge(:no_defaults =>true), [])
find_host_records(old_opts).each do |h|
old_record = opts_to_record(h)
new_record = opts_to_record(h.merge(new_opts))
@@ -78,6 +79,18 @@ def update_host_record(old_opts = {}, new_opts = {})
private
+ def find_or_create_zone(domain)
+ domain_info = @client.list_domain(domain)
+ if domain_info.record
+ check_status domain_info
+ else
+ check_status @client.create_zone(domain)
+ domain_info = check_status @client.list_domain(domain)
+ raise "Could not create zone in nettica: #{domain}" unless domain_info.record
+ end
+ return domain_info
+ end
+
def opts_to_record(opts)
record = @client.create_domain_record(opts[:domain],
opts[:host],
@@ -90,12 +103,12 @@ def opts_to_record(opts)
def record_to_opts(record)
opts = {}
- opts[:host] = record.hostName
+ opts[:host] = record.hostName || ''
opts[:domain] = record.domainName
opts[:type] = record.recordType
- opts[:data] = record.data
- opts[:ttl] = record.tTL
- opts[:priority] = record.priority
+ opts[:data] = record.data if record.data
+ opts[:ttl] = record.tTL if record.tTL
+ opts[:priority] = record.priority if record.priority
return opts
end
end
View
19 lib/rubber/dns/zerigo.rb
@@ -88,7 +88,8 @@ def refresh
if ! @zone
zone = new_zone()
zone['domain'] = @domain
- @zone = check_status self.class.post('/zones.xml', :body => {:zone => zone})
+ zones = check_status self.class.post('/zones.xml', :body => {:zone => zone})
+ @zone = zones['zone']
end
end
@@ -109,20 +110,21 @@ def new_zone
def opts_to_host(opts, host={})
host['hostname'] = opts[:host]
host['host_type'] = opts[:type]
- host['data'] = opts[:data]
- host['ttl'] = opts[:ttl]
- host['priority'] = opts[:priority]
+ host['data'] = opts[:data] if opts[:data]
+ host['ttl'] = opts[:ttl] if opts[:ttl]
+ host['priority'] = opts[:priority] if opts[:priority]
return host
end
def host_to_opts(host)
opts = {}
opts[:id] = host['id']
- opts[:host] = host['hostname']
+ opts[:domain] = @domain
+ opts[:host] = host['hostname'] || ''
opts[:type] = host['host_type']
- opts[:data] = host['data']
- opts[:ttl] = host['ttl']
- opts[:priority] = host['priority']
+ opts[:data] = host['data'] if host['data']
+ opts[:ttl] = host['ttl'] if host['ttl']
+ opts[:priority] = host['priority'] if host['priority']
return opts
end
end
@@ -158,6 +160,7 @@ def destroy_host_record(opts = {})
def update_host_record(old_opts={}, new_opts={})
old_opts = setup_opts(old_opts, [:host, :domain])
+ new_opts = setup_opts(new_opts.merge(:no_defaults =>true), [])
zone = Zone.get_zone(old_opts[:domain], provider_env)
find_host_records(old_opts).each do |h|
View
77 lib/rubber/recipes/rubber/setup.rb
@@ -62,6 +62,83 @@
end
desc <<-DESC
+ Sets up the additional dns records supplied in the dns_records config in rubber.yml
+ DESC
+ required_task :setup_dns_records do
+ records = rubber_env.dns_records
+ if records && rubber_env.dns_provider
+ provider = Rubber::Dns::get_provider(rubber_env.dns_provider, rubber_env)
+
+ # collect the round robin records (those with the same host/domain/type)
+ rr_records = []
+ records.each_with_index do |record, i|
+ m = records.find_all {|r| record['host'] == r['host'] && record['domain'] == r['domain'] && record['type'] == r['type']}
+ m = m.sort {|a,b| a.object_id <=> b.object_id}
+ rr_records << m if m.size > 1 && ! rr_records.include?(m)
+ end
+
+ # simple records are those that aren't round robin ones
+ simple_records = records - rr_records.flatten
+
+ # for each simple record, create or update as necessary
+ simple_records.each do |record|
+ matching = provider.find_host_records(:host => record['host'], :domain =>record['domain'], :type => record['type'])
+ if matching.size > 1
+ msg = "Multiple records in dns provider, but not in rubber.yml\n"
+ msg << "Round robin records need to be in both, or neither.\n"
+ msg << "Please fix manually:\n"
+ msg << matching.pretty_inspect
+ fatal(msg)
+ end
+
+ record = provider.setup_opts(record)
+ if matching.size == 1
+ match = matching.first
+ if provider.host_records_equal?(record, match)
+ logger.info "Simple dns record already up to date: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}"
+ else
+ logger.info "Updating simple dns record: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}"
+ provider.update_host_record(match, record)
+ end
+ else
+ logger.info "Creating simple dns record: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}"
+ provider.create_host_record(record)
+ end
+ end
+
+ # group round robin records
+ rr_records.each do |rr_group|
+ host = rr_group.first['host']
+ domain = rr_group.first['domain']
+ type = rr_group.first['type']
+ matching = provider.find_host_records(:host => host, :domain => domain, :type => type)
+
+ # remove from consideration the local records that are the same as remote ones
+ matching.clone.each do |r|
+ rr_group.delete_if {|rg| provider.host_records_equal?(r, rg) }
+ matching.delete_if {|rg| provider.host_records_equal?(r, rg) }
+ end
+ if rr_group.size == 0 && matching.size == 0
+ logger.info "Round robin dns records already up to date: #{host}.#{domain}:#{type}"
+ end
+
+ # create the local records that don't exist remotely
+ rr_group.each do |r|
+ r = provider.setup_opts(r)
+ logger.info "Creating round robin dns record: #{r[:host]}.#{r[:domain]}:#{r[:type]} => #{r[:data]}"
+ provider.create_host_record(r)
+ end
+
+ # remove the remote records that don't exist locally
+ matching.each do |r|
+ logger.info "Removing round robin dns record: #{r[:host]}.#{r[:domain]}:#{r[:type]} => #{r[:data]}"
+ provider.destroy_host_record(r)
+ end
+ end
+ end
+ end
+
+ desc <<-DESC
Sets up aliases for instance hostnames based on contents of instance.yml.
Generates /etc/hosts for remote machines and sets hostname on remote instances
DESC

0 comments on commit 61ca87e

Please sign in to comment.