From 0d72dcf586956dde35e7378bdcaa20423646d2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Tarti=C3=A8re?= Date: Thu, 27 Apr 2023 16:40:28 -1000 Subject: [PATCH] Rewrite validate_domain_name() as a Puppet 4.x function The 3.x function rely on is_domain_name() which is deprecated. Rewrite it using the more modern puppet 4.x function to rely on data types for better parameters validation. While here, adjust the Stdlib::Fqdn data type to ensure the last component (TLD) is non-numeric as [suggested by @ekohl](https://github.com/puppetlabs/puppetlabs-stdlib/issues/1282#issuecomment-1315182022), and add a Stdlib::Dns_zone data type that is basically the same but with a trailing dot. --- lib/puppet/functions/validate_domain_name.rb | 28 +++++++++++ .../parser/functions/validate_domain_name.rb | 48 ------------------- spec/functions/validate_domain_name_spec.rb | 27 +++++------ spec/type_aliases/dns_zone_spec.rb | 39 +++++++++++++++ types/dns_zone.pp | 2 + types/fqdn.pp | 2 +- 6 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 lib/puppet/functions/validate_domain_name.rb delete mode 100644 lib/puppet/parser/functions/validate_domain_name.rb create mode 100644 spec/type_aliases/dns_zone_spec.rb create mode 100644 types/dns_zone.pp diff --git a/lib/puppet/functions/validate_domain_name.rb b/lib/puppet/functions/validate_domain_name.rb new file mode 100644 index 000000000..9f069acea --- /dev/null +++ b/lib/puppet/functions/validate_domain_name.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# @summary +# Validate that all values passed are syntactically correct domain names. +# Fail compilation if any value fails this check. +Puppet::Functions.create_function(:validate_domain_name) do + # @param values A domain name or an array of domain names to check + # + # @return + # passes when the given values are syntactically correct domain names or raise an error when they are not and fails compilation + # + # @example Passing examples + # $my_domain_name = 'server.domain.tld' + # validate_domain_name($my_domain_name) + # validate_domain_name('domain.tld', 'puppet.com', $my_domain_name) + # + # @exapmle Failing examples (causing compilation to abort) + # validate_domain_name(1) + # validate_domain_name(true) + # validate_domain_name('invalid domain') + # validate_domain_name('-foo.example.com') + # validate_domain_name('www.example.2com') + dispatch :validate_domain_name do + repeated_param 'Variant[Stdlib::Fqdn, Stdlib::Dns_zone]', :values + end + + def validate_domain_name(*_values); end +end diff --git a/lib/puppet/parser/functions/validate_domain_name.rb b/lib/puppet/parser/functions/validate_domain_name.rb deleted file mode 100644 index 0ed576f16..000000000 --- a/lib/puppet/parser/functions/validate_domain_name.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -# -# validate_domain_name.rb -# -module Puppet::Parser::Functions - newfunction(:validate_domain_name, doc: <<-DOC - @summary - Validate that all values passed are syntactically correct domain names. - Fail compilation if any value fails this check. - - @return - passes when the given values are syntactically correct domain names or raise an error when they are not and fails compilation - - @example **Usage** - - The following values will pass: - - $my_domain_name = 'server.domain.tld' - validate_domain_name($my_domain_name) - validate_domain_name('domain.tld', 'puppet.com', $my_domain_name) - - The following values will fail, causing compilation to abort: - - validate_domain_name(1) - validate_domain_name(true) - validate_domain_name('invalid domain') - validate_domain_name('-foo.example.com') - validate_domain_name('www.example.2com') - DOC - ) do |args| - rescuable_exceptions = [ArgumentError] - - if args.empty? - raise Puppet::ParseError, "validate_domain_name(): wrong number of arguments (#{args.length}; must be > 0)" - end - - args.each do |arg| - raise Puppet::ParseError, "#{arg.inspect} is not a string." unless arg.is_a?(String) - - begin - raise Puppet::ParseError, "#{arg.inspect} is not a syntactically correct domain name" unless function_is_domain_name([arg]) - rescue *rescuable_exceptions - raise Puppet::ParseError, "#{arg.inspect} is not a syntactically correct domain name" - end - end - end -end diff --git a/spec/functions/validate_domain_name_spec.rb b/spec/functions/validate_domain_name_spec.rb index f48d31ab5..132af578f 100644 --- a/spec/functions/validate_domain_name_spec.rb +++ b/spec/functions/validate_domain_name_spec.rb @@ -5,7 +5,6 @@ describe 'validate_domain_name' do describe 'signature validation' do it { is_expected.not_to eq(nil) } - it { is_expected.to run.with_params.and_raise_error(Puppet::ParseError, %r{wrong number of arguments}i) } end describe 'valid inputs' do @@ -18,20 +17,20 @@ end describe 'invalid inputs' do - it { is_expected.to run.with_params([]).and_raise_error(Puppet::ParseError, %r{is not a string}) } - it { is_expected.to run.with_params({}).and_raise_error(Puppet::ParseError, %r{is not a string}) } - it { is_expected.to run.with_params(1).and_raise_error(Puppet::ParseError, %r{is not a string}) } - it { is_expected.to run.with_params(true).and_raise_error(Puppet::ParseError, %r{is not a string}) } + it { is_expected.to run.with_params([]).and_raise_error(ArgumentError, %r{got Array}) } + it { is_expected.to run.with_params({}).and_raise_error(ArgumentError, %r{got Hash}) } + it { is_expected.to run.with_params(1).and_raise_error(ArgumentError, %r{got Integer}) } + it { is_expected.to run.with_params(true).and_raise_error(ArgumentError, %r{got Boolean}) } - it { is_expected.to run.with_params('foo.example.com', []).and_raise_error(Puppet::ParseError, %r{is not a string}) } - it { is_expected.to run.with_params('foo.example.com', {}).and_raise_error(Puppet::ParseError, %r{is not a string}) } - it { is_expected.to run.with_params('foo.example.com', 1).and_raise_error(Puppet::ParseError, %r{is not a string}) } - it { is_expected.to run.with_params('foo.example.com', true).and_raise_error(Puppet::ParseError, %r{is not a string}) } + it { is_expected.to run.with_params('foo.example.com', []).and_raise_error(ArgumentError, %r{got Array}) } + it { is_expected.to run.with_params('foo.example.com', {}).and_raise_error(ArgumentError, %r{got Hash}) } + it { is_expected.to run.with_params('foo.example.com', 1).and_raise_error(ArgumentError, %r{got Integer}) } + it { is_expected.to run.with_params('foo.example.com', true).and_raise_error(ArgumentError, %r{got Boolean}) } - it { is_expected.to run.with_params('').and_raise_error(Puppet::ParseError, %r{is not a syntactically correct domain name}) } - it { is_expected.to run.with_params('invalid domain').and_raise_error(Puppet::ParseError, %r{is not a syntactically correct domain name}) } - it { is_expected.to run.with_params('-foo.example.com').and_raise_error(Puppet::ParseError, %r{is not a syntactically correct domain name}) } - it { is_expected.to run.with_params('www.example.2com').and_raise_error(Puppet::ParseError, %r{is not a syntactically correct domain name}) } - it { is_expected.to run.with_params('192.168.1.1').and_raise_error(Puppet::ParseError, %r{is not a syntactically correct domain name}) } + it { is_expected.to run.with_params('').and_raise_error(ArgumentError, %r{got ''}) } + it { is_expected.to run.with_params('invalid domain').and_raise_error(ArgumentError, %r{got 'invalid domain'}) } + it { is_expected.to run.with_params('-foo.example.com').and_raise_error(ArgumentError, %r{got '-foo\.example\.com'}) } + it { is_expected.to run.with_params('www.example.2com').and_raise_error(ArgumentError, %r{got 'www\.example\.2com'}) } + it { is_expected.to run.with_params('192.168.1.1').and_raise_error(ArgumentError, %r{got '192\.168\.1\.1'}) } end end diff --git a/spec/type_aliases/dns_zone_spec.rb b/spec/type_aliases/dns_zone_spec.rb new file mode 100644 index 000000000..14279236d --- /dev/null +++ b/spec/type_aliases/dns_zone_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Stdlib::Dns_zone' do + describe 'accepts dns zones' do + [ + '.', + 'com.', + 'example.com.', + ].each do |value| + describe value.inspect do + it { is_expected.to allow_value(value) } + end + end + end + + describe 'rejects other values' do + [ + true, + false, + '', + 'iAmAString', + {}, + { 'key' => 'value' }, + { 1 => 2 }, + :undef, + 3, + '2com.', + 'com2.', + 'www..com.', + '127.0.0.1', + ].each do |value| + describe value.inspect do + it { is_expected.not_to allow_value(value) } + end + end + end +end diff --git a/types/dns_zone.pp b/types/dns_zone.pp new file mode 100644 index 000000000..663163e44 --- /dev/null +++ b/types/dns_zone.pp @@ -0,0 +1,2 @@ +# @summary Validate a DNS zone name +type Stdlib::Dns_zone = Pattern[/\A((([[:alnum:]]|[[:alnum:]][[:alnum:]-]*[[:alnum:]])\.)*[[:alpha:]]+\.|\.)\z/] diff --git a/types/fqdn.pp b/types/fqdn.pp index c2fbe09ae..a74697835 100644 --- a/types/fqdn.pp +++ b/types/fqdn.pp @@ -1,2 +1,2 @@ # @summary Validate a Fully Qualified Domain Name -type Stdlib::Fqdn = Pattern[/\A(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\z/] +type Stdlib::Fqdn = Pattern[/\A((([[:alnum:]]|[[:alnum:]][[:alnum:]-]*[[:alnum:]])\.)*[[:alpha:]]+)\z/]