├── Gemfile ├── lib ├── beaker-http │ ├── version.rb │ ├── middleware │ │ └── response │ │ │ └── faraday_beaker_logger.rb │ ├── helpers │ │ └── puppet_helpers.rb │ ├── dsl │ │ └── web_helpers.rb │ └── http.rb └── beaker-http.rb ├── HISTORY.md ├── Rakefile ├── spec ├── spec_helper.rb └── beaker-http │ ├── middleware │ └── response │ │ └── faraday_beaker_logger_spec.rb │ ├── http_spec.rb │ └── dsl │ └── web_helpers_spec.rb ├── acceptance └── tests │ └── puppetserver_requests.rb ├── README.md └── beaker-http.gemspec /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || "https://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/beaker-http/version.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module Http 3 | module Version 4 | STRING = '0.2.0' 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # default - History 2 | ## Tags 3 | * [LATEST - 9 Dec, 2016 (81e072fa)](#LATEST) 4 | 5 | ## Details 6 | ### LATEST - 9 Dec, 2016 (81e072fa) 7 | 8 | * Initial release. 9 | -------------------------------------------------------------------------------- /lib/beaker-http.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'faraday_middleware' 3 | require 'forwardable' 4 | 5 | require 'beaker' 6 | 7 | require 'beaker-http/helpers/puppet_helpers' 8 | require 'beaker-http/dsl/web_helpers' 9 | require "beaker-http/http" 10 | require 'beaker-http/middleware/response/faraday_beaker_logger' 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | namespace :test do 4 | 5 | namespace :spec do 6 | 7 | desc "Run spec tests" 8 | RSpec::Core::RakeTask.new(:run) do |t| 9 | t.rspec_opts = ['--color'] 10 | t.pattern = 'spec/' 11 | end 12 | end 13 | end 14 | 15 | task 'test:spec' => 'test:spec:run' 16 | task :test => 'test:spec' 17 | task :default => :test 18 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'beaker-http' 2 | 3 | module HttpHelpers 4 | DEFAULT_MIDDLEWARE_STACK = [FaradayMiddleware::EncodeJson, 5 | FaradayMiddleware::FollowRedirects, 6 | Faraday::Response::RaiseError, 7 | FaradayMiddleware::ParseJson, 8 | Beaker::Http::FaradayBeakerLogger, 9 | Faraday::Adapter::NetHttp] 10 | end 11 | -------------------------------------------------------------------------------- /acceptance/tests/puppetserver_requests.rb: -------------------------------------------------------------------------------- 1 | require 'beaker-http' 2 | 3 | test_name 'Ensure that requests can be built out to the puppetserver' do 4 | 5 | step 'install latest released puppet agent' do 6 | install_puppet_agent_on(hosts) 7 | end 8 | 9 | step 'install the latest puppet-server on the master' do 10 | install_package(master, 'puppetserver') 11 | on master, 'service puppetserver start' 12 | end 13 | 14 | step 'generate a new beaker http connection object' 15 | http_connection = generate_new_http_connection(master) 16 | 17 | step 'configure the connection to connect to the master port' do 18 | http_connection.url_prefix.port = 8140 19 | end 20 | 21 | step 'call the environments endpoint on the puppetserver' do 22 | response = http_connection.get('/puppet/v3/environments') 23 | assert_equal(200, response.status) 24 | end 25 | 26 | step 'call the environments endpoint with the #https_request method' do 27 | response = http_request("https://#{master.hostname}:8140/puppet/v3/environments", 28 | :get, 29 | http_connection.ssl['client_cert'], 30 | http_connection.ssl['client_key']) 31 | assert_equal(200, response.status) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beaker-http 2 | 3 | This library adds the ability to send http traffic from the beaker coordinator itself, 4 | reducing the need to use beaker's DSL method `on` as an interface to `curl` on a 5 | SUT(System Under Test). It utilizes the Faraday library to generate requests, and the 6 | [Connection](lib/beaker-http/http.rb) class in the `Beaker::Http` module is the class 7 | you want to use directly, either utilizing it directly or subclassing it to build your 8 | own Connection class. 9 | 10 | Please use the DSL methods included in this library [here](lib/beaker-http/dsl/web_helpers.rb). 11 | Reference the [rubydocs](http://www.rubydoc.info/github/puppetlabs/beaker-http/master/Beaker/DSL/Helpers/WebHelpers) for more information on how to use these methods. 12 | 13 | 14 | ## spec testing 15 | 16 | Spec tests all live under the `spec` folder. These are the default rake task, & 17 | so can be run with a simple `bundle exec rake`, as well as being fully specified 18 | by running `bundle exec rake test:spec:run` or using the `test:spec` task. 19 | 20 | ## acceptance testing 21 | 22 | The acceptance folder currently contains [one acceptance test](acceptance/tests/puppetserver_requests.rb) 23 | that demonstrates how to use this in a beaker test; as we refine this module and solidify the 24 | API, more thorough acceptance testing will be coming. For now, please use that test as an example 25 | of how to use this library. 26 | 27 | -------------------------------------------------------------------------------- /beaker-http.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'beaker-http/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "beaker-http" 8 | spec.version = Beaker::Http::Version::STRING 9 | spec.authors = ["Puppet"] 10 | spec.email = ["qe@puppet.com"] 11 | spec.summary = %q{Puppet testing tool} 12 | spec.description = %q{Puppet testing tool coupled with Beaker} 13 | spec.homepage = "https://github.com/puppetlabs/beaker-http" 14 | 15 | spec.files = `git ls-files -z`.split("\x0") 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ["lib"] 19 | 20 | #Development dependencies 21 | spec.add_development_dependency 'bundler', '~> 1.6' 22 | spec.add_development_dependency 'rake' 23 | spec.add_development_dependency 'rspec', '>= 3.0.0' 24 | 25 | #Documentation dependencies 26 | spec.add_development_dependency 'yard', '>= 0.9.11' 27 | spec.add_development_dependency 'markdown', '~> 0' 28 | spec.add_development_dependency 'activesupport', '4.2.6' 29 | 30 | #Run time dependencies 31 | spec.add_runtime_dependency 'json', '~> 1.8' 32 | spec.add_runtime_dependency 'beaker', '> 3.0' 33 | spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.1' 34 | spec.add_runtime_dependency 'faraday_middleware', '~> 0.9' 35 | end 36 | -------------------------------------------------------------------------------- /lib/beaker-http/middleware/response/faraday_beaker_logger.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module Http 3 | class FaradayBeakerLogger < Faraday::Response::Middleware 4 | extend Forwardable 5 | 6 | DEFAULT_OPTIONS = { :bodies => true } 7 | 8 | def initialize(app, logger, options = {} ) 9 | super(app) 10 | @logger = logger 11 | @options = DEFAULT_OPTIONS.merge(options) 12 | end 13 | 14 | def_delegators :@logger, :trace, :debug, :info, :notify, :warn 15 | 16 | def call(env) 17 | @start_time = Time.now 18 | info "#{env.method.upcase}: #{env.url.to_s}" 19 | debug "REQUEST HEADERS:\n#{dump_headers env.request_headers}" 20 | debug "REQUEST BODY:\n#{dump_body env[:body]}" if env[:body] && log_body?(:request) 21 | super 22 | end 23 | 24 | def on_complete(env) 25 | info "RESPONSE CODE: #{env.status.to_s}" 26 | debug "ELAPSED TIME: #{Time.now - @start_time}" 27 | debug "RESPONSE HEADERS:\n#{dump_headers env.response_headers}" 28 | debug "RESPONSE BODY:\n#{dump_body env[:body]}" if env[:body] && log_body?(:response) 29 | end 30 | private 31 | 32 | def dump_headers(headers) 33 | headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n") 34 | end 35 | 36 | def pretty_inspect(body) 37 | require 'pp' unless body.respond_to?(:pretty_inspect) 38 | body.pretty_inspect 39 | end 40 | 41 | def dump_body(body) 42 | if body.respond_to?(:to_str) 43 | body.to_str 44 | else 45 | pretty_inspect(body) 46 | end 47 | end 48 | 49 | def log_body?(type) 50 | case @options[:bodies] 51 | when Hash then @options[:bodies][type] 52 | else @options[:bodies] 53 | end 54 | end 55 | 56 | end 57 | end 58 | end 59 | 60 | Faraday::Response.register_middleware :faraday_beaker_logger => lambda { Beaker::Http::FaradayBeakerLogger } 61 | -------------------------------------------------------------------------------- /lib/beaker-http/helpers/puppet_helpers.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module Http 3 | module Helpers 4 | 5 | # Given a Beaker::Host object, introspect the host for the CA cert and save it to 6 | # the coordinator's filesystem. 7 | # @param host[Beaker::Host] host to ssh into and find the CA cert 8 | # @return [String] File path to the CA cert saved on the coordinator 9 | def get_host_cacert(host) 10 | cacert_on_host= host.puppet['localcacert'] 11 | # puppet may not have laid down the cacert yet, so check to make sure 12 | # the file exists 13 | host.execute("test -f #{cacert_on_host}") 14 | ca_cert = host.execute("cat #{cacert_on_host}", :silent => true) 15 | cert_dir = Dir.mktmpdir("pe_certs") 16 | ca_cert_file = File.join(cert_dir, "cacert.pem") 17 | File.open(ca_cert_file, "w+") do |f| 18 | f.write(ca_cert) 19 | end 20 | ca_cert_file 21 | end 22 | 23 | # Given a Beaker::Host object, introspect the host for the private key and save it to 24 | # the coordinator's filesystem. 25 | # @param host[Beaker::Host] host to ssh into and find the private key 26 | # @return [String] A String of the private key 27 | def get_host_private_key(host) 28 | private_key = host.puppet['hostprivkey'] 29 | # puppet may not have laid down the private_key yet, so check to make sure 30 | # the file exists 31 | host.execute("test -f #{private_key}") 32 | host.execute("cat #{private_key}", :silent => true) 33 | end 34 | 35 | # Given a Beaker::Host object, introspect the host for the host cert and save it to 36 | # the coordinator's filesystem. 37 | # @param host[Beaker::Host] host to ssh into and find the host cert 38 | # @return [String] A String of the host cert 39 | def get_host_cert(host) 40 | hostcert = host.puppet['hostcert'] 41 | # puppet may not have laid down the hostcert yet, so check to make sure 42 | # the file exists 43 | host.execute("test -f #{hostcert}") 44 | host.execute("cat #{hostcert}", :silent => true) 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/beaker-http/dsl/web_helpers.rb: -------------------------------------------------------------------------------- 1 | module Beaker::DSL::Helpers::WebHelpers 2 | 3 | # Generates a new http connection object, using the ever-present options hash to 4 | # configure the connection. 5 | # 6 | # @param host [Beaker::Host optional] supply a SUT host object; will use puppet on host 7 | # to configure certs, and use the options in the host object instead of the global. 8 | # @return [Beaker::Http::Connection] an object wrapping the Faraday::Connection object. 9 | def generate_new_http_connection(host = nil) 10 | if host 11 | raise ArgumentError.new "host must be Beaker::Host, not #{host.class}" if !host.is_a?(Beaker::Host) 12 | connection = Beaker::Http::Connection.new(host.options) 13 | connection.configure_private_key_and_cert_with_puppet(host) 14 | connection 15 | else 16 | Beaker::Http::Connection.new(options) 17 | end 18 | end 19 | 20 | # Make a single http request and discard the http connection object. Returns a Faraday::Response 21 | # object that can be introspected for all response information. 22 | # 23 | # @param url [String] String that will be parsed into a URI object. 24 | # @param request_method [Symbol] Represents any valid http verb. 25 | # @param cert [OpenSSL::X509::Certificate] Certifcate for authentication. 26 | # @param key [OpenSSL::PKey::RSA] Private Key for authentication. 27 | # @param body [String, Hash] For requests that can send a body. Strings are sent unformatted and 28 | # Hashes are JSON.parsed by the Faraday Middleware. 29 | # @param [Hash] options Hash of options extra options for the request 30 | # @option options [Boolean] :read_timeout How long to wait before closing the connection. 31 | # @return [Faraday::Response] 32 | def http_request(url, request_method, cert=nil, key=nil, body=nil, options={}) 33 | connection = generate_new_http_connection 34 | 35 | 36 | connection.url_prefix = URI.parse(url) 37 | 38 | if cert 39 | if cert.is_a?(OpenSSL::X509::Certificate) 40 | connection.ssl['client_cert'] = cert 41 | else 42 | raise TypeError, "cert must be an OpenSSL::X509::Certificate object, not #{cert.class}" 43 | end 44 | end 45 | 46 | if key 47 | if key.is_a?(OpenSSL::PKey::RSA) 48 | connection.ssl['client_key'] = key 49 | else 50 | raise TypeError, "key must be an OpenSSL::PKey:RSA object, not #{key.class}" 51 | end 52 | end 53 | 54 | # ewwww 55 | connection.ssl[:verify] = false 56 | 57 | connection.connection.options.timeout = options[:read_timeout] if options[:read_timeout] 58 | 59 | response = connection.send(request_method) { |conn| conn.body = body } 60 | response 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/beaker-http/middleware/response/faraday_beaker_logger_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Beaker::Http::FaradayBeakerLogger do 4 | 5 | let(:conn) { Faraday.new(:url => 'http://test.com/path') } 6 | let (:logger) { Beaker::Logger.new } 7 | 8 | context 'with log bodies turned off' do 9 | 10 | before do 11 | conn.builder.insert(0, Beaker::Http::FaradayBeakerLogger, logger, :bodies => false) 12 | conn.adapter :test do |stub| 13 | stub.get('/path') {[200, {}, 'success']} 14 | end 15 | end 16 | 17 | it 'sends info and debug requests to the logger' do 18 | expect(logger).to receive(:info).with('GET: http://test.com/path').once 19 | expect(logger).to receive(:info).with(/RESPONSE CODE: 200/).once 20 | expect(logger).to receive(:debug).with(/ELAPSED TIME:/).once 21 | expect(logger).to receive(:debug).with(/REQUEST HEADERS:/).once 22 | expect(logger).to receive(:debug).with(/RESPONSE HEADERS:/).once 23 | expect(logger).to_not receive(:debug).with(/RESPONSE BODY:/) 24 | expect(logger).to_not receive(:debug).with(/REQUEST BODY:/) 25 | conn.get 26 | end 27 | end 28 | 29 | context 'with log bodies turned on' do 30 | 31 | before do 32 | conn.builder.insert(0, Beaker::Http::FaradayBeakerLogger, logger, :bodies => true) 33 | conn.adapter :test do |stub| 34 | stub.post('/path') {[201, {}, 'success']} 35 | end 36 | end 37 | 38 | it 'sends extra debug requests to the logger' do 39 | expect(logger).to receive(:info).with('POST: http://test.com/path').once 40 | expect(logger).to receive(:info).with(/RESPONSE CODE: 201/).once 41 | expect(logger).to receive(:debug).with(/ELAPSED TIME:/).once 42 | expect(logger).to receive(:debug).with(/RESPONSE BODY:\nsuccess/).once 43 | expect(logger).to receive(:debug).with(/REQUEST BODY:\nBODY MOVIN'/).once 44 | expect(logger).to receive(:debug).with(/REQUEST HEADERS:/).once 45 | expect(logger).to receive(:debug).with(/RESPONSE HEADERS:/).once 46 | conn.post() { |connection| connection.body = "BODY MOVIN'" } 47 | end 48 | end 49 | 50 | context 'when body responses do not respond to :to_str' do 51 | let (:array_response) { [1, 2, 3] } 52 | before do 53 | conn.builder.insert(0, Beaker::Http::FaradayBeakerLogger, logger, :bodies => true) 54 | conn.adapter :test do |stub| 55 | stub.post('/path') {[201, {}, array_response]} 56 | end 57 | end 58 | 59 | it 'calls pretty print for that response' do 60 | expect_any_instance_of(Beaker::Http::FaradayBeakerLogger).to receive(:pretty_inspect).with(array_response).once.and_call_original 61 | expect(logger).to receive(:info).with('POST: http://test.com/path').once 62 | expect(logger).to receive(:info).with(/RESPONSE CODE: 201/).once 63 | expect(logger).to receive(:debug).with(/ELAPSED TIME:/).once 64 | expect(logger).to receive(:debug).with("RESPONSE BODY:\n[1, 2, 3]\n").once 65 | expect(logger).to receive(:debug).with(/REQUEST BODY:\nBODY MOVIN'/).once 66 | expect(logger).to receive(:debug).with(/REQUEST HEADERS:/).once 67 | expect(logger).to receive(:debug).with(/RESPONSE HEADERS:/).once 68 | conn.post() { |connection| connection.body = "BODY MOVIN'" } 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/beaker-http/http_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Beaker 4 | module Http 5 | describe Connection do 6 | 7 | let(:options) {{ :logger => Beaker::Logger.new}} 8 | 9 | subject { Connection.new( options ) } 10 | 11 | context 'with a beaker logger in the options passed in ' do 12 | 13 | it 'does not raise errors' do 14 | expect {subject}.to_not raise_error 15 | end 16 | 17 | it 'sets a Faraday object as the instance connection object' do 18 | expect(subject.connection).to be_instance_of(Faraday::Connection) 19 | end 20 | 21 | it 'sets the middleware stack' do 22 | expect(subject.connection.builder.handlers).to eq(HttpHelpers::DEFAULT_MIDDLEWARE_STACK) 23 | end 24 | 25 | it 'routes all http verbs to the connection object' do 26 | http_verbs = [:get, :post, :put, :delete, :head, :patch] 27 | 28 | http_verbs.each do |verb| 29 | expect(subject.connection).to receive(verb) 30 | subject.send(verb) 31 | end 32 | end 33 | 34 | it 'routes other useful methods to the connection object' do 35 | useful_methods = [:url_prefix, :url_prefix=, :ssl] 36 | 37 | useful_methods.each do |method| 38 | expect(subject.connection).to receive(method) 39 | subject.send(method) 40 | end 41 | end 42 | 43 | describe '#remove_error_checking' do 44 | it 'removes the faraday middleware raising errors on 4xx and 5xx requests' do 45 | subject.remove_error_checking 46 | expect(subject.connection.builder.handlers).not_to include(Faraday::Response::RaiseError) 47 | end 48 | end 49 | 50 | context 'with a beaker host passed in' do 51 | unixhost = { roles: ['test_role'], 52 | 'platform' => 'debian-7-x86_64' } 53 | let(:host) { Beaker::Host.create('test.com', unixhost, {}) } 54 | 55 | describe '#configure_cacert_with_puppet' 56 | it 'adds a ca_cert to the connection and changes the scheme to https' do 57 | allow(subject).to receive(:get_host_cacert).with(host).and_return('ca_file') 58 | subject.configure_cacert_with_puppet(host) 59 | expect(subject.connection.ssl['ca_file']).to eq('ca_file') 60 | expect(subject.connection.scheme).to eq('https') 61 | end 62 | 63 | describe '#configure_private_key_and_cert_with_puppet' 64 | it 'calls #configure_cacert_only_with_puppet and adds the host private key and cert' do 65 | allow(subject).to receive(:configure_cacert_with_puppet) 66 | allow(subject).to receive(:get_host_private_key).with(host).and_return('private_key') 67 | allow(subject).to receive(:get_host_cert).with(host).and_return('host_cert') 68 | 69 | allow(OpenSSL::PKey).to receive(:read).with('private_key').and_return('ssl_private_key') 70 | allow(OpenSSL::X509::Certificate).to receive(:new).with('host_cert').and_return('ssl_host_cert') 71 | 72 | subject.configure_private_key_and_cert_with_puppet(host) 73 | expect(subject.connection.ssl['client_key']).to eq('ssl_private_key') 74 | expect(subject.connection.ssl['client_cert']).to eq('ssl_host_cert') 75 | end 76 | end 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/beaker-http/http.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module Http 3 | 4 | # == Beaker::Http::Connection object instantiation examples 5 | # These examples are for using the Connection object directly. If you are trying to use 6 | # this from within a test, consider using the 7 | # {Beaker::DSL::Helpers::WebHelpers DSL constructors} instead. 8 | # @see Beaker::DSL::Helpers::WebHelpers 9 | class Connection 10 | include Beaker::Http::Helpers 11 | extend Forwardable 12 | 13 | attr_reader :connection 14 | 15 | # Beaker::Http::Connection objects can be instantiated with object that 16 | # utilizes a object for easier setup during testing. 17 | # 18 | # @param [Hash] options Typically the global options provided by Beaker. 19 | # @option options [Beaker::Logger] :logger 20 | # @option options [Boolean] :log_http_bodies 21 | def initialize(options) 22 | @connection = create_default_connection(options) 23 | end 24 | 25 | def_delegators :connection, :get, :post, :put, :delete, :head, :patch, :url_prefix, :url_prefix=, :ssl 26 | 27 | def create_default_connection(options) 28 | Faraday.new do |conn| 29 | conn.request :json 30 | 31 | conn.response :follow_redirects 32 | conn.response :raise_error 33 | conn.response :json, :content_type => /\bjson$/ 34 | 35 | # We can supply a third argument, a Hash with key of :bodies set to true or false, 36 | # to configure whether or not to log http bodies in requests and responses. 37 | # However, to uncomplicate things, we will just use the default 38 | # set in the middleware and not allow the http log level to be set 39 | # independently of the beaker log level. If we find that we should allow setting 40 | # of http bodies independent of the beaker log level, we should expose that setting 41 | # here. 42 | conn.response :faraday_beaker_logger, options[:logger] 43 | 44 | conn.adapter :net_http 45 | end 46 | end 47 | 48 | # If you would like to run tests that expect 400 or even 500 responses, 49 | # apply this method to remove the :raise_error middleware. 50 | def remove_error_checking 51 | connection.builder.delete(Faraday::Response::RaiseError) 52 | nil 53 | end 54 | 55 | def set_cacert(ca_file) 56 | ssl['ca_file'] = ca_file 57 | url_prefix.scheme = 'https' 58 | end 59 | 60 | def set_client_key(client_key) 61 | ssl['client_key'] = client_key 62 | end 63 | 64 | def set_client_cert(client_cert) 65 | ssl['client_cert'] = client_cert 66 | end 67 | 68 | # Use this method if you are connecting as a user to the system; it will 69 | # provide the correct SSL context but not provide authentication. 70 | def configure_cacert_with_puppet(host) 71 | set_cacert(get_host_cacert(host)) 72 | connection.host = host.hostname 73 | nil 74 | end 75 | 76 | # Use this method if you want to connect to the system using certificate 77 | # based authentication. This method will provide the ssl context and use 78 | # the private key and cert from the host provided for authentication. 79 | def configure_private_key_and_cert_with_puppet(host) 80 | configure_cacert_with_puppet(host) 81 | 82 | client_key_raw = get_host_private_key(host) 83 | client_cert_raw = get_host_cert(host) 84 | 85 | ssl['client_key'] = OpenSSL::PKey.read(client_key_raw) 86 | ssl['client_cert'] = OpenSSL::X509::Certificate.new(client_cert_raw) 87 | 88 | nil 89 | end 90 | 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/beaker-http/dsl/web_helpers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class ClassMixedWithDSLWebHelpers 4 | include Beaker::DSL::Helpers 5 | 6 | attr_accessor :options 7 | 8 | def initialize 9 | @options = { logger: Beaker::Logger.new } 10 | end 11 | end 12 | 13 | describe ClassMixedWithDSLWebHelpers do 14 | 15 | describe '#generate_new_http_connection' do 16 | 17 | it 'raises an argument error if a non Beaker::Host is supplied' do 18 | expect{subject.generate_new_http_connection(Object.new)}.to raise_error(ArgumentError) 19 | end 20 | 21 | it 'raises no errors when no argument is supplied' do 22 | expect{subject.generate_new_http_connection}.to_not raise_error 23 | end 24 | 25 | context 'when passed a beaker host' do 26 | unixhost = { roles: ['test_role'], 27 | 'platform' => 'debian-7-x86_64' } 28 | let(:host) { Beaker::Host.create('test.com', unixhost, {}) } 29 | let(:mock_options) { {:logger => Beaker::Logger.new} } 30 | let(:mock_connection) {double('connection')} 31 | 32 | it 'configures the connection object' do 33 | expect(mock_connection).to receive(:configure_private_key_and_cert_with_puppet).with(host) 34 | expect(Beaker::Http::Connection).to receive(:new).with(mock_options).and_return(mock_connection) 35 | allow(host).to receive(:options).and_return(mock_options) 36 | expect{subject.generate_new_http_connection(host)}.to_not raise_error 37 | end 38 | end 39 | 40 | end 41 | 42 | describe '#http_request' do 43 | let (:url) {"http://wwww.test.com"} 44 | let (:request_method) { :get } 45 | let (:mock_response) {double('reponse')} 46 | let (:mock_connection) { subject.generate_new_http_connection } 47 | let (:read_timeout) {double('read_timeout')} 48 | 49 | it 'sends a GET request to the url with the minimum required params' do 50 | expect(mock_connection).to receive(:get).and_return(mock_response) 51 | expect(URI).to receive(:parse).with(url).and_call_original 52 | expect(mock_connection).to receive(:url_prefix=).and_call_original 53 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 54 | expect(subject.http_request(url, request_method)).to eq(mock_response) 55 | end 56 | 57 | context 'send a DELETE request to the url with the minimum required params' do 58 | let (:request_method) { :delete } 59 | it 'sends a DELETE request to the url with the minimum required params' do 60 | expect(mock_connection).to receive(:delete).and_return(mock_response) 61 | expect(URI).to receive(:parse).with(url).and_call_original 62 | expect(mock_connection).to receive(:url_prefix=).and_call_original 63 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 64 | expect(subject.http_request(url, request_method)).to eq(mock_response) 65 | end 66 | end 67 | 68 | context 'when the request_method is POST' do 69 | let (:request_method) { :post } 70 | let (:body) {double('body')} 71 | let (:conn) { double('conn') } 72 | 73 | it 'sends a body along in the request' do 74 | expect(conn).to receive(:body=).with(body) 75 | expect(mock_connection).to receive(:post).and_yield(conn).and_return(mock_response) 76 | expect(URI).to receive(:parse).with(url).and_call_original 77 | expect(mock_connection).to receive(:url_prefix=).and_call_original 78 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 79 | expect(subject.http_request(url, request_method, nil, nil, body, {})).to eq(mock_response) 80 | end 81 | end 82 | 83 | 84 | context 'when the request_method is PUT' do 85 | let (:request_method) { :put } 86 | let (:body) {double('body')} 87 | let (:conn) { double('conn') } 88 | 89 | it 'sends a body along in the request' do 90 | expect(conn).to receive(:body=).with(body) 91 | expect(mock_connection).to receive(:put).and_yield(conn).and_return(mock_response) 92 | expect(URI).to receive(:parse).with(url).and_call_original 93 | expect(mock_connection).to receive(:url_prefix=).and_call_original 94 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 95 | expect(subject.http_request(url, request_method, nil, nil, body, {})).to eq(mock_response) 96 | end 97 | 98 | end 99 | 100 | it 'can set the timeout from the options hash passed in' do 101 | expect(mock_connection).to receive(:get).and_return(mock_response) 102 | expect(URI).to receive(:parse).with(url).and_call_original 103 | expect(mock_connection).to receive(:url_prefix=).and_call_original 104 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 105 | expect(subject.http_request(url, request_method, nil, nil, nil, :read_timeout => read_timeout)).to eq(mock_response) 106 | expect(mock_connection.connection.options.timeout).to eq(read_timeout) 107 | end 108 | 109 | context 'with a key and cert provided' do 110 | let (:key) {'key'} 111 | let (:cert) {'cert'} 112 | it 'checks to ensure they are valid types and then adds them to the request' do 113 | 114 | expect(mock_connection).to receive(:get).and_return(mock_response) 115 | expect(key).to receive(:is_a?).with(OpenSSL::PKey::RSA).and_return(true) 116 | expect(cert).to receive(:is_a?).with(OpenSSL::X509::Certificate).and_return(true) 117 | expect(URI).to receive(:parse).with(url).and_call_original 118 | expect(mock_connection).to receive(:url_prefix=).and_call_original 119 | 120 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 121 | 122 | expect(subject.http_request(url, request_method, cert, key)).to eq(mock_response) 123 | expect(mock_connection.ssl['client_key']).to eq(key) 124 | expect(mock_connection.ssl['client_cert']).to eq(cert) 125 | end 126 | 127 | it 'errors when an invalid key is provided' do 128 | expect(mock_connection).to_not receive(:get) 129 | expect(key).to receive(:is_a?).with(OpenSSL::PKey::RSA).and_call_original 130 | expect(cert).to receive(:is_a?).with(OpenSSL::X509::Certificate).and_return(true) 131 | expect(URI).to receive(:parse).with(url).and_call_original 132 | expect(mock_connection).to receive(:url_prefix=).and_call_original 133 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 134 | expect{subject.http_request(url, request_method, cert, key)}.to raise_error(TypeError) 135 | end 136 | 137 | it 'errors when an invalid cert is provided' do 138 | expect(mock_connection).to_not receive(:get) 139 | expect(key).to_not receive(:is_a?).with(OpenSSL::PKey::RSA) 140 | expect(cert).to receive(:is_a?).with(OpenSSL::X509::Certificate).and_call_original 141 | expect(URI).to receive(:parse).with(url).and_call_original 142 | expect(mock_connection).to receive(:url_prefix=).and_call_original 143 | expect(subject).to receive(:generate_new_http_connection).and_return(mock_connection) 144 | expect{subject.http_request(url, request_method, cert, key)}.to raise_error(TypeError) 145 | end 146 | 147 | end 148 | 149 | end 150 | 151 | end 152 | --------------------------------------------------------------------------------