1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge pull request #486 from brianhartsock/rs_dns

[rackspace|dns] Rackspace DNS
This commit is contained in:
Wesley Beary 2011-08-29 08:33:54 -07:00
commit 22a7278a1e
30 changed files with 1124 additions and 37 deletions

View file

@ -66,7 +66,7 @@ You can add more specifics if you need to, but reasonable defaults make it just
## No Zerigo? No Problem
If you already have an account with another service you can just as easily use this same code with different credentials. fog currently supports <a href="http://aws.amazon.com/route53/">AWS Route 53</a>, <a href="http://bluebox.net">Blue Box</a>, <a href="http://dnsimple.com">DNSimple</a>, <a href="http://www.linode.com">Linode</a>, <a href="http://www.slicehost.com">Slicehost</a> and <a href="http://www.zerigo.com/managed-dns">Zerigo</a>; so you can have your pick. As an example you can connect to AWS instead of Zerigo:
If you already have an account with another service you can just as easily use this same code with different credentials. fog currently supports <a href="http://aws.amazon.com/route53/">AWS Route 53</a>, <a href="http://bluebox.net">Blue Box</a>, <a href="http://dnsimple.com">DNSimple</a>, <a href="http://www.linode.com">Linode</a>, <a href="http://www.rackspace.com">Rackspace</a>, <a href="http://www.slicehost.com">Slicehost</a> and <a href="http://www.zerigo.com/managed-dns">Zerigo</a>; so you can have your pick. As an example you can connect to AWS instead of Zerigo:
dns = Fog::DNS.new({
:provider => 'AWS',

View file

@ -11,7 +11,9 @@ class Rackspace < Fog::Bin
Fog::Storage::Rackspace
when :load_balancers
Fog::Rackspace::LoadBalancers
else
when :dns
Fog::DNS::Rackspace
else
raise ArgumentError, "Unrecognized service: #{key}"
end
end

View file

@ -32,6 +32,9 @@ module Fog
when :zerigo
require 'fog/zerigo/dns'
Fog::DNS::Zerigo.new(attributes)
when :rackspace
require 'fog/dns/rackspace'
Fog::DNS::Rackspace.new(attributes)
else
raise ArgumentError.new("#{provider} is not a recognized dns provider")
end

View file

@ -0,0 +1,28 @@
module Fog
module DNS
class Rackspace
module Callback
protected
def wait_for_job(job_id, timeout=Fog.timeout, interval=1)
retries = 5
response = nil
Fog.wait_for(timeout, interval) do
response = connection.callback job_id
if response.status != 202
true
elsif retries == 0
raise Fog::Errors::Error.new("Wait on job #{job_id} took too long")
else
retries -= 1
false
end
end
response
end
end
end
end
end

View file

@ -0,0 +1,78 @@
require 'fog/core/model'
require 'fog/dns/models/rackspace/callback'
module Fog
module DNS
class Rackspace
class Record < Fog::Model
include Fog::DNS::Rackspace::Callback
extend Fog::Deprecation
deprecate :ip, :value
deprecate :ip=, :value=
identity :id
attribute :name
attribute :value, :aliases => 'data'
attribute :ttl
attribute :type
attribute :priority
attribute :created
attribute :updated
def destroy
requires :zone, :identity
wait_for_job connection.remove_record(@zone.identity, identity).body['jobId']
true
end
def zone
@zone
end
def save
if identity
update
else
create
end
end
private
def create
requires :name, :type, :value, :zone
options = {
:name => name,
:type => type,
:data => value
}
response = wait_for_job connection.add_records(@zone.identity, [options]).body['jobId']
merge_attributes(response.body['records'].first)
true
end
def update
requires :identity, :zone
options = {}
options[:name] = name if name
options[:type] = type if type
options[:data] = value if value
options[:priority] = priority if priority
wait_for_job connection.modify_record(@zone.identity, identity, options).body['jobId']
true
end
def zone=(new_zone)
@zone = new_zone
end
end
end
end
end

View file

@ -0,0 +1,38 @@
require 'fog/core/collection'
require 'fog/dns/models/rackspace/record'
module Fog
module DNS
class Rackspace
class Records < Fog::Collection
attribute :zone
model Fog::DNS::Rackspace::Record
def all
requires :zone
data = connection.list_records(zone.identity)
load(data.body['records'])
end
def get(record_id)
requires :zone
data = connection.list_record_details(zone.identity, record_id).body
new(data)
#nil or empty string will trigger an argument error
rescue ArgumentError
nil
rescue Fog::Rackspace::Errors::NotFound
nil
end
def new(attributes = {})
requires :zone
super({ :zone => zone }.merge!(attributes))
end
end
end
end
end

View file

@ -0,0 +1,67 @@
require 'fog/core/model'
require 'fog/dns/models/rackspace/records'
module Fog
module DNS
class Rackspace
class Zone < Fog::Model
include Fog::DNS::Rackspace::Callback
identity :id
attribute :email, :aliases => 'emailAddress'
attribute :domain, :aliases => 'name'
attribute :created
attribute :updated
attribute :account_id, :aliases => 'accountId'
attribute :ttl
attribute :nameservers
attribute :comment
def destroy
response = connection.remove_domain(identity)
wait_for_job response.body['jobId'], Fog.timeout
true
end
def records
@records ||= begin
Fog::DNS::Rackspace::Records.new(
:zone => self,
:connection => connection
)
end
end
def save
if identity
update
else
create
end
true
end
private
def create
requires :domain, :email
data = { :name => domain, :email => email }
response = connection.create_domains([data])
response = wait_for_job response.body['jobId']
merge_attributes(response.body['domains'].first)
end
def update
requires :ttl, :email
response = connection.modify_domain(identity, { :ttl => ttl, :comment => comment, :email => email})
wait_for_job response.body['jobId']
end
end
end
end
end

View file

@ -0,0 +1,33 @@
require 'fog/core/collection'
require 'fog/dns/models/rackspace/zone'
module Fog
module DNS
class Rackspace
class Zones < Fog::Collection
model Fog::DNS::Rackspace::Zone
def all
clear
data = connection.list_domains.body['domains']
load(data)
end
def get(zone_id)
if zone_id.nil? or zone_id.to_s.empty?
return nil
end
data = connection.list_domain_details(zone_id).body
new(data)
rescue Fog::Rackspace::Errors::NotFound
nil
#Accessing a valid (but other customer's) id returns a 503 error
rescue Fog::Rackspace::Errors::ServiceUnavailable
nil
end
end
end
end
end

100
lib/fog/dns/rackspace.rb Normal file
View file

@ -0,0 +1,100 @@
module Fog
module DNS
class Rackspace < Fog::Service
US_ENDPOINT = 'https://dns.api.rackspacecloud.com/v1.0'
UK_ENDPOINT = 'https://lon.dns.api.rackspacecloud.com/v1.0'
requires :rackspace_api_key, :rackspace_username
recognizes :rackspace_auth_url
recognizes :rackspace_auth_token
model_path 'fog/dns/models/rackspace'
model :record
collection :records
model :zone
collection :zones
request_path 'fog/dns/requests/rackspace'
#TODO - Import/Export, modify multiple domains, modify multiple records
request :callback
request :list_domains
request :list_domain_details
request :modify_domain
request :create_domains
request :remove_domain
request :remove_domains
request :list_subdomains
request :list_records
request :list_record_details
request :modify_record
request :remove_record
request :remove_records
request :add_records
class Mock
end
class Real
def initialize(options={})
require 'multi_json'
@rackspace_api_key = options[:rackspace_api_key]
@rackspace_username = options[:rackspace_username]
@rackspace_auth_url = options[:rackspace_auth_url]
uri = URI.parse(options[:rackspace_dns_endpoint] || US_ENDPOINT)
@auth_token, @account_id = *authenticate
@path = "#{uri.path}/#{@account_id}"
headers = { 'Content-Type' => 'application/json', 'X-Auth-Token' => @auth_token }
@connection = Fog::Connection.new(uri.to_s, options[:persistent], { :headers => headers})
end
private
def request(params)
#TODO - Unify code with other rackspace services
begin
response = @connection.request(params.merge!({
:path => "#{@path}/#{params[:path]}"
}))
rescue Excon::Errors::BadRequest => error
raise Fog::Rackspace::Errors::BadRequest.slurp error
rescue Excon::Errors::Conflict => error
raise Fog::Rackspace::Errors::Conflict.slurp error
rescue Excon::Errors::NotFound => error
raise Fog::Rackspace::Errors::NotFound.slurp error
rescue Excon::Errors::ServiceUnavailable => error
raise Fog::Rackspace::Errors::ServiceUnavailable.slurp error
end
unless response.body.empty?
response.body = MultiJson.decode(response.body)
end
response
end
def authenticate
options = {
:rackspace_api_key => @rackspace_api_key,
:rackspace_username => @rackspace_username,
:rackspace_auth_url => @rackspace_auth_url
}
credentials = Fog::Rackspace.authenticate(options)
auth_token = credentials['X-Auth-Token']
account_id = credentials['X-Server-Management-Url'].match(/.*\/([\d]+)$/)[1]
[auth_token, account_id]
end
def array_to_query_string(arr)
arr.collect {|k,v| "#{k}=#{v}" }.join('&')
end
def validate_path_fragment(name, fragment)
if fragment.nil? or fragment.to_s.empty?
raise ArgumentError.new ("#{name} cannot be null or empty")
end
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module DNS
class Rackspace
class Real
def add_records(domain_id, records)
validate_path_fragment :domain_id, domain_id
data = {
'records' => records.collect do |record|
record_data = {
'name' => record[:name],
'type' => record[:type],
'data' => record[:data]
}
if record.has_key? :priority
record_data['priority'] = record[:priority]
end
record_data
end
}
request(
:expects => 202,
:method => 'POST',
:path => "domains/#{domain_id}/records",
:body => MultiJson.encode(data)
)
end
end
end
end
end

View file

@ -0,0 +1,18 @@
module Fog
module DNS
class Rackspace
class Real
def callback(job_id)
validate_path_fragment :job_id, job_id
request(
:expects => [200, 202, 204],
:method => 'GET',
:path => "status/#{job_id}"
)
end
end
end
end
end

View file

@ -0,0 +1,48 @@
module Fog
module DNS
class Rackspace
class Real
def create_domains(domains)
data = {
'domains' => []
}
domains.each do |domain|
domain_data =
{
'name' => domain[:name],
'emailAddress' => domain[:email]
}
if domain.has_key? :records
domain_data['recordsList'] = {
'records' => domain[:records].collect do |record|
record_data = {
'ttl' => record[:ttl],
'data' => record[:data],
'name' => record[:name],
'type' => record[:type],
}
if record.has_key? :priority
record_data.merge!({'priority' => record[:priority]})
else
record_data
end
end
}
end
data['domains'] << domain_data
end
request(
:expects => 202,
:method => 'POST',
:path => 'domains',
:body => MultiJson.encode(data)
)
end
end
end
end
end

View file

@ -0,0 +1,32 @@
module Fog
module DNS
class Rackspace
class Real
def list_domain_details(domain_id, options={})
validate_path_fragment :domain_id, domain_id
path = "domains/#{domain_id}"
query_data = {}
if options.has_key? :show_records
query_data['showRecords'] = options[:show_records]
end
if options.has_key? :show_subdomains
query_data['showSubdomains'] = options[:show_subdomains]
end
if !query_data.empty?
path = path + '?' + array_to_query_string(query_data)
end
request(
:expects => 200,
:method => 'GET',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module DNS
class Rackspace
class Real
def list_domains(options={})
path = 'domains'
if !options.empty?
path = path + '?' + array_to_query_string(options)
end
request(
:expects => 200,
:method => 'GET',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module DNS
class Rackspace
class Real
def list_record_details(domain_id, record_id)
validate_path_fragment :domain_id, domain_id
validate_path_fragment :record_id, record_id
path = "domains/#{domain_id}/records/#{record_id}"
request(
:expects => 200,
:method => 'GET',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,23 @@
module Fog
module DNS
class Rackspace
class Real
def list_records(domain_id, options={})
validate_path_fragment :domain_id, domain_id
path = "domains/#{domain_id}/records"
if !options.empty?
path = path + '?' + array_to_query_string(options)
end
request(
:expects => 200,
:method => 'GET',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,23 @@
module Fog
module DNS
class Rackspace
class Real
def list_subdomains(domain_id, options={})
validate_path_fragment :domain_id, domain_id
path = "domains/#{domain_id}/subdomains"
if !options.empty?
path = path + '?' + array_to_query_string(options)
end
request(
:expects => 200,
:method => 'GET',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module DNS
class Rackspace
class Real
def modify_domain(domain_id, options={})
validate_path_fragment :domain_id, domain_id
path = "domains/#{domain_id}"
data = {}
if options.has_key? :ttl
data['ttl'] = options[:ttl]
end
if options.has_key? :comment
data['comment'] = options[:comment]
end
if options.has_key? :email
data['emailAddress'] = options[:email]
end
if data.empty?
return
end
request(
:expects => [202, 204],
:method => 'PUT',
:path => path,
:body => MultiJson.encode(data)
)
end
end
end
end
end

View file

@ -0,0 +1,37 @@
module Fog
module DNS
class Rackspace
class Real
def modify_record(domain_id, record_id, options={})
validate_path_fragment :domain_id, domain_id
validate_path_fragment :record_id, record_id
path = "domains/#{domain_id}/records/#{record_id}"
data = {}
if options.has_key? :ttl
data['ttl'] = options[:ttl]
end
if options.has_key? :name
data['name'] = options[:name]
end
if options.has_key? :data
data['data'] = options[:data]
end
if data.empty?
return
end
request(
:expects => [202, 204],
:method => 'PUT',
:path => path,
:body => MultiJson.encode(data)
)
end
end
end
end
end

View file

@ -0,0 +1,29 @@
module Fog
module DNS
class Rackspace
class Real
def remove_domain(domain_id, options={})
validate_path_fragment :domain_id, domain_id
path = "domains/#{domain_id}"
query_data = {}
if options.has_key? :delete_subdomains
query_data['deleteSubdomains'] = options[:delete_subdomains].to_s
end
if !query_data.empty?
path = path + '?' + array_to_query_string(query_data)
end
request(
:expects => [202, 204],
:method => 'DELETE',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,27 @@
module Fog
module DNS
class Rackspace
class Real
def remove_domains(domain_ids, options={})
path = "domains?" + domain_ids.collect { |domain_id| "id=#{domain_id}" }.join('&')
query_data = {}
if options.has_key? :delete_subdomains
query_data['deleteSubdomains'] = options[:delete_subdomains]
end
if !query_data.empty?
path = path + '&' + array_to_query_string(query_data)
end
request(
:expects => [202, 204],
:method => 'DELETE',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module DNS
class Rackspace
class Real
def remove_record(domain_id, record_id)
validate_path_fragment :domain_id, domain_id
validate_path_fragment :record_id, record_id
path = "domains/#{domain_id}/records/#{record_id}"
request(
:expects => [202, 204],
:method => 'DELETE',
:path => path
)
end
end
end
end
end

View file

@ -0,0 +1,20 @@
module Fog
module DNS
class Rackspace
class Real
def remove_records(domain_id, record_ids)
validate_path_fragment :domain_id, domain_id
path = "domains/#{domain_id}/records?" + record_ids.collect { |record_id| "id=#{record_id}" }.join('&')
request(
:expects => [202, 204],
:method => 'DELETE',
:path => path
)
end
end
end
end
end

View file

@ -2,13 +2,51 @@ require 'fog/core'
module Fog
module Rackspace
extend Fog::Provider
module Errors
class ServiceError < Fog::Errors::Error
attr_reader :response_data
def self.slurp(error)
if error.response.body.empty?
data = nil
message = nil
else
data = MultiJson.decode(error.response.body)
message = data['message']
end
new_error = super(error, message)
new_error.instance_variable_set(:@response_data, data)
new_error
end
end
class InternalServerError < ServiceError; end
class Conflict < ServiceError; end
class NotFound < ServiceError; end
class ServiceUnavailable < ServiceError; end
class BadRequest < ServiceError
#TODO - Need to find a bette way to print out these validation errors when they are thrown
attr_reader :validation_errors
def self.slurp(error)
new_error = super(error)
unless new_error.response_data.nil?
new_error.instance_variable_set(:@validation_errors, new_error.response_data['validationErrors'])
end
new_error
end
end
end
service(:cdn, 'rackspace/cdn')
service(:compute, 'rackspace/compute')
service(:storage, 'rackspace/storage')
service(:load_balancers, 'rackspace/load_balancers')
service(:dns, 'dns/rackspace')
def self.authenticate(options)
rackspace_auth_url = options[:rackspace_auth_url] || "auth.api.rackspacecloud.com"
@ -32,6 +70,5 @@ module Fog
!['X-Server-Management-Url', 'X-Storage-Url', 'X-CDN-Management-Url', 'X-Auth-Token'].include?(key)
end
end
end
end

View file

@ -1,39 +1,12 @@
module Fog
module Rackspace
class LoadBalancers < Fog::Service
class ServiceError < Fog::Errors::Error
attr_reader :response_data
def self.slurp(error)
if error.response.body.empty?
data = nil
message = nil
else
data = MultiJson.decode(error.response.body)
message = data['message']
end
new_error = super(error, message)
new_error.instance_variable_set(:@response_data, data)
new_error
end
end
class InternalServerError < ServiceError; end
class BadRequest < ServiceError
#TODO - Need to find a bette way to print out these validation errors when they are thrown
attr_reader :validation_errors
def self.slurp(error)
new_error = super(error)
unless new_error.response_data.nil?
new_error.instance_variable_set(:@validation_errors, new_error.response_data['validationErrors'])
end
new_error
end
end
#These references exist for backwards compatibility
class ServiceError < Fog::Rackspace::Errors::ServiceError; end
class InternalServerError < Fog::Rackspace::Errors::InternalServerError; end
class BadRequest < Fog::Rackspace::Errors::BadRequest; end
DFW_ENDPOINT = 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/'
ORD_ENDPOINT = 'https://ord.loadbalancers.api.rackspacecloud.com/v1.0/'

View file

@ -32,6 +32,12 @@ def dns_providers
},
:zerigo => {
:mocked => false
},
:rackspace => {
:mocked => false,
:zone_attributes => {
:email => 'fog@example.com'
}
}
}
end

View file

@ -0,0 +1,119 @@
Shindo.tests('Fog::DNS[:rackspace] | DNS requests', ['rackspace', 'dns']) do
@service = Fog::DNS[:rackspace]
tests('success on simple domain') do
domain_tests(@service, {:name => 'basictestdomain.com', :email => 'hostmaster@basictestdomain.com', :records => [{:ttl => 300, :name => 'basictestdomain.com', :type => 'A', :data => '192.168.1.1'}]}) do
tests('list_domains').formats(LIST_DOMAIN_FORMAT) do
@service.list_domains.body
end
tests("list_domains :limit => 5, :offset => 10, :domain => #{@domain_details.first['name']} --> All possible attributes").formats(LIST_DOMAIN_FORMAT) do
@service.list_domains(:limit => 5, :offset => 10, :domain => @domain_details.first['name']).body
end
tests("list_domain_details('#{@domain_id}')").formats(LIST_DOMAIN_DETAILS_WITH_RECORDS) do
@service.list_domain_details(@domain_id).body
end
tests("modify_domain('#{@domain_id}', :ttl => 500, :comment => 'woot', :email => 'randomemail@randomhost.com')").succeeds do
response = @service.modify_domain @domain_id, :ttl => 500, :comment => 'woot', :email => 'randomemail@randomhost.com'
wait_for @service, response
end
end
end
tests('success for domain with multiple records') do
domain_tests(@service,
{
:name => 'testdomainwithmultiplerecords.com',
:email => 'hostmaster@testdomainwithmultiplerecords.com',
:records =>
[
{
:ttl => 300,
:name => 'testdomainwithmultiplerecords.com',
:type => 'A',
:data => '192.168.1.1'
},
{
:ttl => 3600,
:name => 'testdomainwithmultiplerecords.com',
:type => 'MX',
:data => 'mx.testdomainwithmultiplerecords.com',
:priority => 10
}
]
})
end
tests('success for multiple domains') do
domains_tests(@service,
[
{:name => 'basictestdomain1.com', :email => 'hostmaster@basictestdomain1.com', :records => [{:ttl => 300, :name =>'basictestdomain1.com', :type => 'A', :data => '192.168.1.1'}]},
{:name => 'basictestdomain2.com', :email => 'hostmaster@basictestdomain2.com', :records => [{:ttl => 300, :name =>'basictestdomain2.com', :type => 'A', :data => '192.168.1.1'}]}
])
end
tests('success for domain with subdomain') do
domains_tests(@service,
[
{:name => 'basictestdomain.com', :email => 'hostmaster@basictestdomain.com', :records => [{:ttl => 300, :name =>'basictestdomain.com', :type => 'A', :data => '192.168.1.1'}]},
{:name => 'subdomain.basictestdomain.com', :email => 'hostmaster@subdomain.basictestdomain.com', :records => [{:ttl => 300, :name =>'subdomain.basictestdomain.com', :type => 'A', :data => '192.168.1.1'}]}
], true) do
@root_domain_id = @domain_details.find { |domain| domain['name'] == 'basictestdomain.com' }['id']
tests("list_domain_details('#{@root_domain_id}', :show_records => false, :show_subdomains => false)") do
response = @service.list_domain_details(@root_domain_id, :show_records => false, :show_subdomains => false)
formats(LIST_DOMAIN_DETAILS_WITHOUT_RECORDS_AND_SUBDOMAINS_FORMAT) { response.body }
returns(nil) { response.body['recordsList'] }
returns(nil) { response.body['subdomains'] }
end
tests("list_domain_details('#{@root_domain_id}', :show_records => true, :show_subdomains => true)") do
response = @service.list_domain_details(@root_domain_id, :show_records => true, :show_subdomains => true)
formats(LIST_DOMAIN_DETAILS_WITH_RECORDS_AND_SUBDOMAINS_FORMAT) { response.body }
returns(false) { response.body['recordsList'].nil? }
returns(false) { response.body['subdomains'].nil? }
end
tests("list_subdomains('#{@root_domain_id}')").formats(LIST_SUBDOMAINS_FORMAT) do
@service.list_subdomains(@root_domain_id).body
end
tests("remove_domain('#{@root_domain_id}', :delete_subdomains => true)") do
wait_for @service, @service.remove_domain(@root_domain_id, :delete_subdomains => true)
test('domain and subdomains were really deleted') do
(@service.list_domains.body['domains'].collect { |domain| domain['name'] } & ['basictestdomain.com', 'subdomain.basictestdomain.com']).empty?
end
end
end
end
tests( 'failure') do
tests('create_domain(invalid)').raises(Fog::Rackspace::Errors::BadRequest) do
wait_for @service, @service.create_domains([{:name => 'badtestdomain.com', :email => '', :records => [{:ttl => 300, :name => 'badtestdomain.com', :type => 'A', :data => '192.168.1.1'}]}])
end
tests('list_domains :limit => 5, :offset => 8').raises(Fog::Rackspace::Errors::BadRequest) do
@service.list_domains :limit => 5, :offset => 8
end
tests('list_domain_details 34335353').raises(Fog::Rackspace::Errors::NotFound) do
@service.list_domain_details 34335353
end
#tests('create_domains(#{domains})').raises(Fog::Rackspace::Errors::Conflict) do
# wait_for @service.create_domains(domains)
#end
#tests('remove_domain(34343435)').raises(Fog::DNS::Rackspace::DeleteFault) do
# @service.remove_domain 34343435
#end
end
end

View file

@ -0,0 +1,132 @@
SUBDOMAIN_FORMAT = {
'name' => String,
'id' => Integer,
'created' => String,
'updated' => String
}
LIST_SUBDOMAINS_FORMAT = {
'domains' => [SUBDOMAIN_FORMAT],
'totalEntries' => Integer
}
LIST_DOMAIN_FORMAT = {
'domains' => [
{
'name' => String,
'id' => Integer,
'accountId' => Integer,
'updated' => String,
'created' => String
}
],
'totalEntries' => Integer,
'links' => [
{
'rel' => String,
'href' => String
}
]
}
RECORD_FORMAT = {
'name' => String,
'id' => String,
'type' => String,
'data' => String,
'updated' => String,
'created' => String,
'ttl' => Integer,
'priority' => Fog::Nullable::Integer
}
RECORD_LIST_FORMAT = {
'records' => [RECORD_FORMAT],
#In some cases this is returned (domain details) and in some cases it isn't (create domain). Marking as nullable.
'totalEntries' => Fog::Nullable::Integer
}
NAME_SERVERS_FORMAT = [{
'name' => String
}]
BASIC_DOMAIN_DETAIL_FORMAT = {
'name' => String,
'id' => Integer,
'accountId' => Integer,
'updated' => String,
'created' =>String,
'ttl' => Integer,
'emailAddress' => String,
'nameservers' => NAME_SERVERS_FORMAT
}
LIST_DOMAIN_DETAILS_WITH_RECORDS = BASIC_DOMAIN_DETAIL_FORMAT.merge({
'recordsList' => RECORD_LIST_FORMAT
})
LIST_DOMAIN_DETAILS_WITH_RECORDS_AND_SUBDOMAINS_FORMAT = BASIC_DOMAIN_DETAIL_FORMAT.merge({
'recordsList' => RECORD_LIST_FORMAT,
'subdomains' => [SUBDOMAIN_FORMAT]
})
LIST_DOMAIN_DETAILS_WITHOUT_RECORDS_AND_SUBDOMAINS_FORMAT = BASIC_DOMAIN_DETAIL_FORMAT
CREATE_DOMAINS_FORMAT = {
'domains' => [
BASIC_DOMAIN_DETAIL_FORMAT.merge({
'recordsList' => RECORD_LIST_FORMAT
})
]
}
def wait_for(service, response)
job_id = response.body['jobId']
while true
response = service.callback(job_id)
return response if response.status != 202
sleep 5
end
end
def domain_tests(service, domain_attributes)
tests("create_domains([#{domain_attributes}])").formats(CREATE_DOMAINS_FORMAT) do
response = wait_for service, service.create_domains([domain_attributes])
@domain_details = response.body['domains']
@domain_id = @domain_details[0]['id']
response.body
end
begin
if block_given?
yield
end
ensure
tests("remove_domain('#{@domain_id}')").succeeds do
wait_for service, service.remove_domain(@domain_id)
end
end
end
def domains_tests(service, domains_attributes, custom_delete = false)
tests("create_domains(#{domains_attributes})").formats(CREATE_DOMAINS_FORMAT) do
response = wait_for service, service.create_domains(domains_attributes)
@domain_details = response.body['domains']
@domain_ids = @domain_details.collect { |domain| domain['id'] }
response.body
end
begin
if block_given?
yield
end
ensure
if !custom_delete
tests("remove_domains(#{@domain_ids})").succeeds do
wait_for service, service.remove_domains(@domain_ids)
end
end
end
end

View file

@ -0,0 +1,81 @@
Shindo.tests('Fog::DNS[:rackspace] | dns records requests', ['rackspace', 'dns']) do
@service = Fog::DNS[:rackspace]
domain_tests(@service, {:name => 'basictestdomain.com', :email => 'hostmaster@basictestdomain.com', :records => [{:ttl => 300, :name => 'basictestdomain.com', :type => 'A', :data => '192.168.1.1'}]}) do
tests('success on single record') do
tests("list_records(#{@domain_id})").formats(RECORD_LIST_FORMAT) do
@service.list_records(@domain_id).body
end
tests("add_records(#{@domain_id}, [{ :name => 'test1.basictestdomain.com', :type => 'A', :data => '192.168.2.1'}])").formats(RECORD_LIST_FORMAT) do
response = wait_for @service, @service.add_records(@domain_id, [{ :name => 'test1.basictestdomain.com', :type => 'A', :data => '192.168.2.1'}])
@record_id = response.body['records'].first['id']
response.body
end
tests("list_record_details(#{@domain_id}, #{@record_id})").formats(RECORD_FORMAT) do
@service.list_record_details(@domain_id, @record_id).body
end
tests("modify_record(#{@domain_id}, #{@record_id}, { :ttl => 500, :name => 'test2.basictestdomain.com', :data => '192.168.3.1' })").succeeds do
wait_for @service, @service.modify_record(@domain_id, @record_id, { :ttl => 500, :name => 'test2.basictestdomain.com', :data => '192.168.3.1' })
end
tests("remove_record(#{@domain_id}, #{@record_id})").succeeds do
wait_for @service, @service.remove_record(@domain_id, @record_id)
end
end
tests('success on multiple records') do
records_attributes =
[
{ :name => 'test1.basictestdomain.com', :type => 'A', :data => '192.168.2.1'},
{ :name => 'basictestdomain.com', :type => 'MX', :priority => 10, :data => 'mx.basictestdomain.com'}
]
tests("add_records(#{@domain_id}, #{records_attributes})").formats(RECORD_LIST_FORMAT) do
response = wait_for @service, @service.add_records(@domain_id, records_attributes)
@record_ids = response.body['records'].collect { |record| record['id'] }
response.body
end
tests("remove_records(#{@domain_id}, #{@record_ids})").succeeds do
wait_for @service, @service.remove_records(@domain_id, @record_ids)
end
end
tests( 'failure') do
tests("list_records('')").raises(ArgumentError) do
@service.list_records('')
end
tests("list_records('abc')").raises(Fog::Rackspace::Errors::NotFound) do
@service.list_records('abc')
end
tests("list_record_details(#{@domain_id}, '')").raises(ArgumentError) do
@service.list_record_details(@domain_id, '')
end
tests("list_record_details(#{@domain_id}, 'abc')").raises(Fog::Rackspace::Errors::NotFound) do
@service.list_record_details(@domain_id, 'abc')
end
tests("remove_record(#{@domain_id}, '')").raises(ArgumentError) do
@service.remove_record(@domain_id, '')
end
tests("remove_record(#{@domain_id}, 'abc')").raises(Fog::Rackspace::Errors::NotFound) do
@service.remove_record(@domain_id, 'abc')
end
tests("add_record(#{@domain_id}, [{ :name => '', :type => '', :data => ''}])").raises(Fog::Rackspace::Errors::BadRequest) do
@service.add_records(@domain_id, [{ :name => '', :type => '', :data => ''}])
end
end
end
end

View file

@ -21,7 +21,7 @@ Shindo.tests('Fog::Rackspace::LoadBalancers | virtual_ip_tests', ['rackspace'])
tests('failure') do
#TODO - I feel like this should really be a BadRequest, need to dig in
tests('create_virtual_ip(invalid type)').raises(Fog::Rackspace::LoadBalancers::ServiceError) do
tests('create_virtual_ip(invalid type)').raises(Fog::Rackspace::LoadBalancers::InteralServerError) do
@service.create_virtual_ip(@lb.id, 'badtype')
end
tests('delete_virtual_ip(0)').raises(Fog::Rackspace::LoadBalancers::NotFound) do