diff --git a/lib/puppet/resource_api.rb b/lib/puppet/resource_api.rb index cef97edf..6511ea32 100644 --- a/lib/puppet/resource_api.rb +++ b/lib/puppet/resource_api.rb @@ -89,7 +89,7 @@ def my_provider # if type.instance?(v) # return true # else - # inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value) + # inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(v) # error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch("#{DEFINITION[:name]}.#{name}", type, inferred_type) # raise Puppet::ResourceError, error_msg # end @@ -99,12 +99,33 @@ def my_provider defaultto options[:default] end - case options[:type] - when 'String' + type = Puppet::Pops::Types::TypeParser.singleton.parse(options[:type]) + validate do |value| + return true if type.instance?(value) + + inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value) + if inferred_type.is_a? Puppet::Pops::Types::PStringType + # when running under `puppet resource`, we need to try to coerce from strings to the real type + case value + when %r{^-?\d+$}, Puppet::Pops::Patterns::NUMERIC + value = Puppet::Pops::Utils.to_n(value) + when %r{\Atrue|false\Z} + value = value == 'true' + end + return true if type.instance?(value) + + error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch("#{options[:name]}.#{name}", type, inferred_type) + raise Puppet::ResourceError, error_msg + end + end + + # provide better handling of the standard types + case type + when Puppet::Pops::Types::PStringType # require any string value Puppet::ResourceApi.def_newvalues(self, param_or_property, %r{}) # rubocop:disable Lint/BooleanSymbol - when 'Boolean' + when Puppet::Pops::Types::PBooleanType Puppet::ResourceApi.def_newvalues(self, param_or_property, 'true', 'false') aliasvalue true, 'true' aliasvalue false, 'false' @@ -122,30 +143,21 @@ def my_provider end end # rubocop:enable Lint/BooleanSymbol - when 'Integer' + when Puppet::Pops::Types::PIntegerType Puppet::ResourceApi.def_newvalues(self, param_or_property, %r{^-?\d+$}) munge do |v| Puppet::Pops::Utils.to_n(v) end - when 'Float', 'Numeric' + when Puppet::Pops::Types::PFloatType, Puppet::Pops::Types::PNumericType Puppet::ResourceApi.def_newvalues(self, param_or_property, Puppet::Pops::Patterns::NUMERIC) munge do |v| Puppet::Pops::Utils.to_n(v) end + end + + case options[:type] when 'Enum[present, absent]' Puppet::ResourceApi.def_newvalues(self, param_or_property, :absent, :present) - when 'Variant[Pattern[/\A(0x)?[0-9a-fA-F]{8}\Z/], Pattern[/\A(0x)?[0-9a-fA-F]{16}\Z/], Pattern[/\A(0x)?[0-9a-fA-F]{40}\Z/]]' - # the namevar needs to be a Parameter, which only has newvalue*s* - Puppet::ResourceApi.def_newvalues(self, param_or_property, %r{\A(0x)?[0-9a-fA-F]{8}\Z}, %r{\A(0x)?[0-9a-fA-F]{16}\Z}, %r{\A(0x)?[0-9a-fA-F]{40}\Z}) - when 'Optional[String]' - Puppet::ResourceApi.def_newvalues(self, param_or_property, %r{}, :undef) - when 'Variant[Stdlib::Absolutepath, Pattern[/\A(https?|ftp):\/\//]]' - # TODO: this is wrong, but matches original implementation - Puppet::ResourceApi.def_newvalues(self, param_or_property, /^\//, /\A(https?|ftp):\/\//) # rubocop:disable Style/RegexpLiteral - when 'Pattern[/\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/]' - Puppet::ResourceApi.def_newvalues(self, param_or_property, /\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/) # rubocop:disable Style/RegexpLiteral - else - raise Puppet::DevError, "Datatype #{options[:type]} is not yet supported in this prototype" end end end diff --git a/spec/acceptance/device_spec.rb b/spec/acceptance/device_spec.rb index 3cd1e18f..69165b6f 100644 --- a/spec/acceptance/device_spec.rb +++ b/spec/acceptance/device_spec.rb @@ -9,11 +9,7 @@ describe 'using `puppet resource`' do it 'reads resources from the target system' do stdout_str, status = Open3.capture2e("puppet resource #{common_args} device_provider") - if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.4') - expect(stdout_str.strip).to eq 'DL is deprecated, please use Fiddle' - else - expect(stdout_str.strip).to be_empty - end + expect(stdout_str.strip).to match %r{\A()|(DL is deprecated, please use Fiddle)\Z} expect(status).to eq 0 end it 'manages resources on the target system' do diff --git a/spec/puppet/resource_api_spec.rb b/spec/puppet/resource_api_spec.rb index 18c70d26..c9adf793 100644 --- a/spec/puppet/resource_api_spec.rb +++ b/spec/puppet/resource_api_spec.rb @@ -64,10 +64,6 @@ type: 'Variant[Pattern[/\A(0x)?[0-9a-fA-F]{8}\Z/], Pattern[/\A(0x)?[0-9a-fA-F]{16}\Z/], Pattern[/\A(0x)?[0-9a-fA-F]{40}\Z/]]', desc: 'a pattern value', }, - test_path: { - type: 'Variant[Stdlib::Absolutepath, Pattern[/\A(https?|ftp):\/\//]]', - desc: 'a path or URL', - }, test_url: { type: 'Pattern[/\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/]', desc: 'a hkp or http(s) url', @@ -107,7 +103,6 @@ test_float: '-1.5', test_ensure: 'present', test_variant_pattern: 'a' * 8, - test_path: '/var/log/example', test_url: 'hkp://example.com', } end @@ -117,7 +112,6 @@ it('the test_float value is set correctly') { expect(instance[:test_float]).to eq(-1.5) } it('the test_ensure value is set correctly') { expect(instance[:test_ensure]).to eq(:present) } it('the test_variant_pattern value is set correctly') { expect(instance[:test_variant_pattern]).to eq('a' * 8) } - it('the test_path value is set correctly') { expect(instance[:test_path]).to eq('/var/log/example') } it('the test_url value is set correctly') { expect(instance[:test_url]).to eq('hkp://example.com') } end @@ -162,6 +156,11 @@ it('the test_boolean value is set correctly') { expect(instance[:test_boolean]).to eq false } end + context 'when using an unparsable value' do + let(:the_boolean) { 'flubb' } + + it('the test_boolean value is set correctly') { expect { instance }.to raise_error Puppet::ResourceError, %r{test_boolean expect.* Boolean .* got String} } + end end end end @@ -207,11 +206,6 @@ desc: 'a pattern parameter', behaviour: :parameter, }, - test_path: { - type: 'Variant[Stdlib::Absolutepath, Pattern[/\A(https?|ftp):\/\//]]', - desc: 'a path or URL parameter', - behaviour: :parameter, - }, test_url: { type: 'Pattern[/\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/]', desc: 'a hkp or http(s) url parameter', @@ -252,7 +246,6 @@ test_float: '-1.5', test_ensure: 'present', test_variant_pattern: 'a' * 8, - test_path: '/var/log/example', test_url: 'hkp://example.com', } end @@ -262,13 +255,12 @@ it('the test_float value is set correctly') { expect(instance[:test_float]).to eq(-1.5) } it('the test_ensure value is set correctly') { expect(instance[:test_ensure]).to eq(:present) } it('the test_variant_pattern value is set correctly') { expect(instance[:test_variant_pattern]).to eq('a' * 8) } - it('the test_path value is set correctly') { expect(instance[:test_path]).to eq('/var/log/example') } it('the test_url value is set correctly') { expect(instance[:test_url]).to eq('hkp://example.com') } end end end - context 'when registering an attribute with an invalid data type' do + context 'when registering an attribute with an optional data type' do let(:definition) do { name: 'no_type', @@ -281,7 +273,14 @@ } end - it { expect { described_class.register_type(definition) }.to raise_error Puppet::DevError, %r{is not yet supported in this prototype} } + it { expect { described_class.register_type(definition) }.not_to raise_error } + + describe 'the registered type' do + subject(:type) { Puppet::Type.type(:with_parameters) } + + it { is_expected.not_to be_nil } + it { expect(type.parameters[1]).to eq :test_string } + end end context 'when registering a type with a malformed attributes' do