From 80faf28c1b443ad05c2c344cdaadb5ad28377273 Mon Sep 17 00:00:00 2001 From: Dave Armstrong Date: Tue, 8 Jan 2019 11:09:31 +0000 Subject: [PATCH] (FM-7600) RSAPI Transport connect method --- lib/puppet/resource_api/transport.rb | 21 ++++++ lib/puppet/resource_api/type_definition.rb | 16 +++++ .../resource_api/transport_schema_def_spec.rb | 17 +++++ spec/puppet/resource_api/transport_spec.rb | 71 ++++++++++++++++++- 4 files changed, 124 insertions(+), 1 deletion(-) diff --git a/lib/puppet/resource_api/transport.rb b/lib/puppet/resource_api/transport.rb index 8e4c0935..ac66a8ac 100644 --- a/lib/puppet/resource_api/transport.rb +++ b/lib/puppet/resource_api/transport.rb @@ -12,4 +12,25 @@ def register(schema) @transports[schema[:name]] = Puppet::ResourceApi::TransportSchemaDef.new(schema) end module_function :register # rubocop:disable Style/AccessModifierDeclarations + + def connect(name, connection_info) + validate(name, connection_info) + begin + require "puppet/transport/#{name}" + class_name = name.split('_').map { |e| e.capitalize }.join + Puppet::Transport.const_get(class_name).new(connection_info) + rescue LoadError, NameError => detail + raise Puppet::DevError, 'Cannot load transport for `%{target}`: %{detail}' % { target: name, detail: detail } + end + end + module_function :connect # rubocop:disable Style/AccessModifierDeclarations + + def self.validate(name, connection_info) + @transports ||= {} + transport_schema = @transports[name] + raise Puppet::DevError, 'Transport for `%{target}` not registered' % { target: name } if transport_schema.nil? + + transport_schema.check_schema(connection_info) + transport_schema.validate(connection_info) + end end diff --git a/lib/puppet/resource_api/type_definition.rb b/lib/puppet/resource_api/type_definition.rb index a5bf2ccb..130bf524 100644 --- a/lib/puppet/resource_api/type_definition.rb +++ b/lib/puppet/resource_api/type_definition.rb @@ -50,6 +50,22 @@ class TransportSchemaDef < BaseTypeDefinition def initialize(definition) super(definition, :connection_info) end + + def validate(resource) + # enforce mandatory attributes + missing_attrs = [] + + attributes.each do |name, _options| + type = @data_type_cache[attributes[name][:type]] + + if resource[name].nil? && !(type.instance_of? Puppet::Pops::Types::POptionalType) + missing_attrs << name + end + end + + error_msg = "The following mandatory attributes were not provided:\n * " + missing_attrs.join(", \n * ") + raise Puppet::ResourceError, error_msg if missing_attrs.any? + end end # Base RSAPI schema Object diff --git a/spec/puppet/resource_api/transport_schema_def_spec.rb b/spec/puppet/resource_api/transport_schema_def_spec.rb index e88bb0e8..92cac1fa 100644 --- a/spec/puppet/resource_api/transport_schema_def_spec.rb +++ b/spec/puppet/resource_api/transport_schema_def_spec.rb @@ -25,4 +25,21 @@ it { expect(type.attributes).to be_key(:user) } end end + + describe '#validate' do + context 'when resource is missing attributes' do + let(:resource) { {} } + + it 'raises an error listing the missing attributes' do + expect { type.validate(resource) }.to raise_error Puppet::ResourceError, %r{host} + expect { type.validate(resource) }.to raise_error Puppet::ResourceError, %r{user} + end + end + + context 'when resource has all its attributes' do + let(:resource) { { host: '1234', user: '4321' } } + + it { expect { type.validate(resource) }.not_to raise_error } + end + end end diff --git a/spec/puppet/resource_api/transport_spec.rb b/spec/puppet/resource_api/transport_spec.rb index 7bbeca6a..f1f27d5a 100644 --- a/spec/puppet/resource_api/transport_spec.rb +++ b/spec/puppet/resource_api/transport_spec.rb @@ -44,7 +44,8 @@ desc: 'the user to connect as', }, password: { - type: 'Sensitive[String]', + type: 'String', + sensitive: true, desc: 'the password to make the connection', }, }, @@ -76,4 +77,72 @@ ) } end + + context 'when connecting to a transport' do + let(:name) { 'test_target' } + let(:connection_info) do + { + name: 'test_target', + desc: 'a basic transport', + connection_info: { + host: { + type: 'String', + desc: 'the host ip address or hostname', + }, + }, + } + end + + context 'when the transport file does not exist' do + it 'throws a DevError' do + expect(described_class).to receive(:validate).with(name, connection_info) + expect { described_class.connect(name, connection_info) }.to raise_error Puppet::DevError, + %r{Cannot load transport for `test_target`} + end + end + + context 'when the transport file does exist' do + context 'with and incorrectly defined transport' do + it 'throws a DevError' do + expect(described_class).to receive(:validate).with(name, connection_info) + expect(described_class).to receive(:require).with('puppet/transport/test_target') + expect { described_class.connect(name, connection_info) }.to raise_error Puppet::DevError, + %r{uninitialized constant Puppet::Transport} + end + end + + context 'with a correctly defined transport' do + let(:test_target) { double('Puppet::Transport::TestTarget') } # rubocop:disable RSpec/VerifiedDoubles + + it 'loads initiates the class successfully' do + expect(described_class).to receive(:require).with('puppet/transport/test_target') + expect(described_class).to receive(:validate).with(name, connection_info) + + stub_const('Puppet::Transport::TestTarget', test_target) + expect(test_target).to receive(:new).with(connection_info) + + described_class.connect(name, connection_info) + end + end + end + end + + describe '#self.validate' do + context 'when the transport being validated has not be registered' do + it { expect { described_class.validate('wibble', {}) }.to raise_error Puppet::DevError, %r{Transport for `wibble` not registered} } + end + + context 'when the transport being validated has been registered' do + let(:schema) { { name: 'validate', desc: 'a minimal connection', connection_info: {} } } + + it 'continues to validate the connection_info' do + # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(Puppet::ResourceApi::TransportSchemaDef).to receive(:check_schema).with({}) + expect_any_instance_of(Puppet::ResourceApi::TransportSchemaDef).to receive(:validate).with({}) + # rubocop:enable RSpec/AnyInstance + described_class.register(schema) + described_class.validate('validate', {}) + end + end + end end