diff options
46 files changed, 1038 insertions, 89 deletions
@@ -2,6 +2,7 @@ pkg/  .DS_Store  metadata.json  coverage/ +spec/fixtures/  Gemfile.lock  .bundle/  vendor/bundle/ diff --git a/.project b/.project new file mode 100644 index 0000000..4e2c033 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> +	<name>stdlib</name> +	<comment></comment> +	<projects> +	</projects> +	<buildSpec> +		<buildCommand> +			<name>org.cloudsmith.geppetto.pp.dsl.ui.modulefileBuilder</name> +			<arguments> +			</arguments> +		</buildCommand> +		<buildCommand> +			<name>org.eclipse.xtext.ui.shared.xtextBuilder</name> +			<arguments> +			</arguments> +		</buildCommand> +	</buildSpec> +	<natures> +		<nature>org.cloudsmith.geppetto.pp.dsl.ui.puppetNature</nature> +		<nature>org.eclipse.xtext.ui.shared.xtextNature</nature> +	</natures> +</projectDescription> diff --git a/.travis.yml b/.travis.yml index 7e40b3f..1bb1889 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,20 +3,18 @@ bundler_args: --without development  script: "bundle exec rake spec SPEC_OPTS='--color --format documentation'"  rvm:    - 1.8.7 +  - 1.9.3 +  - 2.0.0 +  - ruby-head  env: -  - PUPPET_VERSION=">= 3.0.0" -  - PUPPET_VERSION="~> 2.7.0" -  - PUPPET_VERSION=2.7.13 -  - PUPPET_VERSION=2.7.6 -  - PUPPET_VERSION=2.6.9 +  - PUPPET_GEM_VERSION=">= 3.0.0"  matrix:    allow_failures:      - rvm: 2.0.0 +    - rvm: ruby-head    include: -    - rvm: 2.0.0 -      env: PUPPET_VERSION=">= 3.0.0" -    - rvm: 1.9.3 -      env: PUPPET_VERSION=">= 3.0.0" +    - rvm: 1.8.7 +      env: PUPPET_GEM_VERSION="~> 2.7"  notifications:    email: false    webhooks: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bd11f63 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# How to contribute + +Third-party patches are essential for keeping stdlib great. We simply can't +access the huge number of platforms and myriad configurations for running +stdlib. We want to keep it as easy as possible to contribute changes that +get things working in your environment. There are a few guidelines that we +need contributors to follow so that we can have a chance of keeping on +top of things. + +## Getting Started + +* Make sure you have a [Redmine account](http://projects.puppetlabs.com) +* Make sure you have a [GitHub account](https://github.com/signup/free) +* Submit a ticket for your issue, assuming one does not already exist. +  * Clearly describe the issue including steps to reproduce when it is a bug. +  * Make sure you fill in the earliest version that you know has the issue. +* Fork the repository on GitHub + +## Making Changes + +* Create a topic branch from where you want to base your work. +  * This is usually the master branch. +  * Only target release branches if you are certain your fix must be on that +    branch. +  * To quickly create a topic branch based on master; `git branch +    fix/master/my_contribution master` then checkout the new branch with `git +    checkout fix/master/my_contribution`.  Please avoid working directly on the +    `master` branch. +* Make commits of logical units. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your commit messages are in the proper format. + +```` +    (#99999) Make the example in CONTRIBUTING imperative and concrete + +    Without this patch applied the example commit message in the CONTRIBUTING +    document is not a concrete example.  This is a problem because the +    contributor is left to imagine what the commit message should look like +    based on a description rather than an example.  This patch fixes the +    problem by making the example concrete and imperative. + +    The first line is a real life imperative statement with a ticket number +    from our issue tracker.  The body describes the behavior without the patch, +    why this is a problem, and how the patch fixes the problem when applied. +```` + +* Make sure you have added the necessary tests for your changes. +* Run _all_ the tests to assure nothing else was accidentally broken. + +## Submitting Changes + +* Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla). +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the repository in the puppetlabs organization. +* Update your Redmine ticket to mark that you have submitted code and are ready for it to be reviewed. +  * Include a link to the pull request in the ticket + +# Additional Resources + +* [More information on contributing](http://links.puppetlabs.com/contribute-to-puppet) +* [Bug tracker (Redmine)](http://projects.puppetlabs.com) +* [Contributor License Agreement](http://links.puppetlabs.com/cla) +* [General GitHub documentation](http://help.github.com/) +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) +* #puppet-dev IRC channel on freenode.org @@ -1,8 +1,21 @@  source "https://rubygems.org" -if puppetversion = ENV['PUPPET_VERSION'] -  gem 'puppet', puppetversion +group :development do +  gem 'watchr' +end + +group :development, :test do +  gem 'rake' +  gem 'rspec', "~> 2.11.0", :require => false +  gem 'mocha', "~> 0.10.5", :require => false +  gem 'puppetlabs_spec_helper', :require => false +  gem 'rspec-puppet', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] +  gem 'puppet', puppetversion, :require => false  else -  gem 'puppet' +  gem 'puppet', :require => false  end -gem 'puppetlabs_spec_helper', '>= 0.1.0' + +# vim:ft=ruby diff --git a/README.markdown b/README.markdown index 130753d..336d0ab 100644 --- a/README.markdown +++ b/README.markdown @@ -1,5 +1,7 @@  # Puppet Labs Standard Library # +[](https://travis-ci.org/puppetlabs/puppetlabs-stdlib) +  This module provides a "standard library" of resources for developing Puppet  Modules.  This modules will include the following additions to Puppet @@ -46,7 +48,7 @@ All stdlib releases in the 2.0 major version support Puppet 2.6 and Puppet 2.7.  ## stdlib 3.x ##  The 3.0 major release of stdlib drops support for Puppet 2.6.  Stdlib 3.x -supports Puppet 2.7. +supports Puppet 2 and Puppet 3.  # Functions # @@ -96,6 +98,13 @@ Requires a string or array of strings as input.  - *Type*: rvalue +concat +----- +Appends the contents of the second array onto the first array. + + +- *Type*: rvalue +  defined_with_params  -------------------  Takes a resource reference and an optional hash of attributes. @@ -189,6 +198,20 @@ Would return: ['a','b','c']  - *Type*: rvalue +floor +----- +Returns the largest integer less or equal to the argument. +Takes a single numeric value as an argument. + +*Examples:* + +    floor('3.8') + +Would return: '3' + + +- *Type*: rvalue +  fqdn_rotate  -----------  Rotates an array a random number of times based on a nodes fqdn. @@ -207,6 +230,25 @@ Example:  - *Type*: rvalue +getparam +-------- + +Takes a resource reference and name of the parameter and returns +value of resource's parameter. + +For example: + +    define example_resource($param) { +    } + +    example_resource { "example_resource_instance": +        param => "param_value" +    } + +    getparam(Example_resource["example_resource_instance"], "param") + +- *Type*: rvalue +  getvar  ------  Lookup a variable in a remote namespace. @@ -437,7 +479,7 @@ prefix  ------  This function applies a prefix to all elements in an array. -*Examles:* +*Examples:*      prefix(['a','b','c'], 'p') @@ -619,6 +661,19 @@ Would result in: "aaa"  - *Type*: rvalue +suffix +------ +This function applies a suffix to all elements in an array. + +*Examples:* + +    suffix(['a','b','c'], 'p') + +Will return: ['ap','bp','cp'] + + +- *Type*: rvalue +  swapcase  --------  This function will swap the existing case of a string. @@ -751,6 +806,38 @@ The following values will fail, causing compilation to abort:  - *Type*: statement +validate_augeas +-------------- +Perform validation of a string using an Augeas lens +The first argument of this function should be a string to +test, and the second argument should be the name of the Augeas lens to use. +If Augeas fails to parse the string with the lens, the compilation will +abort with a parse error. + +A third argument can be specified, listing paths which should +not be found in the file. The `$file` variable points to the location +of the temporary file being tested in the Augeas tree. + +For example, if you want to make sure your passwd content never contains +a user `foo`, you could write: + +    validate_augeas($passwdcontent, 'Passwd.lns', ['$file/foo']) + +Or if you wanted to ensure that no users used the '/bin/barsh' shell, +you could use: + +    validate_augeas($passwdcontent, 'Passwd.lns', ['$file/*[shell="/bin/barsh"]'] + +If a fourth argument is specified, this will be the error message raised and +seen by the user. + +A helpful error message can be returned like this: + +    validate_augeas($sudoerscontent, 'Sudoers.lns', [], 'Failed to validate sudoers content with Augeas') + + +- *Type*: statement +  validate_bool  -------------  Validate that all passed values are either true or false. Abort catalog @@ -773,6 +860,29 @@ The following values will fail, causing compilation to abort:  - *Type*: statement + +validate_cmd +------------- +Perform validation of a string with an external command. +The first argument of this function should be a string to +test, and the second argument should be a path to a test command +taking a file as last argument. If the command, launched against +a tempfile containing the passed string, returns a non-null value, +compilation will abort with a parse error. + +If a third argument is specified, this will be the error message raised and +seen by the user. + +A helpful error message can be returned like this: + +Example: + +    validate_cmd($sudoerscontent, '/usr/sbin/visudo -c -f', 'Visudo failed to validate sudoers content') + + + +- *Type*: statement +  validate_hash  -------------  Validate that all passed values are hash data structures. Abort catalog diff --git a/RELEASE_PROCESS.markdown b/RELEASE_PROCESS.markdown index 3982c84..0f9328e 100644 --- a/RELEASE_PROCESS.markdown +++ b/RELEASE_PROCESS.markdown @@ -22,4 +22,3 @@   * Build a new package with puppet-module or the rake build task if it exists   * Publish the new package to the forge   * Bonus points for an announcement to puppet-users. - diff --git a/lib/puppet/parser/functions/abs.rb b/lib/puppet/parser/functions/abs.rb index ade5462..11d2d7f 100644 --- a/lib/puppet/parser/functions/abs.rb +++ b/lib/puppet/parser/functions/abs.rb @@ -4,7 +4,7 @@  module Puppet::Parser::Functions    newfunction(:abs, :type => :rvalue, :doc => <<-EOS -    Returns the absolute value of a number, for example -34.56 becomes  +    Returns the absolute value of a number, for example -34.56 becomes      34.56. Takes a single integer and float value as an argument.      EOS    ) do |arguments| diff --git a/lib/puppet/parser/functions/any2array.rb b/lib/puppet/parser/functions/any2array.rb new file mode 100644 index 0000000..e71407e --- /dev/null +++ b/lib/puppet/parser/functions/any2array.rb @@ -0,0 +1,33 @@ +# +# any2array.rb +# + +module Puppet::Parser::Functions +  newfunction(:any2array, :type => :rvalue, :doc => <<-EOS +This converts any object to an array containing that object. Empty argument +lists are converted to an empty array. Arrays are left untouched. Hashes are +converted to arrays of alternating keys and values. +    EOS +  ) do |arguments| + +    if arguments.empty? +        return [] +    end + +    if arguments.length == 1 +        if arguments[0].kind_of?(Array) +            return arguments[0] +        elsif arguments[0].kind_of?(Hash) +            result = [] +            arguments[0].each do |key, value| +                result << key << value +            end +            return result +        end +    end + +    return arguments +  end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/chomp.rb b/lib/puppet/parser/functions/chomp.rb index c99d139..4564a00 100644 --- a/lib/puppet/parser/functions/chomp.rb +++ b/lib/puppet/parser/functions/chomp.rb @@ -4,7 +4,7 @@  module Puppet::Parser::Functions    newfunction(:chomp, :type => :rvalue, :doc => <<-'EOS' -    Removes the record separator from the end of a string or an array of  +    Removes the record separator from the end of a string or an array of      strings, for example `hello\n` becomes `hello`.      Requires a single string or array as an input.      EOS diff --git a/lib/puppet/parser/functions/chop.rb b/lib/puppet/parser/functions/chop.rb index 636b990..f242af3 100644 --- a/lib/puppet/parser/functions/chop.rb +++ b/lib/puppet/parser/functions/chop.rb @@ -4,9 +4,9 @@  module Puppet::Parser::Functions    newfunction(:chop, :type => :rvalue, :doc => <<-'EOS' -    Returns a new string with the last character removed. If the string ends  -    with `\r\n`, both characters are removed. Applying chop to an empty  -    string returns an empty string. If you wish to merely remove record  +    Returns a new string with the last character removed. If the string ends +    with `\r\n`, both characters are removed. Applying chop to an empty +    string returns an empty string. If you wish to merely remove record      separators then you should use the `chomp` function.      Requires a string or array of strings as input.      EOS diff --git a/lib/puppet/parser/functions/concat.rb b/lib/puppet/parser/functions/concat.rb new file mode 100644 index 0000000..c86aa00 --- /dev/null +++ b/lib/puppet/parser/functions/concat.rb @@ -0,0 +1,37 @@ +# +# concat.rb +# + +module Puppet::Parser::Functions +  newfunction(:concat, :type => :rvalue, :doc => <<-EOS +Appends the contents of array 2 onto array 1. + +*Example:* + +    concat(['1','2','3'],['4','5','6']) + +Would result in: + +  ['1','2','3','4','5','6'] +    EOS +  ) do |arguments| + +    # Check that 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] +    b = arguments[1] + +    # Check that both args are arrays. +    unless a.is_a?(Array) and b.is_a?(Array) +      raise(Puppet::ParseError, 'concat(): Requires array to work with') +    end + +    result = a.concat(b) + +    return result +  end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/floor.rb b/lib/puppet/parser/functions/floor.rb new file mode 100644 index 0000000..a401923 --- /dev/null +++ b/lib/puppet/parser/functions/floor.rb @@ -0,0 +1,20 @@ +module Puppet::Parser::Functions +  newfunction(:floor, :type => :rvalue, :doc => <<-EOS +    Returns the largest integer less or equal to the argument. +    Takes a single numeric value as an argument. +    EOS +  ) do |arguments| + +    raise(Puppet::ParseError, "floor(): Wrong number of arguments " + +          "given (#{arguments.size} for 1)") if arguments.size != 1 + +    arg = arguments[0] + +    raise(Puppet::ParseError, "floor(): Wrong argument type " + +          "given (#{arg.class} for Numeric)") if arg.is_a?(Numeric) == false + +    arg.floor +  end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/getparam.rb b/lib/puppet/parser/functions/getparam.rb new file mode 100644 index 0000000..6d51006 --- /dev/null +++ b/lib/puppet/parser/functions/getparam.rb @@ -0,0 +1,35 @@ +# Test whether a given class or definition is defined +require 'puppet/parser/functions' + +Puppet::Parser::Functions.newfunction(:getparam, +                                      :type => :rvalue, +                                      :doc => <<-'ENDOFDOC' +Takes a resource reference and name of the parameter and +returns value of resource's parameter. + +*Examples:* + +    define example_resource($param) { +    } + +    example_resource { "example_resource_instance": +        param => "param_value" +    } + +    getparam(Example_resource["example_resource_instance"], "param") + +Would return: param_value +ENDOFDOC +) do |vals| +  reference, param = vals +  raise(ArgumentError, 'Must specify a reference') unless reference +  raise(ArgumentError, 'Must specify name of a parameter') unless param and param.instance_of? String + +  return '' if param.empty? + +  if resource = findresource(reference.to_s) +    return resource[param] if resource[param] +  end + +  return '' +end diff --git a/lib/puppet/parser/functions/is_function_available.rb b/lib/puppet/parser/functions/is_function_available.rb new file mode 100644 index 0000000..6cbd35c --- /dev/null +++ b/lib/puppet/parser/functions/is_function_available.rb @@ -0,0 +1,23 @@ +# +# is_function_available.rb +# + +module Puppet::Parser::Functions +  newfunction(:is_function_available, :type => :rvalue, :doc => <<-EOS +This function accepts a string as an argument, determines whether the +Puppet runtime has access to a function by that name.  It returns a +true if the function exists, false if not. +    EOS +  ) do |arguments| + +    if (arguments.size != 1) then +      raise(Puppet::ParseError, "is_function_available?(): Wrong number of arguments "+ +        "given #{arguments.size} for 1") +    end + +    function = Puppet::Parser::Functions.function(arguments[0].to_sym) +    function.is_a?(String) and not function.empty? +  end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_ip_address.rb b/lib/puppet/parser/functions/is_ip_address.rb index b4a9a15..a90adab 100644 --- a/lib/puppet/parser/functions/is_ip_address.rb +++ b/lib/puppet/parser/functions/is_ip_address.rb @@ -15,7 +15,7 @@ Returns true if the string passed to this function is a valid IP address.          "given #{arguments.size} for 1")      end -    begin  +    begin        ip = IPAddr.new(arguments[0])      rescue ArgumentError        return false diff --git a/lib/puppet/parser/functions/num2bool.rb b/lib/puppet/parser/functions/num2bool.rb index 874db22..af0e6ed 100644 --- a/lib/puppet/parser/functions/num2bool.rb +++ b/lib/puppet/parser/functions/num2bool.rb @@ -2,38 +2,41 @@  # num2bool.rb  # -# TODO(Krzysztof Wilczynski): We probably need to approach numeric values differently ... -  module Puppet::Parser::Functions    newfunction(:num2bool, :type => :rvalue, :doc => <<-EOS -This function converts a number into a true boolean. Zero becomes false. Numbers -higher then 0 become true. +This function converts a number or a string representation of a number into a +true boolean. Zero or anything non-numeric becomes false. Numbers higher then 0 +become true.      EOS    ) do |arguments|      raise(Puppet::ParseError, "num2bool(): Wrong number of arguments " + -      "given (#{arguments.size} for 1)") if arguments.size < 1 +      "given (#{arguments.size} for 1)") if arguments.size != 1      number = arguments[0] -    # Only numbers allowed ... -    unless number.match(/^\-?\d+$/) -      raise(Puppet::ParseError, 'num2bool(): Requires integer to work with') +    case number +    when Numeric +      # Yay, it's a number +    when String +      begin +        number = Float(number) +      rescue ArgumentError => ex +        raise(Puppet::ParseError, "num2bool(): '#{number}' does not look like a number: #{ex.message}") +      end +    else +      begin +        number = number.to_s +      rescue NoMethodError => ex +        raise(Puppet::ParseError, "num2bool(): Unable to parse argument: #{ex.message}") +      end      end -    result = case number -      when /^0$/ -        false -      when /^\-?\d+$/ -        # Numbers in Puppet are often string-encoded which is troublesome ... -        number = number.to_i -        # We yield true for any positive number and false otherwise ... -        number > 0 ? true : false -      else -        raise(Puppet::ParseError, 'num2bool(): Unknown numeric format given') -    end +    # Truncate Floats +    number = number.to_i -    return result +    # Return true for any positive number and false otherwise +    return number > 0    end  end diff --git a/lib/puppet/parser/functions/parseyaml.rb b/lib/puppet/parser/functions/parseyaml.rb index e8ac8a4..53d54fa 100644 --- a/lib/puppet/parser/functions/parseyaml.rb +++ b/lib/puppet/parser/functions/parseyaml.rb @@ -4,7 +4,7 @@  module Puppet::Parser::Functions    newfunction(:parseyaml, :type => :rvalue, :doc => <<-EOS -This function accepts YAML as a string and converts it into the correct  +This function accepts YAML as a string and converts it into the correct  Puppet structure.      EOS    ) do |arguments| diff --git a/lib/puppet/parser/functions/prefix.rb b/lib/puppet/parser/functions/prefix.rb index 4593976..62211ae 100644 --- a/lib/puppet/parser/functions/prefix.rb +++ b/lib/puppet/parser/functions/prefix.rb @@ -6,7 +6,7 @@ module Puppet::Parser::Functions    newfunction(:prefix, :type => :rvalue, :doc => <<-EOS  This function applies a prefix to all elements in an array. -*Examles:* +*Examples:*      prefix(['a','b','c'], 'p') @@ -21,14 +21,14 @@ Will return: ['pa','pb','pc']      array = arguments[0]      unless array.is_a?(Array) -      raise(Puppet::ParseError, 'prefix(): Requires array to work with') +      raise Puppet::ParseError, "prefix(): expected first argument to be an Array, got #{array.inspect}"      end      prefix = arguments[1] if arguments[1]      if prefix        unless prefix.is_a?(String) -        raise(Puppet::ParseError, 'prefix(): Requires string to work with') +        raise Puppet::ParseError, "prefix(): expected second argument to be a String, got #{suffix.inspect}"        end      end diff --git a/lib/puppet/parser/functions/squeeze.rb b/lib/puppet/parser/functions/squeeze.rb index 65c174a..81fadfd 100644 --- a/lib/puppet/parser/functions/squeeze.rb +++ b/lib/puppet/parser/functions/squeeze.rb @@ -14,9 +14,9 @@ Returns a new string where runs of the same character that occur in this set are      end      item = arguments[0] -    squeezeval = arguments[1]  +    squeezeval = arguments[1] -    if item.is_a?(Array) then   +    if item.is_a?(Array) then        if squeezeval then          item.collect { |i| i.squeeze(squeezeval) }        else diff --git a/lib/puppet/parser/functions/str2bool.rb b/lib/puppet/parser/functions/str2bool.rb index 9ea6dd5..fece7a6 100644 --- a/lib/puppet/parser/functions/str2bool.rb +++ b/lib/puppet/parser/functions/str2bool.rb @@ -4,7 +4,7 @@  module Puppet::Parser::Functions    newfunction(:str2bool, :type => :rvalue, :doc => <<-EOS -This converts a string to a boolean. This attempt to convert strings that  +This converts a string to a boolean. This attempt to convert strings that  contain things like: y, 1, t, true to 'true' and strings that contain things  like: 0, f, n, false, no to 'false'.      EOS diff --git a/lib/puppet/parser/functions/suffix.rb b/lib/puppet/parser/functions/suffix.rb new file mode 100644 index 0000000..f7792d6 --- /dev/null +++ b/lib/puppet/parser/functions/suffix.rb @@ -0,0 +1,45 @@ +# +# suffix.rb +# + +module Puppet::Parser::Functions +  newfunction(:suffix, :type => :rvalue, :doc => <<-EOS +This function applies a suffix to all elements in an array. + +*Examples:* + +    suffix(['a','b','c'], 'p') + +Will return: ['ap','bp','cp'] +    EOS +  ) do |arguments| + +    # Technically we support two arguments but only first is mandatory ... +    raise(Puppet::ParseError, "suffix(): Wrong number of arguments " + +      "given (#{arguments.size} for 1)") if arguments.size < 1 + +    array = arguments[0] + +    unless array.is_a?(Array) +      raise Puppet::ParseError, "suffix(): expected first argument to be an Array, got #{array.inspect}" +    end + +    suffix = arguments[1] if arguments[1] + +    if suffix +      unless suffix.is_a? String +        raise Puppet::ParseError, "suffix(): expected second argument to be a String, got #{suffix.inspect}" +      end +    end + +    # Turn everything into string same as join would do ... +    result = array.collect do |i| +      i = i.to_s +      suffix ? i + suffix : i +    end + +    return result +  end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/validate_augeas.rb b/lib/puppet/parser/functions/validate_augeas.rb new file mode 100644 index 0000000..154d660 --- /dev/null +++ b/lib/puppet/parser/functions/validate_augeas.rb @@ -0,0 +1,81 @@ +module Puppet::Parser::Functions +  newfunction(:validate_augeas, :doc => <<-'ENDHEREDOC') do |args| +    Perform validation of a string using an Augeas lens +    The first argument of this function should be a string to +    test, and the second argument should be the name of the Augeas lens to use. +    If Augeas fails to parse the string with the lens, the compilation will +    abort with a parse error. + +    A third argument can be specified, listing paths which should +    not be found in the file. The `$file` variable points to the location +    of the temporary file being tested in the Augeas tree. + +    For example, if you want to make sure your passwd content never contains +    a user `foo`, you could write: + +        validate_augeas($passwdcontent, 'Passwd.lns', ['$file/foo']) + +    Or if you wanted to ensure that no users used the '/bin/barsh' shell, +    you could use: + +        validate_augeas($passwdcontent, 'Passwd.lns', ['$file/*[shell="/bin/barsh"]'] + +    If a fourth argument is specified, this will be the error message raised and +    seen by the user. + +    A helpful error message can be returned like this: + +        validate_augeas($sudoerscontent, 'Sudoers.lns', [], 'Failed to validate sudoers content with Augeas') + +    ENDHEREDOC +    unless Puppet.features.augeas? +      raise Puppet::ParseError, ("validate_augeas(): this function requires the augeas feature. See http://projects.puppetlabs.com/projects/puppet/wiki/Puppet_Augeas#Pre-requisites for how to activate it.") +    end + +    if (args.length < 2) or (args.length > 4) then +      raise Puppet::ParseError, ("validate_augeas(): wrong number of arguments (#{args.length}; must be 2, 3, or 4)") +    end + +    msg = args[3] || "validate_augeas(): Failed to validate content against #{args[1].inspect}" + +    require 'augeas' +    aug = Augeas::open(nil, nil, Augeas::NO_MODL_AUTOLOAD) +    begin +      content = args[0] + +      # Test content in a temporary file +      tmpfile = Tempfile.new("validate_augeas") +      begin +        tmpfile.write(content) +      ensure +        tmpfile.close +      end + +      # Check for syntax +      lens = args[1] +      aug.transform( +        :lens => lens, +        :name => 'Validate_augeas', +        :incl => tmpfile.path +      ) +      aug.load! + +      unless aug.match("/augeas/files#{tmpfile.path}//error").empty? +        error = aug.get("/augeas/files#{tmpfile.path}//error/message") +        msg += " with error: #{error}" +        raise Puppet::ParseError, (msg) +      end + +      # Launch unit tests +      tests = args[2] || [] +      aug.defvar('file', "/files#{tmpfile.path}") +      tests.each do |t| +        msg += " testing path #{t}" +        raise Puppet::ParseError, (msg) unless aug.match(t).empty? +      end +    ensure +      aug.close +      tmpfile.unlink +    end +  end +end diff --git a/lib/puppet/parser/functions/validate_cmd.rb b/lib/puppet/parser/functions/validate_cmd.rb new file mode 100644 index 0000000..344a80c --- /dev/null +++ b/lib/puppet/parser/functions/validate_cmd.rb @@ -0,0 +1,47 @@ +require 'puppet/util/execution' + +module Puppet::Parser::Functions +  newfunction(:validate_cmd, :doc => <<-'ENDHEREDOC') do |args| +    Perform validation of a string with an external command. +    The first argument of this function should be a string to +    test, and the second argument should be a path to a test command +    taking a file as last argument. If the command, launched against +    a tempfile containing the passed string, returns a non-null value, +    compilation will abort with a parse error. + +    If a third argument is specified, this will be the error message raised and +    seen by the user. + +    A helpful error message can be returned like this: + +    Example: + +        validate_cmd($sudoerscontent, '/usr/sbin/visudo -c -f', 'Visudo failed to validate sudoers content') + +    ENDHEREDOC +    if (args.length < 2) or (args.length > 3) then +      raise Puppet::ParseError, ("validate_cmd(): wrong number of arguments (#{args.length}; must be 2 or 3)") +    end + +    msg = args[2] || "validate_cmd(): failed to validate content with command #{args[1].inspect}" + +    content = args[0] +    checkscript = args[1] + +    # Test content in a temporary file +    tmpfile = Tempfile.new("validate_cmd") +    begin +      tmpfile.write(content) +      if Puppet::Util::Execution.respond_to?('execute') +        Puppet::Util::Execution.execute("#{checkscript} #{tmpfile.path}") +      else +        Puppet::Util.execute("#{checkscript} #{tmpfile.path}") +      end +    rescue Puppet::ExecutionFailure => detail +      msg += "\n#{detail}" +      raise Puppet::ParseError, msg +    ensure +      tmpfile.unlink +    end +  end +end diff --git a/spec/functions/getparam_spec.rb b/spec/functions/getparam_spec.rb new file mode 100644 index 0000000..d9c50a6 --- /dev/null +++ b/spec/functions/getparam_spec.rb @@ -0,0 +1,34 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +require 'rspec-puppet' +describe 'getparam' do +  describe 'when a resource is not specified' do +    it do +      should run.with_params().and_raise_error(ArgumentError) +      should run.with_params('User[dan]').and_raise_error(ArgumentError) +      should run.with_params('User[dan]', {}).and_raise_error(ArgumentError) +      should run.with_params('User[dan]', '').and_return('') +    end +  end +  describe 'when compared against a resource with no params' do +    let :pre_condition do +      'user { "dan": }' +    end +    it do +      should run.with_params('User[dan]', 'shell').and_return('') +    end +  end + +  describe 'when compared against a resource with params' do +    let :pre_condition do +      'user { "dan": ensure => present, shell => "/bin/sh", managehome => false}' +    end +    it do +      should run.with_params('User[dan]', 'shell').and_return('/bin/sh') +      should run.with_params('User[dan]', '').and_return('') +      should run.with_params('User[dan]', 'ensure').and_return('present') +      should run.with_params('User[dan]', 'managehome').and_return(false) +    end +  end +end diff --git a/spec/monkey_patches/publicize_methods.rb b/spec/monkey_patches/publicize_methods.rb index b39e9c0..f3a1abf 100755 --- a/spec/monkey_patches/publicize_methods.rb +++ b/spec/monkey_patches/publicize_methods.rb @@ -8,4 +8,3 @@ class Class          self.class_eval { private(*saved_private_instance_methods) }      end  end - diff --git a/spec/unit/puppet/parser/functions/any2array_spec.rb b/spec/unit/puppet/parser/functions/any2array_spec.rb new file mode 100644 index 0000000..b266e84 --- /dev/null +++ b/spec/unit/puppet/parser/functions/any2array_spec.rb @@ -0,0 +1,55 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the any2array function" do +  let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + +  it "should exist" do +    Puppet::Parser::Functions.function("any2array").should == "function_any2array" +  end + +  it "should return an empty array if there is less than 1 argument" do +    result = scope.function_any2array([]) +    result.should(eq([])) +  end + +  it "should convert boolean true to [ true ] " do +    result = scope.function_any2array([true]) +    result.should(eq([true])) +  end + +  it "should convert one object to [object]" do +    result = scope.function_any2array(['one']) +    result.should(eq(['one'])) +  end + +  it "should convert multiple objects to [objects]" do +    result = scope.function_any2array(['one', 'two']) +    result.should(eq(['one', 'two'])) +  end + +  it "should return empty array it was called with" do +    result = scope.function_any2array([[]]) +    result.should(eq([])) +  end + +  it "should return one-member array it was called with" do +    result = scope.function_any2array([['string']]) +    result.should(eq(['string'])) +  end + +  it "should return multi-member array it was called with" do +    result = scope.function_any2array([['one', 'two']]) +    result.should(eq(['one', 'two'])) +  end + +  it "should return members of a hash it was called with" do +    result = scope.function_any2array([{ 'key' => 'value' }]) +    result.should(eq(['key', 'value'])) +  end + +  it "should return an empty array if it was called with an empty hash" do +    result = scope.function_any2array([{ }]) +    result.should(eq([])) +  end +end diff --git a/spec/unit/puppet/parser/functions/concat_spec.rb b/spec/unit/puppet/parser/functions/concat_spec.rb new file mode 100644 index 0000000..123188b --- /dev/null +++ b/spec/unit/puppet/parser/functions/concat_spec.rb @@ -0,0 +1,15 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the concat function" do +  let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + +  it "should raise a ParseError if there is less than 1 arguments" do +    lambda { scope.function_concat([]) }.should( raise_error(Puppet::ParseError)) +  end + +  it "should be able to concat an array" do +    result = scope.function_concat([['1','2','3'],['4','5','6']]) +    result.should(eq(['1','2','3','4','5','6'])) +  end +end diff --git a/spec/unit/puppet/parser/functions/floor_spec.rb b/spec/unit/puppet/parser/functions/floor_spec.rb new file mode 100644 index 0000000..dbc8c77 --- /dev/null +++ b/spec/unit/puppet/parser/functions/floor_spec.rb @@ -0,0 +1,39 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the floor function" do +  let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + +  it "should exist" do +    Puppet::Parser::Functions.function("floor").should == "function_floor" +  end + +  it "should raise a ParseError if there is less than 1 argument" do +    lambda { scope.function_floor([]) }.should( raise_error(Puppet::ParseError, /Wrong number of arguments/)) +  end + +  it "should should raise a ParseError if input isn't numeric (eg. String)" do +    lambda { scope.function_floor(["foo"]) }.should( raise_error(Puppet::ParseError, /Wrong argument type/)) +  end + +  it "should should raise a ParseError if input isn't numeric (eg. Boolean)" do +    lambda { scope.function_floor([true]) }.should( raise_error(Puppet::ParseError, /Wrong argument type/)) +  end + +  it "should return an integer when a numeric type is passed" do +    result = scope.function_floor([12.4]) +    result.is_a?(Integer).should(eq(true)) +  end + +  it "should return the input when an integer is passed" do +    result = scope.function_floor([7]) +    result.should(eq(7)) +  end + +  it "should return the largest integer less than or equal to the input" do +    result = scope.function_floor([3.8]) +    result.should(eq(3)) +  end +end + diff --git a/spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb b/spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb index 4eb799d..2577723 100644 --- a/spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb +++ b/spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb @@ -19,7 +19,7 @@ describe "the fqdn_rotate function" do    end    it "should rotate a string to give the same results for one host" do -    scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1").twice  +    scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1").twice      scope.function_fqdn_rotate(["abcdefg"]).should eql(scope.function_fqdn_rotate(["abcdefg"]))    end diff --git a/spec/unit/puppet/parser/functions/get_module_path_spec.rb b/spec/unit/puppet/parser/functions/get_module_path_spec.rb index e761706..486bef6 100644 --- a/spec/unit/puppet/parser/functions/get_module_path_spec.rb +++ b/spec/unit/puppet/parser/functions/get_module_path_spec.rb @@ -15,11 +15,11 @@ describe Puppet::Parser::Functions.function(:get_module_path) do    end    it 'should only allow one argument' do -    expect { scope.function_get_module_path([]) }.should raise_error(Puppet::ParseError, /Wrong number of arguments, expects one/) -    expect { scope.function_get_module_path(['1','2','3']) }.should raise_error(Puppet::ParseError, /Wrong number of arguments, expects one/) +    expect { scope.function_get_module_path([]) }.to raise_error(Puppet::ParseError, /Wrong number of arguments, expects one/) +    expect { scope.function_get_module_path(['1','2','3']) }.to raise_error(Puppet::ParseError, /Wrong number of arguments, expects one/)    end    it 'should raise an exception when the module cannot be found' do -    expect { scope.function_get_module_path(['foo']) }.should raise_error(Puppet::ParseError, /Could not find module/) +    expect { scope.function_get_module_path(['foo']) }.to raise_error(Puppet::ParseError, /Could not find module/)    end    describe 'when locating a module' do      let(:modulepath) { "/tmp/does_not_exist" } diff --git a/spec/unit/puppet/parser/functions/is_function_available.rb b/spec/unit/puppet/parser/functions/is_function_available.rb new file mode 100644 index 0000000..bd40c51 --- /dev/null +++ b/spec/unit/puppet/parser/functions/is_function_available.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe "the is_function_available function" do +  before :all do +    Puppet::Parser::Functions.autoloader.loadall +  end + +  before :each do +    @scope = Puppet::Parser::Scope.new +  end + +  it "should exist" do +    Puppet::Parser::Functions.function("is_function_available").should == "function_is_function_available" +  end + +  it "should raise a ParseError if there is less than 1 arguments" do +    lambda { @scope.function_is_function_available([]) }.should( raise_error(Puppet::ParseError)) +  end + +  it "should return false if a nonexistent function is passed" do +    result = @scope.function_is_function_available(['jeff_mccunes_left_sock']) +    result.should(eq(false)) +  end + +  it "should return true if an available function is passed" do +    result = @scope.function_is_function_available(['require']) +    result.should(eq(true)) +  end + +end diff --git a/spec/unit/puppet/parser/functions/merge_spec.rb b/spec/unit/puppet/parser/functions/merge_spec.rb index db7d837..04169e7 100644 --- a/spec/unit/puppet/parser/functions/merge_spec.rb +++ b/spec/unit/puppet/parser/functions/merge_spec.rb @@ -25,7 +25,7 @@ describe Puppet::Parser::Functions.function(:merge) do    describe 'when calling merge on the scope instance' do      it 'should require all parameters are hashes' do -      expect { new_hash = scope.function_merge([{}, '2'])}.should raise_error(Puppet::ParseError, /unexpected argument type String/) +      expect { new_hash = scope.function_merge([{}, '2'])}.to raise_error(Puppet::ParseError, /unexpected argument type String/)      end      it 'should be able to merge two hashes' do diff --git a/spec/unit/puppet/parser/functions/num2bool_spec.rb b/spec/unit/puppet/parser/functions/num2bool_spec.rb index 640c689..b56196d 100644 --- a/spec/unit/puppet/parser/functions/num2bool_spec.rb +++ b/spec/unit/puppet/parser/functions/num2bool_spec.rb @@ -8,17 +8,60 @@ describe "the num2bool function" do      Puppet::Parser::Functions.function("num2bool").should == "function_num2bool"    end -  it "should raise a ParseError if there is less than 1 arguments" do +  it "should raise a ParseError if there are no arguments" do      lambda { scope.function_num2bool([]) }.should( raise_error(Puppet::ParseError))    end -  it "should return true if 1" do +  it "should raise a ParseError if there are more than 1 arguments" do +    lambda { scope.function_num2bool(["foo","bar"]) }.should( raise_error(Puppet::ParseError)) +  end + +  it "should raise a ParseError if passed something non-numeric" do +    lambda { scope.function_num2bool(["xyzzy"]) }.should( raise_error(Puppet::ParseError)) +  end + +  it "should return true if passed string 1" do      result = scope.function_num2bool(["1"])      result.should(be_true)    end -  it "should return false if 0" do +  it "should return true if passed string 1.5" do +    result = scope.function_num2bool(["1.5"]) +    result.should(be_true) +  end + +  it "should return true if passed number 1" do +    result = scope.function_num2bool([1]) +    result.should(be_true) +  end + +  it "should return false if passed string 0" do      result = scope.function_num2bool(["0"])      result.should(be_false)    end + +  it "should return false if passed number 0" do +    result = scope.function_num2bool([0]) +    result.should(be_false) +  end + +  it "should return false if passed string -1" do +    result = scope.function_num2bool(["-1"]) +    result.should(be_false) +  end + +  it "should return false if passed string -1.5" do +    result = scope.function_num2bool(["-1.5"]) +    result.should(be_false) +  end + +  it "should return false if passed number -1" do +    result = scope.function_num2bool([-1]) +    result.should(be_false) +  end + +  it "should return false if passed float -1.5" do +    result = scope.function_num2bool([-1.5]) +    result.should(be_false) +  end  end diff --git a/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb b/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb index a692c31..df8fb8e 100644 --- a/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb +++ b/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb @@ -9,11 +9,11 @@ describe "the str2saltedsha512 function" do    end    it "should raise a ParseError if there is less than 1 argument" do -    expect { scope.function_str2saltedsha512([]) }.should( raise_error(Puppet::ParseError) ) +    expect { scope.function_str2saltedsha512([]) }.to( raise_error(Puppet::ParseError) )    end    it "should raise a ParseError if there is more than 1 argument" do -    expect { scope.function_str2saltedsha512(['foo', 'bar', 'baz']) }.should( raise_error(Puppet::ParseError) ) +    expect { scope.function_str2saltedsha512(['foo', 'bar', 'baz']) }.to( raise_error(Puppet::ParseError) )    end    it "should return a salted-sha512 password hash 136 characters in length" do @@ -22,7 +22,7 @@ describe "the str2saltedsha512 function" do    end    it "should raise an error if you pass a non-string password" do -    expect { scope.function_str2saltedsha512([1234]) }.should( raise_error(Puppet::ParseError) ) +    expect { scope.function_str2saltedsha512([1234]) }.to( raise_error(Puppet::ParseError) )    end    it "should generate a valid password" do diff --git a/spec/unit/puppet/parser/functions/suffix_spec.rb b/spec/unit/puppet/parser/functions/suffix_spec.rb new file mode 100644 index 0000000..c28f719 --- /dev/null +++ b/spec/unit/puppet/parser/functions/suffix_spec.rb @@ -0,0 +1,19 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the suffix function" do +  let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + +  it "should exist" do +    Puppet::Parser::Functions.function("suffix").should == "function_suffix" +  end + +  it "should raise a ParseError if there is less than 1 arguments" do +    lambda { scope.function_suffix([]) }.should( raise_error(Puppet::ParseError)) +  end + +  it "should return a suffixed array" do +    result = scope.function_suffix([['a','b','c'], 'p']) +    result.should(eq(['ap','bp','cp'])) +  end +end diff --git a/spec/unit/puppet/parser/functions/validate_array_spec.rb b/spec/unit/puppet/parser/functions/validate_array_spec.rb index 8eee72a..4b31cfd 100644 --- a/spec/unit/puppet/parser/functions/validate_array_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_array_spec.rb @@ -9,12 +9,12 @@ describe Puppet::Parser::Functions.function(:validate_array) do      %w{ true false }.each do |the_string|        it "should not compile when #{the_string} is a string" do          Puppet[:code] = "validate_array('#{the_string}')" -        expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not an Array/) +        expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not an Array/)        end        it "should not compile when #{the_string} is a bare word" do          Puppet[:code] = "validate_array(#{the_string})" -        expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not an Array/) +        expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not an Array/)        end      end @@ -32,7 +32,7 @@ describe Puppet::Parser::Functions.function(:validate_array) do          $foo = undef          validate_array($foo)        ENDofPUPPETcode -      expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not an Array/) +      expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not an Array/)      end    end  end diff --git a/spec/unit/puppet/parser/functions/validate_augeas_spec.rb b/spec/unit/puppet/parser/functions/validate_augeas_spec.rb new file mode 100644 index 0000000..ab5c140 --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_augeas_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_augeas), :if => Puppet.features.augeas? do +  let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + +  # The subject of these examplres is the method itself. +  subject do +    # This makes sure the function is loaded within each test +    function_name = Puppet::Parser::Functions.function(:validate_augeas) +    scope.method(function_name) +  end + +  context 'Using Puppet::Parser::Scope.new' do + +    describe 'Garbage inputs' do +      inputs = [ +        [ nil ], +        [ [ nil ] ], +        [ { 'foo' => 'bar' } ], +        [ { } ], +        [ '' ], +        [ "one", "one", "MSG to User", "4th arg" ], +      ] + +      inputs.each do |input| +        it "validate_augeas(#{input.inspect}) should fail" do +          expect { subject.call [input] }.to raise_error Puppet::ParseError +        end +      end +    end + +    describe 'Valid inputs' do +      inputs = [ +        [ "root:x:0:0:root:/root:/bin/bash\n", 'Passwd.lns' ], +        [ "proc /proc   proc    nodev,noexec,nosuid     0       0\n", 'Fstab.lns'], +      ] + +      inputs.each do |input| +        it "validate_augeas(#{input.inspect}) should not fail" do +          expect { subject.call input }.not_to raise_error +        end +      end +    end + +    describe "Valid inputs which should raise an exception without a message" do +      # The intent here is to make sure valid inputs raise exceptions when they +      # don't specify an error message to display.  This is the behvior in +      # 2.2.x and prior. +      inputs = [ +        [ "root:x:0:0:root\n", 'Passwd.lns' ], +        [ "127.0.1.1\n", 'Hosts.lns' ], +      ] + +      inputs.each do |input| +        it "validate_augeas(#{input.inspect}) should fail" do +          expect { subject.call input }.to raise_error /validate_augeas.*?matched less than it should/ +        end +      end +    end + +    describe "Nicer Error Messages" do +      # The intent here is to make sure the function returns the 3rd argument +      # in the exception thrown +      inputs = [ +        [ "root:x:0:0:root\n", 'Passwd.lns', [], 'Failed to validate passwd content' ], +        [ "127.0.1.1\n", 'Hosts.lns', [], 'Wrong hosts content' ], +      ] + +      inputs.each do |input| +        it "validate_augeas(#{input.inspect}) should fail" do +          expect { subject.call input }.to raise_error /#{input[2]}/ +        end +      end +    end + +    describe "Passing simple unit tests" do +      inputs = [ +        [ "root:x:0:0:root:/root:/bin/bash\n", 'Passwd.lns', ['$file/foobar']], +        [ "root:x:0:0:root:/root:/bin/bash\n", 'Passwd.lns', ['$file/root/shell[.="/bin/sh"]', 'foobar']], +      ] + +      inputs.each do |input| +        it "validate_augeas(#{input.inspect}) should fail" do +          expect { subject.call input }.not_to raise_error +        end +      end +    end + +    describe "Failing simple unit tests" do +      inputs = [ +        [ "foobar:x:0:0:root:/root:/bin/bash\n", 'Passwd.lns', ['$file/foobar']], +        [ "root:x:0:0:root:/root:/bin/sh\n", 'Passwd.lns', ['$file/root/shell[.="/bin/sh"]', 'foobar']], +      ] + +      inputs.each do |input| +        it "validate_augeas(#{input.inspect}) should fail" do +          expect { subject.call input }.to raise_error /testing path/ +        end +      end +    end +  end +end diff --git a/spec/unit/puppet/parser/functions/validate_bool_spec.rb b/spec/unit/puppet/parser/functions/validate_bool_spec.rb index 31ab8fb..261fb23 100644 --- a/spec/unit/puppet/parser/functions/validate_bool_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_bool_spec.rb @@ -10,7 +10,7 @@ describe Puppet::Parser::Functions.function(:validate_bool) do        it "should not compile when #{the_string} is a string" do          Puppet[:code] = "validate_bool('#{the_string}')" -        expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a boolean/) +        expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a boolean/)        end        it "should compile when #{the_string} is a bare word" do @@ -22,12 +22,12 @@ describe Puppet::Parser::Functions.function(:validate_bool) do      it "should not compile when an arbitrary string is passed" do        Puppet[:code] = 'validate_bool("jeff and dan are awesome")' -      expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a boolean/) +      expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a boolean/)      end      it "should not compile when no arguments are passed" do        Puppet[:code] = 'validate_bool()' -      expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) +      expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /wrong number of arguments/)      end      it "should compile when multiple boolean arguments are passed" do @@ -45,7 +45,7 @@ describe Puppet::Parser::Functions.function(:validate_bool) do          $bar = false          validate_bool($foo, $bar, true, false, 'jeff')        ENDofPUPPETcode -      expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a boolean/) +      expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a boolean/)      end    end  end diff --git a/spec/unit/puppet/parser/functions/validate_cmd_spec.rb b/spec/unit/puppet/parser/functions/validate_cmd_spec.rb new file mode 100644 index 0000000..69ea7f4 --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_cmd_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_cmd) do +  let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + +  # The subject of these examplres is the method itself. +  subject do +    # This makes sure the function is loaded within each test +    function_name = Puppet::Parser::Functions.function(:validate_cmd) +    scope.method(function_name) +  end + +  context 'Using Puppet::Parser::Scope.new' do + +    describe 'Garbage inputs' do +      inputs = [ +        [ nil ], +        [ [ nil ] ], +        [ { 'foo' => 'bar' } ], +        [ { } ], +        [ '' ], +        [ "one", "one", "MSG to User", "4th arg" ], +      ] + +      inputs.each do |input| +        it "validate_cmd(#{input.inspect}) should fail" do +          expect { subject.call [input] }.to raise_error Puppet::ParseError +        end +      end +    end + +    describe 'Valid inputs' do +      inputs = [ +        [ '/full/path/to/something', '/bin/echo' ], +        [ '/full/path/to/something', '/bin/cat' ], +      ] + +      inputs.each do |input| +        it "validate_cmd(#{input.inspect}) should not fail" do +          expect { subject.call input }.not_to raise_error +        end +      end +    end + +    describe "Valid inputs which should raise an exception without a message" do +      # The intent here is to make sure valid inputs raise exceptions when they +      # don't specify an error message to display.  This is the behvior in +      # 2.2.x and prior. +      inputs = [ +        [ "hello", "/bin/false" ], +      ] + +      inputs.each do |input| +        it "validate_cmd(#{input.inspect}) should fail" do +          expect { subject.call input }.to raise_error /validate_cmd.*?failed to validate content with command/ +        end +      end +    end + +    describe "Nicer Error Messages" do +      # The intent here is to make sure the function returns the 3rd argument +      # in the exception thrown +      inputs = [ +        [ "hello", [ "bye", "later", "adios" ], "MSG to User" ], +        [ "greetings", "salutations", "Error, greetings does not match salutations" ], +      ] + +      inputs.each do |input| +        it "validate_cmd(#{input.inspect}) should fail" do +          expect { subject.call input }.to raise_error /#{input[2]}/ +        end +      end +    end + +    describe "Test output message" do +      it "validate_cmd('whatever', 'kthnksbye') should fail" do +          expect { subject.call ['whatever', 'kthnksbye'] }.to raise_error /kthnksbye.* returned 1/ +      end +    end +  end +end diff --git a/spec/unit/puppet/parser/functions/validate_hash_spec.rb b/spec/unit/puppet/parser/functions/validate_hash_spec.rb index f63db1d..a0c35c2 100644 --- a/spec/unit/puppet/parser/functions/validate_hash_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_hash_spec.rb @@ -11,12 +11,12 @@ describe Puppet::Parser::Functions.function(:validate_hash) do        it "should not compile when #{the_string} is a string" do          Puppet[:code] = "validate_hash('#{the_string}')" -        expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a Hash/) +        expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a Hash/)        end        it "should not compile when #{the_string} is a bare word" do          Puppet[:code] = "validate_hash(#{the_string})" -        expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a Hash/) +        expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a Hash/)        end      end @@ -35,10 +35,9 @@ describe Puppet::Parser::Functions.function(:validate_hash) do          $foo = undef          validate_hash($foo)        ENDofPUPPETcode -      expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a Hash/) +      expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a Hash/)      end    end  end - diff --git a/spec/unit/puppet/parser/functions/validate_string_spec.rb b/spec/unit/puppet/parser/functions/validate_string_spec.rb index f40bf2a..3b4fb3e 100644 --- a/spec/unit/puppet/parser/functions/validate_string_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_string_spec.rb @@ -29,7 +29,7 @@ describe Puppet::Parser::Functions.function(:validate_string) do        it "should not compile when #{the_string} is a bare word" do          Puppet[:code] = "validate_string(#{the_string})" -        expect { scope.compiler.compile }.should raise_error(Puppet::ParseError, /is not a string/) +        expect { scope.compiler.compile }.to raise_error(Puppet::ParseError, /is not a string/)        end      end @@ -58,4 +58,3 @@ describe Puppet::Parser::Functions.function(:validate_string) do      end    end  end - diff --git a/spec/unit/puppet/type/file_line_spec.rb b/spec/unit/puppet/type/file_line_spec.rb index 0cd8a26..edc64bd 100644 --- a/spec/unit/puppet/type/file_line_spec.rb +++ b/spec/unit/puppet/type/file_line_spec.rb @@ -37,13 +37,13 @@ describe Puppet::Type.type(:file_line) do      file_line[:path].should == '/tmp/path'    end    it 'should not accept unqualified path' do -    expect { file_line[:path] = 'file' }.should raise_error(Puppet::Error, /File paths must be fully qualified/) +    expect { file_line[:path] = 'file' }.to raise_error(Puppet::Error, /File paths must be fully qualified/)    end    it 'should require that a line is specified' do -    expect { Puppet::Type.type(:file_line).new(:name => 'foo', :path => '/tmp/file') }.should raise_error(Puppet::Error, /Both line and path are required attributes/) +    expect { Puppet::Type.type(:file_line).new(:name => 'foo', :path => '/tmp/file') }.to raise_error(Puppet::Error, /Both line and path are required attributes/)    end    it 'should require that a file is specified' do -    expect { Puppet::Type.type(:file_line).new(:name => 'foo', :line => 'path') }.should raise_error(Puppet::Error, /Both line and path are required attributes/) +    expect { Puppet::Type.type(:file_line).new(:name => 'foo', :line => 'path') }.to raise_error(Puppet::Error, /Both line and path are required attributes/)    end    it 'should default to ensure => present' do      file_line[:ensure].should eq :present diff --git a/tests/has_interface_with.pp b/tests/has_interface_with.pp index 8b9e51e..e1f1353 100644 --- a/tests/has_interface_with.pp +++ b/tests/has_interface_with.pp @@ -1,10 +1,10 @@  include stdlib -info("has_interface_with('lo'):", has_interface_with('lo')) -info("has_interface_with('loX'):", has_interface_with('loX')) -info("has_interface_with('ipaddress', '127.0.0.1'):", has_interface_with('ipaddress', '127.0.0.1')) -info("has_interface_with('ipaddress', '127.0.0.100'):", has_interface_with('ipaddress', '127.0.0.100')) -info("has_interface_with('network', '127.0.0.0'):", has_interface_with('network', '127.0.0.0')) -info("has_interface_with('network', '128.0.0.0'):", has_interface_with('network', '128.0.0.0')) -info("has_interface_with('netmask', '255.0.0.0'):", has_interface_with('netmask', '255.0.0.0')) -info("has_interface_with('netmask', '256.0.0.0'):", has_interface_with('netmask', '256.0.0.0')) +info('has_interface_with(\'lo\'):', has_interface_with('lo')) +info('has_interface_with(\'loX\'):', has_interface_with('loX')) +info('has_interface_with(\'ipaddress\', \'127.0.0.1\'):', has_interface_with('ipaddress', '127.0.0.1')) +info('has_interface_with(\'ipaddress\', \'127.0.0.100\'):', has_interface_with('ipaddress', '127.0.0.100')) +info('has_interface_with(\'network\', \'127.0.0.0\'):', has_interface_with('network', '127.0.0.0')) +info('has_interface_with(\'network\', \'128.0.0.0\'):', has_interface_with('network', '128.0.0.0')) +info('has_interface_with(\'netmask\', \'255.0.0.0\'):', has_interface_with('netmask', '255.0.0.0')) +info('has_interface_with(\'netmask\', \'256.0.0.0\'):', has_interface_with('netmask', '256.0.0.0')) diff --git a/tests/has_ip_address.pp b/tests/has_ip_address.pp index e770a11..8429a88 100644 --- a/tests/has_ip_address.pp +++ b/tests/has_ip_address.pp @@ -1,3 +1,3 @@  include stdlib -info("has_ip_address('192.168.1.256'):", has_ip_address('192.168.1.256')) -info("has_ip_address('127.0.0.1'):", has_ip_address('127.0.0.1')) +info('has_ip_address(\'192.168.1.256\'):', has_ip_address('192.168.1.256')) +info('has_ip_address(\'127.0.0.1\'):', has_ip_address('127.0.0.1')) diff --git a/tests/has_ip_network.pp b/tests/has_ip_network.pp index 036d649..a15d8c0 100644 --- a/tests/has_ip_network.pp +++ b/tests/has_ip_network.pp @@ -1,4 +1,4 @@  include stdlib -info("has_ip_network('127.0.0.0'):", has_ip_network('127.0.0.0')) -info("has_ip_network('128.0.0.0'):", has_ip_network('128.0.0.0')) +info('has_ip_network(\'127.0.0.0\'):', has_ip_network('127.0.0.0')) +info('has_ip_network(\'128.0.0.0\'):', has_ip_network('128.0.0.0'))  | 
