[aws|dns] 1st pass at Route 53 support. All functions added but not tested

have added each of the Route 53 functions
list_hosted_zones is working.  rest still need to be tested

[aws|linode|slicehost|zerigo|dns] added complete test cases for linode & slicehost DNS.  Also added initial support for AWS Route 53
This commit is contained in:
Athir Nuaimi 2010-12-14 00:04:23 -05:00
parent f18cdbdf62
commit eb523fb80a
27 changed files with 1809 additions and 68 deletions

View File

@ -1,85 +1,279 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'fog'
require File.join(File.dirname(File.expand_path(__FILE__)), '..', 'lib', 'fog')
require 'fog/core/bin'
LINODE_API_KEY = '--put-your-key-here--'
SLICEHOST_PASSWORD = '--put-your-key-here--'
Fog.bin = true
#mark true for each cloud you wish to enable/run the sample for
RUN_AWS_SAMPLE = true
RUN_LINODE_SAMPLE = true
RUN_SLICEHOST_SAMPLE = false
RUN_ZERIGO_SAMPLE = false
#sample data
TEST_DOMAIN='sample53-domain.com'
# example of how to use AWS Route 53 DNS calls
def show_aws_dns_usage
begin
aws = AWS[:dns].list_hosted_zones
debugger
response = AWS[:dns].create_hosted_zone( TEST_DOMAIN)
if response.status == 201
zone_id = response.body['HostedZone']['Id']
aws.delete_hosted_zone( zone_id)
(response.status == 200) ? true : false
end
#get a list of the zones AWS is hosting for this account
options= { :max_items => 50 }
response= AWS[:dns].list_hosted_zones( options)
if response.status == 200
domain_count = response.body['HostedZones'].count
puts "you have #{domain_count} zones hosted at AWS Route 53"
end
# debugger
#delete all the zones
zones= response.body['HostedZones']
zones.each { |zone|
zone_id = zone['Id']
puts "starting process of deleting zone #{zone_id}"
#get the list of record sets
options = { :max_items => 50 }
response = AWS[:dns].list_resource_record_sets( zone_id, options)
if response.status == 200
records= response.body['ResourceRecordSets']
count= records.count
if count > 0
puts "#{count} resource records to delete"
#need to build a change batch
change_batch = []
records.each { |record|
#can't delete NS or SOA that AWS added so skip
if record[:type] == 'NS' or record[:type] == 'SOA'
continue
else
#add to batch of records to delete
resource_record_set = { :action => 'DELETE', :name => record['Name'], :type => record['Type'],
:ttl => record['TTL'], :resource_records => record['ResourceRecords'] }
change_batch << resource_record_set
end
}
if change_batch.count > 0
response = AWS[:dns].change_resource_record_sets( zone_id, change_batch)
debugger
if response.status == 200
#zone should now be in a state where it can be deleted
end
end
end
end
#delete the records
response = AWS[:dns].delete_hosted_zone( zone_id)
if response.status == 200
change_id = response.body['ChangeInfo']['Id']
puts "request delete zone #{zone_id} is in progress and has change ID #{change_id}"
end
}
# debugger
#add a zone - note required period at end of domain
options= { :comment => 'for client ABC' }
response= AWS[:dns].create_hosted_zone( 'sample-domain.com.' )
if response.status == 201
zone_id = response.body['HostedZone']['Id']
change_id = response.body['ChangeInfo']['Id']
status = response.body['ChangeInfo']['Status']
end
# debugger
#wait until zone ready
while status == 'PENDING'
sleep 2
response = AWS[:dns].get_change( change_id)
if response.status == 200
change_id = response.body['Id']
status = response.body['Status']
end
puts "your changes are #{status}"
end
# debugger
response = AWS[:dns].get_hosted_zone( zone_id)
if response.status == 200
name_servers = response.body['NameServers']
end
debugger
#add resource records to zone
change_batch = []
resource_record_set = { :action => 'CREATE', :name => 'www.sample-domain.com.', :type => 'A',
:ttl => 3600, :resource_records => ['1.2.3.4'] }
change_batch << resource_record_set
=begin
resource_record_set = { :action => 'CREATE', :name => 'mail.sample-domain.com.', :type => 'CNAME',
:ttl => 3600, :resource_records => ['www.sample-domain.com'] }
change_batch << resource_record_set
resource_record_set = { :action => 'CREATE', :name => 'sample-domain.com.', :type => 'MX',
:ttl => 3600, :resource_records => ['10 mail.sample-domain.com'] }
change_batch << resource_record
=end
options = { :comment => 'migrate records from BIND'}
response = AWS[:dns].change_resource_record_sets( zone_id, change_batch, options)
if response.status == 200
change_id = response.body['Id']
status = response.body['Status']
end
debugger
#wait until zone ready
while status == 'PENDING'
sleep 2
response = AWS[:dns].get_change( change_id)
if response.status == 200
change_id = response.body['Id']
status = response.body['Id']
end
puts "your changes are #{status}"
end
#get a list of the zones AWS is hosting for this account
options= { :max_items => 5 }
response= AWS[:dns].list_hosted_zones( )
if response.status == 200
end
response = AWS[:dns].get_hosted_zone()
if response.status == 200
end
response = AWS[:dns].list_resource_record_sets
if response.status == 200
end
#delete the resource records
response = AWS[:dns].change_resource_record_sets( zone_id, change_batch, options)
if response.status == 200
change_id = response.body['ChangeInfo']['Id']
end
#wait?
#delete the zone
response = AWS[:dns].delete_hosted_zone( zone_id)
if response.status == 200
change_id = response.body['ChangeInfo']['Id']
end
rescue
#opps, ran into a problem
puts $!.message
return false
end
true
end
# example of how to use Linode DNS calls
def show_linode_dns_usage( api_key)
def show_linode_dns_usage
if api_key == '--put-your-key-here--'
return false
end
begin
#connect to Linode
options = { :linode_api_key => api_key }
cloud= Fog::Linode::Compute.new( options)
#create a zone for a domain
domain = 'sample-domain.com'
type = 'master'
options = { :SOA_email => 'netops@sample-domain.com', :description => "Sample-Domain Inc", :status => 0}
response = cloud.domain_create( domain, type, options)
response = Linode[:compute].domain_create( TEST_DOMAIN, type, options)
if response.status == 200
master_zone_id = response.body['DATA']['DomainID']
puts "created zone for #{TEST_DOMAIN} - ID: #{master_zone_id}"
end
#create a slave zone
domain = 'sample-slave-domain.com'
type = 'slave'
options = { :master_ips => '1.2.3.4; 1.2.3.5'}
response = cloud.domain_create( domain, type, options)
response = Linode[:compute].domain_create( domain, type, options)
if response.status == 200
slave_zone_id = response.body['DATA']['DomainID']
puts "created slave zone for #{domain} - ID: #{slave_zone_id}"
end
#get a list of zones Linode hosted for account
response = cloud.domain_list()
response = Linode[:compute].domain_list()
if response.status == 200
num_zones = response.body['DATA'].count
puts "Linode is hosting #{num_zones} DNS zones for this account"
zones = response.body['DATA']
num_zones = zones.count
puts "Linode is hosting #{num_zones} DNS zones for this account:"
zones.each { |zone|
puts " #{zone['DOMAIN']}\n"
}
end
#add an A and a MX record
options = { :name => 'www.sample-domain.com', :target => '4.5.6.7', :ttl_sec => 7200 }
response = cloud.domain_resource_create( master_zone_id, 'A', options)
domain= 'www.' + TEST_DOMAIN
options = { :name => domain, :target => '4.5.6.7', :ttl_sec => 7200 }
response = Linode[:compute].domain_resource_create( master_zone_id, 'A', options)
if response.status == 200
resource_id = response.body['DATA']['ResourceID']
puts "added an A record for #{domain} - ID: #{resource_id}"
end
options = { :target => 'mail.sample-domain.com', :priority => 1 }
response = cloud.domain_resource_create( master_zone_id, 'MX', options)
domain= 'mail.' + TEST_DOMAIN
options = { :target => domain, :priority => 1 }
response = Linode[:compute].domain_resource_create( master_zone_id, 'MX', options)
if response.status == 200
resource_id = response.body['DATA']['ResourceID']
puts "added a MX record for #{TEST_DOMAIN} - ID: #{resource_id}"
end
#change MX to have a lower priority
options = { :priority => 5 }
response = cloud.domain_resource_update( master_zone_id, resource_id, options)
response = Linode[:compute].domain_resource_update( master_zone_id, resource_id, options)
if response.status == 200
resource_id = response.body['DATA']['ResourceID']
puts "updated MX record for #{TEST_DOMAIN} with new priority - ID: #{resource_id}"
end
#get the list of resource records for the domain
response = cloud.domain_resource_list( master_zone_id)
response = Linode[:compute].domain_resource_list( master_zone_id)
if response.status == 200
num_records = response.body['DATA'].count
puts "Domain has #{num_records} records for this domain"
puts "#{TEST_DOMAIN} zone has #{num_records} resource records"
end
#finally cleanup by deleting the zone we created
response = cloud.domain_delete( master_zone_id)
response = Linode[:compute].domain_delete( master_zone_id)
if response.status == 200
puts "master zone deleted"
puts "deleted #{TEST_DOMAIN} zone"
end
response = cloud.domain_delete( slave_zone_id)
response = Linode[:compute].domain_delete( slave_zone_id)
if response.status == 200
puts "slave zone deleted"
puts "deleted slave zone"
end
rescue
@ -91,58 +285,57 @@ def show_linode_dns_usage( api_key)
true
end
# example of how to use Slicehost DNS calls
def show_slicehost_dns_usage( password)
#check if we have a value api key for this cloud
if password == '--put-your-key-here--'
return false
end
# example of how to use Slicehost DNS calls
def show_slicehost_dns_usage
begin
#connect to Slicehost
options = { :slicehost_password => password }
cloud= Fog::Slicehost::Compute.new( options)
#create a zone for a domain
zone_id = 0
options = { :ttl => 1800, :active => 'N' }
response= cloud.create_zone( "sample-domain.com", options)
if response.status == 200
response= Slicehost[:compute].create_zone( TEST_DOMAIN, options)
if response.status == 201
zone_id= response.body['id']
puts "created zone for #{TEST_DOMAIN} - ID: #{zone_id}"
end
#add an A record for website
record_id = 0
options = { :ttl => 3600, :active => 'N' }
response= cloud.create_record( 'A', 159631, "www.sample-domain.com", "1.2.3.4", options)
if response.status == 200
host = "www.#{TEST_DOMAIN}"
response= Slicehost[:compute].create_record( 'A', zone_id, host, "1.2.3.4", options)
if response.status == 201
record_id= response.body['id']
puts "created 'A' record for #{host} - ID: #{record_id}"
end
#get a list of zones Slicehost hosted for account
response = cloud.get_zones()
response = Slicehost[:compute].get_zones()
if response.status == 200
num_zones= response.body['zones'].count
zones = response.body['zones']
num_zones= zones.count
puts "Slicehost is hosting #{num_zones} DNS zones for this account"
zones.each { |zone|
puts " #{zone['origin']}\n"
}
end
#now get details on www record for the zone
if record_id > 0
response = cloud.get_record( record_id)
response = Slicehost[:compute].get_record( record_id)
if response.status == 200
record = response.body['records'][0]
name = record['name']
type = record['record-type']
puts "record is an #{type} record for #{name} domain"
puts "got details on record #{record_id} -is an #{type} record for #{name} domain"
end
end
#finally cleanup by deleting the zone we created
if zoned_id > 0
response = cloud.delete_zone( zone_id)
if zone_id > 0
response = Slicehost[:compute].delete_zone( zone_id)
if response.status == 200
puts "sample-domain.com removed from Slicehost DNS"
puts "#{TEST_DOMAIN} removed from Slicehost DNS"
end
end
@ -155,10 +348,27 @@ def show_slicehost_dns_usage( password)
true
end
######################################
# note, if you have not added your key for a given provider, the related function will do nothing
# make sure Fog credentials file has been setup
# (will throw exception if not)
Fog::credentials
show_linode_dns_usage( LINODE_API_KEY)
show_slicehost_dns_usage( SLICEHOST_PASSWORD)
# AWS Route 53
if RUN_AWS_SAMPLE and Fog::credentials[:aws_access_key_id] and Fog::credentials[:aws_secret_access_key]
show_aws_dns_usage
end
# Linode
if RUN_LINODE_SAMPLE and Fog::credentials[:linode_api_key]
show_linode_dns_usage
end
# Slicehost
if RUN_SLICEHOST_SAMPLE and Fog::credentials[:slicehost_password]
show_slicehost_dns_usage
end
# if RUN_ZERIGO_SAMPLE and Fog::credentials[:zerigo_user] and Fog::credentials[:zerigo_password]
# show_zerigo_dns_usage
# end

View File

@ -9,6 +9,7 @@ module Fog
service_path 'fog/aws'
service 'cdn'
service 'compute'
service 'dns'
service 'ec2'
service 'elb'
service 'iam'

View File

@ -7,6 +7,8 @@ class AWS < Fog::Bin
Fog::AWS::CDN
when :compute, :ec2
Fog::AWS::Compute
when :dns
Fog::AWS::DNS
when :elb
Fog::AWS::ELB
when :iam
@ -46,7 +48,7 @@ class AWS < Fog::Bin
end
def services
[:cdn, :compute, :elb, :iam, :sdb, :storage]
[:cdn, :compute, :dns, :elb, :iam, :sdb, :storage]
end
end

101
lib/fog/aws/dns.rb Normal file
View File

@ -0,0 +1,101 @@
module Fog
module AWS
class DNS < Fog::Service
requires :aws_access_key_id, :aws_secret_access_key, &inject_parameter_specs
recognizes :host, :path, :port, :scheme, :version, :persistent, &inject_parameter_specs
# model_path 'fog/aws/models/cdn'
request_path 'fog/aws/requests/dns'
request :create_hosted_zone
request :get_hosted_zone
request :delete_hosted_zone
request :list_hosted_zones
request :change_resource_record_sets
request :list_resource_record_sets
request :get_change
class Mock
def self.data
@data ||= Hash.new do |hash, region|
hash[region] = Hash.new do |region_hash, key|
region_hash[key] = {
:buckets => {}
}
end
end
end
def self.reset_data(keys=data.keys)
for key in [*keys]
data.delete(key)
end
end
def initialize(options={})
require 'mime/types'
@aws_access_key_id = options[:aws_access_key_id]
@data = self.class.data[options[:region]][@aws_access_key_id]
end
def signature(params)
"foo"
end
end
class Real
# Initialize connection to Route 53 DNS service
#
# ==== Notes
# options parameter must include values for :aws_access_key_id and
# :aws_secret_access_key in order to create a connection
#
# ==== Examples
# dns = Fog::AWS::DNS.new(
# :aws_access_key_id => your_aws_access_key_id,
# :aws_secret_access_key => your_aws_secret_access_key
# )
#
# ==== Parameters
# * options<~Hash> - config arguments for connection. Defaults to {}.
#
# ==== Returns
# * dns object with connection to aws.
def initialize(options={})
@aws_access_key_id = options[:aws_access_key_id]
@aws_secret_access_key = options[:aws_secret_access_key]
@hmac = Fog::HMAC.new('sha1', @aws_secret_access_key)
@host = options[:host] || 'route53.amazonaws.com'
@path = options[:path] || '/'
@port = options[:port] || 443
@scheme = options[:scheme] || 'https'
@version = options[:version] || '2010-10-01'
@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", options[:persistent] || true)
end
def reload
@connection.reset
end
private
def request(params, &block)
params[:headers] ||= {}
params[:headers]['Date'] = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S +0000")
params[:headers]['X-Amzn-Authorization'] = "AWS3-HTTPS AWSAccessKeyId=#{@aws_access_key_id},Algorithm=HmacSHA1,Signature=#{signature(params)}"
params[:path] = "/#{@version}/#{params[:path]}"
@connection.request(params, &block)
end
def signature(params)
string_to_sign = params[:headers]['Date']
signed_string = @hmac.sign(string_to_sign)
signature = Base64.encode64(signed_string).chomp!
end
end
end
end
end

View File

@ -0,0 +1,24 @@
module Fog
module Parsers
module AWS
module DNS
class ChangeResourceRecordSets < Fog::Parsers::Base
def reset
@response = {}
end
def end_element(name)
case name
when 'Id', 'Status', 'SubmittedAt'
@response[name] = @value
end
end
end
end
end
end
end

View File

@ -0,0 +1,55 @@
module Fog
module Parsers
module AWS
module DNS
class CreateHostedZone < Fog::Parsers::Base
def reset
@hosted_zone = {}
@change_info = {}
@name_servers = []
@response = {}
@section = :hosted_zone
end
def end_element(name)
if @section == :hosted_zone
case name
when 'Id'
@hosted_zone[name]= @value.sub('/hostedzone/', '')
when 'Name', 'CallerReference', 'Comment'
@hosted_zone[name]= @value
when 'HostedZone'
@response['HostedZone'] = @hosted_zone
@hosted_zone = {}
@section = :change_info
end
elsif @section == :change_info
case name
when 'Id'
@change_info[name]= @value.sub('/change/', '')
when 'Status', 'SubmittedAt'
@change_info[name] = @value
when 'ChangeInfo'
@response['ChangeInfo'] = @change_info
@change_info = {}
@section = :name_servers
end
elsif @section == :name_servers
case name
when 'NameServer'
@name_servers << @value
when 'NameServers'
@response['NameServers'] = @name_servers
@name_servers = {}
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,25 @@
module Fog
module Parsers
module AWS
module DNS
class DeleteHostedZone < Fog::Parsers::Base
def reset
@response = {}
@response['ChangeInfo'] = {}
end
def end_element(name)
case name
when 'Id', 'Status', 'SubmittedAt'
@response['ChangeInfo'][name] = @value
end
end
end
end
end
end
end

View File

@ -0,0 +1,24 @@
module Fog
module Parsers
module AWS
module DNS
class GetChange < Fog::Parsers::Base
def reset
@response = {}
end
def end_element(name)
case name
when 'Id', 'Status', 'SubmittedAt'
@response[name] = @value
end
end
end
end
end
end
end

View File

@ -0,0 +1,43 @@
module Fog
module Parsers
module AWS
module DNS
class GetHostedZone < Fog::Parsers::Base
def reset
@hosted_zone = {}
@name_servers = []
@response = {}
@section = :hosted_zone
end
def end_element(name)
if @section == :hosted_zone
case name
when 'Id'
@hosted_zone[name]= @value.sub('/hostedzone/', '')
when 'Name', 'CallerReference', 'Comment'
@hosted_zone[name]= @value
when 'HostedZone'
@response['HostedZone'] = @hosted_zone
@hosted_zone = {}
@section = :name_servers
end
elsif @section == :name_servers
case name
when 'NameServer'
@name_servers << @value
when 'NameServers'
@response['NameServers'] = @name_servers
@name_servers = {}
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,35 @@
module Fog
module Parsers
module AWS
module DNS
class ListHostedZones < Fog::Parsers::Base
def reset
@hosted_zones = []
@zone = {}
@response = {}
end
def end_element(name)
case name
when 'Id', 'Name', 'CallerReference', 'Comment'
@zone[name] = @value
when 'HostedZone'
@hosted_zones << @zone
@zone = {}
when 'HostedZones'
@response['HostedZones'] = @hosted_zones
when 'MaxItems'
@response[name] = @value.to_i
when 'IsTruncated', 'Marker', 'NextMarker'
@response[name] = @value
end
end
end
end
end
end
end

View File

@ -0,0 +1,46 @@
module Fog
module Parsers
module AWS
module DNS
class ListResourceRecordSets < Fog::Parsers::Base
def reset
@resource_record = []
@resource_record_set = {}
@resource_record_set['ResourceRecords'] = []
@response = {}
@response['ResourceRecordSets'] = []
@section = :resource_record_set
end
def end_element(name)
if @section == :resource_record_set
case name
when 'Name', 'Type', 'TTL'
@resource_record_set[name] = @value
when 'Value'
@resource_record_set['ResourceRecords'] << @value
when 'ResourceRecordSet'
@response['ResourceRecordSets'] << @resource_record_set
@resource_record_set = {}
@resource_record_set['ResourceRecords'] = []
when 'ResourceRecordSets'
@section = :main
end
elsif @section == :main
case name
when 'MaxItems'
@response[name]= @value.to_i
when 'IsTruncated', 'NextRecordName', 'NextRecordType'
@response[name]= @value
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,90 @@
module Fog
module AWS
class DNS
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
#
# ==== 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> -
# * resource_record<~String>
#
# ==== 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> - 201 when successful
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 { |option, value|
case option
when :comment
optional_tags+= "<Comment>#{value}</Comment>"
end
}
#build XML
if change_batch.count > 0
changes= "<ChangeBatch>#{optional_tags}<Changes>"
change_batch.each { |change_item|
action_tag = %Q{<Action>#{change_item[:action]}</Action>}
name_tag = %Q{<Name>#{change_item[:name]}</Name>}
type_tag = %Q{<Type>#{change_item[:type]}</Type>}
ttl_tag = %Q{<TTL>#{change_item[:ttl]}</TTL>}
resource_records= change_item[:resource_records]
resource_record_tags = ''
resource_records.each { |record|
resource_record_tags+= %Q{<ResourceRecord><Value>#{record}</Value></ResourceRecord>}
}
resource_tag= %Q{<ResourceRecords>#{resource_record_tags}</ResourceRecords>}
change_tags = %Q{<Change>#{action_tag}<ResourceRecordSet>#{name_tag}#{type_tag}#{ttl_tag}#{resource_tag}</ResourceRecordSet></Change>}
changes+= change_tags
}
changes+= '</Changes></ChangeBatch>'
end
body = %Q{<?xml version="1.0" encoding="UTF-8"?><ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2010-10-01/">#{changes}</ChangeResourceRecordSetsRequest>}
request({
:body => body,
:parser => Fog::Parsers::AWS::DNS::ChangeResourceRecordSets.new,
:expects => 200,
:method => 'POST',
:path => "hostedzone/#{zone_id}/rrset"
})
end
end
class Mock
def change_resource_record_sets()
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -0,0 +1,72 @@
module Fog
module AWS
class DNS
class Real
require 'fog/aws/parsers/dns/create_hosted_zone'
# Creates a new hosted zone
#
# ==== Parameters
# * name<~String> - The name of the domain. Must be a fully-specified domain that ends with a period
# * options<~Hash>
# * caller_ref<~String> - unique string that identifies the request & allows failed
# calls to be retried without the risk of executing the operation twice
# * comment<~Integer> -
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'HostedZone'<~Hash>:
# * 'Id'<~String> -
# * 'Name'<~String> -
# * 'CallerReference'<~String>
# * 'Comment'<~String> -
# * 'ChangeInfo'<~Hash> -
# * 'Id'<~String>
# * 'Status'<~String>
# * 'SubmittedAt'<~String>
# * 'NameServers'<~Array>
# * 'NameServer'<~String>
# * status<~Integer> - 201 when successful
def create_hosted_zone( name, options = {})
optional_tags = ''
options.each { |option, value|
case option
when :caller_ref
optional_tags+= "<CallerReference>#{value}</CallerReference>"
when :comment
optional_tags+= "<HostedZoneConfig><Comment>#{value}</Comment></HostedZoneConfig>"
end
}
#make sure we have a unique call reference
if options[:caller_ref].nil?
caller_ref = "ref-#{rand(1000000).to_s}"
optional_tags+= "<CallerReference>#{caller_ref}</CallerReference>"
end
request({
:body => %Q{<?xml version="1.0" encoding="UTF-8"?><CreateHostedZoneRequest xmlns="https://route53.amazonaws.com/doc/2010-10-01/"><Name>#{name}</Name>#{optional_tags}</CreateHostedZoneRequest>},
:parser => Fog::Parsers::AWS::DNS::CreateHostedZone.new,
:expects => 201,
:method => 'POST',
:path => "hostedzone"
})
end
end
class Mock
def create_hosted_zone(filters = {})
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -0,0 +1,48 @@
module Fog
module AWS
class DNS
class Real
require 'fog/aws/parsers/dns/delete_hosted_zone'
# Delete a hosted zone
#
# ==== Parameters
# * zone_id<~String> -
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'ChangeInfo'<~Hash> -
# * 'Id'<~String> The ID of the request
# * 'Status'<~String> The current state of the hosted zone
# * 'SubmittedAt'<~String> The date and time the change was made
# * status<~Integer> - 200 when successful
def delete_hosted_zone( zone_id )
# 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/', '')
request({
:expects => 200,
:parser => Fog::Parsers::AWS::DNS::DeleteHostedZone.new,
:method => 'DELETE',
:path => "hostedzone/#{zone_id}"
})
end
end
class Mock
def delete_hosted_zone( zone_id)
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -0,0 +1,47 @@
module Fog
module AWS
class DNS
class Real
require 'fog/aws/parsers/dns/get_change'
# returns the current state of a change request
#
# ==== Parameters
# * change_id<~String>
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'Id'<~String>
# * 'Status'<~String>
# * 'SubmittedAt'<~String>
# * status<~Integer> - 200 when successful
def get_change( change_id )
# AWS methods return change_ids that looks like '/change/id'. Let the caller either use
# that form or just the actual id (which is what this request needs)
change_id = change_id.sub('/change/', '')
request({
:expects => 200,
:parser => Fog::Parsers::AWS::DNS::GetChange.new,
:method => 'GET',
:path => "change/#{change_id}"
})
end
end
class Mock
def get_change( change_id )
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -0,0 +1,51 @@
module Fog
module AWS
class DNS
class Real
require 'fog/aws/parsers/dns/get_hosted_zone'
# retrieve information about a hosted zone
#
# ==== Parameters
# * zone_id<~String> - The ID of the hosted zone
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'HostedZone'<~Hash>:
# * 'Id'<~String> -
# * 'Name'<~String> -
# * 'CallerReference'<~String>
# * 'Comment'<~String> -
# * 'NameServers'<~Array>
# * 'NameServer'<~String>
# * status<~Integer> - 201 when successful
def get_hosted_zone( zone_id)
# 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/', '')
request({
:expects => 200,
:parser => Fog::Parsers::AWS::DNS::GetHostedZone.new,
:method => 'GET',
:path => "hostedzone/#{zone_id}"
})
end
end
class Mock
def get_hosted_zone()
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -0,0 +1,63 @@
module Fog
module AWS
class DNS
class Real
require 'fog/aws/parsers/dns/list_hosted_zones'
# Describe all or specified instances
#
# ==== Parameters
# * options<~Hash>
# * marker<~String> - Indicates where to begin in your list of hosted zones.
# * max_items<~Integer> - The maximum number of hosted zones to be included in the response body
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'HostedZones'<~Array>:
# * 'HostedZone'<~Hash>:
# * 'Id'<~String> -
# * 'Name'<~String> -
# * 'CallerReference'<~String>
# * 'Comment'<~String> -
# * 'Marker'<~String> -
# * 'MaxItems'<~Integer> -
# * 'IsTruncated'<~String> -
# * 'NextMarket'<~String>
# * status<~Integer> - 200 when successful
def list_hosted_zones( options = {})
parameters = {}
options.each { |option, value|
case option
when :marker
parameters[option] = value
when :max_items
parameters[:maxitems] = value
end
}
request({
:query => parameters,
:parser => Fog::Parsers::AWS::DNS::ListHostedZones.new,
:expects => 200,
:method => 'GET',
:path => "hostedzone"
})
end
end
class Mock
def list_hosted_zones( options = {})
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -0,0 +1,68 @@
module Fog
module AWS
class DNS
class Real
require 'fog/aws/parsers/dns/list_resource_record_sets'
# list your resource record sets
#
# ==== Parameters
# * zone_id<~String> -
# * options<~Hash>
# * type<~String> -
# * name<~String> -
# * max_items<~Integer> -
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'ResourceRecordSet'<~Array>:
# * 'Name'<~String> -
# * 'Type'<~String> -
# * 'TTL'<~Integer> -
# * 'ResourceRecords'<~Array>
# * 'Value'<~String> -
# * 'IsTruncated'<~String> -
# * 'MaxItems'<~String> -
# * 'NextRecordName'<~String>
# * 'NexRecordType'<~String>
# * status<~Integer> - 201 when successful
def list_resource_record_sets( zone_id, 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/', '')
parameters = {}
options.each { |option, value|
case option
when :type, :name
parameters[option]= "#{value}"
when :max_items
parameters['maxitems']= "#{value}"
end
}
request({
:query => parameters,
:parser => Fog::Parsers::AWS::DNS::ListResourceRecordSets.new,
:expects => 200,
:method => 'GET',
:path => "hostedzone/#{zone_id}/rrset"
})
end
end
class Mock
def create_hosted_zone(filters = {})
Fog::Mock.not_implemented
end
end
end
end
end

View File

@ -25,17 +25,13 @@ module Fog
# * DATA<~Hash>:
# * 'DomainID'<~Integer>: domain ID
def domain_update( domain_id, options = {})
query= {}
request(
:expects => 200,
:method => 'GET',
:query => {
:api_action => 'domain.update',
:domain => domain,
:type => type
}.merge!( options)
:query => { :api_action => 'domain.update', :domainId => domain_id }.merge!(options)
)
end
end

View File

@ -0,0 +1,75 @@
# notes: slicehost finds records by id - need to check if others do the same (for zones & records)
# linode uses IDs
#
# array won't work because ID will change when delete items
# hash - but need to generate a unique number - counter
class DnsManager
def initialize
@zones = {}
@zone_id_counter= 0
@record_id_counter= 0
end
# add domain to list of zones and return a zone id. note, only domain name is manatory. any
# other desired fields can be included using options parameter
def create_zone( domain, options)
#see if domain already has zone
zone_id= 0
@zone.each { |id, zone|
if domain.casecmp zone[:domain]
zone_id= id
break
end
}
#if did not find, get a new zone ID
if zone_id == 0
zone_id= get_zone_id
#add fields to zone
zone = { :domain => domain }
options.each { |option, value|
zone[option] = value
}
#add zone to dns manager
@zones[zone_id] = zone
zone_id
end
# update an existing zone with new data. any field can be updated included domain name
def update_zone( zone_id, domain, options)
#build zone hash - merge?
zone = {}
@zones[zone_id] = zone
end
# remove a zone from the dns manager
def delete_zone( zone_id)
@zones.delete( zone_id)
end
# get
def get_zone( zone_id)
@zones[zone_id]
end
#----------------------
def get_zone_id
@zone_id_counter+=1
end
private :get_zone_id
def get_record_id
@record_id_counter+=1
end
private :get_record_id
end

View File

@ -9,11 +9,12 @@ module Fog
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Array>:
# * 'origin'<~String> - domain name to host (ie example.com)
# * 'id'<~Integer> - Id of the zone
# * 'ttl'<~Integer> - TimeToLive (ttl) for the domain, in seconds (> 60)
# * 'active'<~String> - whether zone is active in Slicehost DNS server - 'Y' or 'N'
# * body<~Hash>:
# * 'zones'<~Array>
# * 'origin'<~String> - domain name to host (ie example.com)
# * 'id'<~Integer> - Id of the zone
# * 'ttl'<~Integer> - TimeToLive (ttl) for the domain, in seconds (> 60)
# * 'active'<~String> - whether zone is active in Slicehost DNS server - 'Y' or 'N'
def get_zones
request(
:expects => 200,

View File

@ -0,0 +1,16 @@
module Fog
module Slicehost
class Compute
class Mock
def initialize
@slices = []
@zones = []
end
end
end
end
end

View File

@ -0,0 +1,69 @@
Shindo.tests('AWS::DNS | DNS requests', ['aws', 'dns']) do
@test_domain = 'sample53-domain.com'
tests( 'success') do
tests('#create_hosted_zones') {
test('simple zone') {
pending if Fog.mocking?
response = AWS[:dns].create_hosted_zone( @test_domain)
if response.status == 201
zone_id = response.body['HostedZone']['Id']
response = AWS[:dns].delete_hosted_zone( zone_id)
(response.status == 200) ? true : false
end
}
}
tests('#get_hosted_zones') {
}
tests('#delete_hosted_zones') {
}
tests('#list_hosted_zones') {
test( 'simple list') {
pending if Fog.mocking?
response = AWS[:dns].list_hosted_zones()
response.status == 200
}
}
end
tests( 'failure') do
tests('#create_hosted_zone') do
tests('invalid domain name') {
pending if Fog.mocking?
raises( Excon::Errors::BadRequest) {
response = AWS[:dns].create_hosted_zone( 'invalid-domain')
}
}
end
tests('#get_hosted_zone') do
tests('for invalid zone ID') do
pending if Fog.mocking?
raises(Excon::Errors::BadRequest) {
zone_id = 'dummy-id'
response = AWS[:dns].get_hosted_zone( zone_id)
}
end
end
end
end

View File

@ -0,0 +1,250 @@
Shindo.tests('Linode::Compute | DNS requests', ['linode', 'dns']) do
@test_domain = 'test-fog.com'
@new_zones = []
@new_records =[]
tests( 'success') do
test('get current zone count') do
pending if Fog.mocking?
@org_zone_count= 0
response = Linode[:compute].domain_list()
if response.status == 200
zones = response.body['DATA']
@org_zone_count = zones.count
end
response.status == 200
end
test('create zone - simple') do
pending if Fog.mocking?
type = 'master'
options = { :SOA_email => "netops@#{@test_domain}", :description => "Sample-Domain Inc", :status => 0}
response = Linode[:compute].domain_create( @test_domain, type, options)
if response.status == 200
@master_zone_id = response.body['DATA']['DomainID']
@new_zones << @master_zone_id
end
response.status == 200
end
test('create zone - set all parameters') do
pending if Fog.mocking?
type = 'slave'
options = { :SOA_email => "netops@#{@test_domain}", :refresh_sec => 14400, :retry_sec => 3600,
:expire_sec => 604800, :ttl_sec => 28800, :status => 0, :master_ips => '1.2.3.4;2.3.4.5' }
domain= 'sub.' + @test_domain
response = Linode[:compute].domain_create( domain, type, options)
if response.status == 200
@slave_zone_id = response.body['DATA']['DomainID']
@new_zones << @slave_zone_id
end
response.status == 200
end
test("get zone #{@slave_zone_id} - check all parameters") do
pending if Fog.mocking?
result= false
domain= 'sub.' + @test_domain
response = Linode[:compute].domain_list( @slave_zone_id)
if response.status == 200
zones = response.body['DATA']
num_zones = zones.count
if num_zones == 1
zone= zones[0]
if (zone['SOA_EMAIL'] == "netops@#{@test_domain}") and (zone['REFRESH_SEC'] == 14400) and
(zone['RETRY_SEC'] == 3600) and (zone['EXPIRE_SEC'] == 604800) and (zone['TTL_SEC'] == 28800) and
(zone['STATUS'] == 0) and (zone['DOMAIN'] == domain) and (zone['TYPE'] == 'slave')
(zone['MASTER_IPS'] == '1.2.3.4;2.3.4.5')
result= true
end
end
end
result
end
test("update zone #{@slave_zone_id}- update TTL parameter") do
pending if Fog.mocking?
result= false
options = { :ttl_sec => 14400 }
response = Linode[:compute].domain_update( @slave_zone_id, options)
if response.status == 200
result= true
end
result
end
test("get zone #{@slave_zone_id} - check TTL parameters") do
pending if Fog.mocking?
result= false
response = Linode[:compute].domain_list( @slave_zone_id)
if response.status == 200
zones = response.body['DATA']
num_zones = zones.count
if num_zones == 1
zone= zones[0]
if (zone['TTL_SEC'] == 14400)
result= true
end
end
end
result
end
test('create record - simple A record') do
pending if Fog.mocking?
domain= 'www.' + @test_domain
options = { :name => domain, :target => '4.5.6.7', :ttl_sec => 3600 }
response = Linode[:compute].domain_resource_create( @master_zone_id, 'A', options)
if response.status == 200
record_id = response.body['DATA']['ResourceID']
@new_records << record_id
end
response.status == 200
end
test('create record - CNAME record') do
pending if Fog.mocking?
domain= 'mail'
options = { :name => domain, :target => 'www.' + @test_domain }
response = Linode[:compute].domain_resource_create( @master_zone_id, 'CNAME', options)
if response.status == 200
record_id = response.body['DATA']['ResourceID']
@new_records << record_id
end
response.status == 200
end
test('create record - NS record') do
pending if Fog.mocking?
options = { :name => @test_domain, :target => 'ns.' + @test_domain}
response = Linode[:compute].domain_resource_create( @master_zone_id, 'NS', options)
if response.status == 200
record_id = response.body['DATA']['ResourceID']
@new_records << record_id
end
response.status == 200
end
test('create record - MX record') do
pending if Fog.mocking?
options = { :target => 'mail.' + @test_domain, :ttl_sec => 7200, :priority => 5 }
response = Linode[:compute].domain_resource_create( @master_zone_id, 'MX', options)
if response.status == 200
@record_id = response.body['DATA']['ResourceID']
@new_records << @record_id
end
response.status == 200
end
test("get record #{@record_id} - verify all parameters") do
pending if Fog.mocking?
result= false
domain= 'mail.' + @test_domain
response = Linode[:compute].domain_resource_list(@master_zone_id, @record_id)
if response.status == 200
records= response.body['DATA']
if records.count == 1
record = records[0]
if (record['TYPE'] == 'MX') and (record['PRIORITY'] == 5) and (record['TTL_SEC'] == 7200) and
(record['TARGET'] == domain)
result= true
end
end
end
result
end
test("update record #{@record_id} - change target") do
pending if Fog.mocking?
options = { :target => 'mail2.' + @test_domain }
response = Linode[:compute].domain_resource_update( @master_zone_id, @record_id, options)
response.status == 200
end
test("get record #{@record_id} - verify target changed") do
pending if Fog.mocking?
result= false
domain= 'mail2.' + @test_domain
response = Linode[:compute].domain_resource_list(@master_zone_id, @record_id)
if response.status == 200
records= response.body['DATA']
if records.count == 1
record = records[0]
if record['TARGET'] == domain
result= true
end
end
end
result
end
test("delete #{@new_records.count} records created") do
pending if Fog.mocking?
result= true
@new_records.each { |record_id|
response = Linode[:compute].domain_resource_delete( @master_zone_id, record_id)
if response.status != 200
result= false;
end
}
result
end
test("delete #{@new_zones.count} zones created") do
pending if Fog.mocking?
result= true
@new_zones.each { |zone_id|
response = Linode[:compute].domain_delete( zone_id)
if response.status != 200
result= false;
end
}
result
end
end
tests( 'failure') do
end
end

View File

@ -0,0 +1,272 @@
Shindo.tests('Slicehost::Compute | DNS requests', ['slicehost', 'dns']) do
@test_domain = 'test-fog.com.'
@new_zones = []
@new_records =[]
tests( 'success') do
test('get current zone count') do
pending if Fog.mocking?
@org_zone_count= 0
response = Slicehost[:compute].get_zones()
if response.status == 200
zones = response.body['zones']
@org_zone_count = zones.count
end
response.status == 200
end
test('create zone - simple') do
pending if Fog.mocking?
response = Slicehost[:compute].create_zone(@test_domain)
if response.status == 201
zone_id = response.body['id']
@new_zones << zone_id
end
response.status == 201
end
test('create zone - set all parameters') do
pending if Fog.mocking?
options = { :ttl => 1800, :active => 'N' }
domain= 'sub.' + @test_domain
response = Slicehost[:compute].create_zone( domain, options)
if response.status == 201
@zone_id = response.body['id']
@new_zones << @zone_id
end
response.status == 201
end
test("get zone #{@zone_id} - check all parameters") do
pending if Fog.mocking?
result= false
domain= 'sub.' + @test_domain
response = Slicehost[:compute].get_zone( @zone_id)
if response.status == 200
zone = response.body
if (zone['origin'] == 'sub.' + @test_domain) and (zone['ttl'] == 1800) and
(zone['active'] == 'N')
result= true;
end
end
result
end
test('get zones - make sure total count is correct') do
pending if Fog.mocking?
result= false
response = Slicehost[:compute].get_zones()
if response.status == 200
zones = response.body['zones']
if (@org_zone_count+2) == zones.count
result= true;
end
end
result
end
test('get zones - check all parameters for a zone') do
pending if Fog.mocking?
result= false
response = Slicehost[:compute].get_zones()
if response.status == 200
zones = response.body['zones']
zones.each { |zone|
if zone['id'] == @new_zones[1]
if (zone['origin'] == 'sub.' + @test_domain) and (zone['ttl'] == 1800) and
(zone['active'] == 'N')
result= true;
end
end
}
if (@org_zone_count+2) == zones.count
result= true;
end
end
result
end
test('create record - simple A record') do
pending if Fog.mocking?
domain= 'sub.' + @test_domain
zone_id= @new_zones[1]
response = Slicehost[:compute].create_record( 'A', zone_id, domain, '1.2.3.4')
if response.status == 201
record_id = response.body['id']
@new_records << record_id
end
response.status == 201
end
test('create record - A record - all parameters set') do
pending if Fog.mocking?
domain= 'sub2.' + @test_domain
zone_id= @new_zones[1]
options = { :ttl => 3600, :active => 'N'}
response = Slicehost[:compute].create_record( 'A', zone_id, domain, '1.2.3.4', options)
if response.status == 201
record_id = response.body['id']
@new_records << record_id
end
response.status == 201
end
test('create record - CNAME record') do
pending if Fog.mocking?
domain= 'sub.' + @test_domain
zone_id= @new_zones[1]
response = Slicehost[:compute].create_record( 'CNAME', zone_id, 'www', domain)
if response.status == 201
record_id = response.body['id']
@new_records << record_id
end
response.status == 201
end
test('create record - NS record') do
pending if Fog.mocking?
domain= 'sub.' + @test_domain
ns_domain = 'ns.' + @test_domain
zone_id= @new_zones[1]
options = { :ttl => 3600, :active => 'N'}
response = Slicehost[:compute].create_record( 'NS', zone_id, domain, ns_domain, options)
if response.status == 201
record_id = response.body['id']
@new_records << record_id
end
response.status == 201
end
test('create record - MX record') do
pending if Fog.mocking?
domain= 'sub.' + @test_domain
mail_domain = 'mail.' + @test_domain
zone_id= @new_zones[1]
options = { :ttl => 3600, :active => 'N', :aux => '10'}
response = Slicehost[:compute].create_record( 'MX', zone_id, domain, mail_domain, options)
if response.status == 201
@record_id = response.body['id']
@new_records << @record_id
end
response.status == 201
end
test("get record #{@record_id} - verify all parameters") do
pending if Fog.mocking?
result= false
response = Slicehost[:compute].get_record(@record_id)
if response.status == 200
domain= 'sub.' + @test_domain
mail_domain = 'mail.' + @test_domain
record = response.body['records'][0]
if (record['record-type'] == 'MX') and (record['name'] == domain) and
(record['data'] == mail_domain) and (record['ttl'] == 3600) and (record['active'] == 'N') and
(record['aux'] == "10")
result= true
end
end
result
end
test('get records - verify all parameters for one record') do
pending if Fog.mocking?
result= false
response = Slicehost[:compute].get_records()
if response.status == 200
records = response.body['records']
#find mx record
records.each {|record|
if record['record-type'] == 'MX'
domain= 'sub.' + @test_domain
mail_domain = 'mail.' + @test_domain
if (record['record-type'] == 'MX') and (record['name'] == domain) and
(record['data'] == mail_domain) and (record['ttl'] == 3600) and (record['active'] == 'N') and
(record['aux'] == "10")
result= true
break
end
end
}
end
result
end
test("delete #{@new_records.count} records created") do
pending if Fog.mocking?
result= true
@new_records.each { |record_id|
response = Slicehost[:compute].delete_record( record_id)
if response.status != 200
result= false;
end
}
result
end
test("delete #{@new_zones.count} zones created") do
pending if Fog.mocking?
result= true
@new_zones.each { |zone_id|
response = Slicehost[:compute].delete_zone( zone_id)
if response.status != 200
result= false;
end
}
result
end
end
tests( 'failure') do
#create a zone with invalid parameters
#get zonfo info with invalid zone id
#delete a zone with an invalid zone id
tests('#create_zone') do
end
end
end

0
tests/zerigo/helper.rb Normal file
View File

View File

@ -0,0 +1,57 @@
USERNAME=''
PASSWORD=''
TEST_DOMAIN = 'sample53-domain.com.'
Shindo.tests('Zerigo::Compute | DNS requests', ['zerigo', 'dns']) do
#connect to Zerigo
# options = { :zerigo_user => USERNAME, :zerigo_password => PASSWORD }
# @zerigo = Fog::Zerigo::Compute.new( options)
tests( 'success') do
tests('#count_hosts') do
end
tests('#count_zones') do
end
tests('#create_host') do
end
tests('#create_zone') do
end
tests('#delete_host') do
end
tests('#find_hosts') do
end
tests('#get_host') do
end
tests('#get_zone') do
end
tests('#get_zone_stats') do
end
tests('#list_hosts') do
end
tests('#list_zones') do
end
tests('#update_host') do
end
tests('#update_zone') do
end
end
tests( 'failure') do
end
end