module Fog
module DNS
class AWS
class Real
require 'fog/aws/parsers/dns/change_resource_record_sets'
# Use this action to create or change your authoritative DNS information for a zone
# http://docs.amazonwebservices.com/Route53/latest/DeveloperGuide/RRSchanges.html#RRSchanges_API
#
# ==== Parameters
# * zone_id<~String> - ID of the zone these changes apply to
# * options<~Hash>
# * comment<~String> - Any comments you want to include about the change.
# * change_batch<~Array> - The information for a change request
# * changes<~Hash> -
# * action<~String> - 'CREATE' or 'DELETE'
# * name<~String> - This must be a fully-specified name, ending with a final period
# * type<~String> - A | AAAA | CNAME | MX | NS | PTR | SOA | SPF | SRV | TXT
# * ttl<~Integer> - Time-to-live value - omit if using an alias record
# * resource_records<~Array> - Omit if using an alias record
# * alias_target<~Hash> - Information about the domain to which you are redirecting traffic (Alias record sets only)
# * dns_name<~String> - The Elastic Load Balancing domain to which you want to reroute traffic
# * hosted_zone_id<~String> - The ID of the hosted zone that contains the Elastic Load Balancing domain to which you want to reroute traffic
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'ChangeInfo'<~Hash>
# * 'Id'<~String> - The ID of the request
# * 'Status'<~String> - status of the request - PENDING | INSYNC
# * 'SubmittedAt'<~String> - The date and time the change was made
# * status<~Integer> - 200 when successful
#
# ==== Examples
#
# Example changing a CNAME record:
#
# change_batch_options = [
# {
# :action => "DELETE",
# :name => "foo.example.com.",
# :type => "CNAME",
# :ttl => 3600,
# :resource_records => [ "baz.example.com." ]
# },
# {
# :action => "CREATE",
# :name => "foo.example.com.",
# :type => "CNAME",
# :ttl => 3600,
# :resource_records => [ "bar.example.com." ]
# }
# ]
#
# change_resource_record_sets("ABCDEFGHIJKLMN", change_batch_options)
#
def change_resource_record_sets(zone_id, change_batch, options = {})
# AWS methods return zone_ids that looks like '/hostedzone/id'. Let the caller either use
# that form or just the actual id (which is what this request needs)
zone_id = zone_id.sub('/hostedzone/', '')
optional_tags = ''
options.each do |option, value|
case option
when :comment
optional_tags += "#{value}"
end
end
#build XML
if change_batch.count > 0
changes = "#{optional_tags}"
change_batch.each do |change_item|
action_tag = %Q{#{change_item[:action]}}
name_tag = %Q{#{change_item[:name]}}
type_tag = %Q{#{change_item[:type]}}
# TTL must be omitted if using an alias record
ttl_tag = ''
ttl_tag += %Q{#{change_item[:ttl]}} unless change_item[:alias_target]
weight_tag = ''
set_identifier_tag = ''
region_tag = ''
if change_item[:set_identifier]
set_identifier_tag += %Q{#{change_item[:set_identifier]}}
if change_item[:weight] # Weighted Record
weight_tag += %Q{#{change_item[:weight]}}
elsif change_item[:region] # Latency record
region_tag += %Q{#{change_item[:region]}}
end
end
resource_records = change_item[:resource_records] || []
resource_record_tags = ''
resource_records.each do |record|
resource_record_tags += %Q{#{record}}
end
# ResourceRecords must be omitted if using an alias record
resource_tag = ''
resource_tag += %Q{#{resource_record_tags}} if resource_records.any?
alias_target_tag = ''
if change_item[:alias_target]
# Accept either underscore or camel case for hash keys.
dns_name = change_item[:alias_target][:dns_name] || change_item[:alias_target][:DNSName]
hosted_zone_id = change_item[:alias_target][:hosted_zone_id] || change_item[:alias_target][:HostedZoneId] || AWS.hosted_zone_for_alias_target(dns_name)
alias_target_tag += %Q{#{hosted_zone_id}#{dns_name}}
end
change_tags = %Q{#{action_tag}#{name_tag}#{type_tag}#{set_identifier_tag}#{weight_tag}#{region_tag}#{ttl_tag}#{resource_tag}#{alias_target_tag}}
changes += change_tags
end
changes += ''
end
body = %Q{#{changes}}
request({
:body => body,
:parser => Fog::Parsers::DNS::AWS::ChangeResourceRecordSets.new,
:expects => 200,
:method => 'POST',
:path => "hostedzone/#{zone_id}/rrset"
})
end
end
class Mock
def change_resource_record_sets(zone_id, change_batch, options = {})
response = Excon::Response.new
errors = []
if (zone = self.data[:zones][zone_id])
response.status = 200
change_batch.each do |change|
case change[:action]
when "CREATE"
if zone[:records][change[:type]].nil?
zone[:records][change[:type]] = {}
end
if zone[:records][change[:type]][change[:name]].nil?
zone[:records][change[:type]][change[:name]] = {
:name => change[:name],
:type => change[:type],
:ttl => change[:ttl],
:resource_records => change[:resource_records]
}
else
errors << "Tried to create resource record set #{change[:name]}. type #{change[:type]}, but it already exists"
end
when "DELETE"
if zone[:records][change[:type]].nil? || zone[:records][change[:type]].delete(change[:name]).nil?
errors << "Tried to delete resource record set #{change[:name]}. type #{change[:type]}, but it was not found"
end
end
end
if errors.empty?
response.body = {
'ChangeInfo' => {
'Id' => "/change/#{Fog::AWS::Mock.change_id}",
'Status' => 'INSYNC',
'SubmittedAt' => Time.now.utc.iso8601
}
}
response
else
response.status = 400
response.body = "#{errors.map {|e| "#{e}"}.join()}"
raise(Excon::Errors.status_error({:expects => 200}, response))
end
else
response.status = 404
response.body = "NoSuchHostedZone
A hosted zone with the specified hosted zone ID does not exist.#{Fog::AWS::Mock.request_id}"
raise(Excon::Errors.status_error({:expects => 200}, response))
end
end
end
def self.hosted_zone_for_alias_target(dns_name)
k = elb_hosted_zone_mapping.keys.find do |k|
dns_name =~ /\A.+\.#{k}\.elb\.amazonaws\.com\.?\z/
end
elb_hosted_zone_mapping[k]
end
def self.elb_hosted_zone_mapping
@elb_hosted_zone_mapping ||= {
"ap-northeast-1" => "Z2YN17T5R711GT",
"ap-southeast-1" => "Z1WI8VXHPB1R38",
"ap-southeast-2" => "Z2999QAZ9SRTIC",
"eu-west-1" => "Z3NF1Z3NOM5OY2",
"sa-east-1" => "Z2ES78Y61JGQKS",
"us-east-1" => "Z3DZXE0Q79N41H",
"us-west-1" => "Z1M58G0W56PQJA",
"us-west-2" => "Z33MTJ483KN6FU",
}
end
end
end
end