summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMicah <micah@leap.se>2016-05-24 10:19:33 -0400
committerMicah <micah@leap.se>2016-05-24 10:19:33 -0400
commit1da5dc55ee48fcd437f5b5df00a5b2f3991ec9f1 (patch)
tree1794e812d83facd93b3007c42632c63ddf1eb2fc /lib
Squashed 'puppet/modules/stdlib/' content from commit 7112363
git-subtree-dir: puppet/modules/stdlib git-subtree-split: 71123634744b9fe2ec7d6a3e38e9789fd84801e3
Diffstat (limited to 'lib')
-rw-r--r--lib/facter/facter_dot_d.rb202
-rw-r--r--lib/facter/netmask_cidr_interface.rb22
-rw-r--r--lib/facter/pe_version.rb53
-rw-r--r--lib/facter/puppet_vardir.rb26
-rw-r--r--lib/facter/root_home.rb32
-rw-r--r--lib/facter/util/puppet_settings.rb21
-rw-r--r--lib/puppet/functions/type_of.rb17
-rw-r--r--lib/puppet/parser/functions/abs.rb36
-rw-r--r--lib/puppet/parser/functions/any2array.rb33
-rw-r--r--lib/puppet/parser/functions/base64.rb37
-rw-r--r--lib/puppet/parser/functions/basename.rb34
-rw-r--r--lib/puppet/parser/functions/bool2num.rb26
-rw-r--r--lib/puppet/parser/functions/bool2str.rb27
-rw-r--r--lib/puppet/parser/functions/camelcase.rb33
-rw-r--r--lib/puppet/parser/functions/capitalize.rb33
-rw-r--r--lib/puppet/parser/functions/chomp.rb34
-rw-r--r--lib/puppet/parser/functions/chop.rb36
-rw-r--r--lib/puppet/parser/functions/concat.rb41
-rw-r--r--lib/puppet/parser/functions/count.rb22
-rw-r--r--lib/puppet/parser/functions/deep_merge.rb44
-rw-r--r--lib/puppet/parser/functions/defined_with_params.rb35
-rw-r--r--lib/puppet/parser/functions/delete.rb49
-rw-r--r--lib/puppet/parser/functions/delete_at.rb49
-rw-r--r--lib/puppet/parser/functions/delete_undef_values.rb34
-rw-r--r--lib/puppet/parser/functions/delete_values.rb26
-rw-r--r--lib/puppet/parser/functions/difference.rb36
-rw-r--r--lib/puppet/parser/functions/dirname.rb15
-rw-r--r--lib/puppet/parser/functions/downcase.rb32
-rw-r--r--lib/puppet/parser/functions/empty.rb27
-rw-r--r--lib/puppet/parser/functions/ensure_packages.rb35
-rw-r--r--lib/puppet/parser/functions/ensure_resource.rb46
-rw-r--r--lib/puppet/parser/functions/flatten.rb33
-rw-r--r--lib/puppet/parser/functions/floor.rb25
-rw-r--r--lib/puppet/parser/functions/fqdn_rotate.rb45
-rw-r--r--lib/puppet/parser/functions/get_module_path.rb17
-rw-r--r--lib/puppet/parser/functions/getparam.rb35
-rw-r--r--lib/puppet/parser/functions/getvar.rb29
-rw-r--r--lib/puppet/parser/functions/grep.rb33
-rw-r--r--lib/puppet/parser/functions/has_interface_with.rb68
-rw-r--r--lib/puppet/parser/functions/has_ip_address.rb25
-rw-r--r--lib/puppet/parser/functions/has_ip_network.rb25
-rw-r--r--lib/puppet/parser/functions/has_key.rb28
-rw-r--r--lib/puppet/parser/functions/hash.rb41
-rw-r--r--lib/puppet/parser/functions/intersection.rb34
-rw-r--r--lib/puppet/parser/functions/is_array.rb22
-rw-r--r--lib/puppet/parser/functions/is_bool.rb22
-rw-r--r--lib/puppet/parser/functions/is_domain_name.rb50
-rw-r--r--lib/puppet/parser/functions/is_float.rb30
-rw-r--r--lib/puppet/parser/functions/is_function_available.rb26
-rw-r--r--lib/puppet/parser/functions/is_hash.rb22
-rw-r--r--lib/puppet/parser/functions/is_integer.rb45
-rw-r--r--lib/puppet/parser/functions/is_ip_address.rb32
-rw-r--r--lib/puppet/parser/functions/is_mac_address.rb27
-rw-r--r--lib/puppet/parser/functions/is_numeric.rb75
-rw-r--r--lib/puppet/parser/functions/is_string.rb26
-rw-r--r--lib/puppet/parser/functions/join.rb41
-rw-r--r--lib/puppet/parser/functions/join_keys_to_values.rb47
-rw-r--r--lib/puppet/parser/functions/keys.rb26
-rw-r--r--lib/puppet/parser/functions/loadyaml.rb20
-rw-r--r--lib/puppet/parser/functions/lstrip.rb32
-rw-r--r--lib/puppet/parser/functions/max.rb21
-rw-r--r--lib/puppet/parser/functions/member.rb62
-rw-r--r--lib/puppet/parser/functions/merge.rb34
-rw-r--r--lib/puppet/parser/functions/min.rb21
-rw-r--r--lib/puppet/parser/functions/num2bool.rb43
-rw-r--r--lib/puppet/parser/functions/obfuscate_email.rb16
-rw-r--r--lib/puppet/parser/functions/parsejson.rb24
-rw-r--r--lib/puppet/parser/functions/parseyaml.rb24
-rw-r--r--lib/puppet/parser/functions/pick.rb29
-rw-r--r--lib/puppet/parser/functions/pick_default.rb35
-rw-r--r--lib/puppet/parser/functions/prefix.rb45
-rw-r--r--lib/puppet/parser/functions/private.rb29
-rw-r--r--lib/puppet/parser/functions/range.rb88
-rw-r--r--lib/puppet/parser/functions/reject.rb31
-rw-r--r--lib/puppet/parser/functions/reverse.rb27
-rw-r--r--lib/puppet/parser/functions/rstrip.rb31
-rw-r--r--lib/puppet/parser/functions/shuffle.rb45
-rw-r--r--lib/puppet/parser/functions/size.rb48
-rw-r--r--lib/puppet/parser/functions/sort.rb27
-rw-r--r--lib/puppet/parser/functions/squeeze.rb36
-rw-r--r--lib/puppet/parser/functions/str2bool.rb46
-rw-r--r--lib/puppet/parser/functions/str2saltedsha1.rb32
-rw-r--r--lib/puppet/parser/functions/str2saltedsha512.rb32
-rw-r--r--lib/puppet/parser/functions/str2sha1_and_salt.rb36
-rw-r--r--lib/puppet/parser/functions/str_and_salt2sha1.rb32
-rw-r--r--lib/puppet/parser/functions/strftime.rb107
-rw-r--r--lib/puppet/parser/functions/strip.rb38
-rw-r--r--lib/puppet/parser/functions/suffix.rb45
-rw-r--r--lib/puppet/parser/functions/swapcase.rb38
-rw-r--r--lib/puppet/parser/functions/time.rb49
-rw-r--r--lib/puppet/parser/functions/to_bytes.rb31
-rw-r--r--lib/puppet/parser/functions/type.rb19
-rw-r--r--lib/puppet/parser/functions/type3x.rb51
-rw-r--r--lib/puppet/parser/functions/union.rb34
-rw-r--r--lib/puppet/parser/functions/unique.rb50
-rw-r--r--lib/puppet/parser/functions/upcase.rb40
-rw-r--r--lib/puppet/parser/functions/uriescape.rb34
-rw-r--r--lib/puppet/parser/functions/validate_absolute_path.rb69
-rw-r--r--lib/puppet/parser/functions/validate_array.rb33
-rw-r--r--lib/puppet/parser/functions/validate_augeas.rb83
-rw-r--r--lib/puppet/parser/functions/validate_bool.rb34
-rw-r--r--lib/puppet/parser/functions/validate_cmd.rb63
-rw-r--r--lib/puppet/parser/functions/validate_hash.rb33
-rw-r--r--lib/puppet/parser/functions/validate_ipv4_address.rb48
-rw-r--r--lib/puppet/parser/functions/validate_ipv6_address.rb49
-rw-r--r--lib/puppet/parser/functions/validate_re.rb40
-rw-r--r--lib/puppet/parser/functions/validate_slength.rb71
-rw-r--r--lib/puppet/parser/functions/validate_string.rb38
-rw-r--r--lib/puppet/parser/functions/values.rb39
-rw-r--r--lib/puppet/parser/functions/values_at.rb99
-rw-r--r--lib/puppet/parser/functions/zip.rb39
-rw-r--r--lib/puppet/provider/file_line/ruby.rb85
-rw-r--r--lib/puppet/type/anchor.rb46
-rw-r--r--lib/puppet/type/file_line.rb75
114 files changed, 4548 insertions, 0 deletions
diff --git a/lib/facter/facter_dot_d.rb b/lib/facter/facter_dot_d.rb
new file mode 100644
index 00000000..b0584370
--- /dev/null
+++ b/lib/facter/facter_dot_d.rb
@@ -0,0 +1,202 @@
+# A Facter plugin that loads facts from /etc/facter/facts.d
+# and /etc/puppetlabs/facter/facts.d.
+#
+# Facts can be in the form of JSON, YAML or Text files
+# and any executable that returns key=value pairs.
+#
+# In the case of scripts you can also create a file that
+# contains a cache TTL. For foo.sh store the ttl as just
+# a number in foo.sh.ttl
+#
+# The cache is stored in /tmp/facts_cache.yaml as a mode
+# 600 file and will have the end result of not calling your
+# fact scripts more often than is needed
+
+class Facter::Util::DotD
+ require 'yaml'
+
+ def initialize(dir="/etc/facts.d", cache_file=File.join(Puppet[:libdir], "facts_dot_d.cache"))
+ @dir = dir
+ @cache_file = cache_file
+ @cache = nil
+ @types = {".txt" => :txt, ".json" => :json, ".yaml" => :yaml}
+ end
+
+ def entries
+ Dir.entries(@dir).reject { |f| f =~ /^\.|\.ttl$/ }.sort.map { |f| File.join(@dir, f) }
+ rescue
+ []
+ end
+
+ def fact_type(file)
+ extension = File.extname(file)
+
+ type = @types[extension] || :unknown
+
+ type = :script if type == :unknown && File.executable?(file)
+
+ return type
+ end
+
+ def txt_parser(file)
+ File.readlines(file).each do |line|
+ if line =~ /^([^=]+)=(.+)$/
+ var = $1; val = $2
+
+ Facter.add(var) do
+ setcode { val }
+ end
+ end
+ end
+ rescue Exception => e
+ Facter.warn("Failed to handle #{file} as text facts: #{e.class}: #{e}")
+ end
+
+ def json_parser(file)
+ begin
+ require 'json'
+ rescue LoadError
+ retry if require 'rubygems'
+ raise
+ end
+
+ JSON.load(File.read(file)).each_pair do |f, v|
+ Facter.add(f) do
+ setcode { v }
+ end
+ end
+ rescue Exception => e
+ Facter.warn("Failed to handle #{file} as json facts: #{e.class}: #{e}")
+ end
+
+ def yaml_parser(file)
+ require 'yaml'
+
+ YAML.load_file(file).each_pair do |f, v|
+ Facter.add(f) do
+ setcode { v }
+ end
+ end
+ rescue Exception => e
+ Facter.warn("Failed to handle #{file} as yaml facts: #{e.class}: #{e}")
+ end
+
+ def script_parser(file)
+ result = cache_lookup(file)
+ ttl = cache_time(file)
+
+ unless result
+ result = Facter::Util::Resolution.exec(file)
+
+ if ttl > 0
+ Facter.debug("Updating cache for #{file}")
+ cache_store(file, result)
+ cache_save!
+ end
+ else
+ Facter.debug("Using cached data for #{file}")
+ end
+
+ result.split("\n").each do |line|
+ if line =~ /^(.+)=(.+)$/
+ var = $1; val = $2
+
+ Facter.add(var) do
+ setcode { val }
+ end
+ end
+ end
+ rescue Exception => e
+ Facter.warn("Failed to handle #{file} as script facts: #{e.class}: #{e}")
+ Facter.debug(e.backtrace.join("\n\t"))
+ end
+
+ def cache_save!
+ cache = load_cache
+ File.open(@cache_file, "w", 0600) { |f| f.write(YAML.dump(cache)) }
+ rescue
+ end
+
+ def cache_store(file, data)
+ load_cache
+
+ @cache[file] = {:data => data, :stored => Time.now.to_i}
+ rescue
+ end
+
+ def cache_lookup(file)
+ cache = load_cache
+
+ return nil if cache.empty?
+
+ ttl = cache_time(file)
+
+ if cache[file]
+ now = Time.now.to_i
+
+ return cache[file][:data] if ttl == -1
+ return cache[file][:data] if (now - cache[file][:stored]) <= ttl
+ return nil
+ else
+ return nil
+ end
+ rescue
+ return nil
+ end
+
+ def cache_time(file)
+ meta = file + ".ttl"
+
+ return File.read(meta).chomp.to_i
+ rescue
+ return 0
+ end
+
+ def load_cache
+ unless @cache
+ if File.exist?(@cache_file)
+ @cache = YAML.load_file(@cache_file)
+ else
+ @cache = {}
+ end
+ end
+
+ return @cache
+ rescue
+ @cache = {}
+ return @cache
+ end
+
+ def create
+ entries.each do |fact|
+ type = fact_type(fact)
+ parser = "#{type}_parser"
+
+ if respond_to?("#{type}_parser")
+ Facter.debug("Parsing #{fact} using #{parser}")
+
+ send(parser, fact)
+ end
+ end
+ end
+end
+
+
+mdata = Facter.version.match(/(\d+)\.(\d+)\.(\d+)/)
+if mdata
+ (major, minor, patch) = mdata.captures.map { |v| v.to_i }
+ if major < 2
+ # Facter 1.7 introduced external facts support directly
+ unless major == 1 and minor > 6
+ Facter::Util::DotD.new("/etc/facter/facts.d").create
+ Facter::Util::DotD.new("/etc/puppetlabs/facter/facts.d").create
+
+ # Windows has a different configuration directory that defaults to a vendor
+ # specific sub directory of the %COMMON_APPDATA% directory.
+ if Dir.const_defined? 'COMMON_APPDATA' then
+ windows_facts_dot_d = File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'facter', 'facts.d')
+ Facter::Util::DotD.new(windows_facts_dot_d).create
+ end
+ end
+ end
+end
diff --git a/lib/facter/netmask_cidr_interface.rb b/lib/facter/netmask_cidr_interface.rb
new file mode 100644
index 00000000..d628d08c
--- /dev/null
+++ b/lib/facter/netmask_cidr_interface.rb
@@ -0,0 +1,22 @@
+# adds netmask facts for each interface in cidr notation
+# i.e.:
+# ...
+# netmask_cidr_eth2 => 24
+# netmask_cidr_lo => 8
+# netmask_cidr_tun0 => 32
+# netmask_cidr_virbr0 => 24
+# ...
+
+require 'facter/util/ip'
+
+Facter::Util::IP.get_interfaces.each do |interface|
+ netmask = Facter.value("netmask_#{interface}")
+ if netmask != nil
+ Facter.add("netmask_cidr_" + interface ) do
+ setcode do
+ cidr_netmask=IPAddr.new(netmask).to_i.to_s(2).count("1")
+ cidr_netmask
+ end
+ end
+ end
+end
diff --git a/lib/facter/pe_version.rb b/lib/facter/pe_version.rb
new file mode 100644
index 00000000..0cc0f64e
--- /dev/null
+++ b/lib/facter/pe_version.rb
@@ -0,0 +1,53 @@
+# Fact: is_pe, pe_version, pe_major_version, pe_minor_version, pe_patch_version
+#
+# Purpose: Return various facts about the PE state of the system
+#
+# Resolution: Uses a regex match against puppetversion to determine whether the
+# machine has Puppet Enterprise installed, and what version (overall, major,
+# minor, patch) is installed.
+#
+# Caveats:
+#
+Facter.add("pe_version") do
+ setcode do
+ pe_ver = Facter.value("puppetversion").match(/Puppet Enterprise (\d+\.\d+\.\d+)/)
+ pe_ver[1] if pe_ver
+ end
+end
+
+Facter.add("is_pe") do
+ setcode do
+ if Facter.value(:pe_version).to_s.empty? then
+ false
+ else
+ true
+ end
+ end
+end
+
+Facter.add("pe_major_version") do
+ confine :is_pe => true
+ setcode do
+ if pe_version = Facter.value(:pe_version)
+ pe_version.to_s.split('.')[0]
+ end
+ end
+end
+
+Facter.add("pe_minor_version") do
+ confine :is_pe => true
+ setcode do
+ if pe_version = Facter.value(:pe_version)
+ pe_version.to_s.split('.')[1]
+ end
+ end
+end
+
+Facter.add("pe_patch_version") do
+ confine :is_pe => true
+ setcode do
+ if pe_version = Facter.value(:pe_version)
+ pe_version.to_s.split('.')[2]
+ end
+ end
+end
diff --git a/lib/facter/puppet_vardir.rb b/lib/facter/puppet_vardir.rb
new file mode 100644
index 00000000..0e6af40e
--- /dev/null
+++ b/lib/facter/puppet_vardir.rb
@@ -0,0 +1,26 @@
+# This facter fact returns the value of the Puppet vardir setting for the node
+# running puppet or puppet agent. The intent is to enable Puppet modules to
+# automatically have insight into a place where they can place variable data,
+# regardless of the node's platform.
+#
+# The value should be directly usable in a File resource path attribute.
+
+
+begin
+ require 'facter/util/puppet_settings'
+rescue LoadError => e
+ # puppet apply does not add module lib directories to the $LOAD_PATH (See
+ # #4248). It should (in the future) but for the time being we need to be
+ # defensive which is what this rescue block is doing.
+ rb_file = File.join(File.dirname(__FILE__), 'util', 'puppet_settings.rb')
+ load rb_file if File.exists?(rb_file) or raise e
+end
+
+Facter.add(:puppet_vardir) do
+ setcode do
+ # This will be nil if Puppet is not available.
+ Facter::Util::PuppetSettings.with_puppet do
+ Puppet[:vardir]
+ end
+ end
+end
diff --git a/lib/facter/root_home.rb b/lib/facter/root_home.rb
new file mode 100644
index 00000000..b4f87ff2
--- /dev/null
+++ b/lib/facter/root_home.rb
@@ -0,0 +1,32 @@
+# A facter fact to determine the root home directory.
+# This varies on PE supported platforms and may be
+# reconfigured by the end user.
+
+module Facter::Util::RootHome
+ class << self
+ def get_root_home
+ root_ent = Facter::Util::Resolution.exec("getent passwd root")
+ # The home directory is the sixth element in the passwd entry
+ # If the platform doesn't have getent, root_ent will be nil and we should
+ # return it straight away.
+ root_ent && root_ent.split(":")[5]
+ end
+ end
+end
+
+Facter.add(:root_home) do
+ setcode { Facter::Util::RootHome.get_root_home }
+end
+
+Facter.add(:root_home) do
+ confine :kernel => :darwin
+ setcode do
+ str = Facter::Util::Resolution.exec("dscacheutil -q user -a name root")
+ hash = {}
+ str.split("\n").each do |pair|
+ key,value = pair.split(/:/)
+ hash[key] = value
+ end
+ hash['dir'].strip
+ end
+end
diff --git a/lib/facter/util/puppet_settings.rb b/lib/facter/util/puppet_settings.rb
new file mode 100644
index 00000000..1ad94521
--- /dev/null
+++ b/lib/facter/util/puppet_settings.rb
@@ -0,0 +1,21 @@
+module Facter
+ module Util
+ module PuppetSettings
+ # This method is intended to provide a convenient way to evaluate a
+ # Facter code block only if Puppet is loaded. This is to account for the
+ # situation where the fact happens to be in the load path, but Puppet is
+ # not loaded for whatever reason. Perhaps the user is simply running
+ # facter without the --puppet flag and they happen to be working in a lib
+ # directory of a module.
+ def self.with_puppet
+ begin
+ Module.const_get("Puppet")
+ rescue NameError
+ nil
+ else
+ yield
+ end
+ end
+ end
+ end
+end
diff --git a/lib/puppet/functions/type_of.rb b/lib/puppet/functions/type_of.rb
new file mode 100644
index 00000000..02cdd4db
--- /dev/null
+++ b/lib/puppet/functions/type_of.rb
@@ -0,0 +1,17 @@
+# Returns the type when passed a value.
+#
+# @example how to compare values' types
+# # compare the types of two values
+# if type_of($first_value) != type_of($second_value) { fail("first_value and second_value are different types") }
+# @example how to compare against an abstract type
+# unless type_of($first_value) <= Numeric { fail("first_value must be Numeric") }
+# unless type_of{$first_value) <= Collection[1] { fail("first_value must be an Array or Hash, and contain at least one element") }
+#
+# See the documentation for "The Puppet Type System" for more information about types.
+# See the `assert_type()` function for flexible ways to assert the type of a value.
+#
+Puppet::Functions.create_function(:type_of) do
+ def type_of(value)
+ Puppet::Pops::Types::TypeCalculator.infer_set(value)
+ end
+end
diff --git a/lib/puppet/parser/functions/abs.rb b/lib/puppet/parser/functions/abs.rb
new file mode 100644
index 00000000..11d2d7fe
--- /dev/null
+++ b/lib/puppet/parser/functions/abs.rb
@@ -0,0 +1,36 @@
+#
+# abs.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:abs, :type => :rvalue, :doc => <<-EOS
+ Returns the absolute value of a number, for example -34.56 becomes
+ 34.56. Takes a single integer and float value as an argument.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "abs(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ if value.is_a?(String)
+ if value.match(/^-?(?:\d+)(?:\.\d+){1}$/)
+ value = value.to_f
+ elsif value.match(/^-?\d+$/)
+ value = value.to_i
+ else
+ raise(Puppet::ParseError, 'abs(): Requires float or ' +
+ 'integer to work with')
+ end
+ end
+
+ # We have numeric value to handle ...
+ result = value.abs
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/any2array.rb b/lib/puppet/parser/functions/any2array.rb
new file mode 100644
index 00000000..e71407e8
--- /dev/null
+++ b/lib/puppet/parser/functions/any2array.rb
@@ -0,0 +1,33 @@
+#
+# any2array.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:any2array, :type => :rvalue, :doc => <<-EOS
+This converts any object to an array containing that object. Empty argument
+lists are converted to an empty array. Arrays are left untouched. Hashes are
+converted to arrays of alternating keys and values.
+ EOS
+ ) do |arguments|
+
+ if arguments.empty?
+ return []
+ end
+
+ if arguments.length == 1
+ if arguments[0].kind_of?(Array)
+ return arguments[0]
+ elsif arguments[0].kind_of?(Hash)
+ result = []
+ arguments[0].each do |key, value|
+ result << key << value
+ end
+ return result
+ end
+ end
+
+ return arguments
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/base64.rb b/lib/puppet/parser/functions/base64.rb
new file mode 100644
index 00000000..617ba31b
--- /dev/null
+++ b/lib/puppet/parser/functions/base64.rb
@@ -0,0 +1,37 @@
+module Puppet::Parser::Functions
+
+ newfunction(:base64, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
+
+ Base64 encode or decode a string based on the command and the string submitted
+
+ Usage:
+
+ $encodestring = base64('encode','thestring')
+ $decodestring = base64('decode','dGhlc3RyaW5n')
+
+ ENDHEREDOC
+
+ require 'base64'
+
+ raise Puppet::ParseError, ("base64(): Wrong number of arguments (#{args.length}; must be = 2)") unless args.length == 2
+
+ actions = ['encode','decode']
+
+ unless actions.include?(args[0])
+ raise Puppet::ParseError, ("base64(): the first argument must be one of 'encode' or 'decode'")
+ end
+
+ unless args[1].is_a?(String)
+ raise Puppet::ParseError, ("base64(): the second argument must be a string to base64")
+ end
+
+ case args[0]
+ when 'encode'
+ result = Base64.encode64(args[1])
+ when 'decode'
+ result = Base64.decode64(args[1])
+ end
+
+ return result
+ end
+end
diff --git a/lib/puppet/parser/functions/basename.rb b/lib/puppet/parser/functions/basename.rb
new file mode 100644
index 00000000..f7e44384
--- /dev/null
+++ b/lib/puppet/parser/functions/basename.rb
@@ -0,0 +1,34 @@
+module Puppet::Parser::Functions
+ newfunction(:basename, :type => :rvalue, :doc => <<-EOS
+ Strips directory (and optional suffix) from a filename
+ EOS
+ ) do |arguments|
+
+ if arguments.size < 1 then
+ raise(Puppet::ParseError, "basename(): No arguments given")
+ elsif arguments.size > 2 then
+ raise(Puppet::ParseError, "basename(): Too many arguments given (#{arguments.size})")
+ else
+
+ unless arguments[0].is_a?(String)
+ raise(Puppet::ParseError, 'basename(): Requires string as first argument')
+ end
+
+ if arguments.size == 1 then
+ rv = File.basename(arguments[0])
+ elsif arguments.size == 2 then
+
+ unless arguments[1].is_a?(String)
+ raise(Puppet::ParseError, 'basename(): Requires string as second argument')
+ end
+
+ rv = File.basename(arguments[0], arguments[1])
+ end
+
+ end
+
+ return rv
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/bool2num.rb b/lib/puppet/parser/functions/bool2num.rb
new file mode 100644
index 00000000..6ad6cf4e
--- /dev/null
+++ b/lib/puppet/parser/functions/bool2num.rb
@@ -0,0 +1,26 @@
+#
+# bool2num.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:bool2num, :type => :rvalue, :doc => <<-EOS
+ Converts a boolean to a number. Converts the values:
+ false, f, 0, n, and no to 0
+ true, t, 1, y, and yes to 1
+ Requires a single boolean or string as an input.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "bool2num(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = function_str2bool([arguments[0]])
+
+ # We have real boolean values as well ...
+ result = value ? 1 : 0
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/bool2str.rb b/lib/puppet/parser/functions/bool2str.rb
new file mode 100644
index 00000000..fcd37917
--- /dev/null
+++ b/lib/puppet/parser/functions/bool2str.rb
@@ -0,0 +1,27 @@
+#
+# bool2str.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:bool2str, :type => :rvalue, :doc => <<-EOS
+ Converts a boolean to a string.
+ Requires a single boolean as an input.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "bool2str(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ # We can have either true or false, and nothing else
+ unless [FalseClass, TrueClass].include?(klass)
+ raise(Puppet::ParseError, 'bool2str(): Requires a boolean to work with')
+ end
+
+ return value.to_s
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/camelcase.rb b/lib/puppet/parser/functions/camelcase.rb
new file mode 100644
index 00000000..d7f43f7a
--- /dev/null
+++ b/lib/puppet/parser/functions/camelcase.rb
@@ -0,0 +1,33 @@
+#
+# camelcase.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:camelcase, :type => :rvalue, :doc => <<-EOS
+Converts the case of a string or all strings in an array to camel case.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "camelcase(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ klass = value.class
+
+ unless [Array, String].include?(klass)
+ raise(Puppet::ParseError, 'camelcase(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.split('_').map{|e| e.capitalize}.join : i }
+ else
+ result = value.split('_').map{|e| e.capitalize}.join
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/capitalize.rb b/lib/puppet/parser/functions/capitalize.rb
new file mode 100644
index 00000000..98b2d16c
--- /dev/null
+++ b/lib/puppet/parser/functions/capitalize.rb
@@ -0,0 +1,33 @@
+#
+# capitalize.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:capitalize, :type => :rvalue, :doc => <<-EOS
+ Capitalizes the first letter of a string or array of strings.
+ Requires either a single string or an array as an input.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "capitalize(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'capitalize(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.capitalize : i }
+ else
+ result = value.capitalize
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/chomp.rb b/lib/puppet/parser/functions/chomp.rb
new file mode 100644
index 00000000..c55841e3
--- /dev/null
+++ b/lib/puppet/parser/functions/chomp.rb
@@ -0,0 +1,34 @@
+#
+# chomp.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:chomp, :type => :rvalue, :doc => <<-'EOS'
+ Removes the record separator from the end of a string or an array of
+ strings, for example `hello\n` becomes `hello`.
+ Requires a single string or array as an input.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "chomp(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'chomp(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.chomp : i }
+ else
+ result = value.chomp
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/chop.rb b/lib/puppet/parser/functions/chop.rb
new file mode 100644
index 00000000..b24ab785
--- /dev/null
+++ b/lib/puppet/parser/functions/chop.rb
@@ -0,0 +1,36 @@
+#
+# chop.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:chop, :type => :rvalue, :doc => <<-'EOS'
+ Returns a new string with the last character removed. If the string ends
+ with `\r\n`, both characters are removed. Applying chop to an empty
+ string returns an empty string. If you wish to merely remove record
+ separators then you should use the `chomp` function.
+ Requires a string or array of strings as input.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "chop(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'chop(): Requires either an ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.chop : i }
+ else
+ result = value.chop
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/concat.rb b/lib/puppet/parser/functions/concat.rb
new file mode 100644
index 00000000..618e62d4
--- /dev/null
+++ b/lib/puppet/parser/functions/concat.rb
@@ -0,0 +1,41 @@
+#
+# concat.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:concat, :type => :rvalue, :doc => <<-EOS
+Appends the contents of multiple arrays into array 1.
+
+*Example:*
+
+ concat(['1','2','3'],['4','5','6'],['7','8','9'])
+
+Would result in:
+
+ ['1','2','3','4','5','6','7','8','9']
+ EOS
+ ) do |arguments|
+
+ # Check that more than 2 arguments have been given ...
+ raise(Puppet::ParseError, "concat(): Wrong number of arguments " +
+ "given (#{arguments.size} for < 2)") if arguments.size < 2
+
+ a = arguments[0]
+
+ # Check that the first parameter is an array
+ unless a.is_a?(Array)
+ raise(Puppet::ParseError, 'concat(): Requires array to work with')
+ end
+
+ result = a
+ arguments.shift
+
+ arguments.each do |x|
+ result = result + Array(x)
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/count.rb b/lib/puppet/parser/functions/count.rb
new file mode 100644
index 00000000..52de1b8a
--- /dev/null
+++ b/lib/puppet/parser/functions/count.rb
@@ -0,0 +1,22 @@
+module Puppet::Parser::Functions
+ newfunction(:count, :type => :rvalue, :arity => -2, :doc => <<-EOS
+Takes an array as first argument and an optional second argument.
+Count the number of elements in array that matches second argument.
+If called with only an array it counts the number of elements that are not nil/undef.
+ EOS
+ ) do |args|
+
+ if (args.size > 2) then
+ raise(ArgumentError, "count(): Wrong number of arguments "+
+ "given #{args.size} for 1 or 2.")
+ end
+
+ collection, item = args
+
+ if item then
+ collection.count item
+ else
+ collection.count { |obj| obj != nil && obj != :undef && obj != '' }
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/deep_merge.rb b/lib/puppet/parser/functions/deep_merge.rb
new file mode 100644
index 00000000..6df32e9c
--- /dev/null
+++ b/lib/puppet/parser/functions/deep_merge.rb
@@ -0,0 +1,44 @@
+module Puppet::Parser::Functions
+ newfunction(:deep_merge, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
+ Recursively merges two or more hashes together and returns the resulting hash.
+
+ For example:
+
+ $hash1 = {'one' => 1, 'two' => 2, 'three' => { 'four' => 4 } }
+ $hash2 = {'two' => 'dos', 'three' => { 'five' => 5 } }
+ $merged_hash = deep_merge($hash1, $hash2)
+ # The resulting hash is equivalent to:
+ # $merged_hash = { 'one' => 1, 'two' => 'dos', 'three' => { 'four' => 4, 'five' => 5 } }
+
+ When there is a duplicate key that is a hash, they are recursively merged.
+ When there is a duplicate key that is not a hash, the key in the rightmost hash will "win."
+
+ ENDHEREDOC
+
+ if args.length < 2
+ raise Puppet::ParseError, ("deep_merge(): wrong number of arguments (#{args.length}; must be at least 2)")
+ end
+
+ deep_merge = Proc.new do |hash1,hash2|
+ hash1.merge(hash2) do |key,old_value,new_value|
+ if old_value.is_a?(Hash) && new_value.is_a?(Hash)
+ deep_merge.call(old_value, new_value)
+ else
+ new_value
+ end
+ end
+ end
+
+ result = Hash.new
+ args.each do |arg|
+ next if arg.is_a? String and arg.empty? # empty string is synonym for puppet's undef
+ # If the argument was not a hash, skip it.
+ unless arg.is_a?(Hash)
+ raise Puppet::ParseError, "deep_merge: unexpected argument type #{arg.class}, only expects hash arguments"
+ end
+
+ result = deep_merge.call(result, arg)
+ end
+ return( result )
+ end
+end
diff --git a/lib/puppet/parser/functions/defined_with_params.rb b/lib/puppet/parser/functions/defined_with_params.rb
new file mode 100644
index 00000000..d7df306c
--- /dev/null
+++ b/lib/puppet/parser/functions/defined_with_params.rb
@@ -0,0 +1,35 @@
+# Test whether a given class or definition is defined
+require 'puppet/parser/functions'
+
+Puppet::Parser::Functions.newfunction(:defined_with_params,
+ :type => :rvalue,
+ :doc => <<-'ENDOFDOC'
+Takes a resource reference and an optional hash of attributes.
+
+Returns true if a resource with the specified attributes has already been added
+to the catalog, and false otherwise.
+
+ user { 'dan':
+ ensure => present,
+ }
+
+ if ! defined_with_params(User[dan], {'ensure' => 'present' }) {
+ user { 'dan': ensure => present, }
+ }
+ENDOFDOC
+) do |vals|
+ reference, params = vals
+ raise(ArgumentError, 'Must specify a reference') unless reference
+ if (! params) || params == ''
+ params = {}
+ end
+ ret = false
+ if resource = findresource(reference.to_s)
+ matches = params.collect do |key, value|
+ resource[key] == value
+ end
+ ret = params.empty? || !matches.include?(false)
+ end
+ Puppet.debug("Resource #{reference} was not determined to be defined")
+ ret
+end
diff --git a/lib/puppet/parser/functions/delete.rb b/lib/puppet/parser/functions/delete.rb
new file mode 100644
index 00000000..f548b444
--- /dev/null
+++ b/lib/puppet/parser/functions/delete.rb
@@ -0,0 +1,49 @@
+#
+# delete.rb
+#
+
+# TODO(Krzysztof Wilczynski): We need to add support for regular expression ...
+
+module Puppet::Parser::Functions
+ newfunction(:delete, :type => :rvalue, :doc => <<-EOS
+Deletes all instances of a given element from an array, substring from a
+string, or key from a hash.
+
+*Examples:*
+
+ delete(['a','b','c','b'], 'b')
+ Would return: ['a','c']
+
+ delete({'a'=>1,'b'=>2,'c'=>3}, 'b')
+ Would return: {'a'=>1,'c'=>3}
+
+ delete({'a'=>1,'b'=>2,'c'=>3}, ['b','c'])
+ Would return: {'a'=>1}
+
+ delete('abracadabra', 'bra')
+ Would return: 'acada'
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 2) then
+ raise(Puppet::ParseError, "delete(): Wrong number of arguments "+
+ "given #{arguments.size} for 2.")
+ end
+
+ collection = arguments[0].dup
+ Array(arguments[1]).each do |item|
+ case collection
+ when Array, Hash
+ collection.delete item
+ when String
+ collection.gsub! item, ''
+ else
+ raise(TypeError, "delete(): First argument must be an Array, " +
+ "String, or Hash. Given an argument of class #{collection.class}.")
+ end
+ end
+ collection
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/delete_at.rb b/lib/puppet/parser/functions/delete_at.rb
new file mode 100644
index 00000000..3eb4b537
--- /dev/null
+++ b/lib/puppet/parser/functions/delete_at.rb
@@ -0,0 +1,49 @@
+#
+# delete_at.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:delete_at, :type => :rvalue, :doc => <<-EOS
+Deletes a determined indexed value from an array.
+
+*Examples:*
+
+ delete_at(['a','b','c'], 1)
+
+Would return: ['a','c']
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "delete_at(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'delete_at(): Requires array to work with')
+ end
+
+ index = arguments[1]
+
+ if index.is_a?(String) and not index.match(/^\d+$/)
+ raise(Puppet::ParseError, 'delete_at(): You must provide ' +
+ 'non-negative numeric index')
+ end
+
+ result = array.clone
+
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ index = index.to_i
+
+ if index > result.size - 1 # First element is at index 0 is it not?
+ raise(Puppet::ParseError, 'delete_at(): Given index ' +
+ 'exceeds size of array given')
+ end
+
+ result.delete_at(index) # We ignore the element that got deleted ...
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/delete_undef_values.rb b/lib/puppet/parser/functions/delete_undef_values.rb
new file mode 100644
index 00000000..f94d4da8
--- /dev/null
+++ b/lib/puppet/parser/functions/delete_undef_values.rb
@@ -0,0 +1,34 @@
+module Puppet::Parser::Functions
+ newfunction(:delete_undef_values, :type => :rvalue, :doc => <<-EOS
+Returns a copy of input hash or array with all undefs deleted.
+
+*Examples:*
+
+ $hash = delete_undef_values({a=>'A', b=>'', c=>undef, d => false})
+
+Would return: {a => 'A', b => '', d => false}
+
+ $array = delete_undef_values(['A','',undef,false])
+
+Would return: ['A','',false]
+
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError,
+ "delete_undef_values(): Wrong number of arguments given " +
+ "(#{args.size})") if args.size < 1
+
+ unless args[0].is_a? Array or args[0].is_a? Hash
+ raise(Puppet::ParseError,
+ "delete_undef_values(): expected an array or hash, got #{args[0]} type #{args[0].class} ")
+ end
+ result = args[0].dup
+ if result.is_a?(Hash)
+ result.delete_if {|key, val| val.equal? :undef}
+ elsif result.is_a?(Array)
+ result.delete :undef
+ end
+ result
+ end
+end
diff --git a/lib/puppet/parser/functions/delete_values.rb b/lib/puppet/parser/functions/delete_values.rb
new file mode 100644
index 00000000..f6c8c0e6
--- /dev/null
+++ b/lib/puppet/parser/functions/delete_values.rb
@@ -0,0 +1,26 @@
+module Puppet::Parser::Functions
+ newfunction(:delete_values, :type => :rvalue, :doc => <<-EOS
+Deletes all instances of a given value from a hash.
+
+*Examples:*
+
+ delete_values({'a'=>'A','b'=>'B','c'=>'C','B'=>'D'}, 'B')
+
+Would return: {'a'=>'A','c'=>'C','B'=>'D'}
+
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError,
+ "delete_values(): Wrong number of arguments given " +
+ "(#{arguments.size} of 2)") if arguments.size != 2
+
+ hash, item = arguments
+
+ if not hash.is_a?(Hash)
+ raise(TypeError, "delete_values(): First argument must be a Hash. " + \
+ "Given an argument of class #{hash.class}.")
+ end
+ hash.dup.delete_if { |key, val| item == val }
+ end
+end
diff --git a/lib/puppet/parser/functions/difference.rb b/lib/puppet/parser/functions/difference.rb
new file mode 100644
index 00000000..cd258f75
--- /dev/null
+++ b/lib/puppet/parser/functions/difference.rb
@@ -0,0 +1,36 @@
+#
+# difference.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:difference, :type => :rvalue, :doc => <<-EOS
+This function returns the difference between two arrays.
+The returned array is a copy of the original array, removing any items that
+also appear in the second array.
+
+*Examples:*
+
+ difference(["a","b","c"],["b","c","d"])
+
+Would return: ["a"]
+ EOS
+ ) do |arguments|
+
+ # Two arguments are required
+ raise(Puppet::ParseError, "difference(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size != 2
+
+ first = arguments[0]
+ second = arguments[1]
+
+ unless first.is_a?(Array) && second.is_a?(Array)
+ raise(Puppet::ParseError, 'difference(): Requires 2 arrays')
+ end
+
+ result = first - second
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/dirname.rb b/lib/puppet/parser/functions/dirname.rb
new file mode 100644
index 00000000..ea8cc1e0
--- /dev/null
+++ b/lib/puppet/parser/functions/dirname.rb
@@ -0,0 +1,15 @@
+module Puppet::Parser::Functions
+ newfunction(:dirname, :type => :rvalue, :doc => <<-EOS
+ Returns the dirname of a path.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "dirname(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ path = arguments[0]
+ return File.dirname(path)
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/downcase.rb b/lib/puppet/parser/functions/downcase.rb
new file mode 100644
index 00000000..040b84f5
--- /dev/null
+++ b/lib/puppet/parser/functions/downcase.rb
@@ -0,0 +1,32 @@
+#
+# downcase.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:downcase, :type => :rvalue, :doc => <<-EOS
+Converts the case of a string or all strings in an array to lower case.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "downcase(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'downcase(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.downcase : i }
+ else
+ result = value.downcase
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/empty.rb b/lib/puppet/parser/functions/empty.rb
new file mode 100644
index 00000000..cca620fa
--- /dev/null
+++ b/lib/puppet/parser/functions/empty.rb
@@ -0,0 +1,27 @@
+#
+# empty.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:empty, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable is empty.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "empty(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(Hash) || value.is_a?(String)
+ raise(Puppet::ParseError, 'empty(): Requires either ' +
+ 'array, hash or string to work with')
+ end
+
+ result = value.empty?
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/ensure_packages.rb b/lib/puppet/parser/functions/ensure_packages.rb
new file mode 100644
index 00000000..f1da4aaa
--- /dev/null
+++ b/lib/puppet/parser/functions/ensure_packages.rb
@@ -0,0 +1,35 @@
+#
+# ensure_packages.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:ensure_packages, :type => :statement, :doc => <<-EOS
+Takes a list of packages and only installs them if they don't already exist.
+It optionally takes a hash as a second parameter that will be passed as the
+third argument to the ensure_resource() function.
+ EOS
+ ) do |arguments|
+
+ if arguments.size > 2 or arguments.size == 0
+ raise(Puppet::ParseError, "ensure_packages(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1 or 2)")
+ elsif arguments.size == 2 and !arguments[1].is_a?(Hash)
+ raise(Puppet::ParseError, 'ensure_packages(): Requires second argument to be a Hash')
+ end
+
+ packages = Array(arguments[0])
+
+ if arguments[1]
+ defaults = { 'ensure' => 'present' }.merge(arguments[1])
+ else
+ defaults = { 'ensure' => 'present' }
+ end
+
+ Puppet::Parser::Functions.function(:ensure_resource)
+ packages.each { |package_name|
+ function_ensure_resource(['package', package_name, defaults ])
+ }
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/ensure_resource.rb b/lib/puppet/parser/functions/ensure_resource.rb
new file mode 100644
index 00000000..1ba6a447
--- /dev/null
+++ b/lib/puppet/parser/functions/ensure_resource.rb
@@ -0,0 +1,46 @@
+# Test whether a given class or definition is defined
+require 'puppet/parser/functions'
+
+Puppet::Parser::Functions.newfunction(:ensure_resource,
+ :type => :statement,
+ :doc => <<-'ENDOFDOC'
+Takes a resource type, title, and a list of attributes that describe a
+resource.
+
+ user { 'dan':
+ ensure => present,
+ }
+
+This example only creates the resource if it does not already exist:
+
+ ensure_resource('user', 'dan', {'ensure' => 'present' })
+
+If the resource already exists but does not match the specified parameters,
+this function will attempt to recreate the resource leading to a duplicate
+resource definition error.
+
+An array of resources can also be passed in and each will be created with
+the type and parameters specified if it doesn't already exist.
+
+ ensure_resource('user', ['dan','alex'], {'ensure' => 'present'})
+
+ENDOFDOC
+) do |vals|
+ type, title, params = vals
+ raise(ArgumentError, 'Must specify a type') unless type
+ raise(ArgumentError, 'Must specify a title') unless title
+ params ||= {}
+
+ items = [title].flatten
+
+ items.each do |item|
+ Puppet::Parser::Functions.function(:defined_with_params)
+ if function_defined_with_params(["#{type}[#{item}]", params])
+ Puppet.debug("Resource #{type}[#{item}] with params #{params} not created because it already exists")
+ else
+ Puppet.debug("Create new resource #{type}[#{item}] with params #{params}")
+ Puppet::Parser::Functions.function(:create_resources)
+ function_create_resources([type.capitalize, { item => params }])
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/flatten.rb b/lib/puppet/parser/functions/flatten.rb
new file mode 100644
index 00000000..a1ed1832
--- /dev/null
+++ b/lib/puppet/parser/functions/flatten.rb
@@ -0,0 +1,33 @@
+#
+# flatten.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:flatten, :type => :rvalue, :doc => <<-EOS
+This function flattens any deeply nested arrays and returns a single flat array
+as a result.
+
+*Examples:*
+
+ flatten(['a', ['b', ['c']]])
+
+Would return: ['a','b','c']
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "flatten(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'flatten(): Requires array to work with')
+ end
+
+ result = array.flatten
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/floor.rb b/lib/puppet/parser/functions/floor.rb
new file mode 100644
index 00000000..9a6f014d
--- /dev/null
+++ b/lib/puppet/parser/functions/floor.rb
@@ -0,0 +1,25 @@
+module Puppet::Parser::Functions
+ newfunction(:floor, :type => :rvalue, :doc => <<-EOS
+ Returns the largest integer less or equal to the argument.
+ Takes a single numeric value as an argument.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "floor(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ begin
+ arg = Float(arguments[0])
+ rescue TypeError, ArgumentError => e
+ raise(Puppet::ParseError, "floor(): Wrong argument type " +
+ "given (#{arguments[0]} for Numeric)")
+ end
+
+ raise(Puppet::ParseError, "floor(): Wrong argument type " +
+ "given (#{arg.class} for Numeric)") if arg.is_a?(Numeric) == false
+
+ arg.floor
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/fqdn_rotate.rb b/lib/puppet/parser/functions/fqdn_rotate.rb
new file mode 100644
index 00000000..7f4d37d0
--- /dev/null
+++ b/lib/puppet/parser/functions/fqdn_rotate.rb
@@ -0,0 +1,45 @@
+#
+# fqdn_rotate.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:fqdn_rotate, :type => :rvalue, :doc => <<-EOS
+Rotates an array a random number of times based on a nodes fqdn.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "fqdn_rotate(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+ require 'digest/md5'
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'fqdn_rotate(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.clone
+
+ string = value.is_a?(String) ? true : false
+
+ # Check whether it makes sense to rotate ...
+ return result if result.size <= 1
+
+ # We turn any string value into an array to be able to rotate ...
+ result = string ? result.split('') : result
+
+ elements = result.size
+
+ srand(Digest::MD5.hexdigest([lookupvar('::fqdn'),arguments].join(':')).hex)
+ rand(elements).times {
+ result.push result.shift
+ }
+
+ result = string ? result.join : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/get_module_path.rb b/lib/puppet/parser/functions/get_module_path.rb
new file mode 100644
index 00000000..1421b91f
--- /dev/null
+++ b/lib/puppet/parser/functions/get_module_path.rb
@@ -0,0 +1,17 @@
+module Puppet::Parser::Functions
+ newfunction(:get_module_path, :type =>:rvalue, :doc => <<-EOT
+ Returns the absolute path of the specified module for the current
+ environment.
+
+ Example:
+ $module_path = get_module_path('stdlib')
+ EOT
+ ) do |args|
+ raise(Puppet::ParseError, "get_module_path(): Wrong number of arguments, expects one") unless args.size == 1
+ if module_path = Puppet::Module.find(args[0], compiler.environment.to_s)
+ module_path.path
+ else
+ raise(Puppet::ParseError, "Could not find module #{args[0]} in environment #{compiler.environment}")
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/getparam.rb b/lib/puppet/parser/functions/getparam.rb
new file mode 100644
index 00000000..6d510069
--- /dev/null
+++ b/lib/puppet/parser/functions/getparam.rb
@@ -0,0 +1,35 @@
+# Test whether a given class or definition is defined
+require 'puppet/parser/functions'
+
+Puppet::Parser::Functions.newfunction(:getparam,
+ :type => :rvalue,
+ :doc => <<-'ENDOFDOC'
+Takes a resource reference and name of the parameter and
+returns value of resource's parameter.
+
+*Examples:*
+
+ define example_resource($param) {
+ }
+
+ example_resource { "example_resource_instance":
+ param => "param_value"
+ }
+
+ getparam(Example_resource["example_resource_instance"], "param")
+
+Would return: param_value
+ENDOFDOC
+) do |vals|
+ reference, param = vals
+ raise(ArgumentError, 'Must specify a reference') unless reference
+ raise(ArgumentError, 'Must specify name of a parameter') unless param and param.instance_of? String
+
+ return '' if param.empty?
+
+ if resource = findresource(reference.to_s)
+ return resource[param] if resource[param]
+ end
+
+ return ''
+end
diff --git a/lib/puppet/parser/functions/getvar.rb b/lib/puppet/parser/functions/getvar.rb
new file mode 100644
index 00000000..fb336b6a
--- /dev/null
+++ b/lib/puppet/parser/functions/getvar.rb
@@ -0,0 +1,29 @@
+module Puppet::Parser::Functions
+
+ newfunction(:getvar, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
+ Lookup a variable in a remote namespace.
+
+ For example:
+
+ $foo = getvar('site::data::foo')
+ # Equivalent to $foo = $site::data::foo
+
+ This is useful if the namespace itself is stored in a string:
+
+ $datalocation = 'site::data'
+ $bar = getvar("${datalocation}::bar")
+ # Equivalent to $bar = $site::data::bar
+ ENDHEREDOC
+
+ unless args.length == 1
+ raise Puppet::ParseError, ("getvar(): wrong number of arguments (#{args.length}; must be 1)")
+ end
+
+ begin
+ self.lookupvar("#{args[0]}")
+ rescue Puppet::ParseError # Eat the exception if strict_variables = true is set
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/grep.rb b/lib/puppet/parser/functions/grep.rb
new file mode 100644
index 00000000..ceba9ecc
--- /dev/null
+++ b/lib/puppet/parser/functions/grep.rb
@@ -0,0 +1,33 @@
+#
+# grep.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:grep, :type => :rvalue, :doc => <<-EOS
+This function searches through an array and returns any elements that match
+the provided regular expression.
+
+*Examples:*
+
+ grep(['aaa','bbb','ccc','aaaddd'], 'aaa')
+
+Would return:
+
+ ['aaa','aaaddd']
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 2) then
+ raise(Puppet::ParseError, "grep(): Wrong number of arguments "+
+ "given #{arguments.size} for 2")
+ end
+
+ a = arguments[0]
+ pattern = Regexp.new(arguments[1])
+
+ a.grep(pattern)
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/has_interface_with.rb b/lib/puppet/parser/functions/has_interface_with.rb
new file mode 100644
index 00000000..36915246
--- /dev/null
+++ b/lib/puppet/parser/functions/has_interface_with.rb
@@ -0,0 +1,68 @@
+#
+# has_interface_with
+#
+
+module Puppet::Parser::Functions
+ newfunction(:has_interface_with, :type => :rvalue, :doc => <<-EOS
+Returns boolean based on kind and value:
+ * macaddress
+ * netmask
+ * ipaddress
+ * network
+
+has_interface_with("macaddress", "x:x:x:x:x:x")
+has_interface_with("ipaddress", "127.0.0.1") => true
+etc.
+
+If no "kind" is given, then the presence of the interface is checked:
+has_interface_with("lo") => true
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError, "has_interface_with(): Wrong number of arguments " +
+ "given (#{args.size} for 1 or 2)") if args.size < 1 or args.size > 2
+
+ interfaces = lookupvar('interfaces')
+
+ # If we do not have any interfaces, then there are no requested attributes
+ return false if (interfaces == :undefined || interfaces.nil?)
+
+ interfaces = interfaces.split(',')
+
+ if args.size == 1
+ return interfaces.member?(args[0])
+ end
+
+ kind, value = args
+
+ # Bug with 3.7.1 - 3.7.3 when using future parser throws :undefined_variable
+ # https://tickets.puppetlabs.com/browse/PUP-3597
+ factval = nil
+ catch :undefined_variable do
+ factval = lookupvar(kind)
+ end
+ if factval == value
+ return true
+ end
+
+ result = false
+ interfaces.each do |iface|
+ iface.downcase!
+ factval = nil
+ begin
+ # Bug with 3.7.1 - 3.7.3 when using future parser throws :undefined_variable
+ # https://tickets.puppetlabs.com/browse/PUP-3597
+ catch :undefined_variable do
+ factval = lookupvar("#{kind}_#{iface}")
+ end
+ rescue Puppet::ParseError # Eat the exception if strict_variables = true is set
+ end
+ if value == factval
+ result = true
+ break
+ end
+ end
+
+ result
+ end
+end
diff --git a/lib/puppet/parser/functions/has_ip_address.rb b/lib/puppet/parser/functions/has_ip_address.rb
new file mode 100644
index 00000000..842c8ec6
--- /dev/null
+++ b/lib/puppet/parser/functions/has_ip_address.rb
@@ -0,0 +1,25 @@
+#
+# has_ip_address
+#
+
+module Puppet::Parser::Functions
+ newfunction(:has_ip_address, :type => :rvalue, :doc => <<-EOS
+Returns true if the client has the requested IP address on some interface.
+
+This function iterates through the 'interfaces' fact and checks the
+'ipaddress_IFACE' facts, performing a simple string comparison.
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError, "has_ip_address(): Wrong number of arguments " +
+ "given (#{args.size} for 1)") if args.size != 1
+
+ Puppet::Parser::Functions.autoloader.load(:has_interface_with) \
+ unless Puppet::Parser::Functions.autoloader.loaded?(:has_interface_with)
+
+ function_has_interface_with(['ipaddress', args[0]])
+
+ end
+end
+
+# vim:sts=2 sw=2
diff --git a/lib/puppet/parser/functions/has_ip_network.rb b/lib/puppet/parser/functions/has_ip_network.rb
new file mode 100644
index 00000000..9ccf9024
--- /dev/null
+++ b/lib/puppet/parser/functions/has_ip_network.rb
@@ -0,0 +1,25 @@
+#
+# has_ip_network
+#
+
+module Puppet::Parser::Functions
+ newfunction(:has_ip_network, :type => :rvalue, :doc => <<-EOS
+Returns true if the client has an IP address within the requested network.
+
+This function iterates through the 'interfaces' fact and checks the
+'network_IFACE' facts, performing a simple string comparision.
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError, "has_ip_network(): Wrong number of arguments " +
+ "given (#{args.size} for 1)") if args.size != 1
+
+ Puppet::Parser::Functions.autoloader.load(:has_interface_with) \
+ unless Puppet::Parser::Functions.autoloader.loaded?(:has_interface_with)
+
+ function_has_interface_with(['network', args[0]])
+
+ end
+end
+
+# vim:sts=2 sw=2
diff --git a/lib/puppet/parser/functions/has_key.rb b/lib/puppet/parser/functions/has_key.rb
new file mode 100644
index 00000000..4657cc29
--- /dev/null
+++ b/lib/puppet/parser/functions/has_key.rb
@@ -0,0 +1,28 @@
+module Puppet::Parser::Functions
+
+ newfunction(:has_key, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
+ Determine if a hash has a certain key value.
+
+ Example:
+
+ $my_hash = {'key_one' => 'value_one'}
+ if has_key($my_hash, 'key_two') {
+ notice('we will not reach here')
+ }
+ if has_key($my_hash, 'key_one') {
+ notice('this will be printed')
+ }
+
+ ENDHEREDOC
+
+ unless args.length == 2
+ raise Puppet::ParseError, ("has_key(): wrong number of arguments (#{args.length}; must be 2)")
+ end
+ unless args[0].is_a?(Hash)
+ raise Puppet::ParseError, "has_key(): expects the first argument to be a hash, got #{args[0].inspect} which is of type #{args[0].class}"
+ end
+ args[0].has_key?(args[1])
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/hash.rb b/lib/puppet/parser/functions/hash.rb
new file mode 100644
index 00000000..8cc4823b
--- /dev/null
+++ b/lib/puppet/parser/functions/hash.rb
@@ -0,0 +1,41 @@
+#
+# hash.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:hash, :type => :rvalue, :doc => <<-EOS
+This function converts an array into a hash.
+
+*Examples:*
+
+ hash(['a',1,'b',2,'c',3])
+
+Would return: {'a'=>1,'b'=>2,'c'=>3}
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "hash(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'hash(): Requires array to work with')
+ end
+
+ result = {}
+
+ begin
+ # This is to make it compatible with older version of Ruby ...
+ array = array.flatten
+ result = Hash[*array]
+ rescue Exception
+ raise(Puppet::ParseError, 'hash(): Unable to compute ' +
+ 'hash from array given')
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/intersection.rb b/lib/puppet/parser/functions/intersection.rb
new file mode 100644
index 00000000..48f02e9d
--- /dev/null
+++ b/lib/puppet/parser/functions/intersection.rb
@@ -0,0 +1,34 @@
+#
+# intersection.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:intersection, :type => :rvalue, :doc => <<-EOS
+This function returns an array an intersection of two.
+
+*Examples:*
+
+ intersection(["a","b","c"],["b","c","d"])
+
+Would return: ["b","c"]
+ EOS
+ ) do |arguments|
+
+ # Two arguments are required
+ raise(Puppet::ParseError, "intersection(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size != 2
+
+ first = arguments[0]
+ second = arguments[1]
+
+ unless first.is_a?(Array) && second.is_a?(Array)
+ raise(Puppet::ParseError, 'intersection(): Requires 2 arrays')
+ end
+
+ result = first & second
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_array.rb b/lib/puppet/parser/functions/is_array.rb
new file mode 100644
index 00000000..b39e184a
--- /dev/null
+++ b/lib/puppet/parser/functions/is_array.rb
@@ -0,0 +1,22 @@
+#
+# is_array.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_array, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable passed to this function is an array.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_array(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ type = arguments[0]
+
+ result = type.is_a?(Array)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_bool.rb b/lib/puppet/parser/functions/is_bool.rb
new file mode 100644
index 00000000..8bbdbc8a
--- /dev/null
+++ b/lib/puppet/parser/functions/is_bool.rb
@@ -0,0 +1,22 @@
+#
+# is_bool.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_bool, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable passed to this function is a boolean.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_bool(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ type = arguments[0]
+
+ result = type.is_a?(TrueClass) || type.is_a?(FalseClass)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_domain_name.rb b/lib/puppet/parser/functions/is_domain_name.rb
new file mode 100644
index 00000000..b3fee965
--- /dev/null
+++ b/lib/puppet/parser/functions/is_domain_name.rb
@@ -0,0 +1,50 @@
+#
+# is_domain_name.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_domain_name, :type => :rvalue, :doc => <<-EOS
+Returns true if the string passed to this function is a syntactically correct domain name.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_domain_name(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ domain = arguments[0]
+
+ # Limits (rfc1035, 3.1)
+ domain_max_length=255
+ label_min_length=1
+ label_max_length=63
+
+ # Only allow string types
+ return false unless domain.is_a?(String)
+
+ # Allow ".", it is the top level domain
+ return true if domain == '.'
+
+ # Remove the final dot, if present.
+ domain.chomp!('.')
+
+ # Check the whole domain
+ return false if domain.empty?
+ return false if domain.length > domain_max_length
+
+ # Check each label in the domain
+ labels = domain.split('.')
+ vlabels = labels.each do |label|
+ break if label.length < label_min_length
+ break if label.length > label_max_length
+ break if label[-1..-1] == '-'
+ break if label[0..0] == '-'
+ break unless /^[a-z\d-]+$/i.match(label)
+ end
+ return vlabels == labels
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_float.rb b/lib/puppet/parser/functions/is_float.rb
new file mode 100644
index 00000000..a2da9438
--- /dev/null
+++ b/lib/puppet/parser/functions/is_float.rb
@@ -0,0 +1,30 @@
+#
+# is_float.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_float, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable passed to this function is a float.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_float(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ value = arguments[0]
+
+ # Only allow Numeric or String types
+ return false unless value.is_a?(Numeric) or value.is_a?(String)
+
+ if value != value.to_f.to_s and !value.is_a? Float then
+ return false
+ else
+ return true
+ end
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_function_available.rb b/lib/puppet/parser/functions/is_function_available.rb
new file mode 100644
index 00000000..6da82c8c
--- /dev/null
+++ b/lib/puppet/parser/functions/is_function_available.rb
@@ -0,0 +1,26 @@
+#
+# is_function_available.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_function_available, :type => :rvalue, :doc => <<-EOS
+This function accepts a string as an argument, determines whether the
+Puppet runtime has access to a function by that name. It returns a
+true if the function exists, false if not.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_function_available?(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ # Only allow String types
+ return false unless arguments[0].is_a?(String)
+
+ function = Puppet::Parser::Functions.function(arguments[0].to_sym)
+ function.is_a?(String) and not function.empty?
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_hash.rb b/lib/puppet/parser/functions/is_hash.rb
new file mode 100644
index 00000000..ad907f08
--- /dev/null
+++ b/lib/puppet/parser/functions/is_hash.rb
@@ -0,0 +1,22 @@
+#
+# is_hash.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_hash, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable passed to this function is a hash.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_hash(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ type = arguments[0]
+
+ result = type.is_a?(Hash)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_integer.rb b/lib/puppet/parser/functions/is_integer.rb
new file mode 100644
index 00000000..c03d28df
--- /dev/null
+++ b/lib/puppet/parser/functions/is_integer.rb
@@ -0,0 +1,45 @@
+#
+# is_integer.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_integer, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable passed to this function is an Integer or
+a decimal (base 10) integer in String form. The string may
+start with a '-' (minus). A value of '0' is allowed, but a leading '0' digit may not
+be followed by other digits as this indicates that the value is octal (base 8).
+
+If given any other argument `false` is returned.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_integer(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ value = arguments[0]
+
+ # Regex is taken from the lexer of puppet
+ # puppet/pops/parser/lexer.rb but modified to match also
+ # negative values and disallow numbers prefixed with multiple
+ # 0's
+ #
+ # TODO these parameter should be a constant but I'm not sure
+ # if there is no risk to declare it inside of the module
+ # Puppet::Parser::Functions
+
+ # Integer numbers like
+ # -1234568981273
+ # 47291
+ numeric = %r{^-?(?:(?:[1-9]\d*)|0)$}
+
+ if value.is_a? Integer or (value.is_a? String and value.match numeric)
+ return true
+ else
+ return false
+ end
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_ip_address.rb b/lib/puppet/parser/functions/is_ip_address.rb
new file mode 100644
index 00000000..a90adabe
--- /dev/null
+++ b/lib/puppet/parser/functions/is_ip_address.rb
@@ -0,0 +1,32 @@
+#
+# is_ip_address.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_ip_address, :type => :rvalue, :doc => <<-EOS
+Returns true if the string passed to this function is a valid IP address.
+ EOS
+ ) do |arguments|
+
+ require 'ipaddr'
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_ip_address(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ begin
+ ip = IPAddr.new(arguments[0])
+ rescue ArgumentError
+ return false
+ end
+
+ if ip.ipv4? or ip.ipv6? then
+ return true
+ else
+ return false
+ end
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_mac_address.rb b/lib/puppet/parser/functions/is_mac_address.rb
new file mode 100644
index 00000000..1b3088a2
--- /dev/null
+++ b/lib/puppet/parser/functions/is_mac_address.rb
@@ -0,0 +1,27 @@
+#
+# is_mac_address.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_mac_address, :type => :rvalue, :doc => <<-EOS
+Returns true if the string passed to this function is a valid mac address.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_mac_address(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ mac = arguments[0]
+
+ if /^[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}$/.match(mac) then
+ return true
+ else
+ return false
+ end
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_numeric.rb b/lib/puppet/parser/functions/is_numeric.rb
new file mode 100644
index 00000000..e7e1d2a7
--- /dev/null
+++ b/lib/puppet/parser/functions/is_numeric.rb
@@ -0,0 +1,75 @@
+#
+# is_numeric.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_numeric, :type => :rvalue, :doc => <<-EOS
+Returns true if the given argument is a Numeric (Integer or Float),
+or a String containing either a valid integer in decimal base 10 form, or
+a valid floating point string representation.
+
+The function recognizes only decimal (base 10) integers and float but not
+integers in hex (base 16) or octal (base 8) form.
+
+The string representation may start with a '-' (minus). If a decimal '.' is used,
+it must be followed by at least one digit.
+
+Valid examples:
+
+ 77435
+ 10e-12
+ -8475
+ 0.2343
+ -23.561e3
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "is_numeric(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ value = arguments[0]
+
+ # Regex is taken from the lexer of puppet
+ # puppet/pops/parser/lexer.rb but modified to match also
+ # negative values and disallow invalid octal numbers or
+ # numbers prefixed with multiple 0's (except in hex numbers)
+ #
+ # TODO these parameters should be constants but I'm not sure
+ # if there is no risk to declare them inside of the module
+ # Puppet::Parser::Functions
+
+ # TODO decide if this should be used
+ # HEX numbers like
+ # 0xaa230F
+ # 0X1234009C
+ # 0x0012
+ # -12FcD
+ #numeric_hex = %r{^-?0[xX][0-9A-Fa-f]+$}
+
+ # TODO decide if this should be used
+ # OCTAL numbers like
+ # 01234567
+ # -045372
+ #numeric_oct = %r{^-?0[1-7][0-7]*$}
+
+ # Integer/Float numbers like
+ # -0.1234568981273
+ # 47291
+ # 42.12345e-12
+ numeric = %r{^-?(?:(?:[1-9]\d*)|0)(?:\.\d+)?(?:[eE]-?\d+)?$}
+
+ if value.is_a? Numeric or (value.is_a? String and (
+ value.match(numeric) #or
+ # value.match(numeric_hex) or
+ # value.match(numeric_oct)
+ ))
+ return true
+ else
+ return false
+ end
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/is_string.rb b/lib/puppet/parser/functions/is_string.rb
new file mode 100644
index 00000000..f5bef045
--- /dev/null
+++ b/lib/puppet/parser/functions/is_string.rb
@@ -0,0 +1,26 @@
+#
+# is_string.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:is_string, :type => :rvalue, :doc => <<-EOS
+Returns true if the variable passed to this function is a string.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "is_string(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ type = arguments[0]
+
+ result = type.is_a?(String)
+
+ if result and (type == type.to_f.to_s or type == type.to_i.to_s) then
+ return false
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/join.rb b/lib/puppet/parser/functions/join.rb
new file mode 100644
index 00000000..6c0a6ba0
--- /dev/null
+++ b/lib/puppet/parser/functions/join.rb
@@ -0,0 +1,41 @@
+#
+# join.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:join, :type => :rvalue, :doc => <<-EOS
+This function joins an array into a string using a separator.
+
+*Examples:*
+
+ join(['a','b','c'], ",")
+
+Would result in: "a,b,c"
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "join(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'join(): Requires array to work with')
+ end
+
+ suffix = arguments[1] if arguments[1]
+
+ if suffix
+ unless suffix.is_a?(String)
+ raise(Puppet::ParseError, 'join(): Requires string to work with')
+ end
+ end
+
+ result = suffix ? array.join(suffix) : array.join
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/join_keys_to_values.rb b/lib/puppet/parser/functions/join_keys_to_values.rb
new file mode 100644
index 00000000..e9924fe2
--- /dev/null
+++ b/lib/puppet/parser/functions/join_keys_to_values.rb
@@ -0,0 +1,47 @@
+#
+# join.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:join_keys_to_values, :type => :rvalue, :doc => <<-EOS
+This function joins each key of a hash to that key's corresponding value with a
+separator. Keys and values are cast to strings. The return value is an array in
+which each element is one joined key/value pair.
+
+*Examples:*
+
+ join_keys_to_values({'a'=>1,'b'=>2}, " is ")
+
+Would result in: ["a is 1","b is 2"]
+ EOS
+ ) do |arguments|
+
+ # Validate the number of arguments.
+ if arguments.size != 2
+ raise(Puppet::ParseError, "join_keys_to_values(): Takes exactly two " +
+ "arguments, but #{arguments.size} given.")
+ end
+
+ # Validate the first argument.
+ hash = arguments[0]
+ if not hash.is_a?(Hash)
+ raise(TypeError, "join_keys_to_values(): The first argument must be a " +
+ "hash, but a #{hash.class} was given.")
+ end
+
+ # Validate the second argument.
+ separator = arguments[1]
+ if not separator.is_a?(String)
+ raise(TypeError, "join_keys_to_values(): The second argument must be a " +
+ "string, but a #{separator.class} was given.")
+ end
+
+ # Join the keys to their values.
+ hash.map do |k,v|
+ String(k) + separator + String(v)
+ end
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/keys.rb b/lib/puppet/parser/functions/keys.rb
new file mode 100644
index 00000000..f0d13b64
--- /dev/null
+++ b/lib/puppet/parser/functions/keys.rb
@@ -0,0 +1,26 @@
+#
+# keys.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:keys, :type => :rvalue, :doc => <<-EOS
+Returns the keys of a hash as an array.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "keys(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ hash = arguments[0]
+
+ unless hash.is_a?(Hash)
+ raise(Puppet::ParseError, 'keys(): Requires hash to work with')
+ end
+
+ result = hash.keys
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/loadyaml.rb b/lib/puppet/parser/functions/loadyaml.rb
new file mode 100644
index 00000000..10c40050
--- /dev/null
+++ b/lib/puppet/parser/functions/loadyaml.rb
@@ -0,0 +1,20 @@
+module Puppet::Parser::Functions
+
+ newfunction(:loadyaml, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
+ Load a YAML file containing an array, string, or hash, and return the data
+ in the corresponding native data type.
+
+ For example:
+
+ $myhash = loadyaml('/etc/puppet/data/myhash.yaml')
+ ENDHEREDOC
+
+ unless args.length == 1
+ raise Puppet::ParseError, ("loadyaml(): wrong number of arguments (#{args.length}; must be 1)")
+ end
+
+ YAML.load_file(args[0])
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/lstrip.rb b/lib/puppet/parser/functions/lstrip.rb
new file mode 100644
index 00000000..624e4c84
--- /dev/null
+++ b/lib/puppet/parser/functions/lstrip.rb
@@ -0,0 +1,32 @@
+#
+# lstrip.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:lstrip, :type => :rvalue, :doc => <<-EOS
+Strips leading spaces to the left of a string.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "lstrip(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'lstrip(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.lstrip : i }
+ else
+ result = value.lstrip
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/max.rb b/lib/puppet/parser/functions/max.rb
new file mode 100644
index 00000000..60fb94ac
--- /dev/null
+++ b/lib/puppet/parser/functions/max.rb
@@ -0,0 +1,21 @@
+module Puppet::Parser::Functions
+ newfunction(:max, :type => :rvalue, :doc => <<-EOS
+ Returns the highest value of all arguments.
+ Requires at least one argument.
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError, "max(): Wrong number of arguments " +
+ "need at least one") if args.size == 0
+
+ # Sometimes we get numbers as numerics and sometimes as strings.
+ # We try to compare them as numbers when possible
+ return args.max do |a,b|
+ if a.to_s =~ /\A-?\d+(.\d+)?\z/ and b.to_s =~ /\A-?\d+(.\d+)?\z/ then
+ a.to_f <=> b.to_f
+ else
+ a.to_s <=> b.to_s
+ end
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/member.rb b/lib/puppet/parser/functions/member.rb
new file mode 100644
index 00000000..88609ce5
--- /dev/null
+++ b/lib/puppet/parser/functions/member.rb
@@ -0,0 +1,62 @@
+#
+# member.rb
+#
+
+# TODO(Krzysztof Wilczynski): We need to add support for regular expression ...
+# TODO(Krzysztof Wilczynski): Support for strings and hashes too ...
+
+module Puppet::Parser::Functions
+ newfunction(:member, :type => :rvalue, :doc => <<-EOS
+This function determines if a variable is a member of an array.
+The variable can be a string, fixnum, or array.
+
+*Examples:*
+
+ member(['a','b'], 'b')
+
+Would return: true
+
+ member(['a', 'b', 'c'], ['a', 'b'])
+
+would return: true
+
+ member(['a','b'], 'c')
+
+Would return: false
+
+ member(['a', 'b', 'c'], ['d', 'b'])
+
+would return: false
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "member(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'member(): Requires array to work with')
+ end
+
+ unless arguments[1].is_a? String or arguments[1].is_a? Fixnum or arguments[1].is_a? Array
+ raise(Puppet::ParseError, 'member(): Item to search for must be a string, fixnum, or array')
+ end
+
+ if arguments[1].is_a? String or arguments[1].is_a? Fixnum
+ item = Array(arguments[1])
+ else
+ item = arguments[1]
+ end
+
+
+ raise(Puppet::ParseError, 'member(): You must provide item ' +
+ 'to search for within array given') if item.respond_to?('empty?') && item.empty?
+
+ result = (item - array).empty?
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/merge.rb b/lib/puppet/parser/functions/merge.rb
new file mode 100644
index 00000000..1b39f206
--- /dev/null
+++ b/lib/puppet/parser/functions/merge.rb
@@ -0,0 +1,34 @@
+module Puppet::Parser::Functions
+ newfunction(:merge, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
+ Merges two or more hashes together and returns the resulting hash.
+
+ For example:
+
+ $hash1 = {'one' => 1, 'two', => 2}
+ $hash2 = {'two' => 'dos', 'three', => 'tres'}
+ $merged_hash = merge($hash1, $hash2)
+ # The resulting hash is equivalent to:
+ # $merged_hash = {'one' => 1, 'two' => 'dos', 'three' => 'tres'}
+
+ When there is a duplicate key, the key in the rightmost hash will "win."
+
+ ENDHEREDOC
+
+ if args.length < 2
+ raise Puppet::ParseError, ("merge(): wrong number of arguments (#{args.length}; must be at least 2)")
+ end
+
+ # The hash we accumulate into
+ accumulator = Hash.new
+ # Merge into the accumulator hash
+ args.each do |arg|
+ next if arg.is_a? String and arg.empty? # empty string is synonym for puppet's undef
+ unless arg.is_a?(Hash)
+ raise Puppet::ParseError, "merge: unexpected argument type #{arg.class}, only expects hash arguments"
+ end
+ accumulator.merge!(arg)
+ end
+ # Return the fully merged hash
+ accumulator
+ end
+end
diff --git a/lib/puppet/parser/functions/min.rb b/lib/puppet/parser/functions/min.rb
new file mode 100644
index 00000000..6bd6ebf2
--- /dev/null
+++ b/lib/puppet/parser/functions/min.rb
@@ -0,0 +1,21 @@
+module Puppet::Parser::Functions
+ newfunction(:min, :type => :rvalue, :doc => <<-EOS
+ Returns the lowest value of all arguments.
+ Requires at least one argument.
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError, "min(): Wrong number of arguments " +
+ "need at least one") if args.size == 0
+
+ # Sometimes we get numbers as numerics and sometimes as strings.
+ # We try to compare them as numbers when possible
+ return args.min do |a,b|
+ if a.to_s =~ /\A^-?\d+(.\d+)?\z/ and b.to_s =~ /\A-?\d+(.\d+)?\z/ then
+ a.to_f <=> b.to_f
+ else
+ a.to_s <=> b.to_s
+ end
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/num2bool.rb b/lib/puppet/parser/functions/num2bool.rb
new file mode 100644
index 00000000..af0e6ed7
--- /dev/null
+++ b/lib/puppet/parser/functions/num2bool.rb
@@ -0,0 +1,43 @@
+#
+# num2bool.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:num2bool, :type => :rvalue, :doc => <<-EOS
+This function converts a number or a string representation of a number into a
+true boolean. Zero or anything non-numeric becomes false. Numbers higher then 0
+become true.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "num2bool(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ number = arguments[0]
+
+ case number
+ when Numeric
+ # Yay, it's a number
+ when String
+ begin
+ number = Float(number)
+ rescue ArgumentError => ex
+ raise(Puppet::ParseError, "num2bool(): '#{number}' does not look like a number: #{ex.message}")
+ end
+ else
+ begin
+ number = number.to_s
+ rescue NoMethodError => ex
+ raise(Puppet::ParseError, "num2bool(): Unable to parse argument: #{ex.message}")
+ end
+ end
+
+ # Truncate Floats
+ number = number.to_i
+
+ # Return true for any positive number and false otherwise
+ return number > 0
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/obfuscate_email.rb b/lib/puppet/parser/functions/obfuscate_email.rb
new file mode 100644
index 00000000..4e4cb826
--- /dev/null
+++ b/lib/puppet/parser/functions/obfuscate_email.rb
@@ -0,0 +1,16 @@
+module Puppet::Parser::Functions
+ newfunction(:obfuscate_email, :type => :rvalue, :doc => <<-EOS
+Given:
+ a comma seperated email string in form of 'john@doe.com, doe@john.com'
+
+This function will return all emails obfuscated in form of 'john {at} doe {dot} com, doe {at} john {dot} com'
+Works with multiple email adresses as well as with a single email adress.
+
+ EOS
+ ) do |args|
+ args[0].gsub('@', ' {at} ').gsub('.', ' {dot} ')
+ end
+end
+
+# vim: set ts=2 sw=2 et :
+# encoding: utf-8
diff --git a/lib/puppet/parser/functions/parsejson.rb b/lib/puppet/parser/functions/parsejson.rb
new file mode 100644
index 00000000..a9a16a45
--- /dev/null
+++ b/lib/puppet/parser/functions/parsejson.rb
@@ -0,0 +1,24 @@
+#
+# parsejson.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:parsejson, :type => :rvalue, :doc => <<-EOS
+This function accepts JSON as a string and converts into the correct Puppet
+structure.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "parsejson(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ json = arguments[0]
+
+ # PSON is natively available in puppet
+ PSON.load(json)
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/parseyaml.rb b/lib/puppet/parser/functions/parseyaml.rb
new file mode 100644
index 00000000..53d54faf
--- /dev/null
+++ b/lib/puppet/parser/functions/parseyaml.rb
@@ -0,0 +1,24 @@
+#
+# parseyaml.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:parseyaml, :type => :rvalue, :doc => <<-EOS
+This function accepts YAML as a string and converts it into the correct
+Puppet structure.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "parseyaml(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ require 'yaml'
+
+ YAML::load(arguments[0])
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/pick.rb b/lib/puppet/parser/functions/pick.rb
new file mode 100644
index 00000000..fdd0aefd
--- /dev/null
+++ b/lib/puppet/parser/functions/pick.rb
@@ -0,0 +1,29 @@
+module Puppet::Parser::Functions
+ newfunction(:pick, :type => :rvalue, :doc => <<-EOS
+
+This function is similar to a coalesce function in SQL in that it will return
+the first value in a list of values that is not undefined or an empty string
+(two things in Puppet that will return a boolean false value). Typically,
+this function is used to check for a value in the Puppet Dashboard/Enterprise
+Console, and failover to a default value like the following:
+
+ $real_jenkins_version = pick($::jenkins_version, '1.449')
+
+The value of $real_jenkins_version will first look for a top-scope variable
+called 'jenkins_version' (note that parameters set in the Puppet Dashboard/
+Enterprise Console are brought into Puppet as top-scope variables), and,
+failing that, will use a default value of 1.449.
+
+EOS
+) do |args|
+ args = args.compact
+ args.delete(:undef)
+ args.delete(:undefined)
+ args.delete("")
+ if args[0].to_s.empty? then
+ fail Puppet::ParseError, "pick(): must receive at least one non empty value"
+ else
+ return args[0]
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/pick_default.rb b/lib/puppet/parser/functions/pick_default.rb
new file mode 100644
index 00000000..36e33abf
--- /dev/null
+++ b/lib/puppet/parser/functions/pick_default.rb
@@ -0,0 +1,35 @@
+module Puppet::Parser::Functions
+ newfunction(:pick_default, :type => :rvalue, :doc => <<-EOS
+
+This function is similar to a coalesce function in SQL in that it will return
+the first value in a list of values that is not undefined or an empty string
+(two things in Puppet that will return a boolean false value). If no value is
+found, it will return the last argument.
+
+Typically, this function is used to check for a value in the Puppet
+Dashboard/Enterprise Console, and failover to a default value like the
+following:
+
+ $real_jenkins_version = pick_default($::jenkins_version, '1.449')
+
+The value of $real_jenkins_version will first look for a top-scope variable
+called 'jenkins_version' (note that parameters set in the Puppet Dashboard/
+Enterprise Console are brought into Puppet as top-scope variables), and,
+failing that, will use a default value of 1.449.
+
+Note that, contrary to the pick() function, the pick_default does not fail if
+all arguments are empty. This allows pick_default to use an empty value as
+default.
+
+EOS
+) do |args|
+ fail "Must receive at least one argument." if args.empty?
+ default = args.last
+ args = args[0..-2].compact
+ args.delete(:undef)
+ args.delete(:undefined)
+ args.delete("")
+ args << default
+ return args[0]
+ end
+end
diff --git a/lib/puppet/parser/functions/prefix.rb b/lib/puppet/parser/functions/prefix.rb
new file mode 100644
index 00000000..d02286af
--- /dev/null
+++ b/lib/puppet/parser/functions/prefix.rb
@@ -0,0 +1,45 @@
+#
+# prefix.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:prefix, :type => :rvalue, :doc => <<-EOS
+This function applies a prefix to all elements in an array.
+
+*Examples:*
+
+ prefix(['a','b','c'], 'p')
+
+Will return: ['pa','pb','pc']
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "prefix(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise Puppet::ParseError, "prefix(): expected first argument to be an Array, got #{array.inspect}"
+ end
+
+ prefix = arguments[1] if arguments[1]
+
+ if prefix
+ unless prefix.is_a?(String)
+ raise Puppet::ParseError, "prefix(): expected second argument to be a String, got #{prefix.inspect}"
+ end
+ end
+
+ # Turn everything into string same as join would do ...
+ result = array.collect do |i|
+ i = i.to_s
+ prefix ? prefix + i : i
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/private.rb b/lib/puppet/parser/functions/private.rb
new file mode 100644
index 00000000..60210d33
--- /dev/null
+++ b/lib/puppet/parser/functions/private.rb
@@ -0,0 +1,29 @@
+#
+# private.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:private, :doc => <<-'EOS'
+ Sets the current class or definition as private.
+ Calling the class or definition from outside the current module will fail.
+ EOS
+ ) do |args|
+
+ raise(Puppet::ParseError, "private(): Wrong number of arguments "+
+ "given (#{args.size}}) for 0 or 1)") if args.size > 1
+
+ scope = self
+ if scope.lookupvar('module_name') != scope.lookupvar('caller_module_name')
+ message = nil
+ if args[0] and args[0].is_a? String
+ message = args[0]
+ else
+ manifest_name = scope.source.name
+ manifest_type = scope.source.type
+ message = (manifest_type.to_s == 'hostclass') ? 'Class' : 'Definition'
+ message += " #{manifest_name} is private"
+ end
+ raise(Puppet::ParseError, message)
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/range.rb b/lib/puppet/parser/functions/range.rb
new file mode 100644
index 00000000..49fba21c
--- /dev/null
+++ b/lib/puppet/parser/functions/range.rb
@@ -0,0 +1,88 @@
+#
+# range.rb
+#
+
+# TODO(Krzysztof Wilczynski): We probably need to approach numeric values differently ...
+
+module Puppet::Parser::Functions
+ newfunction(:range, :type => :rvalue, :doc => <<-EOS
+When given range in the form of (start, stop) it will extrapolate a range as
+an array.
+
+*Examples:*
+
+ range("0", "9")
+
+Will return: [0,1,2,3,4,5,6,7,8,9]
+
+ range("00", "09")
+
+Will return: [0,1,2,3,4,5,6,7,8,9] (Zero padded strings are converted to
+integers automatically)
+
+ range("a", "c")
+
+Will return: ["a","b","c"]
+
+ range("host01", "host10")
+
+Will return: ["host01", "host02", ..., "host09", "host10"]
+
+Passing a third argument will cause the generated range to step by that
+interval, e.g.
+
+ range("0", "9", "2")
+
+Will return: [0,2,4,6,8]
+ EOS
+ ) do |arguments|
+
+ # We support more than one argument but at least one is mandatory ...
+ raise(Puppet::ParseError, "range(): Wrong number of " +
+ "arguments given (#{arguments.size} for 1)") if arguments.size < 1
+
+ if arguments.size > 1
+ start = arguments[0]
+ stop = arguments[1]
+ step = arguments[2].nil? ? 1 : arguments[2].to_i.abs
+
+ type = '..' # We select simplest type for Range available in Ruby ...
+
+ elsif arguments.size > 0
+ value = arguments[0]
+
+ if m = value.match(/^(\w+)(\.\.\.?|\-)(\w+)$/)
+ start = m[1]
+ stop = m[3]
+
+ type = m[2]
+
+ elsif value.match(/^.+$/)
+ raise(Puppet::ParseError, 'range(): Unable to compute range ' +
+ 'from the value given')
+ else
+ raise(Puppet::ParseError, 'range(): Unknown format of range given')
+ end
+ end
+
+ # Check whether we have integer value if so then make it so ...
+ if start.to_s.match(/^\d+$/)
+ start = start.to_i
+ stop = stop.to_i
+ else
+ start = start.to_s
+ stop = stop.to_s
+ end
+
+ range = case type
+ when /^(\.\.|\-)$/ then (start .. stop)
+ when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ...
+ end
+
+ result = range.step(step).collect { |i| i } # Get them all ... Pokemon ...
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/reject.rb b/lib/puppet/parser/functions/reject.rb
new file mode 100644
index 00000000..1953ffcf
--- /dev/null
+++ b/lib/puppet/parser/functions/reject.rb
@@ -0,0 +1,31 @@
+#
+# reject.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:reject, :type => :rvalue, :doc => <<-EOS) do |args|
+This function searches through an array and rejects all elements that match
+the provided regular expression.
+
+*Examples:*
+
+ reject(['aaa','bbb','ccc','aaaddd'], 'aaa')
+
+Would return:
+
+ ['bbb','ccc']
+EOS
+
+ if (args.size != 2)
+ raise Puppet::ParseError,
+ "reject(): Wrong number of arguments given #{args.size} for 2"
+ end
+
+ ary = args[0]
+ pattern = Regexp.new(args[1])
+
+ ary.reject { |e| e =~ pattern }
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/reverse.rb b/lib/puppet/parser/functions/reverse.rb
new file mode 100644
index 00000000..7f1018f6
--- /dev/null
+++ b/lib/puppet/parser/functions/reverse.rb
@@ -0,0 +1,27 @@
+#
+# reverse.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:reverse, :type => :rvalue, :doc => <<-EOS
+Reverses the order of a string or array.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "reverse(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'reverse(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.reverse
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/rstrip.rb b/lib/puppet/parser/functions/rstrip.rb
new file mode 100644
index 00000000..0cf8d222
--- /dev/null
+++ b/lib/puppet/parser/functions/rstrip.rb
@@ -0,0 +1,31 @@
+#
+# rstrip.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:rstrip, :type => :rvalue, :doc => <<-EOS
+Strips leading spaces to the right of the string.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "rstrip(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'rstrip(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ result = value.collect { |i| i.is_a?(String) ? i.rstrip : i }
+ else
+ result = value.rstrip
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/shuffle.rb b/lib/puppet/parser/functions/shuffle.rb
new file mode 100644
index 00000000..30c663db
--- /dev/null
+++ b/lib/puppet/parser/functions/shuffle.rb
@@ -0,0 +1,45 @@
+#
+# shuffle.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:shuffle, :type => :rvalue, :doc => <<-EOS
+Randomizes the order of a string or array elements.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "shuffle(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'shuffle(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.clone
+
+ string = value.is_a?(String) ? true : false
+
+ # Check whether it makes sense to shuffle ...
+ return result if result.size <= 1
+
+ # We turn any string value into an array to be able to shuffle ...
+ result = string ? result.split('') : result
+
+ elements = result.size
+
+ # Simple implementation of Fisher–Yates in-place shuffle ...
+ elements.times do |i|
+ j = rand(elements - i) + i
+ result[j], result[i] = result[i], result[j]
+ end
+
+ result = string ? result.join : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/size.rb b/lib/puppet/parser/functions/size.rb
new file mode 100644
index 00000000..cc207e3f
--- /dev/null
+++ b/lib/puppet/parser/functions/size.rb
@@ -0,0 +1,48 @@
+#
+# size.rb
+#
+
+# TODO(Krzysztof Wilczynski): Support for hashes would be nice too ...
+
+module Puppet::Parser::Functions
+ newfunction(:size, :type => :rvalue, :doc => <<-EOS
+Returns the number of elements in a string or array.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "size(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ item = arguments[0]
+
+ if item.is_a?(String)
+
+ begin
+ #
+ # Check whether your item is a numeric value or not ...
+ # This will take care about positive and/or negative numbers
+ # for both integer and floating-point values ...
+ #
+ # Please note that Puppet has no notion of hexadecimal
+ # nor octal numbers for its DSL at this point in time ...
+ #
+ Float(item)
+
+ raise(Puppet::ParseError, 'size(): Requires either ' +
+ 'string or array to work with')
+
+ rescue ArgumentError
+ result = item.size
+ end
+
+ elsif item.is_a?(Array)
+ result = item.size
+ else
+ raise(Puppet::ParseError, 'size(): Unknown type given')
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/sort.rb b/lib/puppet/parser/functions/sort.rb
new file mode 100644
index 00000000..cefbe546
--- /dev/null
+++ b/lib/puppet/parser/functions/sort.rb
@@ -0,0 +1,27 @@
+#
+# sort.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:sort, :type => :rvalue, :doc => <<-EOS
+Sorts strings and arrays lexically.
+ EOS
+ ) do |arguments|
+
+ if (arguments.size != 1) then
+ raise(Puppet::ParseError, "sort(): Wrong number of arguments "+
+ "given #{arguments.size} for 1")
+ end
+
+ value = arguments[0]
+
+ if value.is_a?(Array) then
+ value.sort
+ elsif value.is_a?(String) then
+ value.split("").sort.join("")
+ end
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/squeeze.rb b/lib/puppet/parser/functions/squeeze.rb
new file mode 100644
index 00000000..81fadfdb
--- /dev/null
+++ b/lib/puppet/parser/functions/squeeze.rb
@@ -0,0 +1,36 @@
+#
+# squeeze.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:squeeze, :type => :rvalue, :doc => <<-EOS
+Returns a new string where runs of the same character that occur in this set are replaced by a single character.
+ EOS
+ ) do |arguments|
+
+ if ((arguments.size != 2) and (arguments.size != 1)) then
+ raise(Puppet::ParseError, "squeeze(): Wrong number of arguments "+
+ "given #{arguments.size} for 2 or 1")
+ end
+
+ item = arguments[0]
+ squeezeval = arguments[1]
+
+ if item.is_a?(Array) then
+ if squeezeval then
+ item.collect { |i| i.squeeze(squeezeval) }
+ else
+ item.collect { |i| i.squeeze }
+ end
+ else
+ if squeezeval then
+ item.squeeze(squeezeval)
+ else
+ item.squeeze
+ end
+ end
+
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/str2bool.rb b/lib/puppet/parser/functions/str2bool.rb
new file mode 100644
index 00000000..446732ec
--- /dev/null
+++ b/lib/puppet/parser/functions/str2bool.rb
@@ -0,0 +1,46 @@
+#
+# str2bool.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:str2bool, :type => :rvalue, :doc => <<-EOS
+This converts a string to a boolean. This attempt to convert strings that
+contain things like: y, 1, t, true to 'true' and strings that contain things
+like: 0, f, n, false, no to 'false'.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "str2bool(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ string = arguments[0]
+
+ # If string is already Boolean, return it
+ if !!string == string
+ return string
+ end
+
+ unless string.is_a?(String)
+ raise(Puppet::ParseError, 'str2bool(): Requires either ' +
+ 'string to work with')
+ end
+
+ # We consider all the yes, no, y, n and so on too ...
+ result = case string
+ #
+ # This is how undef looks like in Puppet ...
+ # We yield false in this case.
+ #
+ when /^$/, '' then false # Empty string will be false ...
+ when /^(1|t|y|true|yes)$/ then true
+ when /^(0|f|n|false|no)$/ then false
+ when /^(undef|undefined)$/ then false # This is not likely to happen ...
+ else
+ raise(Puppet::ParseError, 'str2bool(): Unknown type of boolean given')
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/str2saltedsha1.rb b/lib/puppet/parser/functions/str2saltedsha1.rb
new file mode 100644
index 00000000..e51a861a
--- /dev/null
+++ b/lib/puppet/parser/functions/str2saltedsha1.rb
@@ -0,0 +1,32 @@
+#
+# str2saltedsha1.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:str2saltedsha1, :type => :rvalue, :doc => <<-EOS
+This converts a string to a salted-SHA1 password hash (which is used for
+OS X versions >= 10.7). Given any simple string, you will get a hex version
+of a salted-SHA1 password hash that can be inserted into your Puppet
+manifests as a valid password attribute.
+ EOS
+ ) do |arguments|
+ require 'digest/sha2'
+
+ raise(Puppet::ParseError, "str2saltedsha1(): Wrong number of arguments " +
+ "passed (#{arguments.size} but we require 1)") if arguments.size != 1
+
+ password = arguments[0]
+
+ unless password.is_a?(String)
+ raise(Puppet::ParseError, 'str2saltedsha1(): Requires a ' +
+ "String argument, you passed: #{password.class}")
+ end
+
+ seedint = rand(2**31 - 1)
+ seedstring = Array(seedint).pack("L")
+ saltedpass = Digest::SHA1.digest(seedstring + password)
+ (seedstring + saltedpass).unpack('H*')[0]
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/str2saltedsha512.rb b/lib/puppet/parser/functions/str2saltedsha512.rb
new file mode 100644
index 00000000..7fe7b012
--- /dev/null
+++ b/lib/puppet/parser/functions/str2saltedsha512.rb
@@ -0,0 +1,32 @@
+#
+# str2saltedsha512.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:str2saltedsha512, :type => :rvalue, :doc => <<-EOS
+This converts a string to a salted-SHA512 password hash (which is used for
+OS X versions >= 10.7). Given any simple string, you will get a hex version
+of a salted-SHA512 password hash that can be inserted into your Puppet
+manifests as a valid password attribute.
+ EOS
+ ) do |arguments|
+ require 'digest/sha2'
+
+ raise(Puppet::ParseError, "str2saltedsha512(): Wrong number of arguments " +
+ "passed (#{arguments.size} but we require 1)") if arguments.size != 1
+
+ password = arguments[0]
+
+ unless password.is_a?(String)
+ raise(Puppet::ParseError, 'str2saltedsha512(): Requires a ' +
+ "String argument, you passed: #{password.class}")
+ end
+
+ seedint = rand(2**31 - 1)
+ seedstring = Array(seedint).pack("L")
+ saltedpass = Digest::SHA512.digest(seedstring + password)
+ (seedstring + saltedpass).unpack('H*')[0]
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/str2sha1_and_salt.rb b/lib/puppet/parser/functions/str2sha1_and_salt.rb
new file mode 100644
index 00000000..9ec382d0
--- /dev/null
+++ b/lib/puppet/parser/functions/str2sha1_and_salt.rb
@@ -0,0 +1,36 @@
+#
+# str2saltedsha1.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:str2sha1_and_salt, :type => :rvalue, :doc => <<-EOS
+This converts a string to an array containing the salted SHA1 password hash in
+the first field, and the salt itself in second field of the returned array.
+This combination is used i.e. for couchdb passwords.
+ EOS
+ ) do |arguments|
+ require 'digest/sha1'
+
+ raise(Puppet::ParseError, "str2saltedsha1(): Wrong number of arguments " +
+ "passed (#{arguments.size} but we require 1)") if arguments.size != 1
+
+ password = arguments[0]
+
+ unless password.is_a?(String)
+ raise(Puppet::ParseError, 'str2saltedsha1(): Requires a ' +
+ "String argument, you passed: #{password.class}")
+ end
+
+ seedint = rand(2**31 - 1)
+ seedstring = Array(seedint).pack("L")
+ salt = Digest::MD5.hexdigest(seedstring)
+ saltedpass = Digest::SHA1.hexdigest(password + salt)
+
+ array = Array.new
+ array << saltedpass
+ array << salt
+ return array
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/str_and_salt2sha1.rb b/lib/puppet/parser/functions/str_and_salt2sha1.rb
new file mode 100644
index 00000000..71d69cf5
--- /dev/null
+++ b/lib/puppet/parser/functions/str_and_salt2sha1.rb
@@ -0,0 +1,32 @@
+#
+# str_and_salt2sha1.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:str_and_salt2sha1, :type => :rvalue, :doc => <<-EOS
+This converts a string to an array containing the salted SHA1 password hash in
+the first field, and the salt itself in second field of the returned array.
+This combination is used i.e. for couchdb passwords.
+ EOS
+ ) do |arguments|
+ require 'digest/sha1'
+
+ raise(Puppet::ParseError, "str_and_salt2sha1(): Wrong number of arguments " +
+ "passed (#{arguments.size} but we require 1)") if arguments.size != 1
+
+ str_and_salt = arguments[0]
+
+ unless str_and_salt.is_a?(Array)
+ raise(Puppet::ParseError, 'str_and_salt2sha1(): Requires a ' +
+ "Array argument, you passed: #{password.class}")
+ end
+
+ str = str_and_salt[0]
+ salt = str_and_salt[1]
+ sha1 = Digest::SHA1.hexdigest(str+ salt)
+
+ return sha1
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/strftime.rb b/lib/puppet/parser/functions/strftime.rb
new file mode 100644
index 00000000..0b52adec
--- /dev/null
+++ b/lib/puppet/parser/functions/strftime.rb
@@ -0,0 +1,107 @@
+#
+# strftime.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:strftime, :type => :rvalue, :doc => <<-EOS
+This function returns formatted time.
+
+*Examples:*
+
+To return the time since epoch:
+
+ strftime("%s")
+
+To return the date:
+
+ strftime("%Y-%m-%d")
+
+*Format meaning:*
+
+ %a - The abbreviated weekday name (``Sun'')
+ %A - The full weekday name (``Sunday'')
+ %b - The abbreviated month name (``Jan'')
+ %B - The full month name (``January'')
+ %c - The preferred local date and time representation
+ %C - Century (20 in 2009)
+ %d - Day of the month (01..31)
+ %D - Date (%m/%d/%y)
+ %e - Day of the month, blank-padded ( 1..31)
+ %F - Equivalent to %Y-%m-%d (the ISO 8601 date format)
+ %h - Equivalent to %b
+ %H - Hour of the day, 24-hour clock (00..23)
+ %I - Hour of the day, 12-hour clock (01..12)
+ %j - Day of the year (001..366)
+ %k - hour, 24-hour clock, blank-padded ( 0..23)
+ %l - hour, 12-hour clock, blank-padded ( 0..12)
+ %L - Millisecond of the second (000..999)
+ %m - Month of the year (01..12)
+ %M - Minute of the hour (00..59)
+ %n - Newline (\n)
+ %N - Fractional seconds digits, default is 9 digits (nanosecond)
+ %3N millisecond (3 digits)
+ %6N microsecond (6 digits)
+ %9N nanosecond (9 digits)
+ %p - Meridian indicator (``AM'' or ``PM'')
+ %P - Meridian indicator (``am'' or ``pm'')
+ %r - time, 12-hour (same as %I:%M:%S %p)
+ %R - time, 24-hour (%H:%M)
+ %s - Number of seconds since 1970-01-01 00:00:00 UTC.
+ %S - Second of the minute (00..60)
+ %t - Tab character (\t)
+ %T - time, 24-hour (%H:%M:%S)
+ %u - Day of the week as a decimal, Monday being 1. (1..7)
+ %U - Week number of the current year,
+ starting with the first Sunday as the first
+ day of the first week (00..53)
+ %v - VMS date (%e-%b-%Y)
+ %V - Week number of year according to ISO 8601 (01..53)
+ %W - Week number of the current year,
+ starting with the first Monday as the first
+ day of the first week (00..53)
+ %w - Day of the week (Sunday is 0, 0..6)
+ %x - Preferred representation for the date alone, no time
+ %X - Preferred representation for the time alone, no date
+ %y - Year without a century (00..99)
+ %Y - Year with century
+ %z - Time zone as hour offset from UTC (e.g. +0900)
+ %Z - Time zone name
+ %% - Literal ``%'' character
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "strftime(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ format = arguments[0]
+
+ raise(Puppet::ParseError, 'strftime(): You must provide ' +
+ 'format for evaluation') if format.empty?
+
+ # The Time Zone argument is optional ...
+ time_zone = arguments[1] if arguments[1]
+
+ time = Time.new
+
+ # There is probably a better way to handle Time Zone ...
+ if time_zone and not time_zone.empty?
+ original_zone = ENV['TZ']
+
+ local_time = time.clone
+ local_time = local_time.utc
+
+ ENV['TZ'] = time_zone
+
+ time = local_time.localtime
+
+ ENV['TZ'] = original_zone
+ end
+
+ result = time.strftime(format)
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/strip.rb b/lib/puppet/parser/functions/strip.rb
new file mode 100644
index 00000000..3fac47d5
--- /dev/null
+++ b/lib/puppet/parser/functions/strip.rb
@@ -0,0 +1,38 @@
+#
+# strip.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:strip, :type => :rvalue, :doc => <<-EOS
+This function removes leading and trailing whitespace from a string or from
+every string inside an array.
+
+*Examples:*
+
+ strip(" aaa ")
+
+Would result in: "aaa"
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "strip(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'strip(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ result = value.collect { |i| i.is_a?(String) ? i.strip : i }
+ else
+ result = value.strip
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/suffix.rb b/lib/puppet/parser/functions/suffix.rb
new file mode 100644
index 00000000..f7792d6f
--- /dev/null
+++ b/lib/puppet/parser/functions/suffix.rb
@@ -0,0 +1,45 @@
+#
+# suffix.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:suffix, :type => :rvalue, :doc => <<-EOS
+This function applies a suffix to all elements in an array.
+
+*Examples:*
+
+ suffix(['a','b','c'], 'p')
+
+Will return: ['ap','bp','cp']
+ EOS
+ ) do |arguments|
+
+ # Technically we support two arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "suffix(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ array = arguments[0]
+
+ unless array.is_a?(Array)
+ raise Puppet::ParseError, "suffix(): expected first argument to be an Array, got #{array.inspect}"
+ end
+
+ suffix = arguments[1] if arguments[1]
+
+ if suffix
+ unless suffix.is_a? String
+ raise Puppet::ParseError, "suffix(): expected second argument to be a String, got #{suffix.inspect}"
+ end
+ end
+
+ # Turn everything into string same as join would do ...
+ result = array.collect do |i|
+ i = i.to_s
+ suffix ? i + suffix : i
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/swapcase.rb b/lib/puppet/parser/functions/swapcase.rb
new file mode 100644
index 00000000..eb7fe137
--- /dev/null
+++ b/lib/puppet/parser/functions/swapcase.rb
@@ -0,0 +1,38 @@
+#
+# swapcase.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:swapcase, :type => :rvalue, :doc => <<-EOS
+This function will swap the existing case of a string.
+
+*Examples:*
+
+ swapcase("aBcD")
+
+Would result in: "AbCd"
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "swapcase(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'swapcase(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.swapcase : i }
+ else
+ result = value.swapcase
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/time.rb b/lib/puppet/parser/functions/time.rb
new file mode 100644
index 00000000..0cddaf86
--- /dev/null
+++ b/lib/puppet/parser/functions/time.rb
@@ -0,0 +1,49 @@
+#
+# time.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:time, :type => :rvalue, :doc => <<-EOS
+This function will return the current time since epoch as an integer.
+
+*Examples:*
+
+ time()
+
+Will return something like: 1311972653
+ EOS
+ ) do |arguments|
+
+ # The Time Zone argument is optional ...
+ time_zone = arguments[0] if arguments[0]
+
+ if (arguments.size != 0) and (arguments.size != 1) then
+ raise(Puppet::ParseError, "time(): Wrong number of arguments "+
+ "given #{arguments.size} for 0 or 1")
+ end
+
+ time = Time.new
+
+ # There is probably a better way to handle Time Zone ...
+ if time_zone and not time_zone.empty?
+ original_zone = ENV['TZ']
+
+ local_time = time.clone
+ local_time = local_time.utc
+
+ ENV['TZ'] = time_zone
+
+ time = local_time.localtime
+
+ ENV['TZ'] = original_zone
+ end
+
+ # Calling Time#to_i on a receiver changes it. Trust me I am the Doctor.
+ result = time.strftime('%s')
+ result = result.to_i
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/to_bytes.rb b/lib/puppet/parser/functions/to_bytes.rb
new file mode 100644
index 00000000..df490ea8
--- /dev/null
+++ b/lib/puppet/parser/functions/to_bytes.rb
@@ -0,0 +1,31 @@
+module Puppet::Parser::Functions
+ newfunction(:to_bytes, :type => :rvalue, :doc => <<-EOS
+ Converts the argument into bytes, for example 4 kB becomes 4096.
+ Takes a single string value as an argument.
+ These conversions reflect a layperson's understanding of
+ 1 MB = 1024 KB, when in fact 1 MB = 1000 KB, and 1 MiB = 1024 KiB.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "to_bytes(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size != 1
+
+ arg = arguments[0]
+
+ return arg if arg.is_a? Numeric
+
+ value,prefix = */([0-9.e+-]*)\s*([^bB]?)/.match(arg)[1,2]
+
+ value = value.to_f
+ case prefix
+ when '' then return value.to_i
+ when 'k' then return (value*(1<<10)).to_i
+ when 'M' then return (value*(1<<20)).to_i
+ when 'G' then return (value*(1<<30)).to_i
+ when 'T' then return (value*(1<<40)).to_i
+ when 'P' then return (value*(1<<50)).to_i
+ when 'E' then return (value*(1<<60)).to_i
+ else raise Puppet::ParseError, "to_bytes(): Unknown prefix #{prefix}"
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/type.rb b/lib/puppet/parser/functions/type.rb
new file mode 100644
index 00000000..016529b0
--- /dev/null
+++ b/lib/puppet/parser/functions/type.rb
@@ -0,0 +1,19 @@
+#
+# type.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:type, :type => :rvalue, :doc => <<-EOS
+ DEPRECATED: This function will cease to function on Puppet 4; please use type3x() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system.
+ EOS
+ ) do |args|
+
+ warning("type() DEPRECATED: This function will cease to function on Puppet 4; please use type3x() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system.")
+ if ! Puppet::Parser::Functions.autoloader.loaded?(:type3x)
+ Puppet::Parser::Functions.autoloader.load(:type3x)
+ end
+ function_type3x(args + [false])
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/type3x.rb b/lib/puppet/parser/functions/type3x.rb
new file mode 100644
index 00000000..0800b4a3
--- /dev/null
+++ b/lib/puppet/parser/functions/type3x.rb
@@ -0,0 +1,51 @@
+#
+# type3x.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:type3x, :type => :rvalue, :doc => <<-EOS
+DEPRECATED: This function will be removed when puppet 3 support is dropped; please migrate to the new parser's typing system.
+
+Returns the type when passed a value. Type can be one of:
+
+* string
+* array
+* hash
+* float
+* integer
+* boolean
+ EOS
+ ) do |args|
+ raise(Puppet::ParseError, "type3x(): Wrong number of arguments " +
+ "given (#{args.size} for 1)") if args.size < 1
+
+ value = args[0]
+
+ klass = value.class
+
+ if not [TrueClass, FalseClass, Array, Bignum, Fixnum, Float, Hash, String].include?(klass)
+ raise(Puppet::ParseError, 'type3x(): Unknown type')
+ end
+
+ klass = klass.to_s # Ugly ...
+
+ # We note that Integer is the parent to Bignum and Fixnum ...
+ result = case klass
+ when /^(?:Big|Fix)num$/ then 'integer'
+ when /^(?:True|False)Class$/ then 'boolean'
+ else klass
+ end
+
+ if result == "String" then
+ if value == value.to_i.to_s then
+ result = "Integer"
+ elsif value == value.to_f.to_s then
+ result = "Float"
+ end
+ end
+
+ return result.downcase
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/union.rb b/lib/puppet/parser/functions/union.rb
new file mode 100644
index 00000000..c91bb805
--- /dev/null
+++ b/lib/puppet/parser/functions/union.rb
@@ -0,0 +1,34 @@
+#
+# union.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:union, :type => :rvalue, :doc => <<-EOS
+This function returns a union of two arrays.
+
+*Examples:*
+
+ union(["a","b","c"],["b","c","d"])
+
+Would return: ["a","b","c","d"]
+ EOS
+ ) do |arguments|
+
+ # Two arguments are required
+ raise(Puppet::ParseError, "union(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size != 2
+
+ first = arguments[0]
+ second = arguments[1]
+
+ unless first.is_a?(Array) && second.is_a?(Array)
+ raise(Puppet::ParseError, 'union(): Requires 2 arrays')
+ end
+
+ result = first | second
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/unique.rb b/lib/puppet/parser/functions/unique.rb
new file mode 100644
index 00000000..cf770f3b
--- /dev/null
+++ b/lib/puppet/parser/functions/unique.rb
@@ -0,0 +1,50 @@
+#
+# unique.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:unique, :type => :rvalue, :doc => <<-EOS
+This function will remove duplicates from strings and arrays.
+
+*Examples:*
+
+ unique("aabbcc")
+
+Will return:
+
+ abc
+
+You can also use this with arrays:
+
+ unique(["a","a","b","b","c","c"])
+
+This returns:
+
+ ["a","b","c"]
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "unique(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'unique(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ result = value.clone
+
+ string = value.is_a?(String) ? true : false
+
+ # We turn any string value into an array to be able to shuffle ...
+ result = string ? result.split('') : result
+ result = result.uniq # Remove duplicates ...
+ result = string ? result.join : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/upcase.rb b/lib/puppet/parser/functions/upcase.rb
new file mode 100644
index 00000000..4302b29e
--- /dev/null
+++ b/lib/puppet/parser/functions/upcase.rb
@@ -0,0 +1,40 @@
+#
+# upcase.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:upcase, :type => :rvalue, :doc => <<-EOS
+Converts a string or an array of strings to uppercase.
+
+*Examples:*
+
+ upcase("abcd")
+
+Will return:
+
+ ASDF
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "upcase(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'upcase(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? i.upcase : i }
+ else
+ result = value.upcase
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/uriescape.rb b/lib/puppet/parser/functions/uriescape.rb
new file mode 100644
index 00000000..a486eee5
--- /dev/null
+++ b/lib/puppet/parser/functions/uriescape.rb
@@ -0,0 +1,34 @@
+#
+# uriescape.rb
+#
+require 'uri'
+
+module Puppet::Parser::Functions
+ newfunction(:uriescape, :type => :rvalue, :doc => <<-EOS
+ Urlencodes a string or array of strings.
+ Requires either a single string or an array as an input.
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "uriescape(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ value = arguments[0]
+
+ unless value.is_a?(Array) || value.is_a?(String)
+ raise(Puppet::ParseError, 'uriescape(): Requires either ' +
+ 'array or string to work with')
+ end
+
+ if value.is_a?(Array)
+ # Numbers in Puppet are often string-encoded which is troublesome ...
+ result = value.collect { |i| i.is_a?(String) ? URI.escape(i,unsafe) : i }
+ else
+ result = URI.escape(value)
+ end
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/validate_absolute_path.rb b/lib/puppet/parser/functions/validate_absolute_path.rb
new file mode 100644
index 00000000..b6966809
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_absolute_path.rb
@@ -0,0 +1,69 @@
+module Puppet::Parser::Functions
+ newfunction(:validate_absolute_path, :doc => <<-'ENDHEREDOC') do |args|
+ Validate the string represents an absolute path in the filesystem. This function works
+ for windows and unix style paths.
+
+ The following values will pass:
+
+ $my_path = 'C:/Program Files (x86)/Puppet Labs/Puppet'
+ validate_absolute_path($my_path)
+ $my_path2 = '/var/lib/puppet'
+ validate_absolute_path($my_path2)
+ $my_path3 = ['C:/Program Files (x86)/Puppet Labs/Puppet','C:/Program Files/Puppet Labs/Puppet']
+ validate_absolute_path($my_path3)
+ $my_path4 = ['/var/lib/puppet','/usr/share/puppet']
+ validate_absolute_path($my_path4)
+
+ The following values will fail, causing compilation to abort:
+
+ validate_absolute_path(true)
+ validate_absolute_path('../var/lib/puppet')
+ validate_absolute_path('var/lib/puppet')
+ validate_absolute_path([ 'var/lib/puppet', '/var/foo' ])
+ validate_absolute_path([ '/var/lib/puppet', 'var/foo' ])
+ $undefined = undef
+ validate_absolute_path($undefined)
+
+ ENDHEREDOC
+
+ require 'puppet/util'
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_absolute_path(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ # put arg to candidate var to be able to replace it
+ candidates = arg
+ # if arg is just a string with a path to test, convert it to an array
+ # to avoid test code duplication
+ unless arg.is_a?(Array) then
+ candidates = Array.new(1,arg)
+ end
+ # iterate over all pathes within the candidates array
+ candidates.each do |path|
+ # This logic was borrowed from
+ # [lib/puppet/file_serving/base.rb](https://github.com/puppetlabs/puppet/blob/master/lib/puppet/file_serving/base.rb)
+ # Puppet 2.7 and beyond will have Puppet::Util.absolute_path? Fall back to a back-ported implementation otherwise.
+ if Puppet::Util.respond_to?(:absolute_path?) then
+ unless Puppet::Util.absolute_path?(path, :posix) or Puppet::Util.absolute_path?(path, :windows)
+ raise Puppet::ParseError, ("#{path.inspect} is not an absolute path.")
+ end
+ else
+ # This code back-ported from 2.7.x's lib/puppet/util.rb Puppet::Util.absolute_path?
+ # Determine in a platform-specific way whether a path is absolute. This
+ # defaults to the local platform if none is specified.
+ # Escape once for the string literal, and once for the regex.
+ slash = '[\\\\/]'
+ name = '[^\\\\/]+'
+ regexes = {
+ :windows => %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i,
+ :posix => %r!^/!,
+ }
+ rval = (!!(path =~ regexes[:posix])) || (!!(path =~ regexes[:windows]))
+ rval or raise Puppet::ParseError, ("#{path.inspect} is not an absolute path.")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/validate_array.rb b/lib/puppet/parser/functions/validate_array.rb
new file mode 100644
index 00000000..34b51182
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_array.rb
@@ -0,0 +1,33 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_array, :doc => <<-'ENDHEREDOC') do |args|
+ Validate that all passed values are array data structures. Abort catalog
+ compilation if any value fails this check.
+
+ The following values will pass:
+
+ $my_array = [ 'one', 'two' ]
+ validate_array($my_array)
+
+ The following values will fail, causing compilation to abort:
+
+ validate_array(true)
+ validate_array('some_string')
+ $undefined = undef
+ validate_array($undefined)
+
+ ENDHEREDOC
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_array(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ unless arg.is_a?(Array)
+ raise Puppet::ParseError, ("#{arg.inspect} is not an Array. It looks to be a #{arg.class}")
+ end
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/validate_augeas.rb b/lib/puppet/parser/functions/validate_augeas.rb
new file mode 100644
index 00000000..4ea4fe07
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_augeas.rb
@@ -0,0 +1,83 @@
+require 'tempfile'
+
+module Puppet::Parser::Functions
+ newfunction(:validate_augeas, :doc => <<-'ENDHEREDOC') do |args|
+ Perform validation of a string using an Augeas lens
+ The first argument of this function should be a string to
+ test, and the second argument should be the name of the Augeas lens to use.
+ If Augeas fails to parse the string with the lens, the compilation will
+ abort with a parse error.
+
+ A third argument can be specified, listing paths which should
+ not be found in the file. The `$file` variable points to the location
+ of the temporary file being tested in the Augeas tree.
+
+ For example, if you want to make sure your passwd content never contains
+ a user `foo`, you could write:
+
+ validate_augeas($passwdcontent, 'Passwd.lns', ['$file/foo'])
+
+ Or if you wanted to ensure that no users used the '/bin/barsh' shell,
+ you could use:
+
+ validate_augeas($passwdcontent, 'Passwd.lns', ['$file/*[shell="/bin/barsh"]']
+
+ If a fourth argument is specified, this will be the error message raised and
+ seen by the user.
+
+ A helpful error message can be returned like this:
+
+ validate_augeas($sudoerscontent, 'Sudoers.lns', [], 'Failed to validate sudoers content with Augeas')
+
+ ENDHEREDOC
+ unless Puppet.features.augeas?
+ raise Puppet::ParseError, ("validate_augeas(): this function requires the augeas feature. See http://projects.puppetlabs.com/projects/puppet/wiki/Puppet_Augeas#Pre-requisites for how to activate it.")
+ end
+
+ if (args.length < 2) or (args.length > 4) then
+ raise Puppet::ParseError, ("validate_augeas(): wrong number of arguments (#{args.length}; must be 2, 3, or 4)")
+ end
+
+ msg = args[3] || "validate_augeas(): Failed to validate content against #{args[1].inspect}"
+
+ require 'augeas'
+ aug = Augeas::open(nil, nil, Augeas::NO_MODL_AUTOLOAD)
+ begin
+ content = args[0]
+
+ # Test content in a temporary file
+ tmpfile = Tempfile.new("validate_augeas")
+ begin
+ tmpfile.write(content)
+ ensure
+ tmpfile.close
+ end
+
+ # Check for syntax
+ lens = args[1]
+ aug.transform(
+ :lens => lens,
+ :name => 'Validate_augeas',
+ :incl => tmpfile.path
+ )
+ aug.load!
+
+ unless aug.match("/augeas/files#{tmpfile.path}//error").empty?
+ error = aug.get("/augeas/files#{tmpfile.path}//error/message")
+ msg += " with error: #{error}"
+ raise Puppet::ParseError, (msg)
+ end
+
+ # Launch unit tests
+ tests = args[2] || []
+ aug.defvar('file', "/files#{tmpfile.path}")
+ tests.each do |t|
+ msg += " testing path #{t}"
+ raise Puppet::ParseError, (msg) unless aug.match(t).empty?
+ end
+ ensure
+ aug.close
+ tmpfile.unlink
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/validate_bool.rb b/lib/puppet/parser/functions/validate_bool.rb
new file mode 100644
index 00000000..59a08056
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_bool.rb
@@ -0,0 +1,34 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_bool, :doc => <<-'ENDHEREDOC') do |args|
+ Validate that all passed values are either true or false. Abort catalog
+ compilation if any value fails this check.
+
+ The following values will pass:
+
+ $iamtrue = true
+ validate_bool(true)
+ validate_bool(true, true, false, $iamtrue)
+
+ The following values will fail, causing compilation to abort:
+
+ $some_array = [ true ]
+ validate_bool("false")
+ validate_bool("true")
+ validate_bool($some_array)
+
+ ENDHEREDOC
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_bool(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ unless function_is_bool([arg])
+ raise Puppet::ParseError, ("#{arg.inspect} is not a boolean. It looks to be a #{arg.class}")
+ end
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/validate_cmd.rb b/lib/puppet/parser/functions/validate_cmd.rb
new file mode 100644
index 00000000..5df3c609
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_cmd.rb
@@ -0,0 +1,63 @@
+require 'puppet/util/execution'
+require 'tempfile'
+
+module Puppet::Parser::Functions
+ newfunction(:validate_cmd, :doc => <<-'ENDHEREDOC') do |args|
+ Perform validation of a string with an external command.
+ The first argument of this function should be a string to
+ test, and the second argument should be a path to a test command
+ taking a % as a placeholder for the file path (will default to the end).
+ If the command, launched against a tempfile containing the passed string,
+ returns a non-null value, compilation will abort with a parse error.
+
+ If a third argument is specified, this will be the error message raised and
+ seen by the user.
+
+ A helpful error message can be returned like this:
+
+ Example:
+
+ # Defaults to end of path
+ validate_cmd($sudoerscontent, '/usr/sbin/visudo -c -f', 'Visudo failed to validate sudoers content')
+
+ # % as file location
+ validate_cmd($haproxycontent, '/usr/sbin/haproxy -f % -c', 'Haproxy failed to validate config content')
+
+ ENDHEREDOC
+ if (args.length < 2) or (args.length > 3) then
+ raise Puppet::ParseError, ("validate_cmd(): wrong number of arguments (#{args.length}; must be 2 or 3)")
+ end
+
+ msg = args[2] || "validate_cmd(): failed to validate content with command #{args[1].inspect}"
+
+ content = args[0]
+ checkscript = args[1]
+
+ # Test content in a temporary file
+ tmpfile = Tempfile.new("validate_cmd")
+ begin
+ tmpfile.write(content)
+ tmpfile.close
+
+ if checkscript =~ /\s%(\s|$)/
+ check_with_correct_location = checkscript.gsub(/%/,tmpfile.path)
+ else
+ check_with_correct_location = "#{checkscript} #{tmpfile.path}"
+ end
+
+ if Puppet::Util::Execution.respond_to?('execute')
+ Puppet::Util::Execution.execute(check_with_correct_location)
+ else
+ Puppet::Util.execute(check_with_correct_location)
+ end
+ rescue Puppet::ExecutionFailure => detail
+ msg += "\n#{detail}"
+ raise Puppet::ParseError, msg
+ rescue Exception => detail
+ msg += "\n#{detail.class.name} #{detail}"
+ raise Puppet::ParseError, msg
+ ensure
+ tmpfile.unlink
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/validate_hash.rb b/lib/puppet/parser/functions/validate_hash.rb
new file mode 100644
index 00000000..9bdd5432
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_hash.rb
@@ -0,0 +1,33 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_hash, :doc => <<-'ENDHEREDOC') do |args|
+ Validate that all passed values are hash data structures. Abort catalog
+ compilation if any value fails this check.
+
+ The following values will pass:
+
+ $my_hash = { 'one' => 'two' }
+ validate_hash($my_hash)
+
+ The following values will fail, causing compilation to abort:
+
+ validate_hash(true)
+ validate_hash('some_string')
+ $undefined = undef
+ validate_hash($undefined)
+
+ ENDHEREDOC
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_hash(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ unless arg.is_a?(Hash)
+ raise Puppet::ParseError, ("#{arg.inspect} is not a Hash. It looks to be a #{arg.class}")
+ end
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/validate_ipv4_address.rb b/lib/puppet/parser/functions/validate_ipv4_address.rb
new file mode 100644
index 00000000..fc02748e
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_ipv4_address.rb
@@ -0,0 +1,48 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_ipv4_address, :doc => <<-ENDHEREDOC
+ Validate that all values passed are valid IPv4 addresses.
+ Fail compilation if any value fails this check.
+
+ The following values will pass:
+
+ $my_ip = "1.2.3.4"
+ validate_ipv4_address($my_ip)
+ validate_bool("8.8.8.8", "172.16.0.1", $my_ip)
+
+ The following values will fail, causing compilation to abort:
+
+ $some_array = [ 1, true, false, "garbage string", "3ffe:505:2" ]
+ validate_ipv4_address($some_array)
+
+ ENDHEREDOC
+ ) do |args|
+
+ require "ipaddr"
+ rescuable_exceptions = [ ArgumentError ]
+
+ if defined?(IPAddr::InvalidAddressError)
+ rescuable_exceptions << IPAddr::InvalidAddressError
+ end
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_ipv4_address(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ unless arg.is_a?(String)
+ raise Puppet::ParseError, "#{arg.inspect} is not a string."
+ end
+
+ begin
+ unless IPAddr.new(arg).ipv4?
+ raise Puppet::ParseError, "#{arg.inspect} is not a valid IPv4 address."
+ end
+ rescue *rescuable_exceptions
+ raise Puppet::ParseError, "#{arg.inspect} is not a valid IPv4 address."
+ end
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/validate_ipv6_address.rb b/lib/puppet/parser/functions/validate_ipv6_address.rb
new file mode 100644
index 00000000..b0f2558d
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_ipv6_address.rb
@@ -0,0 +1,49 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_ipv6_address, :doc => <<-ENDHEREDOC
+ Validate that all values passed are valid IPv6 addresses.
+ Fail compilation if any value fails this check.
+
+ The following values will pass:
+
+ $my_ip = "3ffe:505:2"
+ validate_ipv6_address(1)
+ validate_ipv6_address($my_ip)
+ validate_bool("fe80::baf6:b1ff:fe19:7507", $my_ip)
+
+ The following values will fail, causing compilation to abort:
+
+ $some_array = [ true, false, "garbage string", "1.2.3.4" ]
+ validate_ipv6_address($some_array)
+
+ ENDHEREDOC
+ ) do |args|
+
+ require "ipaddr"
+ rescuable_exceptions = [ ArgumentError ]
+
+ if defined?(IPAddr::InvalidAddressError)
+ rescuable_exceptions << IPAddr::InvalidAddressError
+ end
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_ipv6_address(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ unless arg.is_a?(String)
+ raise Puppet::ParseError, "#{arg.inspect} is not a string."
+ end
+
+ begin
+ unless IPAddr.new(arg).ipv6?
+ raise Puppet::ParseError, "#{arg.inspect} is not a valid IPv6 address."
+ end
+ rescue *rescuable_exceptions
+ raise Puppet::ParseError, "#{arg.inspect} is not a valid IPv6 address."
+ end
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/validate_re.rb b/lib/puppet/parser/functions/validate_re.rb
new file mode 100644
index 00000000..ca25a702
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_re.rb
@@ -0,0 +1,40 @@
+module Puppet::Parser::Functions
+ newfunction(:validate_re, :doc => <<-'ENDHEREDOC') do |args|
+ Perform simple validation of a string against one or more regular
+ expressions. The first argument of this function should be a string to
+ test, and the second argument should be a stringified regular expression
+ (without the // delimiters) or an array of regular expressions. If none
+ of the regular expressions match the string passed in, compilation will
+ abort with a parse error.
+
+ If a third argument is specified, this will be the error message raised and
+ seen by the user.
+
+ The following strings will validate against the regular expressions:
+
+ validate_re('one', '^one$')
+ validate_re('one', [ '^one', '^two' ])
+
+ The following strings will fail to validate, causing compilation to abort:
+
+ validate_re('one', [ '^two', '^three' ])
+
+ A helpful error message can be returned like this:
+
+ validate_re($::puppetversion, '^2.7', 'The $puppetversion fact value does not match 2.7')
+
+ ENDHEREDOC
+ if (args.length < 2) or (args.length > 3) then
+ raise Puppet::ParseError, ("validate_re(): wrong number of arguments (#{args.length}; must be 2 or 3)")
+ end
+
+ msg = args[2] || "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}"
+
+ # We're using a flattened array here because we can't call String#any? in
+ # Ruby 1.9 like we can in Ruby 1.8
+ raise Puppet::ParseError, (msg) unless [args[1]].flatten.any? do |re_str|
+ args[0] =~ Regexp.compile(re_str)
+ end
+
+ end
+end
diff --git a/lib/puppet/parser/functions/validate_slength.rb b/lib/puppet/parser/functions/validate_slength.rb
new file mode 100644
index 00000000..7d534f37
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_slength.rb
@@ -0,0 +1,71 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_slength, :doc => <<-'ENDHEREDOC') do |args|
+ Validate that the first argument is a string (or an array of strings), and
+ less/equal to than the length of the second argument. An optional third
+ parameter can be given a the minimum length. It fails if the first
+ argument is not a string or array of strings, and if arg 2 and arg 3 are
+ not convertable to a number.
+
+ The following values will pass:
+
+ validate_slength("discombobulate",17)
+ validate_slength(["discombobulate","moo"],17)
+ validate_slength(["discombobulate","moo"],17,3)
+
+ The following valueis will not:
+
+ validate_slength("discombobulate",1)
+ validate_slength(["discombobulate","thermometer"],5)
+ validate_slength(["discombobulate","moo"],17,10)
+
+ ENDHEREDOC
+
+ raise Puppet::ParseError, "validate_slength(): Wrong number of arguments (#{args.length}; must be 2 or 3)" unless args.length == 2 or args.length == 3
+
+ input, max_length, min_length = *args
+
+ begin
+ max_length = Integer(max_length)
+ raise ArgumentError if max_length <= 0
+ rescue ArgumentError, TypeError
+ raise Puppet::ParseError, "validate_slength(): Expected second argument to be a positive Numeric, got #{max_length}:#{max_length.class}"
+ end
+
+ if min_length
+ begin
+ min_length = Integer(min_length)
+ raise ArgumentError if min_length < 0
+ rescue ArgumentError, TypeError
+ raise Puppet::ParseError, "validate_slength(): Expected third argument to be unset or a positive Numeric, got #{min_length}:#{min_length.class}"
+ end
+ else
+ min_length = 0
+ end
+
+ if min_length > max_length
+ raise Puppet::ParseError, "validate_slength(): Expected second argument to be larger than third argument"
+ end
+
+ validator = lambda do |str|
+ unless str.length <= max_length and str.length >= min_length
+ raise Puppet::ParseError, "validate_slength(): Expected length of #{input.inspect} to be between #{min_length} and #{max_length}, was #{input.length}"
+ end
+ end
+
+ case input
+ when String
+ validator.call(input)
+ when Array
+ input.each_with_index do |arg, pos|
+ if arg.is_a? String
+ validator.call(arg)
+ else
+ raise Puppet::ParseError, "validate_slength(): Expected element at array position #{pos} to be a String, got #{arg.class}"
+ end
+ end
+ else
+ raise Puppet::ParseError, "validate_slength(): Expected first argument to be a String or Array, got #{input.class}"
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/validate_string.rb b/lib/puppet/parser/functions/validate_string.rb
new file mode 100644
index 00000000..c841f6ab
--- /dev/null
+++ b/lib/puppet/parser/functions/validate_string.rb
@@ -0,0 +1,38 @@
+module Puppet::Parser::Functions
+
+ newfunction(:validate_string, :doc => <<-'ENDHEREDOC') do |args|
+ Validate that all passed values are string data structures. Abort catalog
+ compilation if any value fails this check.
+
+ The following values will pass:
+
+ $my_string = "one two"
+ validate_string($my_string, 'three')
+
+ The following values will fail, causing compilation to abort:
+
+ validate_string(true)
+ validate_string([ 'some', 'array' ])
+
+ Note: validate_string(undef) will not fail in this version of the
+ functions API (incl. current and future parser). Instead, use:
+
+ if $var == undef {
+ fail('...')
+ }
+
+ ENDHEREDOC
+
+ unless args.length > 0 then
+ raise Puppet::ParseError, ("validate_string(): wrong number of arguments (#{args.length}; must be > 0)")
+ end
+
+ args.each do |arg|
+ unless arg.is_a?(String)
+ raise Puppet::ParseError, ("#{arg.inspect} is not a string. It looks to be a #{arg.class}")
+ end
+ end
+
+ end
+
+end
diff --git a/lib/puppet/parser/functions/values.rb b/lib/puppet/parser/functions/values.rb
new file mode 100644
index 00000000..16067561
--- /dev/null
+++ b/lib/puppet/parser/functions/values.rb
@@ -0,0 +1,39 @@
+#
+# values.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:values, :type => :rvalue, :doc => <<-EOS
+When given a hash this function will return the values of that hash.
+
+*Examples:*
+
+ $hash = {
+ 'a' => 1,
+ 'b' => 2,
+ 'c' => 3,
+ }
+ values($hash)
+
+This example would return:
+
+ [1,2,3]
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "values(): Wrong number of arguments " +
+ "given (#{arguments.size} for 1)") if arguments.size < 1
+
+ hash = arguments[0]
+
+ unless hash.is_a?(Hash)
+ raise(Puppet::ParseError, 'values(): Requires hash to work with')
+ end
+
+ result = hash.values
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/values_at.rb b/lib/puppet/parser/functions/values_at.rb
new file mode 100644
index 00000000..f350f539
--- /dev/null
+++ b/lib/puppet/parser/functions/values_at.rb
@@ -0,0 +1,99 @@
+#
+# values_at.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:values_at, :type => :rvalue, :doc => <<-EOS
+Finds value inside an array based on location.
+
+The first argument is the array you want to analyze, and the second element can
+be a combination of:
+
+* A single numeric index
+* A range in the form of 'start-stop' (eg. 4-9)
+* An array combining the above
+
+*Examples*:
+
+ values_at(['a','b','c'], 2)
+
+Would return ['c'].
+
+ values_at(['a','b','c'], ["0-1"])
+
+Would return ['a','b'].
+
+ values_at(['a','b','c','d','e'], [0, "2-3"])
+
+Would return ['a','c','d'].
+ EOS
+ ) do |arguments|
+
+ raise(Puppet::ParseError, "values_at(): Wrong number of " +
+ "arguments given (#{arguments.size} for 2)") if arguments.size < 2
+
+ array = arguments.shift
+
+ unless array.is_a?(Array)
+ raise(Puppet::ParseError, 'values_at(): Requires array to work with')
+ end
+
+ indices = [arguments.shift].flatten() # Get them all ... Pokemon ...
+
+ if not indices or indices.empty?
+ raise(Puppet::ParseError, 'values_at(): You must provide ' +
+ 'at least one positive index to collect')
+ end
+
+ result = []
+ indices_list = []
+
+ indices.each do |i|
+ i = i.to_s
+ if m = i.match(/^(\d+)(\.\.\.?|\-)(\d+)$/)
+ start = m[1].to_i
+ stop = m[3].to_i
+
+ type = m[2]
+
+ if start > stop
+ raise(Puppet::ParseError, 'values_at(): Stop index in ' +
+ 'given indices range is smaller than the start index')
+ elsif stop > array.size - 1 # First element is at index 0 is it not?
+ raise(Puppet::ParseError, 'values_at(): Stop index in ' +
+ 'given indices range exceeds array size')
+ end
+
+ range = case type
+ when /^(\.\.|\-)$/ then (start .. stop)
+ when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ...
+ end
+
+ range.each { |i| indices_list << i.to_i }
+ else
+ # Only positive numbers allowed in this case ...
+ if not i.match(/^\d+$/)
+ raise(Puppet::ParseError, 'values_at(): Unknown format ' +
+ 'of given index')
+ end
+
+ # In Puppet numbers are often string-encoded ...
+ i = i.to_i
+
+ if i > array.size - 1 # Same story. First element is at index 0 ...
+ raise(Puppet::ParseError, 'values_at(): Given index ' +
+ 'exceeds array size')
+ end
+
+ indices_list << i
+ end
+ end
+
+ # We remove nil values as they make no sense in Puppet DSL ...
+ result = indices_list.collect { |i| array[i] }.compact
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/parser/functions/zip.rb b/lib/puppet/parser/functions/zip.rb
new file mode 100644
index 00000000..3074f282
--- /dev/null
+++ b/lib/puppet/parser/functions/zip.rb
@@ -0,0 +1,39 @@
+#
+# zip.rb
+#
+
+module Puppet::Parser::Functions
+ newfunction(:zip, :type => :rvalue, :doc => <<-EOS
+Takes one element from first array and merges corresponding elements from second array. This generates a sequence of n-element arrays, where n is one more than the count of arguments.
+
+*Example:*
+
+ zip(['1','2','3'],['4','5','6'])
+
+Would result in:
+
+ ["1", "4"], ["2", "5"], ["3", "6"]
+ EOS
+ ) do |arguments|
+
+ # Technically we support three arguments but only first is mandatory ...
+ raise(Puppet::ParseError, "zip(): Wrong number of arguments " +
+ "given (#{arguments.size} for 2)") if arguments.size < 2
+
+ a = arguments[0]
+ b = arguments[1]
+
+ unless a.is_a?(Array) and b.is_a?(Array)
+ raise(Puppet::ParseError, 'zip(): Requires array to work with')
+ end
+
+ flatten = function_str2bool([arguments[2]]) if arguments[2]
+
+ result = a.zip(b)
+ result = flatten ? result.flatten : result
+
+ return result
+ end
+end
+
+# vim: set ts=2 sw=2 et :
diff --git a/lib/puppet/provider/file_line/ruby.rb b/lib/puppet/provider/file_line/ruby.rb
new file mode 100644
index 00000000..ae1a8b3d
--- /dev/null
+++ b/lib/puppet/provider/file_line/ruby.rb
@@ -0,0 +1,85 @@
+Puppet::Type.type(:file_line).provide(:ruby) do
+ def exists?
+ lines.find do |line|
+ line.chomp == resource[:line].chomp
+ end
+ end
+
+ def create
+ if resource[:match]
+ handle_create_with_match
+ elsif resource[:after]
+ handle_create_with_after
+ else
+ append_line
+ end
+ end
+
+ def destroy
+ local_lines = lines
+ File.open(resource[:path],'w') do |fh|
+ fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join(''))
+ end
+ 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
+ # file; for now assuming that this type is only used on
+ # small-ish config files that can fit into memory without
+ # too much trouble.
+ @lines ||= File.readlines(resource[:path])
+ end
+
+ def handle_create_with_match()
+ regex = resource[:match] ? Regexp.new(resource[:match]) : nil
+ match_count = count_matches(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(regex.match(l) ? resource[:line] : l)
+ end
+
+ if (match_count == 0)
+ fh.puts(resource[:line])
+ end
+ end
+ end
+
+ def handle_create_with_after
+ regex = Regexp.new(resource[:after])
+ count = count_matches(regex)
+ case count
+ when 1 # find the line to put our line after
+ File.open(resource[:path], 'w') do |fh|
+ lines.each do |l|
+ fh.puts(l)
+ if regex.match(l) then
+ fh.puts(resource[:line])
+ end
+ end
+ end
+ when 0 # append the line to the end of the file
+ append_line
+ else
+ raise Puppet::Error, "#{count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern."
+ end
+ end
+
+ def count_matches(regex)
+ lines.select{|l| l.match(regex)}.size
+ end
+
+ ##
+ # append the line to the file.
+ #
+ # @api private
+ def append_line
+ File.open(resource[:path], 'a') do |fh|
+ fh.puts resource[:line]
+ end
+ end
+end
diff --git a/lib/puppet/type/anchor.rb b/lib/puppet/type/anchor.rb
new file mode 100644
index 00000000..fe1e5aa1
--- /dev/null
+++ b/lib/puppet/type/anchor.rb
@@ -0,0 +1,46 @@
+Puppet::Type.newtype(:anchor) do
+ desc <<-'ENDOFDESC'
+ A simple resource type intended to be used as an anchor in a composite class.
+
+ In Puppet 2.6, when a class declares another class, the resources in the
+ interior class are not contained by the exterior class. This interacts badly
+ with the pattern of composing complex modules from smaller classes, as it
+ makes it impossible for end users to specify order relationships between the
+ exterior class and other modules.
+
+ The anchor type lets you work around this. By sandwiching any interior
+ classes between two no-op resources that _are_ contained by the exterior
+ class, you can ensure that all resources in the module are contained.
+
+ class ntp {
+ # These classes will have the correct order relationship with each
+ # other. However, without anchors, they won't have any order
+ # relationship to Class['ntp'].
+ class { 'ntp::package': }
+ -> class { 'ntp::config': }
+ -> class { 'ntp::service': }
+
+ # These two resources "anchor" the composed classes within the ntp
+ # class.
+ anchor { 'ntp::begin': } -> Class['ntp::package']
+ Class['ntp::service'] -> anchor { 'ntp::end': }
+ }
+
+ This allows the end user of the ntp module to establish require and before
+ relationships with Class['ntp']:
+
+ class { 'ntp': } -> class { 'mcollective': }
+ class { 'mcollective': } -> class { 'ntp': }
+
+ ENDOFDESC
+
+ newparam :name do
+ desc "The name of the anchor resource."
+ end
+
+ def refresh
+ # We don't do anything with them, but we need this to
+ # show that we are "refresh aware" and not break the
+ # chain of propagation.
+ end
+end
diff --git a/lib/puppet/type/file_line.rb b/lib/puppet/type/file_line.rb
new file mode 100644
index 00000000..df263e6a
--- /dev/null
+++ b/lib/puppet/type/file_line.rb
@@ -0,0 +1,75 @@
+Puppet::Type.newtype(:file_line) do
+
+ desc <<-EOT
+ Ensures that a given line is contained within a file. The implementation
+ matches the full line, including whitespace at the beginning and end. If
+ the line is not contained in the given file, Puppet will add the line to
+ ensure the desired state. Multiple resources may be declared to manage
+ multiple lines in the same file.
+
+ Example:
+
+ file_line { 'sudo_rule':
+ path => '/etc/sudoers',
+ line => '%sudo ALL=(ALL) ALL',
+ }
+ file_line { 'sudo_rule_nopw':
+ path => '/etc/sudoers',
+ line => '%sudonopw ALL=(ALL) NOPASSWD: ALL',
+ }
+
+ In this example, Puppet will ensure both of the specified lines are
+ contained in the file /etc/sudoers.
+
+ **Autorequires:** If Puppet is managing the file that will contain the line
+ being managed, the file_line resource will autorequire that file.
+
+ EOT
+
+ ensurable do
+ defaultvalues
+ defaultto :present
+ end
+
+ newparam(:name, :namevar => true) do
+ desc 'An arbitrary name used as the identity of the resource.'
+ end
+
+ newparam(:match) do
+ desc 'An optional regular expression to run against existing lines in the file;\n' +
+ 'if a match is found, we replace that line rather than adding a new line.'
+ end
+
+ newparam(:multiple) do
+ desc 'An optional value to determine if match can change multiple lines.'
+ newvalues(true, false)
+ end
+
+ newparam(:after) do
+ desc 'An optional value used to specify the line after which we will add any new lines. (Existing lines are added in place)'
+ end
+
+ newparam(:line) do
+ desc 'The line to be appended to the file located by the path parameter.'
+ end
+
+ newparam(:path) do
+ desc 'The file Puppet will ensure contains the line specified by the line parameter.'
+ validate do |value|
+ unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/))
+ raise(Puppet::Error, "File paths must be fully qualified, not '#{value}'")
+ end
+ end
+ end
+
+ # Autorequire the file resource if it's being managed
+ autorequire(:file) do
+ self[:path]
+ end
+
+ validate do
+ unless self[:line] and self[:path]
+ raise(Puppet::Error, "Both line and path are required attributes")
+ end
+ end
+end