diff options
35 files changed, 884 insertions, 227 deletions
diff --git a/.travis.yml b/.travis.yml index 4981b25..0c6f904 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,10 @@ matrix: script: bundle exec rake beaker services: docker sudo: required - - rvm: 2.3.1 + - rvm: 2.4.0 bundler_args: --without system_tests - env: PUPPET_GEM_VERSION="~> 4.0" - - rvm: 2.1.7 + env: PUPPET_GEM_VERSION="~> 5.0" + - rvm: 2.1.9 bundler_args: --without system_tests env: PUPPET_GEM_VERSION="~> 4.0" notifications: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b3a5a9..57b9ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,53 @@ +# Change log + +All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org). + +## Supported Release 4.20.0 +### Summary + +This release adds new functions and updated README translations. + +#### Added +- `to_json`, `to_json_pretty`, and `to_yaml` functions +- new Japanese README translations + +#### Fixed +- compatibility issue with older versions of Puppet and the `pw_hash` function ([MODULES-5546](https://tickets.puppet.com/browse/MODULES-5546)) + +#### Removed +- support for EOL platform Debian 6 (Squeeze) + +## Supported Release 4.19.0 +### Summary + +This release adds new functions and better documentation/fixes for existing functions with a noteworthy fix for file_line. + +#### Added +- Add validate_domain_name function +- Add the round function +- Add type for MAC address +- Add support for sensitive data type to pw_hash ([MODULES-4908](https://tickets.puppet.com/browse/MODULES-4908)) +- Add new function, fact() (FACT-932) + +#### Fixed +- Fixes for the file_line provider ([MODULES-5003](https://tickets.puppet.com/browse/MODULES-5003)) +- Add documentation for email functions ([MODULES-5382](https://tickets.puppet.com/browse/MODULES-5382)) +- unique function is deprecated for puppet version > 5. (FM-6239) +- Fix headers in CHANGELOG.md so that headers render correctly +- ensure_packages, converge ensure values 'present' and 'installed' + +#### Changed +- Removes listed support for EOL Ubuntu versions + +## Supported Release 4.18.0 +### Summary + +Small release that reverts the Puppet version requirement lower bound to again include Puppet 2.7+ and bumps the upper bound to now include Puppet 5. + +#### Fixed +- Reverts lower bound of Puppet requirement to 2.7.20 + ## Supported Release 4.17.1 ### Summary @@ -33,13 +33,13 @@ ruby_version_segments = Gem::Version.new(RUBY_VERSION.dup).segments minor_version = "#{ruby_version_segments[0]}.#{ruby_version_segments[1]}" group :development do - gem "puppet-module-posix-default-r#{minor_version}", :require => false, :platforms => "ruby" - gem "puppet-module-win-default-r#{minor_version}", :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] - gem "puppet-module-posix-dev-r#{minor_version}", :require => false, :platforms => "ruby" - gem "puppet-module-win-dev-r#{minor_version}", :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] - gem "json_pure", '<= 2.0.1', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') - gem "fast_gettext", '1.1.0', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') - gem "fast_gettext", :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0') + gem "puppet-module-posix-default-r#{minor_version}", :require => false, :platforms => "ruby" + gem "puppet-module-win-default-r#{minor_version}", :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] + gem "puppet-module-posix-dev-r#{minor_version}", :require => false, :platforms => "ruby" + gem "puppet-module-win-dev-r#{minor_version}", '0.0.7', :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] + gem "json_pure", '<= 2.0.1', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') + gem "fast_gettext", '1.1.0', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') + gem "fast_gettext", :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0') end group :system_tests do @@ -50,6 +50,7 @@ group :system_tests do gem "beaker-rspec", *location_for(ENV['BEAKER_RSPEC_VERSION']) gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION']) gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || '~> 0.1') + gem "puppet-blacksmith", '~> 3.4', :require => false end gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION']) @@ -115,6 +115,18 @@ file_line { 'bashrc_proxy': In the example above, `match` looks for a line beginning with 'export' followed by 'HTTP_PROXY' and replaces it with the value in line. +Match Example: + + file_line { 'bashrc_proxy': + ensure => present, + path => '/etc/bashrc', + line => 'export HTTP_PROXY=http://squid.puppetlabs.vm:3128', + match => '^export\ HTTP_PROXY\=', + append_on_no_match => false, + } + +In this code example, `match` looks for a line beginning with export followed by HTTP_PROXY and replaces it with the value in line. If a match is not found, then no changes are made to the file. + Match Example with `ensure => absent`: ```puppet @@ -289,6 +301,10 @@ Unacceptable input example: httds://notquiteright.org ``` +#### `Stdlib::MAC` + +Matches MAC addresses defined in [RFC5342](https://tools.ietf.org/html/rfc5342). + #### `Stdlib::Unixpath` Matches paths on Unix operating systems. @@ -469,9 +485,9 @@ Converts a Boolean to a number. Converts values: * `false`, 'f', '0', 'n', and 'no' to 0. * `true`, 't', '1', 'y', and 'yes' to 1. - Argument: a single Boolean or string as an input. +Argument: a single Boolean or string as an input. - *Type*: rvalue. +*Type*: rvalue. #### `bool2str` @@ -895,6 +911,31 @@ userlist: ensure_resources('user', hiera_hash('userlist'), {'ensure' => 'present'}) ``` +#### `fact` + +Return the value of a given fact. Supports the use of dot-notation for referring to structured facts. If a fact requested does not exist, returns Undef. + +Example usage: + +```puppet +fact('kernel') +fact('osfamily') +fact('os.architecture') +``` + +Array indexing: + +```puppet +$first_processor = fact('processors.models.0') +$second_processor = fact('processors.models.1') +``` + +Fact containing a "." in the fact name: + +```puppet +fact('vmware."VRA.version"') +``` + #### `flatten` Flattens deeply nested arrays and returns a single flat array as a result. @@ -1167,6 +1208,13 @@ Returns `true` if the string passed to this function is a syntactically correct *Type*: rvalue. +#### `is_email_address` + +Returns true if the string passed to this function is a valid email address. + +*Type*: rvalue. + + #### `is_float` **Deprecated. Will be removed in a future version of stdlib. See [`validate_legacy`](#validate_legacy).** @@ -1509,6 +1557,10 @@ For example, `reject(['aaa','bbb','ccc','aaaddd'], 'aaa')` returns ['bbb','ccc'] Reverses the order of a string or array. +#### `round` + + Rounds a number to the nearest integer + *Type*: rvalue. #### `rstrip` @@ -1702,6 +1754,30 @@ Arguments: A single string. *Type*: rvalue. +#### `to_json` + +Converts input into a JSON String. + +For example, `{ "key" => "value" }` becomes `{"key":"value"}`. + +*Type*: rvalue. + +#### `to_json_pretty` + +Converts input into a pretty JSON String. + +For example, `{ "key" => "value" }` becomes `{\n \"key\": \"value\"\n}`. + +*Type*: rvalue. + +#### `to_yaml` + +Converts input into a YAML String. + +For example, `{ "key" => "value" }` becomes `"---\nkey: value\n"`. + +*Type*: rvalue. + #### `try_get_value` **DEPRECATED:** replaced by `dig()`. @@ -1958,6 +2034,53 @@ validate_cmd($haproxycontent, '/usr/sbin/haproxy -f % -c', 'Haproxy failed to va *Type*: statement. +#### `validate_domain_name` + +**Deprecated. Will be removed in a future version of stdlib. See [`validate_legacy`](#validate_legacy).** + +Validate that all values passed are syntactically correct domain names. Aborts catalog compilation if any value fails this check. + +The following values 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 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') +~~~ + +*Type*: statement. + +#### `validate_email_address` + +Validate that all values passed are valid email addresses. Fail compilation if any value fails this check. + +The following values will pass: + +~~~ +$my_email = "waldo@gmail.com" +validate_email_address($my_email) +validate_email_address("bob@gmail.com", "alice@gmail.com", $my_email) +~~~ + +The following values will fail, causing compilation to abort: + +~~~ +$some_array = [ 'bad_email@/d/efdf.com' ] +validate_email_address($some_array) +~~~ + +*Type*: statement. + #### `validate_hash` **Deprecated. Will be removed in a future version of stdlib. See [`validate_legacy`](#validate_legacy).** @@ -2091,7 +2214,7 @@ Arguments: Example: ```puppet -validate_legacy("Optional[String]", "validate_re", "Value to be validated", ["."]) +validate_legacy('Optional[String]', 'validate_re', 'Value to be validated', ["."]) ``` This function supports updating modules from Puppet 3-style argument validation (using the stdlib `validate_*` functions) to Puppet 4 data types, without breaking functionality for those depending on Puppet 3-style validation. diff --git a/appveyor.yml b/appveyor.yml index c87ed7c..7e05880 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,16 +14,28 @@ environment: RUBY_VER: 21 - PUPPET_GEM_VERSION: ~> 4.0 RUBY_VER: 21-x64 - - PUPPET_GEM_VERSION: ~> 4.0 - RUBY_VER: 23 - - PUPPET_GEM_VERSION: ~> 4.0 - RUBY_VER: 23-x64 - - PUPPET_GEM_VERSION: 4.2.3 + - PUPPET_GEM_VERSION: ~> 5.0 + RUBY_VER: 24 + - PUPPET_GEM_VERSION: ~> 5.0 + RUBY_VER: 24-x64 + - PUPPET_GEM_VERSION: 4.7.1 RUBY_VER: 21-x64 matrix: fast_finish: true install: - SET PATH=C:\Ruby%RUBY_VER%\bin;%PATH% +- ps: | + # AppVeyor appears to have OpenSSL headers available already + # which msys2 would normally install with: + # pacman -S mingw-w64-x86_64-openssl --noconfirm + # + if ( $(ruby --version) -match "^ruby\s+2\.4" ) { + Write-Output "Building OpenSSL gem ~> 2.0.4 to fix Ruby 2.4 / AppVeyor issue" + gem install openssl --version '~> 2.0.4' --no-ri --no-rdoc + } + + gem list openssl + ruby -ropenssl -e 'puts \"OpenSSL Version - #{OpenSSL::OPENSSL_VERSION}\"; puts \"OpenSSL Library Version - #{OpenSSL::OPENSSL_LIBRARY_VERSION}\"' - bundle install --jobs 4 --retry 2 --without system_tests - type Gemfile.lock build: off diff --git a/lib/puppet/functions/fact.rb b/lib/puppet/functions/fact.rb new file mode 100644 index 0000000..dfb048b --- /dev/null +++ b/lib/puppet/functions/fact.rb @@ -0,0 +1,58 @@ +# Digs into the facts hash using dot-notation +# +# Example usage: +# +# fact('osfamily') +# fact('os.architecture') +# +# Array indexing: +# +# fact('mountpoints."/dev".options.1') +# +# Fact containing a "." in the name: +# +# fact('vmware."VRA.version"') +# +Puppet::Functions.create_function(:fact) do + dispatch :fact do + param 'String', :fact_name + end + + def to_dot_syntax(array_path) + array_path.map do |string| + string.include?('.') ? %Q{"#{string}"} : string + end.join('.') + end + + def fact(fact_name) + facts = closure_scope['facts'] + + # Transform the dot-notation string into an array of paths to walk. Make + # sure to correctly extract double-quoted values containing dots as single + # elements in the path. + path = fact_name.scan(/([^."]+)|(?:")([^"]+)(?:")/).map {|x| x.compact.first } + + walked_path = [] + path.reduce(facts) do |d, k| + return nil if d.nil? || k.nil? + + case + when d.is_a?(Array) + begin + result = d[Integer(k)] + rescue ArgumentError => e + Puppet.warning("fact request for #{fact_name} returning nil: '#{to_dot_syntax(walked_path)}' is an array; cannot index to '#{k}'") + result = nil + end + when d.is_a?(Hash) + result = d[k] + else + Puppet.warning("fact request for #{fact_name} returning nil: '#{to_dot_syntax(walked_path)}' is not a collection; cannot walk to '#{k}'") + result = nil + end + + walked_path << k + result + end + end +end diff --git a/lib/puppet/functions/to_json.rb b/lib/puppet/functions/to_json.rb new file mode 100644 index 0000000..782d695 --- /dev/null +++ b/lib/puppet/functions/to_json.rb @@ -0,0 +1,21 @@ +# Take a data structure and output it as JSON +# +# @example how to output JSON +# # output json to a file +# file { '/tmp/my.json': +# ensure => file, +# content => to_json($myhash), +# } +# +# +require 'json' + +Puppet::Functions.create_function(:to_json) do + dispatch :to_json do + param 'Any', :data + end + + def to_json(data) + data.to_json + end +end diff --git a/lib/puppet/functions/to_json_pretty.rb b/lib/puppet/functions/to_json_pretty.rb new file mode 100644 index 0000000..4c28539 --- /dev/null +++ b/lib/puppet/functions/to_json_pretty.rb @@ -0,0 +1,21 @@ +# Take a data structure and output it as pretty JSON +# +# @example how to output pretty JSON +# # output pretty json to a file +# file { '/tmp/my.json': +# ensure => file, +# content => to_json_pretty($myhash), +# } +# +# +require 'json' + +Puppet::Functions.create_function(:to_json_pretty) do + dispatch :to_json_pretty do + param 'Variant[Hash, Array]', :data + end + + def to_json_pretty(data) + JSON.pretty_generate(data) + end +end diff --git a/lib/puppet/functions/to_yaml.rb b/lib/puppet/functions/to_yaml.rb new file mode 100644 index 0000000..fdd7370 --- /dev/null +++ b/lib/puppet/functions/to_yaml.rb @@ -0,0 +1,21 @@ +# Take a data structure and output it as YAML +# +# @example how to output YAML +# # output yaml to a file +# file { '/tmp/my.yaml': +# ensure => file, +# content => to_yaml($myhash), +# } +# +# +require 'yaml' + +Puppet::Functions.create_function(:to_yaml) do + dispatch :to_yaml do + param 'Any', :data + end + + def to_yaml(data) + data.to_yaml + end +end diff --git a/lib/puppet/parser/functions/pw_hash.rb b/lib/puppet/parser/functions/pw_hash.rb index d99ee5b..adcc719 100644 --- a/lib/puppet/parser/functions/pw_hash.rb +++ b/lib/puppet/parser/functions/pw_hash.rb @@ -27,6 +27,13 @@ Puppet::Parser::Functions::newfunction( environment contains several different operating systems, ensure that they are compatible before using this function.") do |args| raise ArgumentError, "pw_hash(): wrong number of arguments (#{args.size} for 3)" if args.size != 3 + args.map! do |arg| + if (defined? Puppet::Pops::Types::PSensitiveType::Sensitive) && (arg.is_a? Puppet::Pops::Types::PSensitiveType::Sensitive) + arg.unwrap + else + arg + end + end raise ArgumentError, "pw_hash(): first argument must be a string" unless args[0].is_a? String or args[0].nil? raise ArgumentError, "pw_hash(): second argument must be a string" unless args[1].is_a? String hashes = { 'md5' => '1', diff --git a/lib/puppet/parser/functions/round.rb b/lib/puppet/parser/functions/round.rb new file mode 100644 index 0000000..489c301 --- /dev/null +++ b/lib/puppet/parser/functions/round.rb @@ -0,0 +1,33 @@ +# +# round.rb +# + +module Puppet::Parser::Functions + newfunction(:round, :type => :rvalue, :doc => <<-EOS + Rounds a number to the nearest integer + + *Examples:* + + round(2.9) + + returns: 3 + + round(2.4) + + returns: 2 + + EOS + ) do |args| + + raise Puppet::ParseError, "round(): Wrong number of arguments given #{args.size} for 1" if args.size != 1 + raise Puppet::ParseError, "round(): Expected a Numeric, got #{args[0].class}" unless args[0].is_a? Numeric + + value = args[0] + + if value >= 0 + Integer(value + 0.5) + else + Integer(value - 0.5) + end + end +end diff --git a/lib/puppet/parser/functions/unique.rb b/lib/puppet/parser/functions/unique.rb index b57431d..1e2a895 100644 --- a/lib/puppet/parser/functions/unique.rb +++ b/lib/puppet/parser/functions/unique.rb @@ -24,6 +24,10 @@ This returns: EOS ) do |arguments| + if Puppet::Util::Package.versioncmp(Puppet.version, '5.0.0') >= 0 + function_deprecation([:unique, 'This method is deprecated, please use the core puppet unique function. There is further documentation for the function in the release notes of Puppet 5.0.']) + end + raise(Puppet::ParseError, "unique(): Wrong number of arguments given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] diff --git a/lib/puppet/parser/functions/validate_domain_name.rb b/lib/puppet/parser/functions/validate_domain_name.rb new file mode 100644 index 0000000..c3fad78 --- /dev/null +++ b/lib/puppet/parser/functions/validate_domain_name.rb @@ -0,0 +1,39 @@ +module Puppet::Parser::Functions + newfunction(:validate_domain_name, :doc => <<-ENDHEREDOC + Validate that all values passed are syntactically correct domain names. + Fail compilation if any value fails this check. + + 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') + + ENDHEREDOC + ) 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/lib/puppet/provider/file_line/ruby.rb b/lib/puppet/provider/file_line/ruby.rb index 1d27424..16f2709 100644 --- a/lib/puppet/provider/file_line/ruby.rb +++ b/lib/puppet/provider/file_line/ruby.rb @@ -1,37 +1,42 @@ Puppet::Type.type(:file_line).provide(:ruby) do def exists? - found = true - if resource[:replace].to_s != 'true' and count_matches(match_regex) > 0 - found = true + found = false + lines_count = 0 + lines.each do |line| + found = line.chomp == resource[:line] + if found + lines_count += 1 + end + end + if resource[:match] == nil + found = lines_count > 0 else - lines.find do |line| - if resource[:ensure].to_s == 'absent' and resource[:match_for_absence].to_s == 'true' - found = line.chomp =~ Regexp.new(resource[:match]) - else - found = line.chomp == resource[:line].chomp - end - if found == false then - break - end + match_count = count_matches(new_match_regex) + if resource[:append_on_no_match].to_s == 'false' + found = true + elsif resource[:replace].to_s == 'true' + found = lines_count > 0 && lines_count == match_count + else + found = match_count > 0 end end found end def create - unless resource[:replace].to_s != 'true' and count_matches(match_regex) > 0 + unless resource[:replace].to_s != 'true' && count_matches(new_match_regex) > 0 if resource[:match] handle_create_with_match elsif resource[:after] handle_create_with_after else - append_line + handle_append_line end end end def destroy - if resource[:match_for_absence].to_s == 'true' and resource[:match] + if resource[:match_for_absence].to_s == 'true' && resource[:match] handle_destroy_with_match else handle_destroy_line @@ -39,6 +44,7 @@ Puppet::Type.type(:file_line).provide(:ruby) do end private + def lines # If this type is ever used with very large files, we should # write this in a different way, using a temp @@ -53,25 +59,34 @@ Puppet::Type.type(:file_line).provide(:ruby) do end end - def match_regex + def new_after_regex + resource[:after] ? Regexp.new(resource[:after]) : nil + end + + def new_match_regex resource[:match] ? Regexp.new(resource[:match]) : nil end + def count_matches(regex) + lines.select{ |line| line.match(regex) }.size + end + def handle_create_with_match() - regex_after = resource[:after] ? Regexp.new(resource[:after]) : nil - match_count = count_matches(match_regex) + after_regex = new_after_regex + match_regex = new_match_regex + match_count = count_matches(new_match_regex) if match_count > 1 && resource[:multiple].to_s != 'true' raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'" end File.open(resource[:path], 'w') do |fh| - lines.each do |l| - fh.puts(match_regex.match(l) ? resource[:line] : l) - if (match_count == 0 and regex_after) - if regex_after.match(l) + lines.each do |line| + fh.puts(match_regex.match(line) ? resource[:line] : line) + if match_count == 0 && after_regex + if after_regex.match(line) fh.puts(resource[:line]) - match_count += 1 #Increment match_count to indicate that the new line has been inserted. + match_count += 1 # Increment match_count to indicate that the new line has been inserted. end end end @@ -83,32 +98,29 @@ Puppet::Type.type(:file_line).provide(:ruby) do end def handle_create_with_after - regex = Regexp.new(resource[:after]) - count = count_matches(regex) + after_regex = new_after_regex + after_count = count_matches(after_regex) - if count > 1 && resource[:multiple].to_s != 'true' - raise Puppet::Error, "#{count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern." + if after_count > 1 && resource[:multiple].to_s != 'true' + raise Puppet::Error, "#{after_count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern." end - File.open(resource[:path], 'w') do |fh| - lines.each do |l| - fh.puts(l) - if regex.match(l) then + File.open(resource[:path],'w') do |fh| + lines.each do |line| + fh.puts(line) + if after_regex.match(line) fh.puts(resource[:line]) end end - end - if (count == 0) # append the line to the end of the file - append_line + if (after_count == 0) + fh.puts(resource[:line]) + end end end - def count_matches(regex) - lines.select{|l| l.match(regex)}.size - end - def handle_destroy_with_match + match_regex = new_match_regex match_count = count_matches(match_regex) if match_count > 1 && resource[:multiple].to_s != 'true' raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'" @@ -116,27 +128,23 @@ Puppet::Type.type(:file_line).provide(:ruby) do local_lines = lines File.open(resource[:path],'w') do |fh| - fh.write(local_lines.reject{|l| match_regex.match(l) }.join('')) + fh.write(local_lines.reject{ |line| match_regex.match(line) }.join('')) end end def handle_destroy_line local_lines = lines File.open(resource[:path],'w') do |fh| - fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join('')) + fh.write(local_lines.reject{ |line| line.chomp == resource[:line] }.join('')) end end - ## - # append the line to the file. - # - # @api private - def append_line - File.open(resource[:path], 'w') do |fh| - lines.each do |l| - fh.puts(l) + def handle_append_line + File.open(resource[:path],'w') do |fh| + lines.each do |line| + fh.puts(line) end - fh.puts resource[:line] + fh.puts(resource[:line]) end end end diff --git a/lib/puppet/type/file_line.rb b/lib/puppet/type/file_line.rb index 3d691bf..b2357b8 100644 --- a/lib/puppet/type/file_line.rb +++ b/lib/puppet/type/file_line.rb @@ -58,7 +58,7 @@ Puppet::Type.newtype(:file_line) do encoding => "iso-8859-1", } - Files with special characters that are not valid UTF-8 will give the + Files with special characters that are not valid UTF-8 will give the error message "invalid byte sequence in UTF-8". In this case, determine the correct file encoding and specify the correct encoding using the encoding attribute, the value of which needs to be a valid Ruby character @@ -136,6 +136,12 @@ Puppet::Type.newtype(:file_line) do defaultto 'UTF-8' end + newparam(:append_on_no_match) do + desc 'If true, append line if match is not found. If false, do not append line if a match is not found' + newvalues(true, false) + defaultto true + end + # Autorequire the file resource if it's being managed autorequire(:file) do self[:path] diff --git a/metadata.json b/metadata.json index f2b0204..f1f9562 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "puppetlabs-stdlib", - "version": "4.17.1", + "version": "4.20.0", "author": "puppetlabs", "summary": "Standard library of resources for Puppet modules.", "license": "Apache-2.0", @@ -55,7 +55,6 @@ { "operatingsystem": "Debian", "operatingsystemrelease": [ - "6", "7", "8" ] @@ -63,8 +62,6 @@ { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ - "10.04", - "12.04", "14.04", "16.04" ] @@ -101,7 +98,7 @@ "requirements": [ { "name": "puppet", - "version_requirement": ">= 4.7.0 < 6.0.0" + "version_requirement": ">=2.7.20 < 6.0.0" } ], "description": "Standard Library for Puppet Modules", diff --git a/readmes/README_ja_JP.md b/readmes/README_ja_JP.md index ae74d25..013b74c 100644 --- a/readmes/README_ja_JP.md +++ b/readmes/README_ja_JP.md @@ -4,8 +4,8 @@ 1. [モジュールの説明 - モジュールの機能とその有益性](#モジュールの説明) 1. [セットアップ - stdlib導入の基本](#セットアップ) -1. [使用 - 設定オプションと追加機能](#使用) -1. [リファレンス - モジュールの機能と動作について](#リファレンス) +1. [使用 - 設定オプションと追加機能](#使用方法) +1. [リファレンス - モジュールの機能と動作について](#参考) 1. [クラス](#クラス) 1. [定義タイプ](#定義タイプ) 1. [データタイプ](#データタイプ) @@ -18,7 +18,7 @@ ## モジュールの説明 -このモジュールでは、Puppetモジュールのリソースの 標準ライブラリを提供しています。Puppetモジュールでは、この標準ライブラリを広く使用しています。stdlibモジュールは、以下のリソースをPuppetに追加します。 +このモジュールでは、Puppetモジュールリソースの標準ライブラリを提供しています。Puppetモジュールでは、この標準ライブラリを広く使用しています。stdlibモジュールは、以下のリソースをPuppetに追加します。 * ステージ * Facts @@ -35,7 +35,7 @@ stdlibモジュールを[インストール](https://docs.puppet.com/puppet/late stdlibに依存するモジュールを記述する場合は、必ずmetadata.jsonで[依存関係を特定](https://docs.puppet.com/puppet/latest/modules_metadata.html#specifying-dependencies)してください。 -## 使用 +## 使用方法 stdlibのほとんどの機能は、Puppetに自動的にロードされます。Puppetで標準化されたランステージを使用するには、`include stdlib`を用いてマニフェスト内でこのクラスを宣言してください。 @@ -63,12 +63,12 @@ node default { ## リファレンス -* [パブリッククラス][] -* [プライベートクラス][] -* [定義タイプ][] -* [データタイプ][] -* [Facts][] -* [関数][] +* [パブリッククラス](#パブリッククラス) +* [プライベートクラス](#プライベートクラス) +* [定義タイプ](#定義タイプ) +* [データタイプ](#データタイプ) +* [Facts](#facts) +* [関数](#関数) ### クラス @@ -115,6 +115,18 @@ file_line { 'bashrc_proxy': 上の例では、`match`により、'export'で始まり'HTTP_PROXY'と続く行が探され、その行が行内の値に置き換えられます。 +マッチ例: + + file_line { 'bashrc_proxy': + ensure => present, + path => '/etc/bashrc', + line => 'export HTTP_PROXY=http://squid.puppetlabs.vm:3128', + match => '^export\ HTTP_PROXY\=', + append_on_no_match => false, + } + +このコードの例では、`match`によってexportで始まりHTTP_PROXYが続く行が検索され、その行が行内の値に置き換えられます。マッチするものが見つからない場合、ファイルは変更されません。 + `ensure => absent`を用いたマッチ例: ```puppet @@ -145,89 +157,89 @@ file_line { "XScreenSaver": **Autorequire:** Puppetが管理しているファイルに、管理対象となる行が含まれている場合は、`file_line`リソースと当該ファイルの暗黙的な依存関係が設定されます。 -##### パラメータ +**パラメータ** パラメータは、別途説明がない限り、すべてオプションです。 -* `after` - - このパラメータで指定された行の後に、Puppetが正規表現を用いて新規の行を追加します(既存の行が規定の位置に追加されます)。 - - 値: 正規表現を含む文字列 - - デフォルト値: `undef` - -* `encoding` - - 適正なファイルエンコードを指定します。 - - 値: 有効なRuby文字エンコードを指定する文字列 - - デフォルト: 'UTF-8' - -* `ensure`: リソースが存在するかどうかを指定します。 - - 値: 'present'、'absent' - - デフォルト値: 'present' - -* `line` - - **必須** - - `path`パラメータにより位置を示されたファイルに追加する行を設定します。 - - 値: 文字列 - -* `match` - - ファイル内の既存の行と比較する正規表現を指定します。マッチが見つかった場合、新規の行を追加するかわりに、置き換えられます。正規表現の比較は行の値に照らして行われ、マッチしない場合は、例外が発生します。 - - 値: 正規表現を含む文字列 - - デフォルト値: `undef` - - -* `match_for_absence` - - `ensure => absent`の場合にマッチを適用するかどうかを指定します。`true`に設定してマッチを設定すると、マッチする行が削除されます。`false`に設定すると(デフォルト)、`ensure => absent`の場合にマッチが無視され、代わりに`line`の値が使用されます。`ensure => present`になっている場合は、このパラメータは無視されます。 - - ブーリアン - - デフォルト値: `false` - -* `multiple` - - `match`および`after`により複数の行を変更できるかどうかを指定します。`false`に設定すると、複数の行がマッチする場合に例外が発生します。 - - 値: `true`、`false` - - デフォルト値: `false` - - -* `name` - - リソースの名称として使用する名前を指定します。リソースのnamevarをリソースの規定の`title`と異なるものにしたい場合は、`name`で名前を指定します。 - - 値: 文字列 - - デフォルト値: タイトルの値 - -* `path` - - **必須** - - `line`で指定された行を確保するファイルを指定します。 - - 値: 当該ファイルの絶対パスを指定する文字列 - -* `replace` - - `match`パラメータとマッチする既存の行を上書きするかどうかを指定します。`false`に設定すると、`match`パラメータにマッチする行が見つかった場合、その行はファイルに配置されません。 - - ブーリアン - - デフォルト値: `true` +##### `after` + +このパラメータで指定された行の後に、Puppetが正規表現を用いて新規の行を追加します(既存の行が規定の位置に追加されます)。 + +値: 正規表現を含む文字列 + +デフォルト値: `undef` + +##### `encoding` + +適正なファイルエンコードを指定します。 + +値: 有効なRuby文字エンコードを指定する文字列 + +デフォルト: 'UTF-8' + +##### `ensure`: リソースが存在するかどうかを指定します。 + +値: 'present'、'absent' + +デフォルト値: 'present' + +##### `line` + +**必須** + +`path`パラメータにより位置を示されたファイルに追加する行を設定します。 + +値: 文字列 + +##### `match` + +ファイル内の既存の行と比較する正規表現を指定します。マッチが見つかった場合、新規の行を追加するかわりに、置き換えられます。正規表現の比較は行の値に照らして行われ、マッチしない場合は、例外が発生します。 + +値: 正規表現を含む文字列 + +デフォルト値: `undef` + + +##### `match_for_absence` + +`ensure => absent`の場合にマッチを適用するかどうかを指定します。`true`に設定してマッチを設定すると、マッチする行が削除されます。`false`に設定すると(デフォルト)、`ensure => absent`の場合にマッチが無視され、代わりに`line`の値が使用されます。`ensure => present`になっている場合は、このパラメータは無視されます。 + +ブーリアン + +デフォルト値: `false` + +##### `multiple` + +`match`および`after`により複数の行を変更できるかどうかを指定します。`false`に設定すると、複数の行がマッチする場合に例外が発生します。 + +値: `true`、`false` + +デフォルト値: `false` + + +##### `name` + +リソースの名称として使用する名前を指定します。リソースのnamevarをリソースの規定の`title`と異なるものにしたい場合は、`name`で名前を指定します。 + +値: 文字列 + +デフォルト値: タイトルの値 + +##### `path` + +**必須** + +`line`で指定された行を確保するファイルを指定します。 + +値: 当該ファイルの絶対パスを指定する文字列 + +##### `replace` + +`match`パラメータとマッチする既存の行を上書きするかどうかを指定します。`false`に設定すると、`match`パラメータにマッチする行が見つかった場合、その行はファイルに配置されません。 + +ブーリアン + +デフォルト値: `true` ### データタイプ @@ -289,6 +301,10 @@ http://hello.com httds://notquiteright.org ``` +#### `Stdlib::MAC` + +[RFC5342](https://tools.ietf.org/html/rfc5342)で定義されるMACアドレスに一致します。 + #### `Stdlib::Unixpath` Unixオペレーティングシステムのパスに一致します。 @@ -311,7 +327,7 @@ C:/whatever Windowsオペレーティングシステムのパスに一致します。 -使用可能なインプット例: +使用可能なインプット例: ```shell C:\\WINDOWS\\System32 @@ -468,10 +484,10 @@ base64('decode', 'aHR0cHM6Ly9wdXBwZXRsYWJzLmNvbQ==', 'urlsafe') * `false`、'f'、'0'、'n'、'no'を0に変換します。 * `true`、't'、'1'、'y'、'yes'を1に変換します。 - - 引数: インプットとして、単一のブーリアンまたは文字列。 - - *タイプ*: 右辺値 + +引数: インプットとして、単一のブーリアンまたは文字列。 + +*タイプ*: 右辺値 #### `bool2str` @@ -538,8 +554,8 @@ bool2str(false, 't', 'f') => 'f' * `clamp('24', [575, 187])`は187を返します。 * `clamp(16, 88, 661)`は88を返します。 * `clamp([4, 3, '99'])`は4を返します。 - -引数: 文字列、配列、数字。 + +引数: 文字列、配列、数字。 *タイプ*: 右辺値 @@ -690,7 +706,7 @@ Puppetの他の設定は、stdlibの`deprecation`関数に影響を与えます 非推奨警告を記録するかどうかを指定します。これは特に、自動テストの際、移行の準備ができる前にログに情報が氾濫するのを避けるうえで役立ちます。 この変数はブーリアンで、以下の効果があります: - + * `true`: 警告を記録します。 * `false`: 警告は記録されません。 * 値を設定しない場合: Puppet 4は警告を出しますが、Puppet 3は出しません。 @@ -791,7 +807,7 @@ $value = dig44($data, ['a', 'b', 'c', 'd'], 'not_found') 与えられた文字列のUnixバージョンを返します。クロスプラットフォームテンプレートでファイルリソースを使用する場合に非常に役立ちます。 ```puppet -file{$config_file: +file { $config_file: ensure => file, content => dos2unix(template('my_module/settings.conf.erb')), } @@ -895,7 +911,32 @@ userlist: ensure_resources('user', hiera_hash('userlist'), {'ensure' => 'present'}) ``` -### `flatten` +#### `fact` + +指定されたfactの値を返します。構造化されたfactを参照する場合にドット表記を使用することができます。指定されたfactが存在しない場合は、Undefを返します。 + +使用例: + +```puppet +fact('kernel') +fact('osfamily') +fact('os.architecture') +``` + +配列のインデックス: + +```puppet +$first_processor = fact('processors.models.0') +$second_processor = fact('processors.models.1') +``` + +名前に「.」を含むfact: + +```puppet +fact('vmware."VRA.version"') +``` + +#### `flatten` ネストの深いアレイを平坦化し、結果として単一のフラット配列を返します。 @@ -1167,6 +1208,13 @@ if $baz.is_a(String) { *タイプ*: 右辺値 +#### `is_email_address` + +この関数に渡された文字列が有効なメールアドレスである場合にtrueを返します。 + +*タイプ*: 右辺値 + + #### `is_float` **非推奨。今後のバージョンのstdlibでは削除されます。[`validate_legacy`](#validate_legacy)を参照してください。** @@ -1253,7 +1301,7 @@ if $baz.is_a(String) { #### `join_keys_to_values` -区切り文字を用いて、ハッシュの各キーをそのキーに対応する値と結合し、結果を文字列として返します。 +区切り文字を用いて、ハッシュの各キーをそのキーに対応する値と結合し、結果を文字列として返します。 値が配列の場合は、キーは各要素の前に置かれます。返される値は、平坦化した配列になります。 @@ -1407,7 +1455,7 @@ YAMLの文字列を正確なPuppet構造に変換します。 * 第1の引数として、変換されるYAML文字列。 * オプションで、第2のエラーとして、変換に失敗した場合に返される結果。 -*タイプ*: 右辺値 +*タイプ*: 右辺値 #### `pick` @@ -1509,6 +1557,10 @@ crypt関数を用いてパスワードをハッシュします。ほとんどの 文字列または配列の順序を逆転します。 +#### `round` + + 数値を最も近い整数に丸めます。 + *タイプ*: 右辺値 #### `rstrip` @@ -1696,19 +1748,43 @@ OS Xバージョン10.7以上で使用されるソルト付きSHA512パスワー 引数をバイトに変換します。 -たとえば、"4 kB"は"4096"になります。 +たとえば、"4 kB"は"4096"になります。 引数: 単一の文字列。 *タイプ*: 右辺値 +#### `to_json` + +入力値をJSON形式の文字列に変換します。 + +例えば、`{ "key" => "value" }`は`{"key":"value"}`になります。 + +*タイプ*: 右辺値 + +#### `to_json_pretty` + +入力値を整形されたJSON形式の文字列に変換します。 + +例えば、`{ "key" => "value" }`は`{\n \"key\": \"value\"\n}`になります。 + +*タイプ*: 右辺値 + +#### `to_yaml` + +入力値をYAML形式の文字列に変換します。 + +例えば、`{ "key" => "value" }`は`"---\nkey: value\n"`になります。 + +*タイプ*: 右辺値 + #### `try_get_value` **非推奨:** `dig()`に置き換えられました。 ハッシュおよび配列の複数レイヤー内の値を取得します。 -引数: +引数: * 第1の引数として、パスを含む文字列。この引数は、ゼロではじまり、パス区切り文字(デフォルトは"/")で区切ったハッシュキーまたは配列インデックスの文字列として提示してください。この関数は各パスコンポーネントにより構造内を移動し、パスの最後で値を返すよう試みます。 @@ -1799,7 +1875,7 @@ $value = try_get_value($data, 'a|b', [], '|') *タイプ*: 右辺値 ```puppet -file{$config_file: +file { $config_file: ensure => file, content => unix2dos(template('my_module/settings.conf.erb')), } @@ -1819,7 +1895,7 @@ file{$config_file: #### `uriescape` -文字列または文字列の配列をURLエンコードします。 +文字列または文字列の配列をURLエンコードします。 引数: 単一の文字列または文字列の配列。 @@ -1915,7 +1991,7 @@ validate_augeas($sudoerscontent, 'Sudoers.lns', [], 'Failed to validate sudoers **非推奨。今後のバージョンのstdlibでは削除されます。[`validate_legacy`](#validate_legacy)を参照してください。** -渡されたすべての値が`true`または`false`のいずれかであることを確認します。 +渡されたすべての値が`true`または`false`のいずれかであることを確認します。 このチェックで不合格となった値がある場合は、カタログコンパイルが中止されます。 以下の値が渡されます: @@ -1958,6 +2034,53 @@ validate_cmd($haproxycontent, '/usr/sbin/haproxy -f % -c', 'Haproxy failed to va *タイプ*: ステートメント +#### `validate_domain_name` + +**非推奨。今後のバージョンのstdlibでは削除されます。[`validate_legacy`](#validate_legacy)を参照してください。** + +渡されたすべての値が構文的に正しいドメイン名であることを確認します。このチェックで不合格となった値がある場合は、カタログコンパイルが中止されます。 + +以下の値が渡されます: + +~~~ +$my_domain_name = 'server.domain.tld' +validate_domain_name($my_domain_name) +validate_domain_name('domain.tld', 'puppet.com', $my_domain_name) +~~~ + +以下の値が不合格となり、コンパイルが中止されます: + +~~~ +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') +~~~ + +*タイプ*: ステートメント + +#### `validate_email_address` + +渡されたすべての値が有効なメールアドレスであることを確認します。このチェックで不合格となった値がある場合、コンパイルが失敗します。 + +以下の値が渡されます: + +~~~ +$my_email = "waldo@gmail.com" +validate_email_address($my_email) +validate_email_address("bob@gmail.com", "alice@gmail.com", $my_email) +~~~ + +以下の値が不合格となり、コンパイルが中止されます: + +~~~ +$some_array = [ 'bad_email@/d/efdf.com' ] +validate_email_address($some_array) +~~~ + +*タイプ*: ステートメント + #### `validate_hash` **非推奨。今後のバージョンのstdlibでは削除されます。[`validate_legacy`](#validate_legacy)を参照してください。** @@ -1992,7 +2115,7 @@ validate_hash($undefined) * 第1の引数として、整数または整数の配列。 * オプションの第2の引数として、最大値。第1の引数(のすべての要素)は、この最大値以下でなければなりません。 -* オプションの第3の引数として、最小値。第1の引数(のすべての要素)は、この最小値以上でなければなりません。 +* オプションの第3の引数として、最小値。第1の引数(のすべての要素)は、この最小値以上でなければなりません。 第1の引数が整数または整数の配列でない場合や、第2または第3の引数が整数に変換できない場合は、この関数は失敗になります。ただし、最小値が与えられている場合は(この場合に限られます)、第2の引数を空文字列または`undef`にすることが可能です。これは、最小チェックを確実に行うためのプレースホルダーとして機能します。 @@ -2046,7 +2169,7 @@ validate_integer(1, 3, true) **非推奨。今後のバージョンのstdlibでは削除されます。[`validate_legacy`](#validate_legacy)を参照してください。** -IPv4アドレスかIPv6アドレスかにかかわらず、引数がIPアドレスであることを確認します。また、ネットマスクによりIPアドレスを確認します。 +IPv4アドレスかIPv6アドレスかにかかわらず、引数がIPアドレスであることを確認します。また、ネットマスクによりIPアドレスを確認します。 引数: IPアドレスを指定する文字列。 @@ -2117,7 +2240,7 @@ Puppet 4とともに、非推奨の `validate_*`関数を用いたモジュー `validate_legacy`関数は、モジュールユーザの使用している機能を中断させずに、 Puppet 3形式の確認からPuppet 4形式の確認に移行するのに役立ちます。 -Puppet 4形式の確認に移行すれば、[データタイプ](https://docs.puppet.com/puppet/latest/reference/lang_data.html)を用いた、より明確な定義タイプチェックが可能になります。Puppet 3の`validate_*` 関数の多くは、確認という点で驚くほど多くの穴があります。たとえば、[validate_numeric](#validate_numeric)では、細部をコントロールできないため、数字だけでなく、数字の配列や数字のように見える文字列も許可されます。 +Puppet 4形式の確認に移行すれば、[データタイプ](https://docs.puppet.com/puppet/latest/reference/lang_data.html)を用いた、より明確な定義タイプチェックが可能になります。Puppet 3の`validate_*` 関数の多くは、確認という点で驚くほど多くの穴があります。たとえば、[validate_numeric](#validate_numeric)では、細部をコントロールできないため、数字だけでなく、数字の配列や数字のように見える文字列も許可されます。 クラスおよび定義タイプの各パラメータについて、使用する新しいPuppet 4データタイプを選択してください。たいていの場合、新しいデータタイプにより、元の`validate_*`関数とは異なる値のセットを使用できるようになります。以下のような状況になります: diff --git a/spec/aliases/absolutepath_spec.rb b/spec/aliases/absolutepath_spec.rb index cd442f2..ff23dc0 100644 --- a/spec/aliases/absolutepath_spec.rb +++ b/spec/aliases/absolutepath_spec.rb @@ -40,7 +40,7 @@ if Puppet::Util::Package.versioncmp(Puppet.version, '4.5.0') >= 0 ].each do |value| describe value.inspect do let(:params) {{ value: value }} - it { is_expected.to compile.and_raise_error(/parameter 'value' expects a match for Variant/) } + it { is_expected.to compile.and_raise_error(/parameter 'value' expects a match for.*Variant/) } end end end diff --git a/spec/aliases/float_spec.rb b/spec/aliases/float_spec.rb index 66079c6..84e1934 100644 --- a/spec/aliases/float_spec.rb +++ b/spec/aliases/float_spec.rb @@ -20,7 +20,7 @@ if Puppet::Util::Package.versioncmp(Puppet.version, '4.5.0') >= 0 [ true, 'true', false, 'false', 'iAmAString', '1test', '1 test', 'test 1', 'test 1 test', {}, { 'key' => 'value' }, { 1=> 2 }, '', :undef , 'x', 3, '3', -3, '-3'].each do |value| describe value.inspect do let(:params) {{ value: value }} - it { is_expected.to compile.and_raise_error(/parameter 'value' expects a value of type Float or Pattern(\[.*\]+)?/) } + it { is_expected.to compile.and_raise_error(/parameter 'value' expects.*Float.*Pattern/) } end end end diff --git a/spec/aliases/integer_spec.rb b/spec/aliases/integer_spec.rb index 8cb4658..9cf0357 100644 --- a/spec/aliases/integer_spec.rb +++ b/spec/aliases/integer_spec.rb @@ -22,7 +22,11 @@ if Puppet::Util::Package.versioncmp(Puppet.version, '4.5.0') >= 0 [ "foo\nbar", true, 'true', false, 'false', 'iAmAString', '1test', '1 test', 'test 1', 'test 1 test', {}, { 'key' => 'value' }, { 1=> 2 }, '', :undef , 'x', 3.7, '3.7',-3.7, '-342.2315e-12' ].each do |value| describe value.inspect do let(:params) {{ value: value }} - it { is_expected.to compile.and_raise_error(/parameter 'value' expects a value of type Integer, Pattern(\[.*\]+)?, or Array/) } + if Puppet::Util::Package.versioncmp(Puppet.version, '5.0.0') >= 0 + it { is_expected.to compile.and_raise_error(/parameter 'value' expects a Stdlib::Compat::Integer = Variant\[Integer, Pattern\[.*\], Array\[.*\]\] value/) } + else + it { is_expected.to compile.and_raise_error(/parameter 'value' expects a value of type Integer, Pattern(\[.*\]+)?, or Array/) } + end end end end diff --git a/spec/aliases/numeric_spec.rb b/spec/aliases/numeric_spec.rb index bc17f4f..09c28ec 100644 --- a/spec/aliases/numeric_spec.rb +++ b/spec/aliases/numeric_spec.rb @@ -24,7 +24,7 @@ if Puppet::Util::Package.versioncmp(Puppet.version, '4.5.0') >= 0 [ true, 'true', false, 'false', 'iAmAString', '1test', '1 test', 'test 1', 'test 1 test', {}, { 'key' => 'value' }, { 1=> 2 }, '', :undef , 'x' ].each do |value| describe value.inspect do let(:params) {{ value: value }} - it { is_expected.to compile.and_raise_error(/parameter 'value' expects a value of type Numeric, Pattern(\[.*\]+)?, or Array/) } + it { is_expected.to compile.and_raise_error(/parameter 'value' expects.*Numeric.*Pattern.*Array/) } end end end diff --git a/spec/aliases/string_spec.rb b/spec/aliases/string_spec.rb index 3ea1967..4fc8ce6 100644 --- a/spec/aliases/string_spec.rb +++ b/spec/aliases/string_spec.rb @@ -24,7 +24,7 @@ if Puppet::Util::Package.versioncmp(Puppet.version, '4.5.0') >= 0 ].each do |value| describe value.inspect do let(:params) {{ value: value }} - it { is_expected.to compile.and_raise_error(/parameter 'value' expects a (?:value of type Undef or )?String/) } + it { is_expected.to compile.and_raise_error(/parameter 'value' expects a (?:value of type Undef or )?.*String/) } end end end diff --git a/spec/functions/deep_merge_spec.rb b/spec/functions/deep_merge_spec.rb index c91a07e..819e025 100755 --- a/spec/functions/deep_merge_spec.rb +++ b/spec/functions/deep_merge_spec.rb @@ -4,7 +4,7 @@ describe 'deep_merge' do it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) } it { is_expected.to run.with_params({ 'key' => 'value' }).and_raise_error(Puppet::ParseError, /wrong number of arguments/i) } it { is_expected.to run.with_params({}, '2').and_raise_error(Puppet::ParseError, /unexpected argument type String/) } - it { is_expected.to run.with_params({}, 2).and_raise_error(Puppet::ParseError, /unexpected argument type Fixnum/) } + it { is_expected.to run.with_params({}, 2).and_raise_error(Puppet::ParseError, /unexpected argument/) } it { is_expected.to run.with_params({}, '').and_return({}) } it { is_expected.to run.with_params({}, {}).and_return({}) } it { is_expected.to run.with_params({}, {}, {}).and_return({}) } diff --git a/spec/functions/ensure_resources_spec.rb b/spec/functions/ensure_resources_spec.rb index aea723e..7cca671 100644 --- a/spec/functions/ensure_resources_spec.rb +++ b/spec/functions/ensure_resources_spec.rb @@ -1,22 +1,25 @@ require 'spec_helper' -describe 'test::ensure_resources', type: :class do - let(:params) {{ resource_type: 'user', title_hash: title_param, attributes_hash: {'ensure' => 'present'} }} +describe 'ensure_resources' do + it { is_expected.not_to eq(nil) } + it { is_expected.to run.with_params().and_raise_error(ArgumentError, /Must specify a type/) } + it { is_expected.to run.with_params('type').and_raise_error(ArgumentError, /Must specify a title/) } describe 'given a title hash of multiple resources' do + before { subject.call(['user', {'dan' => { 'gid' => 'mygroup', 'uid' => '600' }, 'alex' => { 'gid' => 'mygroup', 'uid' => '700'}}, {'ensure' => 'present'}]) } - let(:title_param) { {'dan' => { 'gid' => 'mygroup', 'uid' => '600' }, 'alex' => { 'gid' => 'mygroup', 'uid' => '700'}} } - - it { is_expected.to compile } - it { is_expected.to contain_user('dan').with({ 'gid' => 'mygroup', 'uid' => '600', 'ensure' => 'present'}) } - it { is_expected.to contain_user('alex').with({ 'gid' => 'mygroup', 'uid' => '700', 'ensure' => 'present'}) } + # this lambda is required due to strangeness within rspec-puppet's expectation handling + it { expect(lambda { catalogue }).to contain_user('dan').with_ensure('present') } + it { expect(lambda { catalogue }).to contain_user('alex').with_ensure('present') } + it { expect(lambda { catalogue }).to contain_user('dan').with({ 'gid' => 'mygroup', 'uid' => '600'}) } + it { expect(lambda { catalogue }).to contain_user('alex').with({ 'gid' => 'mygroup', 'uid' => '700'}) } end describe 'given a title hash of a single resource' do + before { subject.call(['user', {'dan' => { 'gid' => 'mygroup', 'uid' => '600' }}, {'ensure' => 'present'}]) } - let(:title_param) { {'dan' => { 'gid' => 'mygroup', 'uid' => '600' }} } - - it { is_expected.to compile } - it { is_expected.to contain_user('dan').with({ 'gid' => 'mygroup', 'uid' => '600', 'ensure' => 'present'}) } + # this lambda is required due to strangeness within rspec-puppet's expectation handling + it { expect(lambda { catalogue }).to contain_user('dan').with_ensure('present') } + it { expect(lambda { catalogue }).to contain_user('dan').with({ 'gid' => 'mygroup', 'uid' => '600'}) } end end diff --git a/spec/functions/merge_spec.rb b/spec/functions/merge_spec.rb index 7b53363..3b2e3ef 100755 --- a/spec/functions/merge_spec.rb +++ b/spec/functions/merge_spec.rb @@ -4,7 +4,7 @@ describe 'merge' do it { is_expected.not_to eq(nil) } it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) } it { is_expected.to run.with_params({}, 'two').and_raise_error(Puppet::ParseError, /unexpected argument type String/) } - it { is_expected.to run.with_params({}, 1).and_raise_error(Puppet::ParseError, /unexpected argument type Fixnum/) } + it { is_expected.to run.with_params({}, 1).and_raise_error(Puppet::ParseError, /unexpected argument type (Fixnum|Integer)/) } it { pending 'should not special case this' is_expected.to run.with_params({}).and_return({}) diff --git a/spec/functions/pw_hash_spec.rb b/spec/functions/pw_hash_spec.rb index df5348c..9e03464 100644 --- a/spec/functions/pw_hash_spec.rb +++ b/spec/functions/pw_hash_spec.rb @@ -65,5 +65,13 @@ describe 'pw_hash' do it { is_expected.to run.with_params('password', 'sha-256', 'salt').and_return('$5$salt$Gcm6FsVtF/Qa77ZKD.iwsJlCVPY0XSMgLJL0Hnww/c1') } it { is_expected.to run.with_params('password', 'sha-512', 'salt').and_return('$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.') } end + + if Puppet::Util::Package.versioncmp(Puppet.version, '4.7.0') >= 0 + describe 'when arguments are sensitive' do + it { is_expected.to run.with_params(Puppet::Pops::Types::PSensitiveType::Sensitive.new('password'), 'md5', 'salt').and_return('$1$salt$qJH7.N4xYta3aEG/dfqo/0') } + it { is_expected.to run.with_params(Puppet::Pops::Types::PSensitiveType::Sensitive.new('password'), 'md5', Puppet::Pops::Types::PSensitiveType::Sensitive.new('salt')).and_return('$1$salt$qJH7.N4xYta3aEG/dfqo/0') } + it { is_expected.to run.with_params('password', 'md5', Puppet::Pops::Types::PSensitiveType::Sensitive.new('salt')).and_return('$1$salt$qJH7.N4xYta3aEG/dfqo/0') } + end + end end end diff --git a/spec/functions/round_spec.rb b/spec/functions/round_spec.rb new file mode 100755 index 0000000..8b13478 --- /dev/null +++ b/spec/functions/round_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe 'round' do + it { is_expected.not_to eq(nil) } + it { is_expected.to run.with_params(34.3).and_return(34) } + it { is_expected.to run.with_params(-34.3).and_return(-34) } + it { is_expected.to run.with_params(34.5).and_return(35) } + it { is_expected.to run.with_params(-34.5).and_return(-35) } + it { is_expected.to run.with_params(34.7).and_return(35) } + it { is_expected.to run.with_params(-34.7).and_return(-35) } + it { is_expected.to run.with_params("test").and_raise_error Puppet::ParseError } + it { is_expected.to run.with_params("test", "best").and_raise_error Puppet::ParseError } + it { is_expected.to run.with_params(3, 4).and_raise_error Puppet::ParseError } +end diff --git a/spec/functions/strftime_spec.rb b/spec/functions/strftime_spec.rb index e76774a..41cda6a 100755 --- a/spec/functions/strftime_spec.rb +++ b/spec/functions/strftime_spec.rb @@ -14,9 +14,9 @@ describe 'strftime' do expect(result.to_i).to(be > 1311953157) end - it "using %s should be lower then 1.5 trillion" do + it "using %s should be greater than 1.5 trillion" do result = scope.function_strftime(["%s"]) - expect(result.to_i).to(be < 1500000000) + expect(result.to_i).to(be > 1500000000) end it "should return a date when given %Y-%m-%d" do diff --git a/spec/functions/to_json_pretty_spec.rb b/spec/functions/to_json_pretty_spec.rb new file mode 100755 index 0000000..abbcc2c --- /dev/null +++ b/spec/functions/to_json_pretty_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe 'to_json_pretty' do + it { is_expected.not_to eq(nil) } + it { is_expected.to run.with_params([]).and_return("[\n\n]") } + it { is_expected.to run.with_params(['one']).and_return("[\n \"one\"\n]") } + it { is_expected.to run.with_params(['one', 'two']).and_return("[\n \"one\",\n \"two\"\n]") } + it { is_expected.to run.with_params({}).and_return("{\n}") } + it { is_expected.to run.with_params({ 'key' => 'value' }).and_return("{\n \"key\": \"value\"\n}") } + it { is_expected.to run.with_params({"one" => {"oneA" => "A","oneB" => {"oneB1" => "1","oneB2" => "2"}},"two" => ["twoA","twoB"]}).and_return("{\n \"one\": {\n \"oneA\": \"A\",\n \"oneB\": {\n \"oneB1\": \"1\",\n \"oneB2\": \"2\"\n }\n },\n \"two\": [\n \"twoA\",\n \"twoB\"\n ]\n}") } +end diff --git a/spec/functions/to_json_spec.rb b/spec/functions/to_json_spec.rb new file mode 100755 index 0000000..925523c --- /dev/null +++ b/spec/functions/to_json_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe 'to_json' do + it { is_expected.not_to eq(nil) } + it { is_expected.to run.with_params('').and_return("\"\"") } + it { is_expected.to run.with_params(true).and_return("true") } + it { is_expected.to run.with_params('one').and_return("\"one\"") } + it { is_expected.to run.with_params([]).and_return("[]") } + it { is_expected.to run.with_params(['one']).and_return("[\"one\"]") } + it { is_expected.to run.with_params(['one', 'two']).and_return("[\"one\",\"two\"]") } + it { is_expected.to run.with_params({}).and_return("{}") } + it { is_expected.to run.with_params({ 'key' => 'value' }).and_return("{\"key\":\"value\"}") } + it { is_expected.to run.with_params({"one" => {"oneA" => "A","oneB" => {"oneB1" => "1","oneB2" => "2"}},"two" => ["twoA","twoB"]}).and_return("{\"one\":{\"oneA\":\"A\",\"oneB\":{\"oneB1\":\"1\",\"oneB2\":\"2\"}},\"two\":[\"twoA\",\"twoB\"]}") } + + it { is_expected.to run.with_params('‰').and_return('"‰"') } + it { is_expected.to run.with_params('竹').and_return('"竹"') } + it { is_expected.to run.with_params('Ü').and_return('"Ü"') } + it { is_expected.to run.with_params('∇').and_return('"∇"') } +end diff --git a/spec/functions/to_yaml_spec.rb b/spec/functions/to_yaml_spec.rb new file mode 100755 index 0000000..3f69a70 --- /dev/null +++ b/spec/functions/to_yaml_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe 'to_yaml' do + it { is_expected.not_to eq(nil) } + it { is_expected.to run.with_params('').and_return("--- ''\n") } + it { is_expected.to run.with_params(true).and_return("--- true\n...\n") } + it { is_expected.to run.with_params('one').and_return("--- one\n...\n") } + it { is_expected.to run.with_params([]).and_return("--- []\n") } + it { is_expected.to run.with_params(['one']).and_return("---\n- one\n") } + it { is_expected.to run.with_params(['one', 'two']).and_return("---\n- one\n- two\n") } + it { is_expected.to run.with_params({}).and_return("--- {}\n") } + it { is_expected.to run.with_params({ 'key' => 'value' }).and_return("---\nkey: value\n") } + it { is_expected.to run.with_params({"one" => {"oneA" => "A","oneB" => {"oneB1" => "1","oneB2" => "2"}},"two" => ["twoA","twoB"]}).and_return("---\none:\n oneA: A\n oneB:\n oneB1: '1'\n oneB2: '2'\ntwo:\n- twoA\n- twoB\n") } + + it { is_expected.to run.with_params('‰').and_return("--- \"‰\"\n") } + it { is_expected.to run.with_params('∇').and_return("--- \"∇\"\n") } +end diff --git a/spec/functions/unique_spec.rb b/spec/functions/unique_spec.rb index 7955acb..76932ec 100755 --- a/spec/functions/unique_spec.rb +++ b/spec/functions/unique_spec.rb @@ -1,29 +1,31 @@ require 'spec_helper' describe 'unique' do - describe 'signature validation' do - it { is_expected.not_to eq(nil) } - it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) } - it { - pending("Current implementation ignores parameters after the first.") - is_expected.to run.with_params([], 'extra').and_raise_error(Puppet::ParseError, /wrong number of arguments/i) - } - it { is_expected.to run.with_params({}).and_raise_error(Puppet::ParseError, /Requires either array or string to work/) } - it { is_expected.to run.with_params(1).and_raise_error(Puppet::ParseError, /Requires either array or string to work/) } - it { is_expected.to run.with_params(true).and_raise_error(Puppet::ParseError, /Requires either array or string to work/) } - end + if Puppet.version.to_f < 5.0 + describe 'signature validation' do + it { is_expected.not_to eq(nil) } + it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) } + it { + pending("Current implementation ignores parameters after the first.") + is_expected.to run.with_params([], 'extra').and_raise_error(Puppet::ParseError, /wrong number of arguments/i) + } + it { is_expected.to run.with_params({}).and_raise_error(Puppet::ParseError, /Requires either array or string to work/) } + it { is_expected.to run.with_params(1).and_raise_error(Puppet::ParseError, /Requires either array or string to work/) } + it { is_expected.to run.with_params(true).and_raise_error(Puppet::ParseError, /Requires either array or string to work/) } + end - context 'when called with an array' do - it { is_expected.to run.with_params([]).and_return([]) } - it { is_expected.to run.with_params(['a']).and_return(['a']) } - it { is_expected.to run.with_params(['a', 'b', 'a']).and_return(['a', 'b']) } - it { is_expected.to run.with_params(['ã', 'ъ', 'ã']).and_return(['ã', 'ъ']) } - end + context 'when called with an array' do + it { is_expected.to run.with_params([]).and_return([]) } + it { is_expected.to run.with_params(['a']).and_return(['a']) } + it { is_expected.to run.with_params(['a', 'b', 'a']).and_return(['a', 'b']) } + it { is_expected.to run.with_params(['ã', 'ъ', 'ã']).and_return(['ã', 'ъ']) } + end - context 'when called with a string' do - it { is_expected.to run.with_params('').and_return('') } - it { is_expected.to run.with_params('a').and_return('a') } - it { is_expected.to run.with_params('aaba').and_return('ab') } - it { is_expected.to run.with_params('ããъã').and_return('ãъ') } + context 'when called with a string' do + it { is_expected.to run.with_params('').and_return('') } + it { is_expected.to run.with_params('a').and_return('a') } + it { is_expected.to run.with_params('aaba').and_return('ab') } + it { is_expected.to run.with_params('ããъã').and_return('ãъ') } + end end end diff --git a/spec/functions/validate_domain_name_spec.rb b/spec/functions/validate_domain_name_spec.rb new file mode 100644 index 0000000..69fcae4 --- /dev/null +++ b/spec/functions/validate_domain_name_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +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, /wrong number of arguments/i) } + end + + describe 'valid inputs' do + it { is_expected.to run.with_params('com', 'com.') } + it { is_expected.to run.with_params('x.com', 'x.com.') } + it { is_expected.to run.with_params('foo.example.com', 'foo.example.com.') } + it { is_expected.to run.with_params('2foo.example.com', '2foo.example.com.') } + it { is_expected.to run.with_params('www.2foo.example.com', 'www.2foo.example.com.') } + it { is_expected.to run.with_params('domain.tld', 'puppet.com') } + end + + describe 'invalid inputs' do + it { is_expected.to run.with_params([]).and_raise_error(Puppet::ParseError, /is not a string/) } + it { is_expected.to run.with_params({}).and_raise_error(Puppet::ParseError, /is not a string/) } + it { is_expected.to run.with_params(1).and_raise_error(Puppet::ParseError, /is not a string/) } + it { is_expected.to run.with_params(true).and_raise_error(Puppet::ParseError, /is not a string/) } + + it { is_expected.to run.with_params('foo.example.com', []).and_raise_error(Puppet::ParseError, /is not a string/) } + it { is_expected.to run.with_params('foo.example.com', {}).and_raise_error(Puppet::ParseError, /is not a string/) } + it { is_expected.to run.with_params('foo.example.com', 1).and_raise_error(Puppet::ParseError, /is not a string/) } + it { is_expected.to run.with_params('foo.example.com', true).and_raise_error(Puppet::ParseError, /is not a string/) } + + it { is_expected.to run.with_params('').and_raise_error(Puppet::ParseError, /is not a syntactically correct domain name/) } + it { is_expected.to run.with_params('invalid domain').and_raise_error(Puppet::ParseError, /is not a syntactically correct domain name/) } + it { is_expected.to run.with_params('-foo.example.com').and_raise_error(Puppet::ParseError, /is not a syntactically correct domain name/) } + it { is_expected.to run.with_params('www.example.2com').and_raise_error(Puppet::ParseError, /is not a syntactically correct domain name/) } + it { is_expected.to run.with_params('192.168.1.1').and_raise_error(Puppet::ParseError, /is not a syntactically correct domain name/) } + end +end diff --git a/spec/unit/puppet/provider/file_line/ruby_spec.rb b/spec/unit/puppet/provider/file_line/ruby_spec.rb index dcf8509..dcca4a4 100755 --- a/spec/unit/puppet/provider/file_line/ruby_spec.rb +++ b/spec/unit/puppet/provider/file_line/ruby_spec.rb @@ -190,10 +190,28 @@ describe provider_class, :unless => Puppet::Util::Platform.windows? do File.open(@tmpfile, 'w') do |fh| fh.write("foo1\nfoo = bar\nfoo2") end - expect(@provider.exists?).to eql(false) + expect(@provider.exists?).to eql(true) @provider.create expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2") end + + it 'should not add line after no matches found' do + @resource = Puppet::Type::File_line.new( + { + :name => 'foo', + :path => @tmpfile, + :line => 'inserted = line', + :match => '^foo3$', + :append_on_no_match => false, + } + ) + @provider = provider_class.new(@resource) + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz") + end + expect(@provider.exists?).to be true + expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz") + end end describe 'using after' do @@ -387,7 +405,7 @@ describe provider_class, :unless => Puppet::Util::Platform.windows? do File.open(@tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end - expect(@provider.exists?).to be_nil + expect(@provider.exists?).to eql (true) end it 'should remove one line if it matches' do diff --git a/types/mac.pp b/types/mac.pp new file mode 100644 index 0000000..4103574 --- /dev/null +++ b/types/mac.pp @@ -0,0 +1,2 @@ +# A type for a MAC address +type Stdlib::MAC = Pattern[/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/] |