diff --git a/lib/puppet/util/puppetdb_validator.rb b/lib/puppet/util/puppetdb_validator.rb index 82112ef..8cd360d 100644 --- a/lib/puppet/util/puppetdb_validator.rb +++ b/lib/puppet/util/puppetdb_validator.rb @@ -1,35 +1,68 @@ require 'puppet/network/http_pool' # Validator class, for testing that PuppetDB is alive class Puppet::Util::PuppetdbValidator - attr_reader :test_uri + attr_reader :puppetdb_server + attr_reader :puppetdb_port + attr_reader :use_ssl + attr_reader :test_path attr_reader :test_headers def initialize(puppetdb_server, puppetdb_port, use_ssl = true, test_path = '/pdb/meta/v1/version') - @test_uri = URI("#{use_ssl ? 'https' : 'http'}://#{puppetdb_server}:#{puppetdb_port}#{test_path}") + @puppetdb_server = puppetdb_server + @puppetdb_port = puppetdb_port + @use_ssl = use_ssl + @test_path = test_path @test_headers = { 'Accept' => 'application/json' } end + def log_error(cause, code = nil) + if code.nil? + Puppet.notice "Unable to connect to puppetdb server (http#{use_ssl ? 's' : ''}://#{puppetdb_server}:#{puppetdb_port}): #{cause}" + else + Puppet.notice "Unable to connect to puppetdb server (http#{use_ssl ? 's' : ''}://#{puppetdb_server}:#{puppetdb_port}): [#{code}] #{cause}" + end + end + + def valid_connection_new_client? + test_uri = URI("#{use_ssl ? 'https' : 'http'}://#{puppetdb_server}:#{puppetdb_port}#{test_path}") + begin + conn = Puppet.runtime[:http] + _response = conn.get(test_uri, headers: test_headers) + true + rescue Puppet::HTTP::ResponseError => e + log_error e.message, e.response.code + false + end + end + + def valid_connection_old_client? + conn = Puppet::Network::HttpPool.http_instance(puppetdb_server, puppetdb_port, use_ssl) + response = conn.get(test_path, test_headers) + unless response.is_a?(Net::HTTPSuccess) + log_error(response.msg, response.code) + return false + end + true + end + # Utility method; attempts to make an http/https connection to the puppetdb server. # This is abstracted out into a method so that it can be called multiple times # for retry attempts. # # @return true if the connection is successful, false otherwise. def attempt_connection # All that we care about is that we are able to connect successfully via # http(s), so here we're simpling hitting a somewhat arbitrary low-impact URL # on the puppetdb server. - conn = Puppet.runtime[:http] - response = conn.get(test_uri, headers: test_headers) - if response.success? - return true + if Gem::Version.new(Puppet.version) >= Gem::Version.new('7.0.0') + valid_connection_new_client? else - Puppet.notice "Unable to connect to puppetdb server (#{test_uri}): [#{response.code}] #{response.reason}" - return false + valid_connection_old_client? end rescue StandardError => e - Puppet.notice "Unable to connect to puppetdb server (#{test_uri}): #{e.message}" + log_error(e.message) return false end end diff --git a/spec/unit/util/puppetdb_validator_spec.rb b/spec/unit/util/puppetdb_validator_spec.rb index 52fa6fb..c96cd80 100644 --- a/spec/unit/util/puppetdb_validator_spec.rb +++ b/spec/unit/util/puppetdb_validator_spec.rb @@ -1,61 +1,92 @@ require 'spec_helper' require 'puppet/util/puppetdb_validator' describe 'Puppet::Util::PuppetdbValidator' do before(:each) do - response_ok = stub - response_ok.stubs(:is_a?).with(Net::HTTPSuccess).returns(true) - response_not_found = stub - response_not_found.stubs(:is_a?).with(Net::HTTPSuccess).returns(false) - response_not_found.stubs(:code).returns(404) - response_not_found.stubs(:msg).returns('Not found') - - conn_ok = stub - conn_ok.stubs(:get).with('/pdb/meta/v1/version', 'Accept' => 'application/json').returns(response_ok) - conn_ok.stubs(:read_timeout=).with(2) - conn_ok.stubs(:open_timeout=).with(2) - - conn_not_found = stub - conn_not_found.stubs(:get).with('/pdb/meta/v1/version', 'Accept' => 'application/json').returns(response_not_found) - - Puppet::Network::HttpPool.stubs(:http_instance).raises('Unknown host') - Puppet::Network::HttpPool.stubs(:http_instance).with('mypuppetdb.com', 8080, true).raises('Connection refused') - Puppet::Network::HttpPool.stubs(:http_instance).with('mypuppetdb.com', 8080, false).returns(conn_ok) - Puppet::Network::HttpPool.stubs(:http_instance).with('mypuppetdb.com', 8081, true).returns(conn_ok) - Puppet::Network::HttpPool.stubs(:http_instance).with('wrongserver.com', 8081, true).returns(conn_not_found) + nethttpok = Net::HTTPOK.new('1.1', 200, 'OK') + notfound = Net::HTTPNotFound.new('1.1', 404, 'Not found') + + url = '/pdb/meta/v1/version' + if Puppet::PUPPETVERSION.to_f < 7 + conn_ok = stub + conn_ok.stubs(:get).with(url, 'Accept' => 'application/json').returns(nethttpok) + conn_ok.stubs(:read_timeout=).with(2) + conn_ok.stubs(:open_timeout=).with(2) + + conn_not_found = stub + conn_not_found.stubs(:get).with('/pdb/meta/v1/version', 'Accept' => 'application/json').returns(notfound) + + Puppet::Network::HttpPool.stubs(:http_instance).raises('Unknown host') + Puppet::Network::HttpPool.stubs(:http_instance).with('mypuppetdb.com', 8080, true).raises('Connection refused') + Puppet::Network::HttpPool.stubs(:http_instance).with('mypuppetdb.com', 8080, false).returns(conn_ok) + Puppet::Network::HttpPool.stubs(:http_instance).with('mypuppetdb.com', 8081, true).returns(conn_ok) + Puppet::Network::HttpPool.stubs(:http_instance).with('wrongserver.com', 8081, true).returns(conn_not_found) + else + http = stub + Puppet::HTTP::Client.stubs(:new).returns(http) + + http.stubs(:get).with { |uri, _opts| + uri.hostname == 'mypuppetdb.com' && + uri.port == 8080 && + uri.scheme == 'https' + }.raises Puppet::HTTP::HTTPError, 'Connection refused' + + http.stubs(:get).with { |uri, _opts| + uri.hostname == 'mypuppetdb.com' && + uri.port == 8080 && + uri.scheme == 'http' + }.returns(Puppet::HTTP::ResponseNetHTTP.new(url, nethttpok)) + + http.stubs(:get).with { |uri, _opts| + uri.hostname == 'mypuppetdb.com' && + uri.port == 8081 && + uri.scheme == 'https' + }.returns(Puppet::HTTP::ResponseNetHTTP.new(url, nethttpok)) + + http.stubs(:get).with { |uri, _opts| + uri.hostname == 'wrongserver.com' && + uri.port == 8081 && + uri.scheme == 'https' + }.raises Puppet::HTTP::ResponseError, Puppet::HTTP::ResponseNetHTTP.new(url, notfound) + + http.stubs(:get).with { |uri, _opts| + uri.hostname == 'non-existing.com' && + uri.scheme == 'https' + }.raises Puppet::HTTP::HTTPError, 'Unknown host' + end end it 'returns true if connection succeeds' do validator = Puppet::Util::PuppetdbValidator.new('mypuppetdb.com', 8081) expect(validator.attempt_connection).to be true end it 'stills validate without ssl' do validator = Puppet::Util::PuppetdbValidator.new('mypuppetdb.com', 8080, false) expect(validator.attempt_connection).to be true end it 'returns false and issues an appropriate notice if connection is refused' do puppetdb_server = 'mypuppetdb.com' puppetdb_port = 8080 validator = Puppet::Util::PuppetdbValidator.new(puppetdb_server, puppetdb_port) Puppet.expects(:notice).with("Unable to connect to puppetdb server (https://#{puppetdb_server}:#{puppetdb_port}): Connection refused") expect(validator.attempt_connection).to be false end it 'returns false and issues an appropriate notice if connection succeeds but puppetdb is not available' do puppetdb_server = 'wrongserver.com' puppetdb_port = 8081 validator = Puppet::Util::PuppetdbValidator.new(puppetdb_server, puppetdb_port) Puppet.expects(:notice).with("Unable to connect to puppetdb server (https://#{puppetdb_server}:#{puppetdb_port}): [404] Not found") expect(validator.attempt_connection).to be false end it 'returns false and issues an appropriate notice if host:port is unreachable or does not exist' do puppetdb_server = 'non-existing.com' puppetdb_port = nil validator = Puppet::Util::PuppetdbValidator.new(puppetdb_server, puppetdb_port) Puppet.expects(:notice).with("Unable to connect to puppetdb server (https://#{puppetdb_server}:#{puppetdb_port}): Unknown host") expect(validator.attempt_connection).to be false end end