diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/functions/fact.rb | 58 | ||||
-rw-r--r-- | lib/puppet/functions/to_json.rb | 21 | ||||
-rw-r--r-- | lib/puppet/functions/to_json_pretty.rb | 21 | ||||
-rw-r--r-- | lib/puppet/functions/to_yaml.rb | 21 | ||||
-rw-r--r-- | lib/puppet/parser/functions/pw_hash.rb | 7 | ||||
-rw-r--r-- | lib/puppet/parser/functions/round.rb | 33 | ||||
-rw-r--r-- | lib/puppet/parser/functions/unique.rb | 4 | ||||
-rw-r--r-- | lib/puppet/parser/functions/validate_domain_name.rb | 39 | ||||
-rw-r--r-- | lib/puppet/provider/file_line/ruby.rb | 106 | ||||
-rw-r--r-- | lib/puppet/type/file_line.rb | 8 |
10 files changed, 268 insertions, 50 deletions
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] |