feat: initial commit

This commit is contained in:
amy 2025-04-01 17:40:03 +00:00
commit 38f495e3f4
457 changed files with 40577 additions and 0 deletions

View file

@ -0,0 +1,21 @@
# frozen_string_literal: true
# Fact: package_provider
#
# Purpose: Returns the default provider Puppet will choose to manage packages
# on this system
#
# Resolution: Instantiates a dummy package resource and return the provider
#
# Caveats:
#
require 'puppet/type'
require 'puppet/type/package'
# These will be nil if Puppet is not available.
Facter.add(:package_provider) do
# Instantiates a dummy package resource and return the provider
setcode do
Puppet::Type.type(:package).newpackage(name: 'dummy', allow_virtual: 'true')[:provider].to_s
end
end

View file

@ -0,0 +1,62 @@
# frozen_string_literal: true
# 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:
#
# Fact: pe_version
Facter.add('pe_version') do
setcode do
found_version = Facter.value('pe_build')
unless found_version
puppet_ver = Facter.value('puppetversion')
unless puppet_ver.nil?
pe_ver = puppet_ver.match(%r{Puppet Enterprise (\d+\.\d+\.\d+)})
found_version = pe_ver[1] if pe_ver
end
end
found_version
end
end
# Fact: is_pe
Facter.add('is_pe') do
setcode do
!Facter.value(:pe_version).to_s.empty?
end
end
# Fact: pe_major_version
Facter.add('pe_major_version') do
confine is_pe: true
setcode do
pe_version = Facter.value(:pe_version)
pe_version.to_s.split('.')[0] if pe_version
end
end
# Fact: pe_minor_version
Facter.add('pe_minor_version') do
confine is_pe: true
setcode do
pe_version = Facter.value(:pe_version)
pe_version.to_s.split('.')[1] if pe_version
end
end
# Fact: pe_patch_version
Facter.add('pe_patch_version') do
confine is_pe: true
setcode do
pe_version = Facter.value(:pe_version)
pe_version.to_s.split('.')[2] if pe_version
end
end

View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
# These facter facts return the value of the Puppet vardir and environment path
# settings 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, or for modules running on the puppet server to know
# where environments are stored.
#
# The values 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.exist?(rb_file) || raise(e)
end
# Facter fact returns the value of the Puppet vardir
Facter.add(:puppet_vardir) do
setcode do
Facter::Util::PuppetSettings.with_puppet do
Puppet[:vardir]
end
end
end
# Facter fact returns the value of the Puppet environment path
Facter.add(:puppet_environmentpath) do
setcode do
Facter::Util::PuppetSettings.with_puppet do
Puppet[:environmentpath]
end
end
end
# Facter fact returns the value of the Puppet server
Facter.add(:puppet_server) do
setcode do
Facter::Util::PuppetSettings.with_puppet do
Puppet[:server]
end
end
end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
Facter.add(:root_home) do
setcode do
require 'etc'
rescue LoadError
# Unavailable on platforms like Windows
else
Etc.getpwnam('root')&.dir
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
# Fact: service_provider
#
# Purpose: Returns the default provider Puppet will choose to manage services
# on this system
#
# Resolution: Instantiates a dummy service resource and return the provider
#
# Caveats:
#
require 'puppet/type'
require 'puppet/type/service'
Facter.add(:service_provider) do
setcode do
Puppet::Type.type(:service).newservice(name: 'dummy')[:provider].to_s
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
# A method to evaluate a Facter code block if puppet is loaded.
module Facter::Util::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
Module.const_get(:Puppet)
rescue NameError
nil
else
yield
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::batch_escape`](#stdlibbatch_escape) instead.
Puppet::Functions.create_function(:batch_escape) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'batch_escape', 'This function is deprecated, please use stdlib::batch_escape instead.', false)
call_function('stdlib::batch_escape', *args)
end
end

View file

@ -0,0 +1,38 @@
# frozen_string_literal: true
# @summary Function to print deprecation warnings, Logs a warning once for a given key.
Puppet::Functions.create_function(:deprecation) do
# @param key
# The uniqueness key. This function logs once for any given key.
# @param message
# Is the message text including any positional information that is formatted by the user/caller of the function.
# @param use_strict_setting
# When `true`, (the default), the function is affected by the puppet setting 'strict', which can be set to :error
# (outputs as an error message), :off (no message / error is displayed) and :warning
# (default, outputs a warning).
dispatch :deprecation do
param 'String', :key
param 'String', :message
optional_param 'Boolean', :use_strict_setting
end
def deprecation(key, message, use_strict_setting = true) # rubocop:disable Style/OptionalBooleanParameter
if defined? Puppet::Pops::PuppetStack.stacktrace
stacktrace = Puppet::Pops::PuppetStack.stacktrace
file = stacktrace[0]
line = stacktrace[1]
message = "#{message} at #{file}:#{line}"
end
# Do nothing if using strict setting and strict is set to `off`
return if use_strict_setting && Puppet.settings[:strict] == :off
# Fail hard if using strict setting and strict is set to `error`
raise("deprecation. #{key}. #{message}") if use_strict_setting && Puppet.settings[:strict] == :error
# Otherwise raise a soft warning
# (unless the STDLIB_LOG_DEPRECATIONS has been set to `false`. This is mainly for use in rspec-puppet testing to suppress noise in logs)
Puppet.deprecation_warning(message, key) unless ENV['STDLIB_LOG_DEPRECATIONS'] == 'false'
nil
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
# @summary DEPRECATED. Use the namespaced function [`stdlib::ensure_packages`](#stdlibensure_packages) instead.
Puppet::Functions.create_function(:ensure_packages, Puppet::Functions::InternalFunction) do
dispatch :deprecation_gen do
scope_param
repeated_param 'Any', :args
end
def deprecation_gen(scope, *args)
call_function('deprecation', 'ensure_packages', 'This function is deprecated, please use stdlib::ensure_packages instead.', false)
scope.call_function('stdlib::ensure_packages', args)
end
end

View file

@ -0,0 +1,65 @@
# frozen_string_literal: true
# @summary
# Digs into the facts hash using dot-notation
#
# Supports the use of dot-notation for referring to structured facts. If a fact requested
# does not exist, returns Undef.
#
# @example Example usage:
# fact('osfamily')
# fact('os.architecture')
#
# @example Array indexing:
# fact('mountpoints."/dev".options.1')
#
# @example Fact containing a "." in the name:
# fact('vmware."VRA.version"')
#
Puppet::Functions.create_function(:fact) do
# @param fact_name
# The name of the fact to check
#
# @return
# All information retrieved on the given fact_name
dispatch :fact do
param 'String', :fact_name
end
def to_dot_syntax(array_path)
array_path.map { |string|
string.include?('.') ? %("#{string}") : string
}.join('.')
end
def fact(fact_name)
facts = closure_scope['facts']
# Transform the dot-notation string into an array of paths to walk. Make
# sure to correctly extract double-quoted values containing dots as single
# elements in the path.
path = fact_name.scan(%r{([^."]+)|(?:")([^"]+)(?:")}).map { |x| x.compact.first }
walked_path = []
path.reduce(facts) do |d, k|
return nil if d.nil? || k.nil?
if d.is_a?(Array)
begin
result = d[Integer(k)]
rescue ArgumentError => e # rubocop:disable Lint/UselessAssignment : Causes errors if assigment is removed.
Puppet.warning("fact request for #{fact_name} returning nil: '#{to_dot_syntax(walked_path)}' is an array; cannot index to '#{k}'")
result = nil
end
elsif d.is_a?(Hash)
result = d[k]
else
Puppet.warning("fact request for #{fact_name} returning nil: '#{to_dot_syntax(walked_path)}' is not a collection; cannot walk to '#{k}'")
result = nil
end
walked_path << k
result
end
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::fqdn_rand_string`](#stdlibfqdn_rand_string) instead.
Puppet::Functions.create_function(:fqdn_rand_string) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'fqdn_rand_string', 'This function is deprecated, please use stdlib::fqdn_rand_string instead.', false)
call_function('stdlib::fqdn_rand_string', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::fqdn_rotate`](#stdlibfqdn_rotate) instead.
Puppet::Functions.create_function(:fqdn_rotate) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'fqdn_rotate', 'This function is deprecated, please use stdlib::fqdn_rotate instead.', false)
call_function('stdlib::fqdn_rotate', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::has_interface_with`](#stdlibhas_interface_with) instead.
Puppet::Functions.create_function(:has_interface_with) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'has_interface_with', 'This function is deprecated, please use stdlib::has_interface_with instead.', false)
call_function('stdlib::has_interface_with', *args)
end
end

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
# @summary
# Boolean check to determine whether a variable is of a given data type.
# This is equivalent to the `=~` type checks.
#
# @example Example Usage:
# # check a data type
# foo = 3
# $bar = [1,2,3]
# $baz = 'A string!'
#
# if $foo.is_a(Integer) {
# notify { 'foo!': }
# }
# if $bar.is_a(Array) {
# notify { 'bar!': }
# }
# if $baz.is_a(String) {
# notify { 'baz!': }
# }
#
# 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(:is_a) do
# @param value
# The value to be checked
#
# @param type
# The expected type
#
# @return [Boolean]
# Return's `true` or `false`.
dispatch :is_a do
param 'Any', :value
param 'Type', :type
end
def is_a(value, type) # rubocop:disable Naming/PredicateName : Used in to many other places to rename at this time, attempting to refactor caused Rubocop to crash.
# See puppet's lib/puppet/pops/evaluator/evaluator_impl.rb eval_MatchExpression
Puppet::Pops::Types::TypeCalculator.instance?(type, value)
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::merge`](#stdlibmerge) instead.
Puppet::Functions.create_function(:merge) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
optional_block_param 'Variant[Callable[2,2], Callable[3,3]]', :block
end
def deprecation_gen(*args, &block)
call_function('deprecation', 'merge', 'This function is deprecated, please use stdlib::merge instead.', false)
call_function('stdlib::merge', *args, &block)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::os_version_gte`](#stdlibos_version_gte) instead.
Puppet::Functions.create_function(:os_version_gte) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'os_version_gte', 'This function is deprecated, please use stdlib::os_version_gte instead.', false)
call_function('stdlib::os_version_gte', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::parsehocon`](#stdlibparsehocon) instead.
Puppet::Functions.create_function(:parsehocon) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'parsehocon', 'This function is deprecated, please use stdlib::parsehocon instead.', false)
call_function('stdlib::parsehocon', *args)
end
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
# @summary
# **Deprecated:** Starting Puppet 8, we no longer natively support PSON usage. This function should be removed once we stop supporting Puppet 7.
#
# This function accepts PSON, a Puppet variant of JSON, as a string and converts
# it into the correct Puppet structure
#
# @example How to parse pson
# $data = parsepson('{"a":"1","b":"2"}')
#
# For more information on PSON please see the following link:
# https://puppet.com/docs/puppet/7/http_api/pson.html
#
Puppet::Functions.create_function(:parsepson) do
# @param pson_string A valid PSON string
# @param default An optional default to return if parsing the pson_string fails
# @return [Data]
dispatch :parsepson do
param 'String[1]', :pson_string
optional_param 'Any', :default
end
def parsepson(pson_string, default = :no_default_provided)
call_function('deprecation', 'parsepson', 'This method is deprecated. From Puppet 8, PSON is no longer natively supported. Please use JSON.parse().')
PSON.load(pson_string) if Puppet::Util::Package.versioncmp(Puppet.version, '8').negative?
rescue StandardError => e
Puppet.debug("Parsing PSON failed with error: #{e.message}")
raise e if default == :no_default_provided
default
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::powershell_escape`](#stdlibpowershell_escape) instead.
Puppet::Functions.create_function(:powershell_escape) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'powershell_escape', 'This function is deprecated, please use stdlib::powershell_escape instead.', false)
call_function('stdlib::powershell_escape', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::seeded_rand`](#stdlibseeded_rand) instead.
Puppet::Functions.create_function(:seeded_rand) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'seeded_rand', 'This function is deprecated, please use stdlib::seeded_rand instead.', false)
call_function('stdlib::seeded_rand', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::seeded_rand_string`](#stdlibseeded_rand_string) instead.
Puppet::Functions.create_function(:seeded_rand_string) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'seeded_rand_string', 'This function is deprecated, please use stdlib::seeded_rand_string instead.', false)
call_function('stdlib::seeded_rand_string', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::shell_escape`](#stdlibshell_escape) instead.
Puppet::Functions.create_function(:shell_escape) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'shell_escape', 'This function is deprecated, please use stdlib::shell_escape instead.', false)
call_function('stdlib::shell_escape', *args)
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
# @summary
# Escapes a string so that it can be safely used in a batch shell command line.
#
# >* Note:* that the resulting string should be used unquoted and is not intended for use in double quotes nor in single
# quotes.
Puppet::Functions.create_function(:'stdlib::batch_escape') do
# @param string
# The string to escape
#
# @return
# An escaped string that can be safely used in a batch command line.
dispatch :batch_escape do
param 'Any', :string
end
def batch_escape(string)
result = ''
string.to_s.chars.each do |char|
result += case char
when '"' then '""'
when '$', '\\' then "\\#{char}"
else char
end
end
%("#{result}")
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
require 'zlib'
# @note
# The CRC32 algorithm can easily generate collisions,
# but may be useful for generating sharding, describing
# secrets, or seeding nonce values.
#
# @summary
# Run a CRC32 calculation against a given value.
Puppet::Functions.create_function(:'stdlib::crc32') do
# @param my_data The ScalarData to evaluate
# @example Check a simple string value
# stdlib::crc32('my string') == '18fbd270'
# @example Check a Sensitive datatype
# stdlib::crc32(sensitive('my string')) == '18fbd270'
# @example Check a number
# stdlib::crc32(100.0) == 'a3fd429a'
# stdlib::crc32(100.00000) == 'a3fd429a'
# @return String
dispatch :crc32 do
param 'Variant[ScalarData, Sensitive[ScalarData], Binary, Sensitive[Binary]]', :my_data
return_type 'String'
end
def crc32(my_data)
Zlib.crc32(my_data.unwrap.to_s).to_s(16).downcase
rescue StandardError
Zlib.crc32(my_data.to_s).to_s(16).downcase
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
# @summary
# Returns true if str ends with one of the prefixes given. Each of the prefixes should be a String.
#
Puppet::Functions.create_function(:'stdlib::end_with') do
# @param test_string The string to check
# @param suffixes The suffixes to check
# @example
# 'foobar'.stdlib::end_with('bar') => true
# 'foobar'.stdlib::end_with('foo') => false
# 'foobar'.stdlib::end_with(['foo', 'baz']) => false
# @return [Boolean] True or False
dispatch :end_with do
param 'String', :test_string
param 'Variant[String[1],Array[String[1], 1]]', :suffixes
return_type 'Boolean'
end
def end_with(test_string, suffixes)
test_string.end_with?(*suffixes)
end
end

View file

@ -0,0 +1,61 @@
# frozen_string_literal: true
# @summary 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.
Puppet::Functions.create_function(:'stdlib::ensure_packages', Puppet::Functions::InternalFunction) do
# @param packages
# The packages to ensure are installed.
# @param default_attributes
# Default attributes to be passed to the `ensure_resource()` function
# @return [Undef] Returns nothing.
dispatch :ensure_packages do
scope_param
param 'Variant[String[1], Array[String[1]]]', :packages
optional_param 'Hash', :default_attributes
return_type 'Undef'
end
# @param packages
# The packages to ensure are installed. The keys are packages and values are the attributes specific to that package.
# @param default_attributes
# Default attributes. Package specific attributes from the `packages` parameter will take precedence.
# @return [Undef] Returns nothing.
dispatch :ensure_packages_hash do
scope_param
param 'Hash[String[1], Any]', :packages
optional_param 'Hash', :default_attributes
return_type 'Undef'
end
def ensure_packages(scope, packages, default_attributes = {})
Array(packages).each do |package_name|
defaults = { 'ensure' => 'installed' }.merge(default_attributes)
# `present` and `installed` are aliases for the `ensure` attribute. If `ensure` is set to either of these values replace
# with `installed` by default but `present` if this package is already in the catalog with `ensure => present`
defaults['ensure'] = default_ensure(package_name) if ['present', 'installed'].include?(defaults['ensure'])
scope.call_function('ensure_resource', ['package', package_name, defaults])
end
nil
end
def ensure_packages_hash(scope, packages, default_attributes = {})
packages.each do |package, attributes|
ensure_packages(scope, package, default_attributes.merge(attributes))
end
nil
end
private
def default_ensure(package_name)
if call_function('defined_with_params', "Package[#{package_name}]", { 'ensure' => 'present' })
'present'
else
'installed'
end
end
end

View file

@ -0,0 +1,28 @@
# frozen_string_literal: true
# @summary
# Returns the Extension (the Portion of Filename in Path starting from the
# last Period).
#
# If Path is a Dotfile, or starts with a Period, then the starting Dot is not
# dealt with the Start of the Extension.
#
# An empty String will also be returned, when the Period is the last Character
# in Path.
Puppet::Functions.create_function(:'stdlib::extname') do
# @param filename The Filename
# @return [String] The Extension starting from the last Period
# @example Determining the Extension of a Filename
# stdlib::extname('test.rb') => '.rb'
# stdlib::extname('a/b/d/test.rb') => '.rb'
# stdlib::extname('test') => ''
# stdlib::extname('.profile') => ''
dispatch :extname do
param 'String', :filename
return_type 'String'
end
def extname(filename)
File.extname(filename)
end
end

View file

@ -0,0 +1,39 @@
# frozen_string_literal: true
# @summary
# Generates a random alphanumeric string. Combining the `$fqdn` fact and an
# optional seed for repeatable randomness.
#
# Optionally, you can specify a character set for the function (defaults to alphanumeric).
Puppet::Functions.create_function(:'stdlib::fqdn_rand_string') do
# @param length The length of the resulting string.
# @param charset The character set to use.
# @param seed The seed for repeatable randomness.
#
# @return [String]
#
# @example Example Usage:
# stdlib::fqdn_rand_string(10)
# stdlib::fqdn_rand_string(10, 'ABCDEF!@$%^')
# stdlib::fqdn_rand_string(10, undef, 'custom seed')
dispatch :fqdn_rand_string do
param 'Integer[1]', :length
optional_param 'Optional[String]', :charset
optional_repeated_param 'Any', :seed
end
def fqdn_rand_string(length, charset = nil, *seed)
charset = if charset && !charset.empty?
charset.chars.to_a
else
(0..9).map(&:to_s) + ('A'..'Z').to_a + ('a'..'z').to_a
end
rand_string = ''
length.times do |current|
rand_string += charset[call_function('fqdn_rand', charset.size, (seed + [current + 1]).join(':'))]
end
rand_string
end
end

View file

@ -0,0 +1,66 @@
# frozen_string_literal: true
# @summary Rotates an array or string a random number of times, combining the `fqdn` fact and an optional seed for repeatable randomness.
Puppet::Functions.create_function(:'stdlib::fqdn_rotate') do
# @param input
# The String you want rotated a random number of times
# @param seeds
# One of more values to use as a custom seed. These will be combined with the host's FQDN
#
# @return [String] Returns the rotated String
#
# @example Rotating a String
# stdlib::fqdn_rotate('abcd')
# @example Using a custom seed
# stdlib::fqdn_rotate('abcd', 'custom seed')
dispatch :fqdn_rotate_string do
param 'String', :input
optional_repeated_param 'Variant[Integer,String]', :seeds
return_type 'String'
end
# @param input
# The Array you want rotated a random number of times
# @param seeds
# One of more values to use as a custom seed. These will be combined with the host's FQDN
#
# @return [String] Returns the rotated Array
#
# @example Rotating an Array
# stdlib::fqdn_rotate(['a', 'b', 'c', 'd'])
# @example Using custom seeds
# stdlib::fqdn_rotate([1, 2, 3], 'custom', 'seed', 1)
dispatch :fqdn_rotate_array do
param 'Array', :input
optional_repeated_param 'Variant[Integer,String]', :seeds
return_type 'Array'
end
def fqdn_rotate_array(input, *seeds)
# Check whether it makes sense to rotate ...
return input if input.size <= 1
result = input.clone
require 'digest/md5'
seed = Digest::MD5.hexdigest([fqdn_fact, seeds].join(':')).hex
offset = Puppet::Util.deterministic_rand(seed, result.size).to_i
offset.times do
result.push result.shift
end
result
end
def fqdn_rotate_string(input, *seeds)
fqdn_rotate_array(input.chars, seeds).join
end
private
def fqdn_fact
closure_scope['facts']['networking']['fqdn']
end
end

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
# @summary
# Returns whether the Puppet runtime has access to a given function.
#
# @example Using stdlib::has_function()
# stdlib::has_function('stdlib::has_function') # true
# stdlib::has_function('not_a_function') # false
#
# Determines whether the Puppet runtime has access to a function by the
# name provided.
#
# @return
# Returns true if the provided function name is available, false otherwise.
#
Puppet::Functions.create_function(:'stdlib::has_function', Puppet::Functions::InternalFunction) do
dispatch :has_function do
scope_param
param 'String[1]', :function_name
return_type 'Boolean'
end
def has_function(scope, function_name) # rubocop:disable Naming/PredicateName
loaders = scope.compiler.loaders
loader = loaders.private_environment_loader
return true unless loader&.load(:function, function_name).nil?
# If the loader cannot find the function it might be
# a 3x-style function stubbed in on-the-fly for testing.
func_3x = Puppet::Parser::Functions.function(function_name.to_sym)
func_3x.is_a?(String) && !func_3x.empty?
end
end

View file

@ -0,0 +1,47 @@
# frozen_string_literal: true
# @summary Returns boolean based on network interfaces present and their attribute values.
#
# Can be called with one, or two arguments.
Puppet::Functions.create_function(:'stdlib::has_interface_with') do
# @param interface
# The name of an interface
# @return [Boolean] Returns `true` if `interface` exists and `false` otherwise
# @example When called with a single argument, the presence of the interface is checked
# stdlib::has_interface_with('lo') # Returns `true`
dispatch :has_interface do
param 'String[1]', :interface
return_type 'Boolean'
end
# @param kind
# A supported interface attribute
# @param value
# The value of the attribute
# @return [Boolean] Returns `true` if any of the interfaces in the `networking` fact has a `kind` attribute with the value `value`. Otherwise returns `false`
# @example Checking if an interface exists with a given mac address
# stdlib::has_interface_with('macaddress', 'x:x:x:x:x:x') # Returns `false`
# @example Checking if an interface exists with a given IP address
# stdlib::has_interface_with('ipaddress', '127.0.0.1') # Returns `true`
dispatch :has_interface_with do
param "Enum['macaddress','netmask','ipaddress','network','ip','mac']", :kind
param 'String[1]', :value
return_type 'Boolean'
end
def has_interface(interface) # rubocop:disable Naming/PredicateName
interfaces.key? interface
end
def has_interface_with(kind, value) # rubocop:disable Naming/PredicateName
# For compatibility with older version of function that used the legacy facts, alias `ip` with `ipaddress` and `mac` with `macaddress`
kind = 'ip' if kind == 'ipaddress'
kind = 'mac' if kind == 'macaddress'
interfaces.any? { |_interface, params| params[kind] == value }
end
def interfaces
closure_scope['facts']['networking']['interfaces']
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
# @summary
# Returns true if the ipaddress is within the given CIDRs
#
# @example ip_in_range(<IPv4 Address>, <IPv4 CIDR>)
# stdlib::ip_in_range('10.10.10.53', '10.10.10.0/24') => true
Puppet::Functions.create_function(:'stdlib::ip_in_range') do
# @param ipaddress The IP address to check
# @param range One CIDR or an array of CIDRs
# defining the range(s) to check against
#
# @return [Boolean] True or False
dispatch :ip_in_range do
param 'String', :ipaddress
param 'Variant[String, Array]', :range
return_type 'Boolean'
end
require 'ipaddr'
def ip_in_range(ipaddress, range)
ip = IPAddr.new(ipaddress)
if range.is_a? Array
ranges = range.map { |r| IPAddr.new(r) }
ranges.any? { |rng| rng.include?(ip) }
elsif range.is_a? String
ranges = IPAddr.new(range)
ranges.include?(ip)
end
end
end

View file

@ -0,0 +1,112 @@
# frozen_string_literal: true
# @summary
# Merges two or more hashes together or hashes resulting from iteration, and returns
# the resulting hash.
#
# @example Using stdlib::merge()
# $hash1 = {'one' => 1, 'two', => 2}
# $hash2 = {'two' => 'dos', 'three', => 'tres'}
# $merged_hash = stdlib::merge($hash1, $hash2) # $merged_hash = {'one' => 1, 'two' => 'dos', 'three' => 'tres'}
#
# When there is a duplicate key, the key in the rightmost hash will "win."
#
# Note that since Puppet 4.0.0 the same merge can be achieved with the + operator.
# `$merged_hash = $hash1 + $hash2`
#
# If stdlib::merge is given a single Iterable (Array, Hash, etc.) it will call a given block with
# up to three parameters, and merge each resulting Hash into the accumulated result. All other types
# of values returned from the block (typically undef) are skipped (not merged).
#
# The codeblock can take 2 or three parameters:
# * with two, it gets the current hash (as built to this point), and each value (for hash the value is a [key, value] tuple)
# * with three, it gets the current hash (as built to this point), the key/index of each value, and then the value
#
# If the iterable is empty, or no hash was returned from the given block, an empty hash is returned. In the given block, a call to `next()`
# will skip that entry, and a call to `break()` will end the iteration.
#
# @example counting occurrences of strings in an array
# ['a', 'b', 'c', 'c', 'd', 'b'].stdlib::merge | $hsh, $v | { { $v => $hsh[$v].lest || { 0 } + 1 } } # results in { a => 1, b => 2, c => 2, d => 1 }
#
# @example skipping values for entries that are longer than 1 char
# ['a', 'b', 'c', 'c', 'd', 'b', 'blah', 'blah'].stdlib::merge | $hsh, $v | { if $v =~ String[1,1] { { $v => $hsh[$v].lest || { 0 } + 1 } } } # results in { a => 1, b => 2, c => 2, d => 1 }
#
# The iterative `stdlib::merge()` has an advantage over doing the same with a general `reduce()` in that the constructed hash
# does not have to be copied in each iteration and thus will perform much better with large inputs.
Puppet::Functions.create_function(:'stdlib::merge') do
# @param args
# Repeated Param - The hashes that are to be merged
#
# @return
# The merged hash
dispatch :merge2hashes do
repeated_param 'Variant[Hash[Scalar,Any], Undef, String[0,0]]', :args # this strange type is backwards compatible
return_type 'Hash[Scalar,Any]'
end
# @param args
# Repeated Param - The hashes that are to be merged
#
# @param block
# A block placed on the repeatable param `args`
#
# @return
# The merged hash
dispatch :merge_iterable3 do
repeated_param 'Iterable', :args
block_param 'Callable[3,3]', :block
return_type 'Hash'
end
# @param args
# Repeated Param - The hashes that are to be merged
#
# @param block
# A block placed on the repeatable param `args`
#
# @return
# The merged hash
dispatch :merge_iterable2 do
repeated_param 'Iterable', :args
block_param 'Callable[2,2]', :block
return_type 'Hash'
end
def merge2hashes(*hashes)
accumulator = {}
hashes.each { |h| accumulator.merge!(h) if h.is_a?(Hash) }
accumulator
end
def merge_iterable2(iterable)
accumulator = {}
enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable)
enum.each do |v|
r = yield(accumulator, v)
accumulator.merge!(r) if r.is_a?(Hash)
end
accumulator
end
def merge_iterable3(iterable)
accumulator = {}
enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable)
if enum.hash_style?
enum.each do |entry|
r = yield(accumulator, *entry)
accumulator.merge!(r) if r.is_a?(Hash)
end
else
begin
index = 0
loop do
r = yield(accumulator, index, enum.next)
accumulator.merge!(r) if r.is_a?(Hash)
index += 1
end
rescue StopIteration
end
end
accumulator
end
end

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
# @summary Get list of nested values from given hash
# This function will return list of nested Hash values and returns list of values in form of Array
#
# @example Example Usage:
# $hash = {
# "key1" => "value1",
# "key2" => { "key2.1" => "value2.1"},
# "key3" => "value3"
# }
# $data = $hash.stdlib::nested_values
# #Output : ["value1", "value2.1", "value3"]
Puppet::Functions.create_function(:'stdlib::nested_values') do
# @param hash A (nested) hash
# @return All the values found in the input hash included those deeply nested.
dispatch :nested_values do
param 'Hash', :hash
return_type 'Array'
end
def nested_values(hash)
hash.each_with_object([]) do |(_k, v), values|
v.is_a?(Hash) ? values.concat(nested_values(v)) : (values << v)
end
end
end

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
# @summary
# Checks if the OS version is at least a certain version.
# > *Note:*
# Only the major version is taken into account.
#
# @example Example usage:#
# if stdlib::os_version_gte('Debian', '9') { }
# if stdlib::os_version_gte('Ubuntu', '18.04') { }
Puppet::Functions.create_function(:'stdlib::os_version_gte') do
# @param os operating system
# @param version
#
# @return [Boolean] `true` or `false
dispatch :os_version_gte do
param 'String[1]', :os
param 'String[1]', :version
return_type 'Boolean'
end
def os_version_gte(os, version)
facts = closure_scope['facts']
(facts['os']['name'] == os &&
Puppet::Util::Package.versioncmp(facts['os']['release']['major'], version) >= 0)
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
# @summary
# This function accepts HOCON as a string and converts it into the correct
# Puppet structure
#
# @example How to parse hocon
# $data = stdlib::parsehocon("{any valid hocon: string}")
#
Puppet::Functions.create_function(:'stdlib::parsehocon') do
# @param hocon_string A valid HOCON string
# @param default An optional default to return if parsing hocon_string fails
# @return [Data]
dispatch :parsehocon do
param 'String', :hocon_string
optional_param 'Any', :default
end
def parsehocon(hocon_string, default = :no_default_provided)
require 'hocon/config_factory'
begin
data = Hocon::ConfigFactory.parse_string(hocon_string)
data.resolve.root.unwrapped
rescue Hocon::ConfigError::ConfigParseError => e
Puppet.debug("Parsing hocon failed with error: #{e.message}")
raise e if default == :no_default_provided
default
end
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
# @summary
# Escapes a string so that it can be safely used in a PowerShell command line.
#
# >* Note:* that the resulting string should be used unquoted and is not intended for use in double quotes nor in single
# quotes.
Puppet::Functions.create_function(:'stdlib::powershell_escape') do
# @param string
# The string to escape
#
# @return
# An escaped string that can be safely used in a PowerShell command line.
dispatch :powershell_escape do
param 'Any', :string
end
def powershell_escape(string)
result = ''
string.to_s.chars.each do |char|
result += case char
when ' ', "'", '`', '|', "\n", '$' then "`#{char}"
when '"' then '\`"'
else char
end
end
result
end
end

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
# @summary
# Generates a random whole number greater than or equal to 0 and less than max, using the value of seed for repeatable randomness.
Puppet::Functions.create_function(:'stdlib::seeded_rand') do
# @param max The maximum value.
# @param seed The seed used for repeatable randomness.
#
# @return [Integer]
# A random number greater than or equal to 0 and less than max
dispatch :seeded_rand do
param 'Integer[1]', :max
param 'String', :seed
end
def seeded_rand(max, seed)
require 'digest/md5'
seed = Digest::MD5.hexdigest(seed).hex
Puppet::Util.deterministic_rand_int(seed, max)
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
# @summary
# Generates a consistent random string of specific length based on provided seed.
#
# @example Generate a consistently random string of length 8 with a seed:
# stdlib::seeded_rand_string(8, "${module_name}::redis_password")
#
# @example Generate a random string from a specific set of characters:
# stdlib::seeded_rand_string(5, '', 'abcdef')
Puppet::Functions.create_function(:'stdlib::seeded_rand_string') do
# @param length Length of string to be generated.
# @param seed Seed string.
# @param charset String that contains characters to use for the random string.
#
# @return [String] Random string.
dispatch :rand_string do
param 'Integer[1]', :length
param 'String', :seed
optional_param 'String[2]', :charset
end
def rand_string(length, seed, charset = nil)
require 'digest/sha2'
charset ||= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
random_generator = Random.new(Digest::SHA256.hexdigest(seed).to_i(16))
Array.new(length) { charset[random_generator.rand(charset.size)] }.join
end
end

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'digest'
# @summary
# Run a SHA256 calculation against a given value.
Puppet::Functions.create_function(:'stdlib::sha256') do
# @param my_data The ScalarData to evaluate
# @example Check a simple string value
# stdlib::sha256('my string') == '2f7e2089add0288a309abd71ffcc3b3567e2d4215e20e6ed3b74d6042f7ef8e5'
# @example Check a Sensitive datatype
# stdlib::sha256(sensitive('my string')) == '2f7e2089add0288a309abd71ffcc3b3567e2d4215e20e6ed3b74d6042f7ef8e5'
# @example Check a number
# stdlib::sha256(100.0) == '43b87f618caab482ebe4976c92bcd6ad308b48055f1c27b4c574f3e31d7683e0'
# stdlib::sha256(100.00000) == '43b87f618caab482ebe4976c92bcd6ad308b48055f1c27b4c574f3e31d7683e0'
# @return String
dispatch :sha256 do
param 'Variant[ScalarData, Sensitive[ScalarData], Binary, Sensitive[Binary]]', :my_data
return_type 'String'
end
def sha256(my_data)
Digest::SHA256.hexdigest(my_data.unwrap.to_s)
rescue StandardError
Digest::SHA256.hexdigest(my_data.to_s)
end
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
# @summary
# Escapes a string so that it can be safely used in a Bourne shell command line.
#
# >* Note:* that the resulting string should be used unquoted and is not intended for use in double quotes nor in single
# quotes.
#
# This function behaves the same as ruby's Shellwords.shellescape() function.
Puppet::Functions.create_function(:'stdlib::shell_escape') do
# @param string
# The string to escape
#
# @return
# An escaped string that can be safely used in a Bourne shell command line.
dispatch :shell_escape do
param 'Any', :string
end
def shell_escape(string)
require 'shellwords'
Shellwords.shellescape(string.to_s)
end
end

View file

@ -0,0 +1,49 @@
# frozen_string_literal: true
# @summary Sort an Array, Hash or String by mapping values through a given block.
#
# @example Sort local devices according to their used space.
# $facts['mountpoints'].stdlib::sort_by |$m| { $m.dig(1, 'used_bytes') }
#
Puppet::Functions.create_function(:'stdlib::sort_by') do
# @param ary The Array to sort.
# @param block The block for transforming elements of ary.
# @return [Array] Returns an ordered copy of ary.
dispatch :sort_by_array do
param 'Array', :ary
block_param 'Callable[1,1]', :block
end
# @param str The String to sort.
# @param block The block for transforming elements of str.
# @return [String] Returns an ordered copy of str.
dispatch :sort_by_string do
param 'String', :str
block_param 'Callable[1,1]', :block
end
# @param hsh The Hash to sort.
# @param block The block for transforming elements of hsh.
# The block may have arity of one or two.
# @return [Hash] Returns an ordered copy of hsh.
dispatch :sort_by_hash do
param 'Hash', :hsh
block_param 'Variant[Callable[1,1], Callable[2,2]]', :block
end
def sort_by_iterable(iterable, &block)
Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).sort_by(&block)
end
def sort_by_array(ary, &block)
sort_by_iterable(ary, &block)
end
def sort_by_string(str, &block)
sort_by_iterable(str, &block).join
end
def sort_by_hash(hsh, &block)
sort_by_iterable(hsh, &block).to_h
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
# @summary
# Returns true if str starts with one of the prefixes given. Each of the prefixes should be a String.
#
Puppet::Functions.create_function(:'stdlib::start_with') do
# @param test_string The string to check
# @param prefixes The prefixes to check.
# @example
# 'foobar'.stdlib::start_with('foo') => true
# 'foobar'.stdlib::start_with('bar') => false
# 'foObar'.stdlib::start_with(['bar', 'baz']) => false
# @return [Boolean] True or False
dispatch :start_with do
param 'String', :test_string
param 'Variant[String[1],Array[String[1], 1]]', :prefixes
return_type 'Boolean'
end
def start_with(test_string, prefixes)
test_string.start_with?(*prefixes)
end
end

View file

@ -0,0 +1,35 @@
# frozen_string_literal: true
# @summary
# This converts a string to a puppet resource.
#
# This attempts to convert a string like 'File[/foo]' into the
# puppet resource `File['/foo']` as detected by the catalog.
#
# Things like 'File[/foo, /bar]' are not supported as a
# title might contain things like ',' or ' '. There is
# no clear value seperator to use.
#
# This function can depend on the parse order of your
# manifests/modules as it inspects the catalog thus far.
Puppet::Functions.create_function(:'stdlib::str2resource') do
# @param res_string The string to lookup as a resource
# @example
# stdlib::str2resource('File[/foo]') => File[/foo]
# @return Puppet::Resource
dispatch :str2resource do
param 'String', :res_string
# return_type 'Puppet::Resource'
return_type 'Any'
end
def str2resource(res_string)
type_name, title = Puppet::Resource.type_and_title(res_string, nil)
resource = closure_scope.findresource(type_name, title)
raise(Puppet::ParseError, "stdlib::str2resource(): could not find #{type_name}[#{title}], this is parse order dependent and values should not be quoted") if resource.nil?
resource
end
end

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'json'
# @summary
# Convert a data structure and output to JSON
Puppet::Functions.create_function(:'stdlib::to_json') do
# @param data
# Data structure which needs to be converted into JSON
#
# @example Output JSON to a file
# file { '/tmp/my.json':
# ensure => file,
# content => stdlib::to_json($myhash),
# }
#
# @return [String] Converted data to JSON
dispatch :to_json do
param 'Any', :data
end
def to_json(data)
data.to_json
end
end

View file

@ -0,0 +1,74 @@
# frozen_string_literal: true
require 'json'
# @summary
# Convert data structure and output to pretty JSON
#
# @example **Usage**
# * how to output pretty JSON to file
# file { '/tmp/my.json':
# ensure => file,
# content => stdlib::to_json_pretty($myhash),
# }
#
# * how to output pretty JSON skipping over keys with undef values
# file { '/tmp/my.json':
# ensure => file,
# content => stdlib::to_json_pretty({
# param_one => 'value',
# param_two => undef,
# }, true),
# }
#
# * how to output pretty JSON using tabs for indentation
# file { '/tmp/my.json':
# ensure => file,
# content => stdlib::to_json_pretty({
# param_one => 'value',
# param_two => {
# param_more => 42,
# },
# }, nil, {indent => ' '}),
# }
Puppet::Functions.create_function(:'stdlib::to_json_pretty') do
# @param data
# data structure which needs to be converted to pretty json
# @param skip_undef
# value `true` or `false`
# @param opts
# hash-map of settings passed to JSON.pretty_generate, see
# https://ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-generate.
# Note that `max_nesting` doesn't take the value `false`; use `-1` instead.
# @return
# converted data to pretty json
dispatch :to_json_pretty do
param 'Variant[Hash, Array]', :data
optional_param 'Optional[Boolean]', :skip_undef
optional_param 'Struct[{
indent => Optional[String],
space => Optional[String],
space_before => Optional[String],
object_nl => Optional[String],
array_nl => Optional[String],
allow_nan => Optional[Boolean],
max_nesting => Optional[Integer[-1,default]],
}]', :opts
end
def to_json_pretty(data, skip_undef = false, opts = nil)
# It's not possible to make an abstract type that can be either a boolean
# false or an integer, so we use -1 as the falsey value
if opts
opts = opts.transform_keys(&:to_sym)
opts[:max_nesting] = false if opts[:max_nesting] == -1
end
data = data.compact if skip_undef && (data.is_a?(Array) || Hash)
# Call ::JSON to ensure it references the JSON library from Ruby's standard library
# instead of a random JSON namespace that might be in scope due to user code.
JSON.pretty_generate(data, opts) << "\n"
end
end

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
# @summary
# Convert an object into a String containing its Python representation
#
# @example how to output Python
# # output Python to a file
# $listen = '0.0.0.0'
# $port = 8000
# file { '/opt/acme/etc/settings.py':
# content => inline_epp(@("SETTINGS")),
# LISTEN = <%= stdlib::to_python($listen) %>
# PORT = <%= stdlib::to_python($mailserver) %>
# | SETTINGS
# }
Puppet::Functions.create_function(:'stdlib::to_python') do
# @param object
# The object to be converted
#
# @return [String]
# The String representation of the object
dispatch :to_python do
param 'Any', :object
end
def to_python(object)
serialized = Puppet::Pops::Serialization::ToDataConverter.convert(object, rich_data: true)
serialized_to_python(serialized)
end
def serialized_to_python(serialized)
case serialized
when true then 'True'
when false then 'False'
when nil then 'None'
when Array then "[#{serialized.map { |x| serialized_to_python(x) }.join(', ')}]"
when Hash then "{#{serialized.map { |k, v| "#{serialized_to_python(k)}: #{serialized_to_python(v)}" }.join(', ')}}"
else serialized.inspect
end
end
end

View file

@ -0,0 +1,39 @@
# frozen_string_literal: true
# @summary
# Convert an object into a String containing its Ruby representation
#
# @example how to output Ruby
# # output Ruby to a file
# $listen = '0.0.0.0'
# $port = 8000
# file { '/opt/acme/etc/settings.rb':
# content => inline_epp(@("SETTINGS")),
# LISTEN = <%= stdlib::to_ruby($listen) %>
# PORT = <%= stdlib::to_ruby($mailserver) %>
# | SETTINGS
# }
Puppet::Functions.create_function(:'stdlib::to_ruby') do
# @param object
# The object to be converted
#
# @return [String]
# The String representation of the object
dispatch :to_ruby do
param 'Any', :object
end
def to_ruby(object)
serialized = Puppet::Pops::Serialization::ToDataConverter.convert(object, rich_data: true)
serialized_to_ruby(serialized)
end
def serialized_to_ruby(serialized)
case serialized
when Array then "[#{serialized.map { |x| serialized_to_ruby(x) }.join(', ')}]"
when Hash then "{#{serialized.map { |k, v| "#{serialized_to_ruby(k)} => #{serialized_to_ruby(v)}" }.join(', ')}}"
else serialized.inspect
end
end
end

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative '../../../puppet_x/stdlib/toml_dumper'
# @summary Convert a data structure and output to TOML.
Puppet::Functions.create_function(:'stdlib::to_toml') do
# @param data Data structure which needs to be converted into TOML
# @return [String] Converted data as TOML string
# @example How to output TOML to a file
# file { '/tmp/config.toml':
# ensure => file,
# content => stdlib::to_toml($myhash),
# }
dispatch :to_toml do
required_param 'Hash', :data
return_type 'String'
end
def to_toml(data)
PuppetX::Stdlib::TomlDumper.new(data).toml_str
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'yaml'
# @summary
# Convert a data structure and output it as YAML
Puppet::Functions.create_function(:'stdlib::to_yaml') do
# @param data
# The data you want to convert to YAML
# @param options
# A hash of options that will be passed to Ruby's Psych library. Note, this could change between Puppet versions, but at time of writing these are `line_width`, `indentation`, and `canonical`.
#
# @example Output YAML to a file
# file { '/tmp/my.yaml':
# ensure => file,
# content => stdlib::to_yaml($myhash),
# }
# @example Use options to control the output format
# file { '/tmp/my.yaml':
# ensure => file,
# content => stdlib::to_yaml($myhash, {indentation => 4})
# }
#
# @return [String] The YAML document
dispatch :to_yaml do
param 'Any', :data
optional_param 'Hash', :options
end
def to_yaml(data, options = {})
data.to_yaml(options.transform_keys(&:to_sym))
end
end

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
# @summary
# Returns the type of the passed value.
#
# @example how to compare values' types
# # compare the types of two values
# if stdlib::type_of($first_value) != stdlib::type_of($second_value) { fail("first_value and second_value are different types") }
# @example how to compare against an abstract type
# unless stdlib::type_of($first_value) <= Numeric { fail("first_value must be Numeric") }
# unless stdlib::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.
#
# The built-in type() function in puppet is generally preferred over this function
# this function is provided for backwards compatibility.
Puppet::Functions.create_function(:'stdlib::type_of') do
# @return [String]
# the type of the passed value
#
# @param value
def type_of(value)
Puppet::Pops::Types::TypeCalculator.infer_set(value)
end
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
# @summary
# Validate that all values passed are syntactically correct domain names.
# Fail compilation if any value fails this check.
Puppet::Functions.create_function(:'stdlib::validate_domain_name') do
# @param values A domain name or an array of domain names to check
#
# @return [Undef]
# passes when the given values are syntactically correct domain names or raise an error when they are not and fails compilation
#
# @example Passing examples
# $my_domain_name = 'server.domain.tld'
# stdlib::validate_domain_name($my_domain_name)
# stdlib::validate_domain_name('domain.tld', 'puppet.com', $my_domain_name)
# stdlib::validate_domain_name('www.example.2com')
#
# @example Failing examples (causing compilation to abort)
# stdlib::validate_domain_name(1)
# stdlib::validate_domain_name(true)
# stdlib::validate_domain_name('invalid domain')
# stdlib::validate_domain_name('-foo.example.com')
dispatch :validate_domain_name do
repeated_param 'Variant[Stdlib::Fqdn, Stdlib::Dns::Zone]', :values
end
def validate_domain_name(*args)
assert_arg_count(args)
end
def assert_arg_count(args)
raise(ArgumentError, 'stdlib::validate_domain_name(): Wrong number of arguments need at least one') if args.empty?
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
# @summary
# Validate that all values passed are valid email addresses.
# Fail compilation if any value fails this check.
Puppet::Functions.create_function(:'stdlib::validate_email_address') do
# @param values An e-mail address or an array of e-mail addresses to check
#
# @return [Undef]
# Fail compilation if any value fails this check.
#
# @example Passing examples
# $my_email = "waldo@gmail.com"
# stdlib::validate_email_address($my_email)
# stdlib::validate_email_address("bob@gmail.com", "alice@gmail.com", $my_email)
#
# @example Failing examples (causing compilation to abort)
# $some_array = [ 'bad_email@/d/efdf.com' ]
# stdlib::validate_email_address($some_array)
dispatch :validate_email_address do
repeated_param 'Stdlib::Email', :values
end
def validate_email_address(*args)
assert_arg_count(args)
end
def assert_arg_count(args)
raise(ArgumentError, 'stdlib::validate_email_address(): Wrong number of arguments need at least one') if args.empty?
end
end

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
# @summary Encode strings for XML files
#
# This function can encode strings such that they can be used directly in XML files.
# It supports encoding for both XML text (CharData) or attribute values (AttValue).
Puppet::Functions.create_function(:'stdlib::xml_encode') do
# @param str The string to encode
# @param type Whether to encode for text or an attribute
# @return Returns the encoded CharData or AttValue string suitable for use in XML
# @example Creating an XML file from a template
# file { '/path/to/config.xml':
# ensure => file,
# content => epp(
# 'mymodule/config.xml.epp',
# {
# password => $password.stdlib::xml_encode,
# },
# ),
# }
dispatch :xml_encode do
param 'String', :str
optional_param "Enum['text','attr']", :type
return_type 'String'
end
def xml_encode(str, type = 'text')
str.encode(xml: type.to_sym)
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
# @summary DEPRECATED. Use the native Puppet fuctionality instead of this function. eg `Integer(Timestamp().strftime('%s'))`
Puppet::Functions.create_function(:time) do
dispatch :call_puppet_function do
repeated_param 'Any', :args
end
def call_puppet_function(*args)
# Note, `stdlib::time` calls `deprecation`, so we don't also do that here.
call_function('stdlib::time', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::to_json`](#stdlibto_json) instead.
Puppet::Functions.create_function(:to_json) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'to_json', 'This function is deprecated, please use stdlib::to_json instead.', false)
call_function('stdlib::to_json', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::to_json_pretty`](#stdlibto_json_pretty) instead.
Puppet::Functions.create_function(:to_json_pretty) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'to_json_pretty', 'This function is deprecated, please use stdlib::to_json_pretty instead.', false)
call_function('stdlib::to_json_pretty', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::to_python`](#stdlibto_python) instead.
Puppet::Functions.create_function(:to_python) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'to_python', 'This function is deprecated, please use stdlib::to_python instead.', false)
call_function('stdlib::to_python', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::to_ruby`](#stdlibto_ruby) instead.
Puppet::Functions.create_function(:to_ruby) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'to_ruby', 'This function is deprecated, please use stdlib::to_ruby instead.', false)
call_function('stdlib::to_ruby', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::to_toml`](#stdlibto_toml) instead.
Puppet::Functions.create_function(:to_toml) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'to_toml', 'This function is deprecated, please use stdlib::to_toml instead.', false)
call_function('stdlib::to_toml', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::to_yaml`](#stdlibto_yaml) instead.
Puppet::Functions.create_function(:to_yaml) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'to_yaml', 'This function is deprecated, please use stdlib::to_yaml instead.', false)
call_function('stdlib::to_yaml', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::type_of`](#stdlibtype_of) instead.
Puppet::Functions.create_function(:type_of) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'type_of', 'This function is deprecated, please use stdlib::type_of instead.', false)
call_function('stdlib::type_of', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::validate_domain_name`](#stdlibvalidate_domain_name) instead.
Puppet::Functions.create_function(:validate_domain_name) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'validate_domain_name', 'This function is deprecated, please use stdlib::validate_domain_name instead.', false)
call_function('stdlib::validate_domain_name', *args)
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
# THIS FILE WAS GENERATED BY `rake regenerate_unamespaced_shims`
# @summary DEPRECATED. Use the namespaced function [`stdlib::validate_email_address`](#stdlibvalidate_email_address) instead.
Puppet::Functions.create_function(:validate_email_address) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'validate_email_address', 'This function is deprecated, please use stdlib::validate_email_address instead.', false)
call_function('stdlib::validate_email_address', *args)
end
end

View file

@ -0,0 +1,68 @@
# frozen_string_literal: true
# @summary
# **Deprecated:** Validate a value against both the target_type (new).
Puppet::Functions.create_function(:validate_legacy) do
# The function checks a value against both the target_type (new).
# @param scope
# The main value that will be passed to the method
# @param target_type
# @param function_name
# Unused
# @param value
# @param args
# Any additional values that are to be passed to the method
# @return
# A boolean value (`true` or `false`) returned from the called function.
dispatch :validate_legacy do
param 'Any', :scope
param 'Type', :target_type
param 'String', :function_name
param 'Any', :value
repeated_param 'Any', :args
end
# @param scope
# The main value that will be passed to the method
# @param type_string
# @param function_name
# Unused
# @param value
# @param args Any additional values that are to be passed to the method
# @return Legacy validation method
#
dispatch :validate_legacy_s do
param 'Any', :scope
param 'String', :type_string
param 'String', :function_name
param 'Any', :value
repeated_param 'Any', :args
end
# Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-
# c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1.
def call(scope, *args)
manipulated_args = [scope] + args
self.class.dispatcher.dispatch(self, scope, manipulated_args)
end
def validate_legacy_s(scope, type_string, *args)
t = Puppet::Pops::Types::TypeParser.new.parse(type_string, scope)
validate_legacy(scope, t, *args)
end
def validate_legacy(_scope, target_type, _function_name, value, *_prev_args)
call_function('deprecation', 'validate_legacy', 'This method is deprecated, please use Puppet data types to validate parameters')
if assert_type(target_type, value)
# "Silently" passes
else
inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value)
error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch("validate_legacy(#{target_type}, ...)", target_type, inferred_type)
call_function('fail', error_msg)
end
end
def assert_type(type, value)
Puppet::Pops::Types::TypeCalculator.instance?(type, value)
end
end

View file

@ -0,0 +1,56 @@
# frozen_string_literal: true
#
# any2array.rb
#
module Puppet::Parser::Functions
newfunction(:any2array, type: :rvalue, doc: <<-DOC
@summary
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.
> *Note:*
since Puppet 5.0.0 it is possible to create new data types for almost any
datatype using the type system and the built-in
[`Array.new`](https://puppet.com/docs/puppet/latest/function.html#conversion-to-array-and-tuple)
function is used to create a new Array..
```
$hsh = {'key' => 42, 'another-key' => 100}
notice(Array($hsh))
```
Would notice `[['key', 42], ['another-key', 100]]`
The Array data type also has a special mode to "create an array if not already an array"
```
notice(Array({'key' => 42, 'another-key' => 100}, true))
```
Would notice `[{'key' => 42, 'another-key' => 100}]`, as the `true` flag prevents the hash from being
transformed into an array.
@return [Array] The new array containing the given object
DOC
) do |arguments|
return [] if arguments.empty?
return arguments unless arguments.length == 1
return arguments[0] if arguments[0].is_a?(Array)
return [] if arguments == ['']
if arguments[0].is_a?(Hash)
result = []
arguments[0].each do |key, value|
result << key << value
end
return result
end
return arguments
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,54 @@
# frozen_string_literal: true
#
# any2bool.rb
#
module Puppet::Parser::Functions
newfunction(:any2bool, type: :rvalue, doc: <<-DOC
@summary
Converts 'anything' to a boolean.
In practise it does the following:
* Strings such as Y,y,1,T,t,TRUE,yes,'true' will return true
* Strings such as 0,F,f,N,n,FALSE,no,'false' will return false
* Booleans will just return their original value
* Number (or a string representation of a number) > 0 will return true, otherwise false
* undef will return false
* Anything else will return true
Also see the built-in [`Boolean.new`](https://puppet.com/docs/puppet/latest/function.html#conversion-to-boolean)
function.
@return [Boolean] The boolean value of the object that was given
DOC
) do |arguments|
raise(Puppet::ParseError, "any2bool(): Wrong number of arguments given (#{arguments.size} for 1)") if arguments.empty?
# If argument is already Boolean, return it
return arguments[0] if !!arguments[0] == arguments[0] # rubocop:disable Style/DoubleNegation : Could not find a better way to check if a boolean
arg = arguments[0]
return false if arg.nil?
return false if arg == :undef
valid_float = begin
!!Float(arg) # rubocop:disable Style/DoubleNegation : Could not find a better way to check if a boolean
rescue StandardError
false
end
return function_num2bool([arguments[0]]) if arg.is_a?(Numeric)
if arg.is_a?(String)
return function_num2bool([arguments[0]]) if valid_float
return function_str2bool([arguments[0]])
end
return true
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
#
# assert_private.rb
#
module Puppet::Parser::Functions
newfunction(:assert_private, doc: <<-DOC
@summary
Sets the current class or definition as private.
@return
set the current class or definition as private.
Calling the class or definition from outside the current module will fail.
DOC
) do |args|
raise(Puppet::ParseError, "assert_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].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

View file

@ -0,0 +1,81 @@
# frozen_string_literal: true
# Please note: This function is an implementation of a Ruby class and as such may not be entirely UTF8 compatible. To ensure compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.
module Puppet::Parser::Functions
newfunction(:base64, type: :rvalue, doc: <<-DOC) do |args|
@summary
Base64 encode or decode a string based on the command and the string submitted
@example Example usage
Encode and decode a string
$encodestring = base64('encode', 'thestring')
$decodestring = base64('decode', 'dGhlc3RyaW5n')
Explicitly define encode/decode method: default, strict, urlsafe
$method = 'default'
$encodestring = base64('encode', 'thestring', $method)
$decodestring = base64('decode', 'dGhlc3RyaW5n', $method)
Encode a string as if it was binary
$encodestring = String(Binary('thestring', '%s'))
Decode a Binary assuming it is an UTF-8 String
$decodestring = String(Binary("dGhlc3RyaW5n"), "%s")
> **Note:*
Since Puppet 4.8.0, the Binary data type can be used to produce base 64 encoded strings.
See the `new()` function for the Binary and String types for documentation. Also see `binary_file()`
function for reading a file with binary (non UTF-8) content.
@return [String] The encoded/decoded value
DOC
require 'base64'
raise Puppet::ParseError, "base64(): Wrong number of arguments (#{args.length}; must be >= 2)" unless args.length >= 2
actions = ['encode', 'decode']
raise Puppet::ParseError, "base64(): the first argument must be one of 'encode' or 'decode'" unless actions.include?(args[0])
raise Puppet::ParseError, 'base64(): the second argument must be a string to base64' unless args[1].is_a?(String)
method = ['default', 'strict', 'urlsafe']
chosen_method = if args.length <= 2
'default'
else
args[2]
end
raise Puppet::ParseError, "base64(): the third argument must be one of 'default', 'strict', or 'urlsafe'" unless method.include?(chosen_method)
case args[0]
when 'encode'
case chosen_method
when 'default'
result = Base64.encode64(args[1])
when 'strict'
result = Base64.strict_encode64(args[1])
when 'urlsafe'
result = Base64.urlsafe_encode64(args[1])
end
when 'decode'
case chosen_method
when 'default'
result = Base64.decode64(args[1])
when 'strict'
result = Base64.strict_decode64(args[1])
when 'urlsafe'
result = Base64.urlsafe_decode64(args[1])
end
end
return result
end
end

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
#
# basename.rb
#
module Puppet::Parser::Functions
newfunction(:basename, type: :rvalue, doc: <<-DOC
@summary
Strips directory (and optional suffix) from a filename
@return [String] The stripped filename
DOC
) do |arguments|
raise(Puppet::ParseError, 'basename(): No arguments given') if arguments.empty?
raise(Puppet::ParseError, "basename(): Too many arguments given (#{arguments.size})") if arguments.size > 2
raise(Puppet::ParseError, 'basename(): Requires string as first argument') unless arguments[0].is_a?(String)
rv = File.basename(arguments[0]) if arguments.size == 1
if arguments.size == 2
raise(Puppet::ParseError, 'basename(): Requires string as second argument') unless arguments[1].is_a?(String)
rv = File.basename(arguments[0], arguments[1])
end
return rv
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
#
# bool2num.rb
#
module Puppet::Parser::Functions
newfunction(:bool2num, type: :rvalue, doc: <<-DOC
@summary
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.
> *Note:*
since Puppet 5.0.0 it is possible to create new data types for almost any
datatype using the type system and the built-in
[`Numeric.new`](https://puppet.com/docs/puppet/latest/function.html#conversion-to-numeric),
[`Integer.new`](https://puppet.com/docs/puppet/latest/function.html#conversion-to-integer), and
[`Float.new`](https://puppet.com/docs/puppet/latest/function.html#conversion-to-float)
function are used to convert to numeric values.
```
notice(Integer(false)) # Notices 0
notice(Float(true)) # Notices 1.0
```
@return [Integer] The converted value as a number
DOC
) do |arguments|
raise(Puppet::ParseError, "bool2num(): Wrong number of arguments given (#{arguments.size} for 1)") if arguments.empty?
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 :

View file

@ -0,0 +1,58 @@
# frozen_string_literal: true
#
# bool2str.rb
#
module Puppet::Parser::Functions
newfunction(:bool2str, type: :rvalue, doc: <<-DOC
@summary
Converts a boolean to a string using optionally supplied arguments.
The optional second and third arguments represent what true and false will be
converted to respectively. If only one argument is given, it will be
converted from a boolean to a string containing 'true' or 'false'.
@return
The converted value to string of the given Boolean
**Examples of usage**
```
bool2str(true) => 'true'
bool2str(true, 'yes', 'no') => 'yes'
bool2str(false, 't', 'f') => 'f'
```
Requires a single boolean as an input.
> *Note:*
since Puppet 5.0.0 it is possible to create new data types for almost any
datatype using the type system and the built-in
[`String.new`](https://puppet.com/docs/puppet/latest/function.html#boolean-to-string)
function is used to convert to String with many different format options.
```
notice(String(false)) # Notices 'false'
notice(String(true)) # Notices 'true'
notice(String(false, '%y')) # Notices 'yes'
notice(String(true, '%y')) # Notices 'no'
```
DOC
) do |arguments|
raise(Puppet::ParseError, "bool2str(): Wrong number of arguments given (#{arguments.size} for 3)") unless arguments.size == 1 || arguments.size == 3
value = arguments[0]
true_string = arguments[1] || 'true'
false_string = arguments[2] || 'false'
klass = value.class
# We can have either true or false, and nothing else
raise(Puppet::ParseError, 'bool2str(): Requires a boolean to work with') unless [FalseClass, TrueClass].include?(klass)
raise(Puppet::ParseError, 'bool2str(): Requires strings to convert to') unless [true_string, false_string].all?(String)
return value ? true_string : false_string
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
#
# clamp.rb
#
module Puppet::Parser::Functions
newfunction(:clamp, type: :rvalue, arity: -2, doc: <<-DOC
@summary
Keeps value within the range [Min, X, Max] by sort based on integer value
(parameter order doesn't matter).
Strings are converted and compared numerically. Arrays of values are flattened
into a list for further handling.
@example Example usage
clamp('24', [575, 187])` returns 187.
clamp(16, 88, 661)` returns 88.
clamp([4, 3, '99'])` returns 4.
> *Note:*
From Puppet 6.0.0 this can be done with only core Puppet like this:
`[$minval, $maxval, $value_to_clamp].sort[1]`
@return [Array[Integer]] The sorted Array
DOC
) do |args|
args.flatten!
raise(Puppet::ParseError, 'clamp(): Wrong number of arguments, need three to clamp') if args.size != 3
# check values out
args.each do |value|
case [value.class]
when [String]
raise(Puppet::ParseError, "clamp(): Required explicit numeric (#{value}:String)") unless %r{^\d+$}.match?(value)
when [Hash]
raise(Puppet::ParseError, "clamp(): The Hash type is not allowed (#{value})")
end
end
# convert to numeric each element
# then sort them and get a middle value
args.map(&:to_i).sort[1]
end
end

View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
#
# concat.rb
#
module Puppet::Parser::Functions
newfunction(:concat, type: :rvalue, doc: <<-DOC
@summary
Appends the contents of multiple arrays into array 1.
@example Example usage
concat(['1','2','3'],'4') returns ['1','2','3','4']
concat(['1','2','3'],'4',['5','6','7']) returns ['1','2','3','4','5','6','7']
> *Note:*
Since Puppet 4.0, you can use the `+`` operator for concatenation of arrays and
merge of hashes, and the `<<`` operator for appending:
`['1','2','3'] + ['4','5','6'] + ['7','8','9']` returns `['1','2','3','4','5','6','7','8','9']`
`[1, 2, 3] << 4` returns `[1, 2, 3, 4]`
`[1, 2, 3] << [4, 5]` returns `[1, 2, 3, [4, 5]]`
@return [Array] The single concatenated array
DOC
) 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
raise(Puppet::ParseError, 'concat(): Requires array to work with') unless a.is_a?(Array)
result = a
arguments.shift
arguments.each do |x|
result += (x.is_a?(Array) ? x : [x])
end
return result
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,49 @@
# frozen_string_literal: true
#
# convert_base.rb
#
module Puppet::Parser::Functions
newfunction(:convert_base, type: :rvalue, arity: 2, doc: <<-DOC) do |args|
@summary
Converts a given integer or base 10 string representing an integer to a
specified base, as a string.
@return
converted value as a string
@example Example usage
convert_base(5, 2)` results in: `'101'`
convert_base('254', '16')` results in: `'fe'`
> *Note:*
Since Puppet 4.5.0 this can be done with the built-in
[`String.new`](https://puppet.com/docs/puppet/latest/function.html#integer-to-string)
function and its many formatting options:
`$binary_repr = String(5, '%b')` return `"101"`
`$hex_repr = String(254, "%x")` return `"fe"`
`$hex_repr = String(254, "%#x")` return `"0xfe"`
@return [String] The converted value as a String
DOC
raise Puppet::ParseError, 'convert_base(): First argument must be either a string or an integer' unless args[0].is_a?(Integer) || args[0].is_a?(String)
raise Puppet::ParseError, 'convert_base(): Second argument must be either a string or an integer' unless args[1].is_a?(Integer) || args[1].is_a?(String)
raise Puppet::ParseError, 'convert_base(): First argument must be an integer or a string corresponding to an integer in base 10' if args[0].is_a?(String) && !%r{^[0-9]+$}.match?(args[0])
raise Puppet::ParseError, 'convert_base(): First argument must be an integer or a string corresponding to an integer in base 10' if args[1].is_a?(String) && !%r{^[0-9]+$}.match?(args[1])
number_to_convert = args[0]
new_base = args[1]
number_to_convert = number_to_convert.to_i
new_base = new_base.to_i
raise Puppet::ParseError, 'convert_base(): base must be at least 2 and must not be greater than 36' unless new_base >= 2 && new_base <= 36
return number_to_convert.to_s(new_base)
end
end

View file

@ -0,0 +1,40 @@
# frozen_string_literal: true
#
# count.rb
#
module Puppet::Parser::Functions
newfunction(:count, type: :rvalue, arity: -2, doc: <<-DOC
@summary
Counts the number of elements in array.
Takes an array as first argument and an optional second argument. Counts the number of elements in array that is equal to the second argument.
If called with only an array, it counts the number of elements that are not nil/undef/empty-string.
> *Note:*
equality is tested with a Ruby method and it is therefore subject to what Ruby considers
to be equal. For strings this means that equality is case sensitive.
In Puppet core, counting can be done in general by using a combination of the core functions
filter() (since Puppet 4.0.0) and length() (since Puppet 5.5.0, before that in stdlib).
Example below shows counting values that are not undef.
```notice([42, "hello", undef].filter |$x| { $x =~ NotUndef }.length)```
Would notice the value 2.
@return [Integer] The amount of elements counted within the array
DOC
) do |args|
raise(ArgumentError, "count(): Wrong number of arguments given #{args.size} for 1 or 2.") if args.size > 2
collection, item = args
if item
collection.count item
else
collection.count { |obj| !obj.nil? && obj != :undef && obj != '' }
end
end
end

View file

@ -0,0 +1,49 @@
# frozen_string_literal: true
#
# deep_merge.rb
#
module Puppet::Parser::Functions
newfunction(:deep_merge, type: :rvalue, doc: <<-DOC) do |args|
@summary
Recursively merges two or more hashes together and returns the resulting hash.
@example Example usage
$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."
@return [Hash] The merged hash
DOC
raise Puppet::ParseError, "deep_merge(): wrong number of arguments (#{args.length}; must be at least 2)" if args.length < 2
deep_merge = proc 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 = {}
args.each do |arg|
next if arg.is_a?(String) && arg.empty? # empty string is synonym for puppet's undef
# If the argument was not a hash, skip it.
raise Puppet::ParseError, "deep_merge: unexpected argument type #{arg.class}, only expects hash arguments" unless arg.is_a?(Hash)
result = deep_merge.call(result, arg)
end
return(result)
end
end

View file

@ -0,0 +1,78 @@
# frozen_string_literal: true
# Test whether a given class or definition is defined
require 'puppet/parser/functions'
Puppet::Parser::Functions.newfunction(:defined_with_params, type: :rvalue, doc: <<-DOC
@summary
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, }
}
```
@return [Boolean]
returns `true` or `false`
DOC
) do |vals|
reference, params = vals
raise(ArgumentError, 'Must specify a reference') unless reference
params = {} if !params || params == ''
ret = false
if Puppet::Util::Package.versioncmp(Puppet.version, '4.6.0') >= 0
# Workaround for PE-20308
if reference.is_a?(String)
type_name, title = Puppet::Resource.type_and_title(reference, nil)
type = Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type_or_class(find_global_scope, type_name.downcase)
elsif reference.is_a?(Puppet::Resource)
type = reference.type
title = reference.title
else
raise(ArgumentError, "Reference is not understood: '#{reference.class}'")
end
# end workaround
else
type = reference.to_s
title = nil
end
resources = if title.empty?
catalog.resources.select { |r| r.type == type }
else
[findresource(type, title)]
end
resources.compact.each do |res|
# If you call this from within a defined type, it will find itself
next if res.to_s == resource.to_s
matches = params.map do |key, value|
# eql? avoids bugs caused by monkeypatching in puppet
res_is_undef = res[key].eql?(:undef) || res[key].nil?
value_is_undef = value.eql?(:undef) || value.nil?
found_match = (res_is_undef && value_is_undef) || (res[key] == value)
Puppet.debug("Matching resource is #{res}") if found_match
found_match
end
ret = params.empty? || !matches.include?(false)
break if ret
end
Puppet.debug("Resource #{reference} was not determined to be defined") unless ret
ret
end

View file

@ -0,0 +1,68 @@
# frozen_string_literal: true
#
# delete.rb
#
module Puppet::Parser::Functions
newfunction(:delete, type: :rvalue, doc: <<-DOC
@summary
Deletes all instances of a given element from an array, substring from a
string, or key from a hash.
@example Example usage
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'
['a', 'b', 'c', 'b'] - 'b'
Would return: ['a', 'c']
{'a'=>1,'b'=>2,'c'=>3} - ['b','c'])
Would return: {'a' => '1'}
'abracadabra'.regsubst(/bra/, '', 'G')
Would return: 'acada'
> *Note:*
From Puppet 4.0.0 the minus (-) operator deletes values from arrays and keys from a hash
`{'a'=>1,'b'=>2,'c'=>3} - ['b','c'])`
>
A global delete from a string can be performed with the
[`regsubst`](https://puppet.com/docs/puppet/latest/function.html#regsubst) function:
`'abracadabra'.regsubst(/bra/, '', 'G')`
In general, the built-in [`filter`](https://puppet.com/docs/puppet/latest/function.html#filter)
function can filter out entries from arrays and hashes based on keys and/or values.
@return [String] The filtered String, if one was given.
@return [Hash] The filtered Hash, if one was given.
@return [Array] The filtered Array, if one was given.
DOC
) do |arguments|
raise(Puppet::ParseError, "delete(): Wrong number of arguments given #{arguments.size} for 2") unless arguments.size == 2
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 :

View file

@ -0,0 +1,57 @@
# frozen_string_literal: true
#
# delete_at.rb
#
module Puppet::Parser::Functions
newfunction(:delete_at, type: :rvalue, doc: <<-DOC) do |arguments|
@summary
Deletes a determined indexed value from an array.
For example
```delete_at(['a','b','c'], 1)```
Would return: `['a','c']`
> *Note:*
Since Puppet 4 this can be done in general with the built-in
[`filter`](https://puppet.com/docs/puppet/latest/function.html#filter) function:
```['a', 'b', 'c'].filter |$pos, $val | { $pos != 1 }```
Or if a delete is wanted from the beginning or end of the array, by using the slice operator [ ]:
```
$array[0, -1] # the same as all the values
$array[2, -1] # all but the first 2 elements
$array[0, -3] # all but the last 2 elements
$array[1, -2] # all but the first and last element
```
@return [Array] The given array, now missing the target value
DOC
raise(Puppet::ParseError, "delete_at(): Wrong number of arguments given (#{arguments.size} for 2)") if arguments.size < 2
array = arguments[0]
raise(Puppet::ParseError, 'delete_at(): Requires array to work with') unless array.is_a?(Array)
index = arguments[1]
raise(Puppet::ParseError, 'delete_at(): You must provide non-negative numeric index') if index.is_a?(String) && !index.match(%r{^\d+$})
result = array.clone
# Numbers in Puppet are often string-encoded which is troublesome ...
index = index.to_i
raise(Puppet::ParseError, 'delete_at(): Given index exceeds size of array given') if index > result.size - 1 # First element is at index 0 is it not?
result.delete_at(index) # We ignore the element that got deleted ...
return result
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,53 @@
# frozen_string_literal: true
#
# delete_regex.rb
# Please note: This function is an implementation of a Ruby class and as such may not be entirely UTF8 compatible. To ensure compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.
#
module Puppet::Parser::Functions
newfunction(:delete_regex, type: :rvalue, doc: <<-DOC
@summary
Deletes all instances of a given element that match a regular expression
from an array or key from a hash.
Multiple regular expressions are assumed to be matched as an OR.
@example Example usage
delete_regex(['a','b','c','b'], 'b')
Would return: ['a','c']
delete_regex(['a','b','c','b'], ['b', 'c'])
Would return: ['a']
delete_regex({'a'=>1,'b'=>2,'c'=>3}, 'b')
Would return: {'a'=>1,'c'=>3}
delete_regex({'a'=>1,'b'=>2,'c'=>3}, '^a$')
Would return: {'b'=>2,'c'=>3}
> *Note:*
Since Puppet 4 this can be done in general with the built-in
[`filter`](https://puppet.com/docs/puppet/latest/function.html#filter) function:
["aaa", "aba", "aca"].filter |$val| { $val !~ /b/ }
Would return: ['aaa', 'aca']
@return [Array] The given array now missing all targeted values.
DOC
) do |arguments|
raise(Puppet::ParseError, "delete_regex(): Wrong number of arguments given #{arguments.size} for 2") unless arguments.size == 2
collection = arguments[0].dup
Array(arguments[1]).each do |item|
case collection
when Array, Hash, String
collection.reject! { |coll_item| (coll_item =~ %r{\b#{item}\b}) }
else
raise(TypeError, "delete_regex(): First argument must be an Array, Hash, or String. Given an argument of class #{collection.class}.")
end
end
collection
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
#
# delete_undef_values.rb
#
module Puppet::Parser::Functions
newfunction(:delete_undef_values, type: :rvalue, doc: <<-DOC
@summary
Returns a copy of input hash or array with all undefs deleted.
@example Example usage
$hash = delete_undef_values({a=>'A', b=>'', c=>undef, d => false})
Would return: {a => 'A', b => '', d => false}
While:
$array = delete_undef_values(['A','',undef,false])
Would return: ['A','',false]
> *Note:*
Since Puppet 4.0.0 the equivalent can be performed with the built-in
[`filter`](https://puppet.com/docs/puppet/latest/function.html#filter) function:
$array.filter |$val| { $val =~ NotUndef }
$hash.filter |$key, $val| { $val =~ NotUndef }
@return [Array] The given array now issing of undefined values.
DOC
) do |args|
raise(Puppet::ParseError, "delete_undef_values(): Wrong number of arguments given (#{args.size})") if args.empty?
raise(Puppet::ParseError, "delete_undef_values(): expected an array or hash, got #{args[0]} type #{args[0].class} ") unless args[0].is_a?(Array) || args[0].is_a?(Hash)
result = args[0].dup
if result.is_a?(Hash)
result.delete_if { |_, val| val.equal?(:undef) || val.nil? }
elsif result.is_a?(Array)
result.delete :undef
result.delete nil
end
result
end
end

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
#
# delete_values.rb
#
module Puppet::Parser::Functions
newfunction(:delete_values, type: :rvalue, doc: <<-DOC
@summary
Deletes all instances of a given value from a hash.
@example Example usage
delete_values({'a'=>'A','b'=>'B','c'=>'C','B'=>'D'}, 'B')
Would return: {'a'=>'A','c'=>'C','B'=>'D'}
> *Note:*
Since Puppet 4.0.0 the equivalent can be performed with the
built-in [`filter`](https://puppet.com/docs/puppet/latest/function.html#filter) function:
$array.filter |$val| { $val != 'B' }
$hash.filter |$key, $val| { $val != 'B' }
@return [Hash] The given hash now missing all instances of the targeted value
DOC
) do |arguments|
raise(Puppet::ParseError, "delete_values(): Wrong number of arguments given (#{arguments.size} of 2)") if arguments.size != 2
hash, item = arguments
raise(TypeError, "delete_values(): First argument must be a Hash. Given an argument of class #{hash.class}.") unless hash.is_a?(Hash)
hash.dup.delete_if { |_key, val| item == val }
end
end

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
#
# difference.rb
#
module Puppet::Parser::Functions
newfunction(:difference, type: :rvalue, doc: <<-DOC
@summary
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.
@example Example usage
difference(["a","b","c"],["b","c","d"])
Would return: `["a"]`
> *Note:*
Since Puppet 4 the minus (-) operator in the Puppet language does the same thing:
['a', 'b', 'c'] - ['b', 'c', 'd']
Would return: `['a']`
@return [Array]
The difference between the two given arrays
DOC
) 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]
raise(Puppet::ParseError, 'difference(): Requires 2 arrays') unless first.is_a?(Array) && second.is_a?(Array)
result = first - second
return result
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
#
# dirname.rb
#
module Puppet::Parser::Functions
newfunction(:dirname, type: :rvalue, doc: <<-DOC
@summary
Returns the dirname of a path.
@return [String] the given path's dirname
DOC
) do |arguments|
raise(Puppet::ParseError, 'dirname(): No arguments given') if arguments.empty?
raise(Puppet::ParseError, "dirname(): Too many arguments given (#{arguments.size})") if arguments.size > 1
raise(Puppet::ParseError, 'dirname(): Requires string as argument') unless arguments[0].is_a?(String)
# undef is converted to an empty string ''
raise(Puppet::ParseError, 'dirname(): Requires a non-empty string as argument') if arguments[0].empty?
return File.dirname(arguments[0])
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
# Custom Puppet function to convert dos to unix format
module Puppet::Parser::Functions
newfunction(:dos2unix, type: :rvalue, arity: 1, doc: <<-DOC
@summary
Returns the Unix version of the given string.
Takes a single string argument.
@return The retrieved version
DOC
) do |arguments|
raise(Puppet::ParseError, 'dos2unix(): Requires string as argument') unless arguments[0].is_a?(String)
arguments[0].gsub(%r{\r\n}, "\n")
end
end

View file

@ -0,0 +1,41 @@
# frozen_string_literal: true
#
# enclose_ipv6.rb
#
module Puppet::Parser::Functions
newfunction(:enclose_ipv6, type: :rvalue, doc: <<-DOC
@summary
Takes an array of ip addresses and encloses the ipv6 addresses with square brackets.
@return
encloses the ipv6 addresses with square brackets.
DOC
) do |arguments|
require 'ipaddr'
rescuable_exceptions = [ArgumentError]
rescuable_exceptions << IPAddr::InvalidAddressError if defined?(IPAddr::InvalidAddressError)
raise(Puppet::ParseError, "enclose_ipv6(): Wrong number of arguments given #{arguments.size} for 1") if arguments.size != 1
raise(Puppet::ParseError, "enclose_ipv6(): Wrong argument type given #{arguments[0].class} expected String or Array") unless arguments[0].is_a?(String) || arguments[0].is_a?(Array)
input = [arguments[0]].flatten.compact
result = []
input.each do |val|
unless val == '*'
begin
ip = IPAddr.new(val)
rescue *rescuable_exceptions
raise(Puppet::ParseError, "enclose_ipv6(): Wrong argument given #{val} is not an ip address.")
end
val = "[#{ip}]" if ip.ipv6?
end
result << val
end
return result.uniq
end
end

View file

@ -0,0 +1,53 @@
# frozen_string_literal: true
# Test whether a given class or definition is defined
require 'puppet/parser/functions'
Puppet::Parser::Functions.newfunction(:ensure_resource, type: :statement, doc: <<-DOC
@summary
Takes a resource type, title, and a list of attributes that describe a
resource.
user { 'dan':
ensure => present,
}
@return
created or recreated the passed resource with the passed type and attributes
@example Example usage
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'})
DOC
) 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

View file

@ -0,0 +1,58 @@
# frozen_string_literal: true
require 'puppet/parser/functions'
Puppet::Parser::Functions.newfunction(:ensure_resources, type: :statement, doc: <<-DOC
@summary
Takes a resource type, title (only hash), and a list of attributes that describe a
resource.
@return
created resources with the passed type and attributes
@example Example usage
user { 'dan':
gid => 'mygroup',
ensure => present,
}
An hash of resources should be passed in and each will be created with
the type and parameters specified if it doesn't already exist.
ensure_resources('user', {'dan' => { gid => 'mygroup', uid => '600' }, 'alex' => { gid => 'mygroup' }}, {'ensure' => 'present'})
From Hiera Backend:
userlist:
dan:
gid: 'mygroup'
uid: '600'
alex:
gid: 'mygroup'
Call:
ensure_resources('user', hiera_hash('userlist'), {'ensure' => 'present'})
DOC
) do |vals|
type, title, params = vals
raise(ArgumentError, 'Must specify a type') unless type
raise(ArgumentError, 'Must specify a title') unless title
params ||= {}
raise(Puppet::ParseError, 'ensure_resources(): Requires second argument to be a Hash') unless title.is_a?(Hash)
resource_hash = title.dup
resources = resource_hash.keys
Puppet::Parser::Functions.function(:ensure_resource)
resources.each do |resource_name|
params_merged = if resource_hash[resource_name]
params.merge(resource_hash[resource_name])
else
params
end
function_ensure_resource([type, resource_name, params_merged])
end
end

View file

@ -0,0 +1,71 @@
# frozen_string_literal: true
require 'digest/sha1'
#
# fqdn_uuid.rb
#
module Puppet::Parser::Functions
newfunction(:fqdn_uuid, type: :rvalue, doc: <<-DOC) do |args|
@summary
Returns a [RFC 4122](https://tools.ietf.org/html/rfc4122) valid version 5 UUID based
on an FQDN string under the DNS namespace
@return
Returns a [RFC 4122](https://tools.ietf.org/html/rfc4122) valid version 5 UUID
@example Example Usage:
fqdn_uuid('puppetlabs.com') # Returns '9c70320f-6815-5fc5-ab0f-debe68bf764c'
fqdn_uuid('google.com') # Returns '64ee70a4-8cc1-5d25-abf2-dea6c79a09c8'
DOC
raise(ArgumentError, 'fqdn_uuid: No arguments given') if args.empty?
raise(ArgumentError, "fqdn_uuid: Too many arguments given (#{args.length})") unless args.length == 1
fqdn = args[0]
# Code lovingly taken from
# https://github.com/puppetlabs/marionette-collective/blob/master/lib/mcollective/ssl.rb
# This is the UUID version 5 type DNS name space which is as follows:
#
# 6ba7b810-9dad-11d1-80b4-00c04fd430c8
#
uuid_name_space_dns = [0x6b,
0xa7,
0xb8,
0x10,
0x9d,
0xad,
0x11,
0xd1,
0x80,
0xb4,
0x00,
0xc0,
0x4f,
0xd4,
0x30,
0xc8].map(&:chr).join
sha1 = Digest::SHA1.new
sha1.update(uuid_name_space_dns)
sha1.update(fqdn)
# first 16 bytes..
bytes = sha1.digest[0, 16].bytes.to_a
# version 5 adjustments
bytes[6] &= 0x0f
bytes[6] |= 0x50
# variant is DCE 1.1
bytes[8] &= 0x3f
bytes[8] |= 0x80
bytes = [4, 2, 2, 2, 6].map do |i|
bytes.slice!(0, i).pack('C*').unpack('H*')
end
bytes.join('-')
end
end

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
#
# get_module_path.rb
#
module Puppet::Parser::Functions
newfunction(:get_module_path, type: :rvalue, doc: <<-DOC
@summary
Returns the absolute path of the specified module for the current
environment.
@return
Returns the absolute path of the specified module for the current
environment.
@example Example Usage:
$module_path = get_module_path('stdlib')
> *Note:*
that since Puppet 5.4.0 the built-in
[`module_directory`](https://puppet.com/docs/puppet/latest/function.html#module_directory)
function in Puppet does the same thing and will return the path to the first found module
if given multiple values or an array.
DOC
) do |args|
raise(Puppet::ParseError, 'get_module_path(): Wrong number of arguments, expects one') unless args.size == 1
module_path = Puppet::Module.find(args[0], compiler.environment.to_s)
raise(Puppet::ParseError, "Could not find module #{args[0]} in environment #{compiler.environment}") unless module_path
module_path.path
end
end

View file

@ -0,0 +1,57 @@
# frozen_string_literal: true
# Test whether a given class or definition is defined
require 'puppet/parser/functions'
Puppet::Parser::Functions.newfunction(:getparam, type: :rvalue, doc: <<-DOC
@summary
Returns the value of a resource's parameter.
@return
value of a resource's parameter.
Takes a resource reference and name of the parameter and
returns value of resource's parameter. Note that user defined
resource types are evaluated lazily.
@example Example Usage:
# define a resource type with a parameter
define example_resource($param) {
}
# declare an instance of that type
example_resource { "example_resource_instance":
param => "'the value we are getting in this example''"
}
# Because of order of evaluation, a second definition is needed
# that will be evaluated after the first resource has been declared
#
define example_get_param {
# This will notice the value of the parameter
notice(getparam(Example_resource["example_resource_instance"], "param"))
}
# Declare an instance of the second resource type - this will call notice
example_get_param { 'show_notify': }
Would notice: 'the value we are getting in this example'
> **Note** that since Puppet 4.0.0 it is possible to get a parameter value by using its data type
and the [ ] operator. The example below is equivalent to a call to getparam():
```Example_resource['example_resource_instance']['param']``
DOC
) do |vals|
reference, param = vals
raise(ArgumentError, 'Must specify a reference') unless reference
raise(ArgumentError, 'Must specify name of a parameter') unless param.instance_of?(String)
return '' if param.empty?
resource = findresource(reference.to_s)
return resource[param] if resource && !resource[param].nil?
return ''
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
#
# glob.rb
#
module Puppet::Parser::Functions
newfunction(:glob, type: :rvalue, doc: <<-DOC
@summary
Uses same patterns as Dir#glob.
@return
Returns an Array of file entries of a directory or an Array of directories.
@example Example Usage:
$confs = glob(['/etc/**/*.conf', '/opt/**/*.conf'])
DOC
) do |arguments|
unless arguments.size == 1
raise(Puppet::ParseError, 'glob(): Wrong number of arguments given ' \
"(#{arguments.size} for 1)")
end
pattern = arguments[0]
unless pattern.is_a?(String) || pattern.is_a?(Array)
raise(Puppet::ParseError, 'glob(): Requires either array or string ' \
'to work')
end
Dir.glob(pattern)
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
#
# grep.rb
#
module Puppet::Parser::Functions
newfunction(:grep, type: :rvalue, doc: <<-DOC
@summary
This function searches through an array and returns any elements that match
the provided regular expression.
@return
array of elements that match the provided regular expression.
@example Example Usage:
grep(['aaa','bbb','ccc','aaaddd'], 'aaa') # Returns ['aaa','aaaddd']
> **Note:** that since Puppet 4.0.0, the built-in
[`filter`](https://puppet.com/docs/puppet/latest/function.html#filter) function does
the "same" - as any logic can be used to filter, as opposed to just regular expressions:
```['aaa', 'bbb', 'ccc', 'aaaddd']. filter |$x| { $x =~ 'aaa' }```
DOC
) do |arguments|
raise(Puppet::ParseError, "grep(): Wrong number of arguments given #{arguments.size} for 2") if arguments.size != 2
a = arguments[0]
pattern = Regexp.new(arguments[1])
a.grep(pattern)
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,68 @@
# frozen_string_literal: true
#
# has_interface_with
#
module Puppet::Parser::Functions
newfunction(:has_interface_with, type: :rvalue, doc: <<-DOC
@summary
Returns boolean based on kind and value.
@return
boolean values `true` or `false`
Valid kinds are `macaddress`, `netmask`, `ipaddress` and `network`.
@example **Usage**
has_interface_with("macaddress", "x:x:x:x:x:x") # Returns `false`
has_interface_with("ipaddress", "127.0.0.1") # Returns `true`
@example If no "kind" is given, then the presence of the interface is checked:
has_interface_with("lo") # Returns `true`
DOC
) do |args|
raise(Puppet::ParseError, "has_interface_with(): Wrong number of arguments given (#{args.size} for 1 or 2)") if args.empty? || 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(',')
return interfaces.member?(args[0]) if args.size == 1
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
begin
catch :undefined_variable do
factval = lookupvar(kind)
end
rescue Puppet::ParseError
end
return true if factval == value
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
end
if value == factval
result = true
break
end
end
result
end
end

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
#
# has_ip_address
#
module Puppet::Parser::Functions
newfunction(:has_ip_address, type: :rvalue, doc: <<-DOC
@summary
Returns true if the client has the requested IP address on some interface.
@return [Boolean]
`true` or `false`
This function iterates through the 'interfaces' fact and checks the
'ipaddress_IFACE' facts, performing a simple string comparison.
DOC
) 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

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
#
# has_ip_network
#
module Puppet::Parser::Functions
newfunction(:has_ip_network, type: :rvalue, doc: <<-DOC
@summary
Returns true if the client has an IP address within the requested network.
@return
Boolean value, `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.
DOC
) 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

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
#
# intersection.rb
#
module Puppet::Parser::Functions
newfunction(:intersection, type: :rvalue, doc: <<-DOC
@summary
This function returns an array of the intersection of two.
@return
an array of the intersection of two.
@example Example Usage:
intersection(["a","b","c"],["b","c","d"]) # returns ["b","c"]
intersection(["a","b","c"],[1,2,3,4]) # returns [] (true, when evaluated as a Boolean)
DOC
) 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]
raise(Puppet::ParseError, "intersection(): Requires 2 arrays, got #{first.class} and #{second.class}") unless first.is_a?(Array) && second.is_a?(Array)
result = first & second
return result
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,53 @@
# frozen_string_literal: true
#
# join_keys_to_values.rb
#
module Puppet::Parser::Functions
newfunction(:join_keys_to_values, type: :rvalue, doc: <<-DOC
@summary
This function joins each key of a hash to that key's corresponding value with a
separator.
Keys are cast to strings. If values are arrays, multiple keys
are added for each element. The return value is an array in
which each element is one joined key/value pair.
@example Example Usage:
join_keys_to_values({'a'=>1,'b'=>2}, " is ") # Results in: ["a is 1","b is 2"]
join_keys_to_values({'a'=>1,'b'=>[2,3]}, " is ") # Results in: ["a is 1","b is 2","b is 3"]
@return [Hash]
The joined hash
> **Note:** Since Puppet 5.0.0 - for more detailed control over the formatting (including indentations and
line breaks, delimiters around arrays and hash entries, between key/values in hash entries, and individual
formatting of values in the array) - see the `new` function for `String` and its formatting
options for `Array` and `Hash`.
DOC
) do |arguments|
# Validate the number of arguments.
raise(Puppet::ParseError, "join_keys_to_values(): Takes exactly two arguments, but #{arguments.size} given.") if arguments.size != 2
# Validate the first argument.
hash = arguments[0]
raise(TypeError, "join_keys_to_values(): The first argument must be a hash, but a #{hash.class} was given.") unless hash.is_a?(Hash)
# Validate the second argument.
separator = arguments[1]
raise(TypeError, "join_keys_to_values(): The second argument must be a string, but a #{separator.class} was given.") unless separator.is_a?(String)
# Join the keys to their values.
hash.map { |k, v|
if v.is_a?(Array)
v.map { |va| String(k) + separator + String(va) }
elsif String(v) == 'undef'
String(k)
else
String(k) + separator + String(v)
end
}.flatten
end
end
# vim: set ts=2 sw=2 et :

View file

@ -0,0 +1,40 @@
# frozen_string_literal: true
#
# load_module_metadata.rb
#
module Puppet::Parser::Functions
newfunction(:load_module_metadata, type: :rvalue, doc: <<-DOC
@summary
This function loads the metadata of a given module.
@example Example Usage:
$metadata = load_module_metadata('archive')
notify { $metadata['author']: }
@return
The modules metadata
DOC
) do |args|
raise(Puppet::ParseError, 'load_module_metadata(): Wrong number of arguments, expects one or two') unless [1, 2].include?(args.size)
mod = args[0]
allow_empty_metadata = args[1]
module_path = function_get_module_path([mod])
metadata_json = File.join(module_path, 'metadata.json')
metadata_exists = File.exist?(metadata_json)
if metadata_exists
metadata = if Puppet::Util::Package.versioncmp(Puppet.version, '8.0.0').negative?
PSON.load(File.read(metadata_json))
else
JSON.parse(File.read(metadata_json))
end
else
metadata = {}
raise(Puppet::ParseError, "load_module_metadata(): No metadata.json file for module #{mod}") unless allow_empty_metadata
end
return metadata
end
end

Some files were not shown because too many files have changed in this diff Show more