require 'fog/dynect/core' module Fog module DNS class Dynect < Fog::Service requires :dynect_customer, :dynect_username, :dynect_password recognizes :timeout, :persistent, :job_poll_timeout recognizes :provider # remove post deprecation model_path 'fog/dynect/models/dns' model :record collection :records model :zone collection :zones request_path 'fog/dynect/requests/dns' request :delete_record request :delete_zone request :get_node_list request :get_all_records request :get_record request :get_zone request :post_record request :post_session request :post_zone request :put_zone request :put_record class JobIncomplete < Error; end class Mock def initialize(options={}) @dynect_customer = options[:dynect_customer] @dynect_username = options[:dynect_username] @dynect_password = options[:dynect_password] end def self.data @data ||= { :zones => {} } end def self.reset @data = nil end def auth_token @auth_token ||= Fog::Dynect::Mock.token end def data self.class.data end def reset_data self.class.reset end end class Real def initialize(options={}) @dynect_customer = options[:dynect_customer] @dynect_username = options[:dynect_username] @dynect_password = options[:dynect_password] @connection_options = options[:connection_options] || {} @host = 'api-v4.dynect.net' @port = options[:port] || 443 @path = options[:path] || '/REST' @persistent = options[:persistent] || false @scheme = options[:scheme] || 'https' @version = options[:version] || '3.5.2' @job_poll_timeout = options[:job_poll_timeout] || 10 @connection = Fog::XML::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options) end def auth_token @auth_token ||= post_session.body['data']['token'] end def request(params) begin # any request could redirect to a job params[:expects] = Array(params[:expects]) | [307] params[:headers] ||= {} params[:headers]['Content-Type'] = 'application/json' params[:headers]['API-Version'] = @version params[:headers]['Auth-Token'] = auth_token unless params[:path] == 'Session' params[:path] = "#{@path}/#{params[:path]}" unless params[:path] =~ %r{^#{Regexp.escape(@path)}/} response = @connection.request(params) if response.body.empty? response.body = {} elsif response.headers['Content-Type'] == 'application/json' response.body = Fog::JSON.decode(response.body) end if response.body['status'] == 'failure' raise Error, response.body['msgs'].first['INFO'] end if params[:path] !~ %r{^/REST/Job/} if response.status == 307 response = poll_job(response, params[:expects], @job_poll_timeout) # Dynect intermittently returns 200 with an incomplete status. When this # happens, the job should still be polled. elsif response.status == 200 && response.body['status'].eql?('incomplete') response.headers['Location'] = "/REST/Job/#{ response.body['job_id'] }" response = poll_job(response, params[:expects], @job_poll_timeout) end end response rescue Excon::Errors::HTTPStatusError => error if @auth_token && error.message =~ /login: (Bad or expired credentials|inactivity logout)/ @auth_token = nil retry else raise error end end response end def poll_job(response, original_expects, time_to_wait) job_location = response.headers['Location'] begin Fog.wait_for(time_to_wait) do response = request( :expects => original_expects, :idempotent => true, :method => :get, :path => job_location ) response.body['status'] != 'incomplete' end rescue Errors::TimeoutError => error if response.body['status'] == 'incomplete' raise JobIncomplete.new("Job #{response.body['job_id']} is still incomplete") else raise error end end response end end end end end