diff --git a/fog.gemspec b/fog.gemspec index 72bc0d45c..809e1bd08 100644 --- a/fog.gemspec +++ b/fog.gemspec @@ -50,6 +50,7 @@ Gem::Specification.new do |s| s.add_dependency('net-ssh', '>=2.1.3') s.add_dependency('nokogiri', '~>1.5.0') s.add_dependency('ruby-hmac') + s.add_dependency('uuid') ## List your development dependencies here. Development dependencies are ## those that are only needed during development diff --git a/lib/fog/bin.rb b/lib/fog/bin.rb index d81432e81..a9285cf64 100644 --- a/lib/fog/bin.rb +++ b/lib/fog/bin.rb @@ -65,6 +65,7 @@ require 'fog/bin/cloudstack' require 'fog/bin/clodo' require 'fog/bin/dnsimple' require 'fog/bin/dnsmadeeasy' +require 'fog/bin/dreamhost' require 'fog/bin/dynect' require 'fog/bin/ecloud' require 'fog/bin/glesys' diff --git a/lib/fog/bin/dreamhost.rb b/lib/fog/bin/dreamhost.rb new file mode 100644 index 000000000..158b8ed11 --- /dev/null +++ b/lib/fog/bin/dreamhost.rb @@ -0,0 +1,31 @@ +class Dreamhost < Fog::Bin + class << self + + def class_for(key) + case key + when :dns + Fog::DNS::Dreamhost + else + raise ArgumentError, "Unrecognized service: #{key}" + end + end + + def [](service) + @@connections ||= Hash.new do |hash, key| + hash[key] = case key + when :dns + Fog::Logger.warning("Dreamhost[:dns] is not recommended, use DNS[:dreamhost] for portability") + Fog::DNS.new(:provider => 'Dreamhost') + else + raise ArgumentError, "Unrecognized service: #{key.inspect}" + end + end + @@connections[service] + end + + def services + Fog::Dreamhost.services + end + + end +end diff --git a/lib/fog/core/errors.rb b/lib/fog/core/errors.rb index aaea7e6f8..54b783ddf 100644 --- a/lib/fog/core/errors.rb +++ b/lib/fog/core/errors.rb @@ -80,6 +80,7 @@ An alternate file may be used by placing its path in the FOG_RC environment vari :dnsimple_password: :dnsmadeeasy_api_key: :dnsmadeeasy_secret_key: + :dreamhost_api_key: :cloudstack_host: :cloudstack_api_key: :cloudstack_secret_access_key: diff --git a/lib/fog/dns.rb b/lib/fog/dns.rb index 7157f4e05..cf003f2ce 100644 --- a/lib/fog/dns.rb +++ b/lib/fog/dns.rb @@ -20,6 +20,9 @@ module Fog when :dnsmadeeasy require 'fog/dnsmadeeasy/dns' Fog::DNS::DNSMadeEasy.new(attributes) + when :dreamhost + require 'fog/dreamhost/dns' + Fog::DNS::Dreamhost.new(attributes) when :dynect require 'fog/dynect/dns' Fog::DNS::Dynect.new(attributes) diff --git a/lib/fog/dreamhost.rb b/lib/fog/dreamhost.rb new file mode 100644 index 000000000..5cae60ad8 --- /dev/null +++ b/lib/fog/dreamhost.rb @@ -0,0 +1,11 @@ +require(File.expand_path(File.join(File.dirname(__FILE__), 'core'))) + +module Fog + module Dreamhost + + extend Fog::Provider + + service(:dns, 'dreamhost/dns', 'DNS') + + end +end diff --git a/lib/fog/dreamhost/dns.rb b/lib/fog/dreamhost/dns.rb new file mode 100644 index 000000000..f805952f0 --- /dev/null +++ b/lib/fog/dreamhost/dns.rb @@ -0,0 +1,97 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'dreamhost')) +require 'fog/dns' +require 'uuid' + +module Fog + module DNS + class Dreamhost < Fog::Service + + requires :dreamhost_api_key + + model_path 'fog/dreamhost/models/dns' + model :record + collection :records + + request_path 'fog/dreamhost/requests/dns' + request :create_record + request :list_records + request :delete_record + request :get_record + + class Mock + + def self.data + @data ||= Hash.new do |hash, key| + hash[key] = {} + end + end + + def self.reset + @data = nil + end + + def initialize(options={}) + @dreamhost_api_key = options[:dreamhost_api_key] + end + + def data + self.class.data + end + + def reset_data + self.class.data.delete + end + + end + + class Real + + def initialize(options={}) + require 'multi_json' + + @dreamhost_api_key = options[:dreamhost_api_key] + if options[:dreamhost_url] + uri = URI.parse(options[:dreamhost_url]) + options[:host] = uri.host + options[:port] = uri.port + options[:scheme] = uri.scheme + end + @host = options[:host] || "api.dreamhost.com" + @persistent = options[:persistent] || false + @port = options[:port] || 443 + @scheme = options[:scheme] || 'https' + @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent) + end + + def reload + @connection.reset + end + + def request(params) + #response = @connection.request(params.merge!({:host => @host})) + params[:query].merge!( { :key => @dreamhost_api_key, + :format => 'json', + :unique_id => UUID.generate } ) + response = @connection.request(params) + + unless response.body.empty? + response.body = MultiJson.decode(response.body) + end + if ENV['DEBUG_DREAMHOST'] + require 'pp' + puts "--- REQUEST PARAMS ---" + pp params + puts "--- END PARAMS --- " + puts "--- REQUEST RESPONSE ---" + pp response + puts "--- END RESPONSE --- " + end + if response.body['result'] != 'success' + raise response.body['data'] + end + response + end + end + end + end +end diff --git a/lib/fog/dreamhost/models/dns/record.rb b/lib/fog/dreamhost/models/dns/record.rb new file mode 100644 index 000000000..16df40f00 --- /dev/null +++ b/lib/fog/dreamhost/models/dns/record.rb @@ -0,0 +1,34 @@ +require 'fog/core/model' + +module Fog + module DNS + class Dreamhost + + class Record < Fog::Model + + identity :name, :aliases => 'record' + + attribute :value + attribute :zone + attribute :type + attribute :editable + attribute :account_id + attribute :comment + + def destroy + connection.delete_record(name, type, value) + true + end + + def save + requires :name, :type, :value + + data = connection.create_record(name, type, value, comment) + true + end + + end + + end + end +end diff --git a/lib/fog/dreamhost/models/dns/records.rb b/lib/fog/dreamhost/models/dns/records.rb new file mode 100644 index 000000000..28985582c --- /dev/null +++ b/lib/fog/dreamhost/models/dns/records.rb @@ -0,0 +1,33 @@ +require 'fog/core/collection' +require 'fog/dreamhost/models/dns/record' + +module Fog + module DNS + class Dreamhost + + class Records < Fog::Collection + + model Fog::DNS::Dreamhost::Record + + def all(filter = {}) + clear + if filter[:zone] + data = connection.list_records.body['data'].find_all { |r| r['zone'] == filter[:zone] } + else + data = connection.list_records.body['data'] + end + load(data) + end + + def get(record_name) + data = connection.get_record(record_name).body['data'].find { |r| r['record'] == record_name } + new(data) + rescue Excon::Errors::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/dreamhost/requests/dns/create_record.rb b/lib/fog/dreamhost/requests/dns/create_record.rb new file mode 100644 index 000000000..c763a7b02 --- /dev/null +++ b/lib/fog/dreamhost/requests/dns/create_record.rb @@ -0,0 +1,32 @@ +module Fog + module DNS + class Dreamhost + + class Mock + + def create_record(record, type, value, comment = "") + Fog::Mock.not_implemented + end + + end + + class Real + + def create_record(record, type, value, comment = "") + request( :expects => 200, + :method => 'GET', + :path => "/", + :query => { + :record => record, + :type => type, + :value => value, + :cmd => 'dns-add_record', + :comment => comment + } + ) + end + + end + end + end +end diff --git a/lib/fog/dreamhost/requests/dns/delete_record.rb b/lib/fog/dreamhost/requests/dns/delete_record.rb new file mode 100644 index 000000000..9a113839e --- /dev/null +++ b/lib/fog/dreamhost/requests/dns/delete_record.rb @@ -0,0 +1,31 @@ +module Fog + module DNS + class Dreamhost + + class Mock + + def delete_record(name, type, value) + raise Fog::Mock.not_implemented + end + + end + + class Real + + def delete_record(name, type, value) + request( :expects => 200, + :method => "GET", + :path => "/", + :query => { + :cmd => 'dns-remove_record', + :type => type, + :record => name, + :value => value, + } + ) + end + + end + end + end +end diff --git a/lib/fog/dreamhost/requests/dns/get_record.rb b/lib/fog/dreamhost/requests/dns/get_record.rb new file mode 100644 index 000000000..676fb913e --- /dev/null +++ b/lib/fog/dreamhost/requests/dns/get_record.rb @@ -0,0 +1,16 @@ +module Fog + module DNS + class Dreamhost + class Real + + def get_record(record_name) + data = request( :expects => 200, + :method => "GET", + :path => "/", + :query => { :cmd => 'dns-list_records' } ) + end + + end + end + end +end diff --git a/lib/fog/dreamhost/requests/dns/list_records.rb b/lib/fog/dreamhost/requests/dns/list_records.rb new file mode 100644 index 000000000..591ce418a --- /dev/null +++ b/lib/fog/dreamhost/requests/dns/list_records.rb @@ -0,0 +1,25 @@ +module Fog + module DNS + class Dreamhost + + class Mock + + def request(*args) + Fog::Mock.not_implemented + end + + end + + class Real + + def list_records + request( :expects => 200, + :method => "GET", + :path => "/", + :query => { :cmd => 'dns-list_records' } ) + end + + end + end + end +end diff --git a/tests/dreamhost/requests/dns/dns_tests.rb b/tests/dreamhost/requests/dns/dns_tests.rb new file mode 100644 index 000000000..4733c37ec --- /dev/null +++ b/tests/dreamhost/requests/dns/dns_tests.rb @@ -0,0 +1,108 @@ +Shindo.tests('Fog::DNS[:dreamhost] | DNS requests', ['dreamhost', 'dns']) do + + tests("success") do + + test("list records") do + pending if Fog.mocking? + + response = Fog::DNS[:dreamhost].list_records + + if response.status == 200 + @records = response.body["data"] + end + + (response.status == 200) and (response.body.size == 2) + end + + test("list all the records") do + pending if Fog.mocking? + + Fog::DNS[:dreamhost].records.all.size > 0 + end + + test("list records from existing zone") do + pending if Fog.mocking? + + Fog::DNS[:dreamhost].records.all(:zone => 'rbel.co').size > 0 + end + + test("list records from nonexistent zone") do + pending if Fog.mocking? + + Fog::DNS[:dreamhost].records.all(:zone => 'foozoone.local').size == 0 + end + + test("create an A resource record without comment") do + pending if Fog.mocking? + + name = "foo.testing.rbel.co" + type = "A" + value = "1.2.3.4" + response = Fog::DNS[:dreamhost].create_record(name, type, value) + + response.body['result'] == 'success' + end + + test("create an A resource record with comment") do + pending if Fog.mocking? + + name = "foo2.testing.rbel.co" + type = "A" + value = "1.2.3.4" + comment = "test" + response = Fog::DNS[:dreamhost].create_record(name, type, value, comment) + + response.body['result'] == 'success' + end + + test("create TXT record") do + pending if Fog.mocking? + + name = "txt.testing.rbel.co" + type = "txt" + value = "foobar" + comment = "test" + response = Fog::DNS[:dreamhost].create_record(name, type, value, comment) + + response.body['result'] == 'success' + end + + test("TXT record found") do + pending if Fog.mocking? + + rec = Fog::DNS[:dreamhost].records.get 'txt.testing.rbel.co' + + rec != nil + end + + test("delete testing records") do + pending if Fog.mocking? + + sleep 5 + + success = true + r = %w( + foo.testing.rbel.co + foo2.testing.rbel.co + txt.testing.rbel.co + ) + r.each do |rec| + name = rec + @records.each do |record| + if record['record'] == name + response = Fog::DNS[:dreamhost].delete_record(name, record["type"], record["value"]) + success = false if (response.body['result'] != 'success') + end + end + end + + success + end + + + end + + tests( 'failure') do + end + +end