diff options
185 files changed, 5612 insertions, 1434 deletions
diff --git a/.gemspec b/.gemspec new file mode 100644 index 0000000..e274950 --- /dev/null +++ b/.gemspec @@ -0,0 +1,40 @@ +# +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = "puppetmodule-stdlib" + + s.version = "4.0.2" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Puppet Labs"] + s.date = "2013-04-12" + s.description = [ 'This Gem format of the stdlib module is intended to make', + 'it easier for _module authors_ to resolve dependencies', + 'using a Gemfile when running automated testing jobs like', + 'Travis or Jenkins. The recommended best practice for', + 'installation by end users is to use the `puppet module', + 'install` command to install stdlib from the [Puppet', + 'Forge](http://forge.puppetlabs.com/puppetlabs/stdlib).' ].join(' ') + s.email = "puppet-dev@puppetlabs.com" + s.executables = [] + s.files = [ 'CHANGELOG', 'CONTRIBUTING.md', 'Gemfile', 'LICENSE', 'Modulefile', + 'README.markdown', 'README_DEVELOPER.markdown', 'RELEASE_PROCESS.markdown', + 'Rakefile', 'spec/spec.opts' ] + s.files += Dir['lib/**/*.rb'] + Dir['manifests/**/*.pp'] + Dir['tests/**/*.pp'] + Dir['spec/**/*.rb'] + s.homepage = "http://forge.puppetlabs.com/puppetlabs/stdlib" + s.rdoc_options = ["--title", "Puppet Standard Library Development Gem", "--main", "README.markdown", "--line-numbers"] + s.require_paths = ["lib"] + s.rubyforge_project = "puppetmodule-stdlib" + s.rubygems_version = "1.8.24" + s.summary = "This gem provides a way to make the standard library available for other module spec testing tasks." + + if s.respond_to? :specification_version then + s.specification_version = 3 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + else + end + else + end +end @@ -2,3 +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> @@ -0,0 +1,4 @@ +--color +--format +progress +--backtrace diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1bb1889 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: ruby +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_GEM_VERSION=">= 3.0.0" +matrix: + allow_failures: + - rvm: 2.0.0 + - rvm: ruby-head + include: + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 2.7" +notifications: + email: false + webhooks: + urls: + - https://puppet-dev-community.herokuapp.com/event/travis-ci/ + on_success: always + on_failure: always + on_start: yes @@ -1,3 +1,228 @@ +2013-05-06 - Jeff McCune <jeff@puppetlabs.com> - 4.1.0 + * (#20582) Restore facter_dot_d to stdlib for PE users (3b887c8) + * (maint) Update Gemfile with GEM_FACTER_VERSION (f44d535) + +2013-05-06 - Alex Cline <acline@us.ibm.com> - 4.1.0 + * Terser method of string to array conversion courtesy of ethooz. (d38bce0) + +2013-05-06 - Alex Cline <acline@us.ibm.com> 4.1.0 + * Refactor ensure_resource expectations (b33cc24) + +2013-05-06 - Alex Cline <acline@us.ibm.com> 4.1.0 + * Changed str-to-array conversion and removed abbreviation. (de253db) + +2013-05-03 - Alex Cline <acline@us.ibm.com> 4.1.0 + * (#20548) Allow an array of resource titles to be passed into the ensure_resource function (e08734a) + +2013-05-02 - Raphaël Pinson <raphael.pinson@camptocamp.com> - 4.1.0 + * Add a dirname function (2ba9e47) + +2013-04-29 - Mark Smith-Guerrero <msmithgu@gmail.com> - 4.1.0 + * (maint) Fix a small typo in hash() description (928036a) + +2013-04-12 - Jeff McCune <jeff@puppetlabs.com> - 4.0.2 + * Update user information in gemspec to make the intent of the Gem clear. + +2013-04-11 - Jeff McCune <jeff@puppetlabs.com> - 4.0.1 + * Fix README function documentation (ab3e30c) + +2013-04-11 - Jeff McCune <jeff@puppetlabs.com> - 4.0.0 + * stdlib 4.0 drops support with Puppet 2.7 + * stdlib 4.0 preserves support with Puppet 3 + +2013-04-11 - Jeff McCune <jeff@puppetlabs.com> - 4.0.0 + * Add ability to use puppet from git via bundler (9c5805f) + +2013-04-10 - Jeff McCune <jeff@puppetlabs.com> - 4.0.0 + * (maint) Make stdlib usable as a Ruby GEM (e81a45e) + +2013-04-10 - Erik Dalén <dalen@spotify.com> - 4.0.0 + * Add a count function (f28550e) + +2013-03-31 - Amos Shapira <ashapira@atlassian.com> - 4.0.0 + * (#19998) Implement any2array (7a2fb80) + +2013-03-29 - Steve Huff <shuff@vecna.org> - 4.0.0 + * (19864) num2bool match fix (8d217f0) + +2013-03-20 - Erik Dalén <dalen@spotify.com> - 4.0.0 + * Allow comparisons of Numeric and number as String (ff5dd5d) + +2013-03-26 - Richard Soderberg <rsoderberg@mozilla.com> - 4.0.0 + * add suffix function to accompany the prefix function (88a93ac) + +2013-03-19 - Kristof Willaert <kristof.willaert@gmail.com> - 4.0.0 + * Add floor function implementation and unit tests (0527341) + +2012-04-03 - Eric Shamow <eric@puppetlabs.com> - 4.0.0 + * (#13610) Add is_function_available to stdlib (961dcab) + +2012-12-17 - Justin Lambert <jlambert@eml.cc> - 4.0.0 + * str2bool should return a boolean if called with a boolean (5d5a4d4) + +2012-10-23 - Uwe Stuehler <ustuehler@team.mobile.de> - 4.0.0 + * Fix number of arguments check in flatten() (e80207b) + +2013-03-11 - Jeff McCune <jeff@puppetlabs.com> - 4.0.0 + * Add contributing document (96e19d0) + +2013-03-04 - Raphaël Pinson <raphael.pinson@camptocamp.com> - 4.0.0 + * Add missing documentation for validate_augeas and validate_cmd to README.markdown (a1510a1) + +2013-02-14 - Joshua Hoblitt <jhoblitt@cpan.org> - 4.0.0 + * (#19272) Add has_element() function (95cf3fe) + +2013-02-07 - Raphaël Pinson <raphael.pinson@camptocamp.com> - 4.0.0 + * validate_cmd(): Use Puppet::Util::Execution.execute when available (69248df) + +2012-12-06 - Raphaël Pinson <raphink@gmail.com> - 4.0.0 + * Add validate_augeas function (3a97c23) + +2012-12-06 - Raphaël Pinson <raphink@gmail.com> - 4.0.0 + * Add validate_cmd function (6902cc5) + +2013-01-14 - David Schmitt <david@dasz.at> - 4.0.0 + * Add geppetto project definition (b3fc0a3) + +2013-01-02 - Jaka Hudoklin <jakahudoklin@gmail.com> - 4.0.0 + * Add getparam function to get defined resource parameters (20e0e07) + +2013-01-05 - Jeff McCune <jeff@puppetlabs.com> - 4.0.0 + * (maint) Add Travis CI Support (d082046) + +2012-12-04 - Jeff McCune <jeff@puppetlabs.com> - 4.0.0 + * Clarify that stdlib 3 supports Puppet 3 (3a6085f) + +2012-11-30 - Erik Dalén <dalen@spotify.com> - 4.0.0 + * maint: style guideline fixes (7742e5f) + +2012-11-09 - James Fryman <james@frymanet.com> - 4.0.0 + * puppet-lint cleanup (88acc52) + +2012-11-06 - Joe Julian <me@joejulian.name> - 4.0.0 + * Add function, uriescape, to URI.escape strings. Redmine #17459 (fd52b8d) + +2012-09-18 - Chad Metcalf <chad@wibidata.com> - 3.2.0 + * Add an ensure_packages function. (8a8c09e) + +2012-11-23 - Erik Dalén <dalen@spotify.com> - 3.2.0 + * (#17797) min() and max() functions (9954133) + +2012-05-23 - Peter Meier <peter.meier@immerda.ch> - 3.2.0 + * (#14670) autorequire a file_line resource's path (dfcee63) + +2012-11-19 - Joshua Harlan Lifton <lifton@puppetlabs.com> - 3.2.0 + * Add join_keys_to_values function (ee0f2b3) + +2012-11-17 - Joshua Harlan Lifton <lifton@puppetlabs.com> - 3.2.0 + * Extend delete function for strings and hashes (7322e4d) + +2012-08-03 - Gary Larizza <gary@puppetlabs.com> - 3.2.0 + * Add the pick() function (ba6dd13) + +2012-03-20 - Wil Cooley <wcooley@pdx.edu> - 3.2.0 + * (#13974) Add predicate functions for interface facts (f819417) + +2012-11-06 - Joe Julian <me@joejulian.name> - 3.2.0 + * Add function, uriescape, to URI.escape strings. Redmine #17459 (70f4a0e) + +2012-10-25 - Jeff McCune <jeff@puppetlabs.com> - 3.1.1 + * (maint) Fix spec failures resulting from Facter API changes (97f836f) + +2012-10-23 - Matthaus Owens <matthaus@puppetlabs.com> - 3.1.0 + * Add PE facts to stdlib (cdf3b05) + +2012-08-16 - Jeff McCune <jeff@puppetlabs.com> - 3.0.1 + * Fix accidental removal of facts_dot_d.rb in 3.0.0 release + +2012-08-16 - Jeff McCune <jeff@puppetlabs.com> - 3.0.0 + * stdlib 3.0 drops support with Puppet 2.6 + * stdlib 3.0 preserves support with Puppet 2.7 + +2012-08-07 - Dan Bode <dan@puppetlabs.com> - 3.0.0 + * Add function ensure_resource and defined_with_params (ba789de) + +2012-07-10 - Hailee Kenney <hailee@puppetlabs.com> - 3.0.0 + * (#2157) Remove facter_dot_d for compatibility with external facts (f92574f) + +2012-04-10 - Chris Price <chris@puppetlabs.com> - 3.0.0 + * (#13693) moving logic from local spec_helper to puppetlabs_spec_helper (85f96df) + +2012-10-25 - Jeff McCune <jeff@puppetlabs.com> - 2.5.1 + * (maint) Fix spec failures resulting from Facter API changes (97f836f) + +2012-10-23 - Matthaus Owens <matthaus@puppetlabs.com> - 2.5.0 + * Add PE facts to stdlib (cdf3b05) + +2012-08-15 - Dan Bode <dan@puppetlabs.com> - 2.5.0 + * Explicitly load functions used by ensure_resource (9fc3063) + +2012-08-13 - Dan Bode <dan@puppetlabs.com> - 2.5.0 + * Add better docs about duplicate resource failures (97d327a) + +2012-08-13 - Dan Bode <dan@puppetlabs.com> - 2.5.0 + * Handle undef for parameter argument (4f8b133) + +2012-08-07 - Dan Bode <dan@puppetlabs.com> - 2.5.0 + * Add function ensure_resource and defined_with_params (a0cb8cd) + +2012-08-20 - Jeff McCune <jeff@puppetlabs.com> - 2.5.0 + * Disable tests that fail on 2.6.x due to #15912 (c81496e) + +2012-08-20 - Jeff McCune <jeff@puppetlabs.com> - 2.5.0 + * (Maint) Fix mis-use of rvalue functions as statements (4492913) + +2012-08-20 - Jeff McCune <jeff@puppetlabs.com> - 2.5.0 + * Add .rspec file to repo root (88789e8) + +2012-06-07 - Chris Price <chris@puppetlabs.com> - 2.4.0 + * Add support for a 'match' parameter to file_line (a06c0d8) + +2012-08-07 - Erik Dalén <dalen@spotify.com> - 2.4.0 + * (#15872) Add to_bytes function (247b69c) + +2012-07-19 - Jeff McCune <jeff@puppetlabs.com> - 2.4.0 + * (Maint) use PuppetlabsSpec::PuppetInternals.scope (master) (deafe88) + +2012-07-10 - Hailee Kenney <hailee@puppetlabs.com> - 2.4.0 + * (#2157) Make facts_dot_d compatible with external facts (5fb0ddc) + +2012-03-16 - Steve Traylen <steve.traylen@cern.ch> - 2.4.0 + * (#13205) Rotate array/string randomley based on fqdn, fqdn_rotate() (fef247b) + +2012-05-22 - Peter Meier <peter.meier@immerda.ch> - 2.3.3 + * fix regression in #11017 properly (f0a62c7) + +2012-05-10 - Jeff McCune <jeff@puppetlabs.com> - 2.3.3 + * Fix spec tests using the new spec_helper (7d34333) + +2012-05-10 - Puppet Labs <support@puppetlabs.com> - 2.3.2 + * Make file_line default to ensure => present (1373e70) + * Memoize file_line spec instance variables (20aacc5) + * Fix spec tests using the new spec_helper (1ebfa5d) + * (#13595) initialize_everything_for_tests couples modules Puppet ver (3222f35) + * (#13439) Fix MRI 1.9 issue with spec_helper (15c5fd1) + * (#13439) Fix test failures with Puppet 2.6.x (665610b) + * (#13439) refactor spec helper for compatibility with both puppet 2.7 and master (82194ca) + * (#13494) Specify the behavior of zero padded strings (61891bb) + +2012-03-29 Puppet Labs <support@puppetlabs.com> - 2.1.3 +* (#11607) Add Rakefile to enable spec testing +* (#12377) Avoid infinite loop when retrying require json + +2012-03-13 Puppet Labs <support@puppetlabs.com> - 2.3.1 +* (#13091) Fix LoadError bug with puppet apply and puppet_vardir fact + +2012-03-12 Puppet Labs <support@puppetlabs.com> - 2.3.0 +* Add a large number of new Puppet functions +* Backwards compatibility preserved with 2.2.x + +2011-12-30 Puppet Labs <support@puppetlabs.com> - 2.2.1 +* Documentation only release for the Forge + +2011-12-30 Puppet Labs <support@puppetlabs.com> - 2.1.2 +* Documentation only release for PE 2.0.x + 2011-11-08 Puppet Labs <support@puppetlabs.com> - 2.2.0 * #10285 - Refactor json to use pson instead. * Maint - Add watchr autotest script 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 @@ -0,0 +1,42 @@ +source "https://rubygems.org" + +def location_for(place, fake_version = nil) + mdata = /^(git:[^#]*)#(.*)/.match(place) + if mdata + [fake_version, { :git => mdata[1], :branch => mdata[2], :require => false }].compact + elsif place =~ /^file:\/\/(.*)/ + ['>= 0', { :path => File.expand_path(mdata[1]), :require => false }] + else + [place, { :require => false }] + end +end + +group :development do + gem 'watchr' +end + +group :development, :test do + gem 'rake' + gem 'puppetmodule-stdlib', ">= 1.0.0", :path => File.expand_path("..", __FILE__) + 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 + +facterversion = ENV['GEM_FACTER_VERSION'] +if facterversion + gem 'facter', *location_for(facterversion) +else + gem 'facter', :require => false +end + +ENV['GEM_PUPPET_VERSION'] ||= ENV['PUPPET_GEM_VERSION'] +puppetversion = ENV['GEM_PUPPET_VERSION'] +if puppetversion + gem 'puppet', *location_for(puppetversion) +else + gem 'puppet', :require => false +end + +# vim:ft=ruby @@ -1,6 +1,6 @@ name 'puppetlabs-stdlib' -version '2.2.0' -source 'git://github.com/puppetlabs/puppetlabs-stdlib' +version '4.1.0' +source 'git://github.com/puppetlabs/puppetlabs-stdlib.git' author 'puppetlabs' license 'Apache 2.0' summary 'Puppet Module Standard Library' diff --git a/README.markdown b/README.markdown index 4f06651..0e40f51 100644 --- a/README.markdown +++ b/README.markdown @@ -1,5 +1,7 @@ # Puppet Labs Standard Library # +[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-stdlib.png?branch=master)](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 @@ -14,47 +16,1221 @@ This module is officially curated and provided by Puppet Labs. The modules Puppet Labs writes and distributes will make heavy use of this standard library. +To report or research a bug with any part of this module, please go to +[http://projects.puppetlabs.com/projects/stdlib](http://projects.puppetlabs.com/projects/stdlib) + +# Versions # + +This module follows semver.org (v1.0.0) versioning guidelines. The standard +library module is released as part of [Puppet +Enterprise](http://puppetlabs.com/puppet/puppet-enterprise/) and as a result +older versions of Puppet Enterprise that Puppet Labs still supports will have +bugfix maintenance branches periodically "merged up" into master. The current +list of integration branches are: + + * v2.1.x (v2.1.1 released in PE 1) + * v2.2.x (Never released as part of PE, only to the Forge) + * v2.3.x (Released in PE 2) + * v3.0.x (Never released as part of PE, only to the Forge) + * v4.0.x (Drops support for Puppet 2.7) + * master (mainline development branch) + +The first Puppet Enterprise version including the stdlib module is Puppet +Enterprise 1.2. + # Compatibility # -This module is designed to work with Puppet version 2.6 and later. It may be -forked if Puppet 2.7 specific features are added. There are currently no plans -for a Puppet 0.25 standard library module. +Puppet Versions | < 2.6 | 2.6 | 2.7 | 3.x | +:---------------|:-----:|:---:|:---:|:----: +**stdlib 2.x** | no | **yes** | **yes** | no +**stdlib 3.x** | no | no | **yes** | **yes** +**stdlib 4.x** | no | no | no | **yes** + +The stdlib module does not work with Puppet versions released prior to Puppet +2.6.0. + +## stdlib 2.x ## + +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 and Puppet 3. + +## stdlib 4.x ## + +The 4.0 major release of stdlib drops support for Puppet 2.7. Stdlib 4.x +supports Puppet 3. Notably, ruby 1.8.5 is no longer supported though ruby +1.8.7, 1.9.3, and 2.0.0 are fully supported. # Functions # - Please see `puppet doc -r function` for documentation on each function. The - current list of functions is: +abs +--- +Returns the absolute value of a number, for example -34.56 becomes +34.56. Takes a single integer and float value as an argument. + + +- *Type*: rvalue + +any2array +--------- +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. + + +- *Type*: rvalue + +base64 +-------- +Converts a string to and from base64 encoding. +Requires an action ['encode','decode'] and either a plain or base64 encoded +string + + +- *Type*: rvalue + +bool2num +-------- +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. + + +- *Type*: rvalue + +capitalize +---------- +Capitalizes the first letter of a string or array of strings. +Requires either a single string or an array as an input. + + +- *Type*: rvalue + +chomp +----- +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. + + +- *Type*: rvalue + +chop +---- +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. + + +- *Type*: rvalue + +concat +------ +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'] + + +- *Type*: rvalue + +count +----- +Takes an array as first argument and an optional second argument. +Count the number of elements in array that matches second argument. +If called with only an array it counts the number of elements that are not nil/undef. + + +- *Type*: rvalue + +defined_with_params +------------------- +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, } + } + + +- *Type*: rvalue + +delete +------ +Deletes all instances of a given element from an array, substring from a +string, or key from a hash. + +*Examples:* + + delete(['a','b','c','b'], 'b') + Would return: ['a','c'] + + delete({'a'=>1,'b'=>2,'c'=>3}, 'b') + Would return: {'a'=>1,'c'=>3} + + delete('abracadabra', 'bra') + Would return: 'acada' + + +- *Type*: rvalue + +delete_at +--------- +Deletes a determined indexed value from an array. + +*Examples:* + + delete_at(['a','b','c'], 1) + +Would return: ['a','c'] + + +- *Type*: rvalue + +difference +---------- +This function returns the difference between two arrays. +The returned array is a copy of the original array, removing any items that +also appear in the second array. + +*Examples:* + + difference(["a","b","c"],["b","c","d"]) + +Would return: ["a"] + +dirname +------- +Returns the `dirname` of a path. + +*Examples:* + + dirname('/path/to/a/file.ext') + +Would return: '/path/to/a' + +downcase +-------- +Converts the case of a string or all strings in an array to lower case. + + +- *Type*: rvalue + +empty +----- +Returns true if the variable is empty. + + +- *Type*: rvalue + +ensure_packages +--------------- +Takes a list of packages and only installs them if they don't already exist. + + +- *Type*: statement + +ensure_resource +--------------- +Takes a resource type, title, and a list of attributes that describe a +resource. + + user { 'dan': + ensure => present, + } + +This example only creates the resource if it does not already exist: + + ensure_resource('user, 'dan', {'ensure' => 'present' }) + +If the resource already exists but does not match the specified parameters, +this function will attempt to recreate the resource leading to a duplicate +resource definition error. + +An array of resources can also be passed in and each will be created with +the type and parameters specified if it doesn't already exist. + + ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) + + + +- *Type*: statement + +flatten +------- +This function flattens any deeply nested arrays and returns a single flat array +as a result. + +*Examples:* + + flatten(['a', ['b', ['c']]]) + +Would return: ['a','b','c'] + + +- *Type*: rvalue + +floor +----- +Returns the largest integer less or equal to the argument. +Takes a single numeric value as an argument. + + +- *Type*: rvalue + +fqdn_rotate +----------- +Rotates an array a random number of times based on a nodes fqdn. + + +- *Type*: rvalue + +get_module_path +--------------- +Returns the absolute path of the specified module for the current +environment. + +Example: + $module_path = get_module_path('stdlib') + + +- *Type*: rvalue + +getparam +-------- +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 + + +- *Type*: rvalue + +getvar +------ +Lookup a variable in a remote namespace. + +For example: + + $foo = getvar('site::data::foo') + # Equivalent to $foo = $site::data::foo + +This is useful if the namespace itself is stored in a string: + + $datalocation = 'site::data' + $bar = getvar("${datalocation}::bar") + # Equivalent to $bar = $site::data::bar + + +- *Type*: rvalue + +grep +---- +This function searches through an array and returns any elements that match +the provided regular expression. + +*Examples:* + + grep(['aaa','bbb','ccc','aaaddd'], 'aaa') + +Would return: + + ['aaa','aaaddd'] + + +- *Type*: rvalue + +has_interface_with +------------------ +Returns boolean based on kind and value: +* macaddress +* netmask +* ipaddress +* network + +has_interface_with("macaddress", "x:x:x:x:x:x") +has_interface_with("ipaddress", "127.0.0.1") => true +etc. + +If no "kind" is given, then the presence of the interface is checked: +has_interface_with("lo") => true + + +- *Type*: rvalue + +has_ip_address +-------------- +Returns true if the client has the requested IP address on some interface. + +This function iterates through the 'interfaces' fact and checks the +'ipaddress_IFACE' facts, performing a simple string comparison. + + +- *Type*: rvalue + +has_ip_network +-------------- +Returns true if the client has an IP address within the requested network. + +This function iterates through the 'interfaces' fact and checks the +'network_IFACE' facts, performing a simple string comparision. + + +- *Type*: rvalue + +has_key +------- +Determine if a hash has a certain key value. + +Example: + + $my_hash = {'key_one' => 'value_one'} + if has_key($my_hash, 'key_two') { + notice('we will not reach here') + } + if has_key($my_hash, 'key_one') { + notice('this will be printed') + } + + + +- *Type*: rvalue + +hash +---- +This function converts an array into a hash. + +*Examples:* + + hash(['a',1,'b',2,'c',3]) + +Would return: {'a'=>1,'b'=>2,'c'=>3} + + +- *Type*: rvalue + +intersection +----------- +This function returns an array an intersection of two. + +*Examples:* + + intersection(["a","b","c"],["b","c","d"]) + +Would return: ["b","c"] + +is_array +-------- +Returns true if the variable passed to this function is an array. + +- *Type*: rvalue + +is_domain_name +-------------- +Returns true if the string passed to this function is a syntactically correct domain name. + +- *Type*: rvalue + +is_float +-------- +Returns true if the variable passed to this function is a float. + +- *Type*: rvalue + +is_function_available +--------------------- +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. + +- *Type*: rvalue + +is_hash +------- +Returns true if the variable passed to this function is a hash. + +- *Type*: rvalue + +is_integer +---------- +Returns true if the variable returned to this string is an integer. + +- *Type*: rvalue + +is_ip_address +------------- +Returns true if the string passed to this function is a valid IP address. + +- *Type*: rvalue + +is_mac_address +-------------- +Returns true if the string passed to this function is a valid mac address. + +- *Type*: rvalue + +is_numeric +---------- +Returns true if the variable passed to this function is a number. + +- *Type*: rvalue + +is_string +--------- +Returns true if the variable passed to this function is a string. + +- *Type*: rvalue + +join +---- +This function joins an array into a string using a seperator. + +*Examples:* + + join(['a','b','c'], ",") + +Would result in: "a,b,c" + +- *Type*: rvalue + +join_keys_to_values +------------------- +This function joins each key of a hash to that key's corresponding value with a +separator. Keys and values are cast to strings. The return value is an array in +which each element is one joined key/value pair. + +*Examples:* + + join_keys_to_values({'a'=>1,'b'=>2}, " is ") + +Would result in: ["a is 1","b is 2"] + +- *Type*: rvalue + +keys +---- +Returns the keys of a hash as an array. + +- *Type*: rvalue + +loadyaml +-------- +Load a YAML file containing an array, string, or hash, and return the data +in the corresponding native data type. + +For example: + + $myhash = loadyaml('/etc/puppet/data/myhash.yaml') + + +- *Type*: rvalue + +lstrip +------ +Strips leading spaces to the left of a string. + +- *Type*: rvalue + +max +--- +Returns the highest value of all arguments. +Requires at least one argument. + +- *Type*: rvalue + +member +------ +This function determines if a variable is a member of an array. + +*Examples:* + + member(['a','b'], 'b') + +Would return: true + + member(['a','b'], 'c') + +Would return: false + +- *Type*: rvalue + +merge +----- +Merges two or more hashes together and returns the resulting hash. + +For example: + + $hash1 = {'one' => 1, 'two', => 2} + $hash2 = {'two' => 'dos', 'three', => 'tres'} + $merged_hash = merge($hash1, $hash2) + # The resulting hash is equivalent to: + # $merged_hash = {'one' => 1, 'two' => 'dos', 'three' => 'tres'} + +When there is a duplicate key, the key in the rightmost hash will "win." + +- *Type*: rvalue + +min +--- +Returns the lowest value of all arguments. +Requires at least one argument. + +- *Type*: rvalue + +num2bool +-------- +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. + +- *Type*: rvalue + +parsejson +--------- +This function accepts JSON as a string and converts into the correct Puppet +structure. + +- *Type*: rvalue + +parseyaml +--------- +This function accepts YAML as a string and converts it into the correct +Puppet structure. + +- *Type*: rvalue + +pick +---- +This function is similar to a coalesce function in SQL in that it will return +the first value in a list of values that is not undefined or an empty string +(two things in Puppet that will return a boolean false value). Typically, +this function is used to check for a value in the Puppet Dashboard/Enterprise +Console, and failover to a default value like the following: + + $real_jenkins_version = pick($::jenkins_version, '1.449') + +The value of $real_jenkins_version will first look for a top-scope variable +called 'jenkins_version' (note that parameters set in the Puppet Dashboard/ +Enterprise Console are brought into Puppet as top-scope variables), and, +failing that, will use a default value of 1.449. + +- *Type*: rvalue + +prefix +------ +This function applies a prefix to all elements in an array. + +*Examples:* + + prefix(['a','b','c'], 'p') + +Will return: ['pa','pb','pc'] + +- *Type*: rvalue + +range +----- +When given range in the form of (start, stop) it will extrapolate a range as +an array. + +*Examples:* + + range("0", "9") + +Will return: [0,1,2,3,4,5,6,7,8,9] + + range("00", "09") + +Will return: [0,1,2,3,4,5,6,7,8,9] (Zero padded strings are converted to +integers automatically) + + range("a", "c") + +Will return: ["a","b","c"] + + range("host01", "host10") + +Will return: ["host01", "host02", ..., "host09", "host10"] + +- *Type*: rvalue + +reject +------ +This function searches through an array and rejects all elements that match +the provided regular expression. + +*Examples:* + + reject(['aaa','bbb','ccc','aaaddd'], 'aaa') + +Would return: + + ['bbb','ccc'] + + +- *Type*: rvalue + +reverse +------- +Reverses the order of a string or array. + +- *Type*: rvalue + +rstrip +------ +Strips leading spaces to the right of the string. + +- *Type*: rvalue + +shuffle +------- +Randomizes the order of a string or array elements. + +- *Type*: rvalue + +size +---- +Returns the number of elements in a string or array. + +- *Type*: rvalue + +sort +---- +Sorts strings and arrays lexically. + +- *Type*: rvalue + +squeeze +------- +Returns a new string where runs of the same character that occur in this set +are replaced by a single character. + +- *Type*: rvalue + +str2bool +-------- +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'. + + +- *Type*: rvalue + +str2saltedsha512 +---------------- +This converts a string to a salted-SHA512 password hash (which is used for +OS X versions >= 10.7). Given any simple string, you will get a hex version +of a salted-SHA512 password hash that can be inserted into your Puppet +manifests as a valid password attribute. + + +- *Type*: rvalue + +strftime +-------- +This function returns formatted time. + +*Examples:* + +To return the time since epoch: + + strftime("%s") + +To return the date: + + strftime("%Y-%m-%d") + +*Format meaning:* + + %a - The abbreviated weekday name (``Sun'') + %A - The full weekday name (``Sunday'') + %b - The abbreviated month name (``Jan'') + %B - The full month name (``January'') + %c - The preferred local date and time representation + %C - Century (20 in 2009) + %d - Day of the month (01..31) + %D - Date (%m/%d/%y) + %e - Day of the month, blank-padded ( 1..31) + %F - Equivalent to %Y-%m-%d (the ISO 8601 date format) + %h - Equivalent to %b + %H - Hour of the day, 24-hour clock (00..23) + %I - Hour of the day, 12-hour clock (01..12) + %j - Day of the year (001..366) + %k - hour, 24-hour clock, blank-padded ( 0..23) + %l - hour, 12-hour clock, blank-padded ( 0..12) + %L - Millisecond of the second (000..999) + %m - Month of the year (01..12) + %M - Minute of the hour (00..59) + %n - Newline ( +) + %N - Fractional seconds digits, default is 9 digits (nanosecond) + %3N millisecond (3 digits) + %6N microsecond (6 digits) + %9N nanosecond (9 digits) + %p - Meridian indicator (``AM'' or ``PM'') + %P - Meridian indicator (``am'' or ``pm'') + %r - time, 12-hour (same as %I:%M:%S %p) + %R - time, 24-hour (%H:%M) + %s - Number of seconds since 1970-01-01 00:00:00 UTC. + %S - Second of the minute (00..60) + %t - Tab character ( ) + %T - time, 24-hour (%H:%M:%S) + %u - Day of the week as a decimal, Monday being 1. (1..7) + %U - Week number of the current year, + starting with the first Sunday as the first + day of the first week (00..53) + %v - VMS date (%e-%b-%Y) + %V - Week number of year according to ISO 8601 (01..53) + %W - Week number of the current year, + starting with the first Monday as the first + day of the first week (00..53) + %w - Day of the week (Sunday is 0, 0..6) + %x - Preferred representation for the date alone, no time + %X - Preferred representation for the time alone, no date + %y - Year without a century (00..99) + %Y - Year with century + %z - Time zone as hour offset from UTC (e.g. +0900) + %Z - Time zone name + %% - Literal ``%'' character + + +- *Type*: rvalue + +strip +----- +This function removes leading and trailing whitespace from a string or from +every string inside an array. + +*Examples:* + + strip(" aaa ") + +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. + +*Examples:* + + swapcase("aBcD") + +Would result in: "AbCd" + + +- *Type*: rvalue + +time +---- +This function will return the current time since epoch as an integer. + +*Examples:* + + time() + +Will return something like: 1311972653 + + +- *Type*: rvalue + +to_bytes +-------- +Converts the argument into bytes, for example 4 kB becomes 4096. +Takes a single string value as an argument. + + +- *Type*: rvalue + +type +---- +Returns the type when passed a variable. Type can be one of: + +* string +* array +* hash +* float +* integer +* boolean + + +- *Type*: rvalue + +union +----- +This function returns a union of two arrays. + +*Examples:* + + union(["a","b","c"],["b","c","d"]) + +Would return: ["a","b","c","d"] + + +unique +------ +This function will remove duplicates from strings and arrays. + +*Examples:* + + unique("aabbcc") + +Will return: + + abc + +You can also use this with arrays: + + unique(["a","a","b","b","c","c"]) + +This returns: + + ["a","b","c"] + + +- *Type*: rvalue + +upcase +------ +Converts a string or an array of strings to uppercase. + +*Examples:* + + upcase("abcd") + +Will return: + + ABCD + + +- *Type*: rvalue + +uriescape +--------- +Urlencodes a string or array of strings. +Requires either a single string or an array as an input. + + +- *Type*: rvalue + +validate_absolute_path +---------------------- +Validate the string represents an absolute path in the filesystem. This function works +for windows and unix style paths. + +The following values will pass: + + $my_path = "C:/Program Files (x86)/Puppet Labs/Puppet" + validate_absolute_path($my_path) + $my_path2 = "/var/lib/puppet" + validate_absolute_path($my_path2) + + +The following values will fail, causing compilation to abort: + + validate_absolute_path(true) + validate_absolute_path([ 'var/lib/puppet', '/var/foo' ]) + validate_absolute_path([ '/var/lib/puppet', 'var/foo' ]) + $undefined = undef + validate_absolute_path($undefined) + + + +- *Type*: statement + +validate_array +-------------- +Validate that all passed values are array data structures. Abort catalog +compilation if any value fails this check. + +The following values will pass: + + $my_array = [ 'one', 'two' ] + validate_array($my_array) + +The following values will fail, causing compilation to abort: + + validate_array(true) + validate_array('some_string') + $undefined = undef + validate_array($undefined) + + + +- *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 +compilation if any value fails this check. + +The following values will pass: + + $iamtrue = true + validate_bool(true) + validate_bool(true, true, false, $iamtrue) + +The following values will fail, causing compilation to abort: + + $some_array = [ true ] + validate_bool("false") + validate_bool("true") + validate_bool($some_array) + + + +- *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 +compilation if any value fails this check. + +The following values will pass: + + $my_hash = { 'one' => 'two' } + validate_hash($my_hash) + +The following values will fail, causing compilation to abort: + + validate_hash(true) + validate_hash('some_string') + $undefined = undef + validate_hash($undefined) + + + +- *Type*: statement + +validate_re +----------- +Perform simple validation of a string against one or more regular +expressions. The first argument of this function should be a string to +test, and the second argument should be a stringified regular expression +(without the // delimiters) or an array of regular expressions. If none +of the regular expressions match the string passed in, compilation will +abort with a parse error. + +If a third argument is specified, this will be the error message raised and +seen by the user. + +The following strings will validate against the regular expressions: + + validate_re('one', '^one$') + validate_re('one', [ '^one', '^two' ]) + +The following strings will fail to validate, causing compilation to abort: + + validate_re('one', [ '^two', '^three' ]) + +A helpful error message can be returned like this: + + validate_re($::puppetversion, '^2.7', 'The $puppetversion fact value does not match 2.7') + + + +- *Type*: statement + +validate_slength +---------------- +Validate that the first argument is a string (or an array of strings), and +less/equal to than the length of the second argument. It fails if the first +argument is not a string or array of strings, and if arg 2 is not convertable +to a number. + +The following values will pass: + + validate_slength("discombobulate",17) + validate_slength(["discombobulate","moo"],17) + +The following valueis will not: + + validate_slength("discombobulate",1) + validate_slength(["discombobulate","thermometer"],5) + + + +- *Type*: statement + +validate_string +--------------- +Validate that all passed values are string data structures. Abort catalog +compilation if any value fails this check. + +The following values will pass: + + $my_string = "one two" + validate_string($my_string, 'three') + +The following values will fail, causing compilation to abort: + + validate_string(true) + validate_string([ 'some', 'array' ]) + $undefined = undef + validate_string($undefined) + + +- *Type*: statement + +values +------ +When given a hash this function will return the values of that hash. + +*Examples:* + + $hash = { + 'a' => 1, + 'b' => 2, + 'c' => 3, + } + values($hash) + +This example would return: + + [1,2,3] + + +- *Type*: rvalue + +values_at +--------- +Finds value inside an array based on location. + +The first argument is the array you want to analyze, and the second element can +be a combination of: + +* A single numeric index +* A range in the form of 'start-stop' (eg. 4-9) +* An array combining the above + +*Examples*: + + values_at(['a','b','c'], 2) + +Would return ['c']. + + values_at(['a','b','c'], ["0-1"]) + +Would return ['a','b']. + + values_at(['a','b','c','d','e'], [0, "2-3"]) + +Would return ['a','c','d']. - * getvar - * has\_key - * loadyaml - * merge.rb - * validate\_array - * validate\_bool - * validate\_hash - * validate\_re - * validate\_string -## validate\_hash ## +- *Type*: rvalue - $somehash = { 'one' => 'two' } - validate\_hash($somehash) +zip +--- +Takes one element from first array and merges corresponding elements from second array. This generates a sequence of n-element arrays, where n is one more than the count of arguments. -## getvar() ## +*Example:* -This function aims to look up variables in user-defined namespaces within -puppet. Note, if the namespace is a class, it should already be evaluated -before the function is used. + zip(['1','2','3'],['4','5','6']) - $namespace = 'site::data' - include "${namespace}" - $myvar = getvar("${namespace}::myvar") +Would result in: -## Facts ## + ["1", "4"], ["2", "5"], ["3", "6"] -Facts in `/etc/facter/facts.d` and `/etc/puppetlabs/facter/facts.d` are now loaded -automatically. This is a direct copy of R.I. Pienaar's custom facter fact -located at: -[https://github.com/ripienaar/facter-facts/tree/master/facts-dot-d](https://github.com/ripienaar/facter-facts/tree/master/facts-dot-d) +- *Type*: rvalue +*This page autogenerated on 2013-04-11 13:54:25 -0700* diff --git a/README_DEVELOPER.markdown b/README_DEVELOPER.markdown new file mode 100644 index 0000000..04349ed --- /dev/null +++ b/README_DEVELOPER.markdown @@ -0,0 +1,35 @@ +Puppet Specific Facts +===================== + +Facter is meant to stand alone and apart from Puppet. However, Facter often +runs inside Puppet and all custom facts included in the stdlib module will +almost always be evaluated in the context of Puppet and Facter working +together. + +Still, we don't want to write custom facts that blow up in the users face if +Puppet is not loaded in memory. This is often the case if the user runs +`facter` without also supplying the `--puppet` flag. + +Ah! But Jeff, the custom fact won't be in the `$LOAD_PATH` unless the user +supplies `--facter`! You might say... + +Not (always) true I say! If the user happens to have a CWD of +`<modulepath>/stdlib/lib` then the facts will automatically be evaluated and +blow up. + +In any event, it's pretty easy to write a fact that has no value if Puppet is +not loaded. Simply do it like this: + + Facter.add(:node_vardir) do + setcode do + # This will be nil if Puppet is not available. + Facter::Util::PuppetSettings.with_puppet do + Puppet[:vardir] + end + end + end + +The `Facter::Util::PuppetSettings.with_puppet` method accepts a block and +yields to it only if the Puppet library is loaded. If the Puppet library is +not loaded, then the method silently returns `nil` which Facter interprets as +an undefined fact value. The net effect is that the fact won't be set. diff --git a/README_SPECS.markdown b/README_SPECS.markdown new file mode 100644 index 0000000..917b631 --- /dev/null +++ b/README_SPECS.markdown @@ -0,0 +1,7 @@ +NOTE +==== + +This project's specs depend on puppet core, and thus they require the +`puppetlabs_spec_helper` project. For more information please see the README +in that project, which can be found here: [puppetlabs spec +helper](https://github.com/puppetlabs/puppetlabs_spec_helper) diff --git a/RELEASE_PROCESS.markdown b/RELEASE_PROCESS.markdown index ea40d5d..0f9328e 100644 --- a/RELEASE_PROCESS.markdown +++ b/RELEASE_PROCESS.markdown @@ -1,13 +1,24 @@ -# Releasing this module # +# Contributing to this module # * Work in a topic branch * Submit a github pull request * Address any comments / feeback * Merge into master using --no-ff + +# Releasing this module # + + * This module adheres to http://semver.org/ + * Look for API breaking changes using git diff vX.Y.Z..master + * If no API breaking changes, the minor version may be bumped. + * If there are API breaking changes, the major version must be bumped. + * If there are only small minor changes, the patch version may be bumped. * Update the CHANGELOG * Update the Modulefile - * Create an annotated tag with git tag -a X.Y.Z -m 'version X.Y.Z' + * Commit these changes with a message along the lines of "Update CHANGELOG and + Modulefile for release" + * Create an annotated tag with git tag -a vX.Y.Z -m 'version X.Y.Z' (NOTE the + leading v as per semver.org) * Push the tag with git push origin --tags - * Build a new package with puppet-module + * 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. @@ -1,16 +1,2 @@ -require 'rake' -require 'rspec/core/rake_task' - -task :default => [:test] - -desc 'Run RSpec' -RSpec::Core::RakeTask.new(:test) do |t| - t.pattern = 'spec/{unit}/**/*.rb' - t.rspec_opts = ['--color'] -end - -desc 'Generate code coverage' -RSpec::Core::RakeTask.new(:coverage) do |t| - t.rcov = true - t.rcov_opts = ['--exclude', 'spec'] -end +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/lib/facter/facter_dot_d.rb b/lib/facter/facter_dot_d.rb index 90586a9..e414b20 100644 --- a/lib/facter/facter_dot_d.rb +++ b/lib/facter/facter_dot_d.rb @@ -1,4 +1,5 @@ -# A Facter plugin that loads facts from /etc/facts.d. +# A Facter plugin that loads facts from /etc/facter/facts.d +# and /etc/puppetlabs/facter/facts.d. # # Facts can be in the form of JSON, YAML or Text files # and any executable that returns key=value pairs. @@ -10,174 +11,192 @@ # The cache is stored in /tmp/facts_cache.yaml as a mode # 600 file and will have the end result of not calling your # fact scripts more often than is needed + class Facter::Util::DotD - require 'yaml' + require 'yaml' - def initialize(dir="/etc/facts.d", cache_file="/tmp/facts_cache.yml") - @dir = dir - @cache_file = cache_file - @cache = nil - @types = {".txt" => :txt, ".json" => :json, ".yaml" => :yaml} - end + def initialize(dir="/etc/facts.d", cache_file="/tmp/facts_cache.yml") + @dir = dir + @cache_file = cache_file + @cache = nil + @types = {".txt" => :txt, ".json" => :json, ".yaml" => :yaml} + end - def entries - Dir.entries(@dir).reject{|f| f =~ /^\.|\.ttl$/}.sort.map {|f| File.join(@dir, f) } - rescue - [] - end + def entries + Dir.entries(@dir).reject{|f| f =~ /^\.|\.ttl$/}.sort.map {|f| File.join(@dir, f) } + rescue + [] + end - def fact_type(file) - extension = File.extname(file) + def fact_type(file) + extension = File.extname(file) - type = @types[extension] || :unknown + type = @types[extension] || :unknown - type = :script if type == :unknown && File.executable?(file) + type = :script if type == :unknown && File.executable?(file) - return type - end + return type + end - def txt_parser(file) - File.readlines(file).each do |line| - if line =~ /^(.+)=(.+)$/ - var = $1; val = $2 + def txt_parser(file) + File.readlines(file).each do |line| + if line =~ /^(.+)=(.+)$/ + var = $1; val = $2 - Facter.add(var) do - setcode { val } - end - end + Facter.add(var) do + setcode { val } end - rescue Exception => e - Facter.warn("Failed to handle #{file} as text facts: #{e.class}: #{e}") + end end - - def json_parser(file) - begin - require 'json' - rescue LoadError - require 'rubygems' - retry - end - - JSON.load(File.read(file)).each_pair do |f, v| - Facter.add(f) do - setcode { v } - end - end - rescue Exception => e - Facter.warn("Failed to handle #{file} as json facts: #{e.class}: #{e}") + rescue Exception => e + Facter.warn("Failed to handle #{file} as text facts: #{e.class}: #{e}") + end + + def json_parser(file) + begin + require 'json' + rescue LoadError + retry if require 'rubygems' + raise end - def yaml_parser(file) - require 'yaml' - - YAML.load_file(file).each_pair do |f, v| - Facter.add(f) do - setcode { v } - end - end - rescue Exception => e - Facter.warn("Failed to handle #{file} as yaml facts: #{e.class}: #{e}") + JSON.load(File.read(file)).each_pair do |f, v| + Facter.add(f) do + setcode { v } + end end + rescue Exception => e + Facter.warn("Failed to handle #{file} as json facts: #{e.class}: #{e}") + end - def script_parser(file) - result = cache_lookup(file) - ttl = cache_time(file) - - unless result - result = Facter::Util::Resolution.exec(file) + def yaml_parser(file) + require 'yaml' - if ttl > 0 - Facter.debug("Updating cache for #{file}") - cache_store(file, result) - cache_save! - end - else - Facter.debug("Using cached data for #{file}") - end + YAML.load_file(file).each_pair do |f, v| + Facter.add(f) do + setcode { v } + end + end + rescue Exception => e + Facter.warn("Failed to handle #{file} as yaml facts: #{e.class}: #{e}") + end + + def script_parser(file) + result = cache_lookup(file) + ttl = cache_time(file) + + unless result + result = Facter::Util::Resolution.exec(file) + + if ttl > 0 + Facter.debug("Updating cache for #{file}") + cache_store(file, result) + cache_save! + end + else + Facter.debug("Using cached data for #{file}") + end - result.split("\n").each do |line| - if line =~ /^(.+)=(.+)$/ - var = $1; val = $2 + result.split("\n").each do |line| + if line =~ /^(.+)=(.+)$/ + var = $1; val = $2 - Facter.add(var) do - setcode { val } - end - end + Facter.add(var) do + setcode { val } end - rescue Exception => e - Facter.warn("Failed to handle #{file} as script facts: #{e.class}: #{e}") - Facter.debug(e.backtrace.join("\n\t")) + end end + rescue Exception => e + Facter.warn("Failed to handle #{file} as script facts: #{e.class}: #{e}") + Facter.debug(e.backtrace.join("\n\t")) + end - def cache_save! - cache = load_cache - File.open(@cache_file, "w", 0600) {|f| f.write(YAML.dump(cache)) } - rescue - end + def cache_save! + cache = load_cache + File.open(@cache_file, "w", 0600) {|f| f.write(YAML.dump(cache)) } + rescue + end - def cache_store(file, data) - load_cache + def cache_store(file, data) + load_cache - @cache[file] = {:data => data, :stored => Time.now.to_i} - rescue - end + @cache[file] = {:data => data, :stored => Time.now.to_i} + rescue + end - def cache_lookup(file) - cache = load_cache + def cache_lookup(file) + cache = load_cache - return nil if cache.empty? + return nil if cache.empty? - ttl = cache_time(file) + ttl = cache_time(file) - if cache[file] - now = Time.now.to_i + if cache[file] + now = Time.now.to_i - return cache[file][:data] if ttl == -1 - return cache[file][:data] if (now - cache[file][:stored]) <= ttl - return nil - else - return nil - end - rescue - return nil + return cache[file][:data] if ttl == -1 + return cache[file][:data] if (now - cache[file][:stored]) <= ttl + return nil + else + return nil + end + rescue + return nil + end + + def cache_time(file) + meta = file + ".ttl" + + return File.read(meta).chomp.to_i + rescue + return 0 + end + + def load_cache + unless @cache + if File.exist?(@cache_file) + @cache = YAML.load_file(@cache_file) + else + @cache = {} + end end - def cache_time(file) - meta = file + ".ttl" + return @cache + rescue + @cache = {} + return @cache + end - return File.read(meta).chomp.to_i - rescue - return 0 - end + def create + entries.each do |fact| + type = fact_type(fact) + parser = "#{type}_parser" - def load_cache - unless @cache - if File.exist?(@cache_file) - @cache = YAML.load_file(@cache_file) - else - @cache = {} - end - end + if respond_to?("#{type}_parser") + Facter.debug("Parsing #{fact} using #{parser}") - return @cache - rescue - @cache = {} - return @cache + send(parser, fact) + end end + end +end - def create - entries.each do |fact| - type = fact_type(fact) - parser = "#{type}_parser" - - if respond_to?("#{type}_parser") - Facter.debug("Parsing #{fact} using #{parser}") - send(parser, fact) - end - end +mdata = Facter.version.match(/(\d+)\.(\d+)\.(\d+)/) +if mdata + (major, minor, patch) = mdata.captures.map { |v| v.to_i } + if major < 2 + # Facter 1.7 introduced external facts support directly + unless major == 1 and minor > 6 + Facter::Util::DotD.new("/etc/facter/facts.d").create + Facter::Util::DotD.new("/etc/puppetlabs/facter/facts.d").create + + # Windows has a different configuration directory that defaults to a vendor + # specific sub directory of the %COMMON_APPDATA% directory. + if Dir.const_defined? 'COMMON_APPDATA' then + windows_facts_dot_d = File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'facter', 'facts.d') + Facter::Util::DotD.new(windows_facts_dot_d).create + end end + end end - -Facter::Util::DotD.new("/etc/facter/facts.d").create -Facter::Util::DotD.new("/etc/puppetlabs/facter/facts.d").create diff --git a/lib/facter/pe_version.rb b/lib/facter/pe_version.rb new file mode 100644 index 0000000..0cc0f64 --- /dev/null +++ b/lib/facter/pe_version.rb @@ -0,0 +1,53 @@ +# Fact: is_pe, pe_version, pe_major_version, pe_minor_version, pe_patch_version +# +# Purpose: Return various facts about the PE state of the system +# +# Resolution: Uses a regex match against puppetversion to determine whether the +# machine has Puppet Enterprise installed, and what version (overall, major, +# minor, patch) is installed. +# +# Caveats: +# +Facter.add("pe_version") do + setcode do + pe_ver = Facter.value("puppetversion").match(/Puppet Enterprise (\d+\.\d+\.\d+)/) + pe_ver[1] if pe_ver + end +end + +Facter.add("is_pe") do + setcode do + if Facter.value(:pe_version).to_s.empty? then + false + else + true + end + end +end + +Facter.add("pe_major_version") do + confine :is_pe => true + setcode do + if pe_version = Facter.value(:pe_version) + pe_version.to_s.split('.')[0] + end + end +end + +Facter.add("pe_minor_version") do + confine :is_pe => true + setcode do + if pe_version = Facter.value(:pe_version) + pe_version.to_s.split('.')[1] + end + end +end + +Facter.add("pe_patch_version") do + confine :is_pe => true + setcode do + if pe_version = Facter.value(:pe_version) + pe_version.to_s.split('.')[2] + end + end +end diff --git a/lib/facter/puppet_vardir.rb b/lib/facter/puppet_vardir.rb new file mode 100644 index 0000000..0e6af40 --- /dev/null +++ b/lib/facter/puppet_vardir.rb @@ -0,0 +1,26 @@ +# This facter fact returns the value of the Puppet vardir setting for the node +# running puppet or puppet agent. The intent is to enable Puppet modules to +# automatically have insight into a place where they can place variable data, +# regardless of the node's platform. +# +# The value should be directly usable in a File resource path attribute. + + +begin + require 'facter/util/puppet_settings' +rescue LoadError => e + # puppet apply does not add module lib directories to the $LOAD_PATH (See + # #4248). It should (in the future) but for the time being we need to be + # defensive which is what this rescue block is doing. + rb_file = File.join(File.dirname(__FILE__), 'util', 'puppet_settings.rb') + load rb_file if File.exists?(rb_file) or raise e +end + +Facter.add(:puppet_vardir) do + setcode do + # This will be nil if Puppet is not available. + Facter::Util::PuppetSettings.with_puppet do + Puppet[:vardir] + end + end +end diff --git a/lib/facter/root_home.rb b/lib/facter/root_home.rb index 61fcf39..8249f7d 100644 --- a/lib/facter/root_home.rb +++ b/lib/facter/root_home.rb @@ -7,7 +7,9 @@ module Facter::Util::RootHome def get_root_home root_ent = Facter::Util::Resolution.exec("getent passwd root") # The home directory is the sixth element in the passwd entry - root_ent.split(":")[5] + # If the platform doesn't have getent, root_ent will be nil and we should + # return it straight away. + root_ent && root_ent.split(":")[5] end end end diff --git a/lib/facter/util/puppet_settings.rb b/lib/facter/util/puppet_settings.rb new file mode 100644 index 0000000..1ad9452 --- /dev/null +++ b/lib/facter/util/puppet_settings.rb @@ -0,0 +1,21 @@ +module Facter + module Util + module PuppetSettings + # This method is intended to provide a convenient way to evaluate a + # Facter code block only if Puppet is loaded. This is to account for the + # situation where the fact happens to be in the load path, but Puppet is + # not loaded for whatever reason. Perhaps the user is simply running + # facter without the --puppet flag and they happen to be working in a lib + # directory of a module. + def self.with_puppet + begin + Module.const_get("Puppet") + rescue NameError + nil + else + yield + end + end + end + end +end diff --git a/lib/puppet/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/base64.rb b/lib/puppet/parser/functions/base64.rb new file mode 100644 index 0000000..d9a590a --- /dev/null +++ b/lib/puppet/parser/functions/base64.rb @@ -0,0 +1,37 @@ +module Puppet::Parser::Functions + + newfunction(:base64, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| + + Base64 encode or decode a string based on the command and the string submitted + + Usage: + + $encodestring = base64('encode','thestring') + $decodestring = base64('decode','dGhlc3RyaW5n') + + ENDHEREDOC + + require 'base64' + + raise Puppet::ParseError, ("base64(): Wrong number of arguments (#{args.length}; must be = 2)") unless args.length == 2 + + actions = ['encode','decode'] + + unless actions.include?(args[0]) + raise Puppet::ParseError, ("base64(): the first argument must be one of 'encode' or 'decode'") + end + + unless args[1].is_a?(String) + raise Puppet::ParseError, ("base64(): the second argument must be a string to base64") + end + + case args[0] + when 'encode' + result = Base64.encode64(args[1]) + when 'decode' + result = Base64.decode64(args[1]) + end + + return result + end +end diff --git a/lib/puppet/parser/functions/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/count.rb b/lib/puppet/parser/functions/count.rb new file mode 100644 index 0000000..52de1b8 --- /dev/null +++ b/lib/puppet/parser/functions/count.rb @@ -0,0 +1,22 @@ +module Puppet::Parser::Functions + newfunction(:count, :type => :rvalue, :arity => -2, :doc => <<-EOS +Takes an array as first argument and an optional second argument. +Count the number of elements in array that matches second argument. +If called with only an array it counts the number of elements that are not nil/undef. + EOS + ) do |args| + + if (args.size > 2) then + raise(ArgumentError, "count(): Wrong number of arguments "+ + "given #{args.size} for 1 or 2.") + end + + collection, item = args + + if item then + collection.count item + else + collection.count { |obj| obj != nil && obj != :undef && obj != '' } + end + end +end diff --git a/lib/puppet/parser/functions/defined_with_params.rb b/lib/puppet/parser/functions/defined_with_params.rb new file mode 100644 index 0000000..d7df306 --- /dev/null +++ b/lib/puppet/parser/functions/defined_with_params.rb @@ -0,0 +1,35 @@ +# Test whether a given class or definition is defined +require 'puppet/parser/functions' + +Puppet::Parser::Functions.newfunction(:defined_with_params, + :type => :rvalue, + :doc => <<-'ENDOFDOC' +Takes a resource reference and an optional hash of attributes. + +Returns true if a resource with the specified attributes has already been added +to the catalog, and false otherwise. + + user { 'dan': + ensure => present, + } + + if ! defined_with_params(User[dan], {'ensure' => 'present' }) { + user { 'dan': ensure => present, } + } +ENDOFDOC +) do |vals| + reference, params = vals + raise(ArgumentError, 'Must specify a reference') unless reference + if (! params) || params == '' + params = {} + end + ret = false + if resource = findresource(reference.to_s) + matches = params.collect do |key, value| + resource[key] == value + end + ret = params.empty? || !matches.include?(false) + end + Puppet.debug("Resource #{reference} was not determined to be defined") + ret +end diff --git a/lib/puppet/parser/functions/delete.rb b/lib/puppet/parser/functions/delete.rb index ab8f75b..f814344 100644 --- a/lib/puppet/parser/functions/delete.rb +++ b/lib/puppet/parser/functions/delete.rb @@ -3,31 +3,43 @@ # # TODO(Krzysztof Wilczynski): We need to add support for regular expression ... -# TODO(Krzysztof Wilczynski): Support for strings and hashes too ... module Puppet::Parser::Functions newfunction(:delete, :type => :rvalue, :doc => <<-EOS -Deletes a selected element from an array. +Deletes all instances of a given element from an array, substring from a +string, or key from a hash. *Examples:* - delete(['a','b','c'], 'b') + delete(['a','b','c','b'], 'b') + Would return: ['a','c'] -Would return: ['a','c'] + delete({'a'=>1,'b'=>2,'c'=>3}, 'b') + Would return: {'a'=>1,'c'=>3} + + delete('abracadabra', 'bra') + Would return: 'acada' EOS ) do |arguments| if (arguments.size != 2) then raise(Puppet::ParseError, "delete(): Wrong number of arguments "+ - "given #{arguments.size} for 2") + "given #{arguments.size} for 2.") end - a = arguments[0] + collection = arguments[0] item = arguments[1] - a.delete(item) - a - + 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 + collection end end diff --git a/lib/puppet/parser/functions/difference.rb b/lib/puppet/parser/functions/difference.rb new file mode 100644 index 0000000..cd258f7 --- /dev/null +++ b/lib/puppet/parser/functions/difference.rb @@ -0,0 +1,36 @@ +# +# difference.rb +# + +module Puppet::Parser::Functions + newfunction(:difference, :type => :rvalue, :doc => <<-EOS +This function returns the difference between two arrays. +The returned array is a copy of the original array, removing any items that +also appear in the second array. + +*Examples:* + + difference(["a","b","c"],["b","c","d"]) + +Would return: ["a"] + EOS + ) do |arguments| + + # Two arguments are required + raise(Puppet::ParseError, "difference(): Wrong number of arguments " + + "given (#{arguments.size} for 2)") if arguments.size != 2 + + first = arguments[0] + second = arguments[1] + + unless first.is_a?(Array) && second.is_a?(Array) + raise(Puppet::ParseError, 'difference(): Requires 2 arrays') + end + + result = first - second + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/dirname.rb b/lib/puppet/parser/functions/dirname.rb new file mode 100644 index 0000000..ea8cc1e --- /dev/null +++ b/lib/puppet/parser/functions/dirname.rb @@ -0,0 +1,15 @@ +module Puppet::Parser::Functions + newfunction(:dirname, :type => :rvalue, :doc => <<-EOS + Returns the dirname of a path. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "dirname(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size < 1 + + path = arguments[0] + return File.dirname(path) + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/ensure_packages.rb b/lib/puppet/parser/functions/ensure_packages.rb new file mode 100644 index 0000000..450ea02 --- /dev/null +++ b/lib/puppet/parser/functions/ensure_packages.rb @@ -0,0 +1,24 @@ +# +# ensure_packages.rb +# +require 'puppet/parser/functions' + +module Puppet::Parser::Functions + newfunction(:ensure_packages, :type => :statement, :doc => <<-EOS +Takes a list of packages and only installs them if they don't already exist. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "ensure_packages(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + raise(Puppet::ParseError, "ensure_packages(): Requires array " + + "given (#{arguments[0].class})") if !arguments[0].kind_of?(Array) + + Puppet::Parser::Functions.function(:ensure_resource) + arguments[0].each { |package_name| + function_ensure_resource(['package', package_name, {'ensure' => 'present' } ]) + } + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/ensure_resource.rb b/lib/puppet/parser/functions/ensure_resource.rb new file mode 100644 index 0000000..a9a1733 --- /dev/null +++ b/lib/puppet/parser/functions/ensure_resource.rb @@ -0,0 +1,45 @@ +# Test whether a given class or definition is defined +require 'puppet/parser/functions' + +Puppet::Parser::Functions.newfunction(:ensure_resource, + :type => :statement, + :doc => <<-'ENDOFDOC' +Takes a resource type, title, and a list of attributes that describe a +resource. + + user { 'dan': + ensure => present, + } + +This example only creates the resource if it does not already exist: + + ensure_resource('user, 'dan', {'ensure' => 'present' }) + +If the resource already exists but does not match the specified parameters, +this function will attempt to recreate the resource leading to a duplicate +resource definition error. + +An array of resources can also be passed in and each will be created with +the type and parameters specified if it doesn't already exist. + + ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) + +ENDOFDOC +) do |vals| + type, title, params = vals + raise(ArgumentError, 'Must specify a type') unless type + raise(ArgumentError, 'Must specify a title') unless title + params ||= {} + + items = [title].flatten + + items.each do |item| + Puppet::Parser::Functions.function(:defined_with_params) + if function_defined_with_params(["#{type}[#{item}]", params]) + Puppet.debug("Resource #{type}[#{item}] not created because it already exists") + else + Puppet::Parser::Functions.function(:create_resources) + function_create_resources([type.capitalize, { item => params }]) + end + end +end diff --git a/lib/puppet/parser/functions/flatten.rb b/lib/puppet/parser/functions/flatten.rb index 781da78..a1ed183 100644 --- a/lib/puppet/parser/functions/flatten.rb +++ b/lib/puppet/parser/functions/flatten.rb @@ -16,7 +16,7 @@ Would return: ['a','b','c'] ) do |arguments| raise(Puppet::ParseError, "flatten(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size < 1 + "given (#{arguments.size} for 1)") if arguments.size != 1 array = arguments[0] 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/fqdn_rotate.rb b/lib/puppet/parser/functions/fqdn_rotate.rb new file mode 100644 index 0000000..6558206 --- /dev/null +++ b/lib/puppet/parser/functions/fqdn_rotate.rb @@ -0,0 +1,46 @@ +# +# fqdn_rotate.rb +# + +module Puppet::Parser::Functions + newfunction(:fqdn_rotate, :type => :rvalue, :doc => <<-EOS +Rotates an array a random number of times based on a nodes fqdn. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "fqdn_rotate(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size < 1 + + value = arguments[0] + klass = value.class + require 'digest/md5' + + unless [Array, String].include?(klass) + raise(Puppet::ParseError, 'fqdn_rotate(): Requires either ' + + 'array or string to work with') + end + + result = value.clone + + string = value.is_a?(String) ? true : false + + # Check whether it makes sense to rotate ... + return result if result.size <= 1 + + # We turn any string value into an array to be able to rotate ... + result = string ? result.split('') : result + + elements = result.size + + srand(Digest::MD5.hexdigest([lookupvar('::fqdn'),arguments].join(':')).hex) + rand(elements).times { + result.push result.shift + } + + result = string ? result.join : result + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/get_module_path.rb b/lib/puppet/parser/functions/get_module_path.rb new file mode 100644 index 0000000..1421b91 --- /dev/null +++ b/lib/puppet/parser/functions/get_module_path.rb @@ -0,0 +1,17 @@ +module Puppet::Parser::Functions + newfunction(:get_module_path, :type =>:rvalue, :doc => <<-EOT + Returns the absolute path of the specified module for the current + environment. + + Example: + $module_path = get_module_path('stdlib') + EOT + ) do |args| + raise(Puppet::ParseError, "get_module_path(): Wrong number of arguments, expects one") unless args.size == 1 + if module_path = Puppet::Module.find(args[0], compiler.environment.to_s) + module_path.path + else + raise(Puppet::ParseError, "Could not find module #{args[0]} in environment #{compiler.environment}") + end + end +end diff --git a/lib/puppet/parser/functions/getparam.rb b/lib/puppet/parser/functions/getparam.rb new file mode 100644 index 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/getvar.rb b/lib/puppet/parser/functions/getvar.rb index ffd774d..1621149 100644 --- a/lib/puppet/parser/functions/getvar.rb +++ b/lib/puppet/parser/functions/getvar.rb @@ -5,11 +5,14 @@ module Puppet::Parser::Functions For example: - $foo = getvar('site::data::foo') + $foo = getvar('site::data::foo') + # Equivalent to $foo = $site::data::foo This is useful if the namespace itself is stored in a string: - $bar = getvar("${datalocation}::bar") + $datalocation = 'site::data' + $bar = getvar("${datalocation}::bar") + # Equivalent to $bar = $site::data::bar ENDHEREDOC unless args.length == 1 diff --git a/lib/puppet/parser/functions/has_interface_with.rb b/lib/puppet/parser/functions/has_interface_with.rb new file mode 100644 index 0000000..7f150a7 --- /dev/null +++ b/lib/puppet/parser/functions/has_interface_with.rb @@ -0,0 +1,52 @@ +# +# has_interface_with +# + +module Puppet::Parser::Functions + newfunction(:has_interface_with, :type => :rvalue, :doc => <<-EOS +Returns boolean based on kind and value: + * macaddress + * netmask + * ipaddress + * network + +has_interface_with("macaddress", "x:x:x:x:x:x") +has_interface_with("ipaddress", "127.0.0.1") => true +etc. + +If no "kind" is given, then the presence of the interface is checked: +has_interface_with("lo") => true + EOS + ) do |args| + + raise(Puppet::ParseError, "has_interface_with(): Wrong number of arguments " + + "given (#{args.size} for 1 or 2)") if args.size < 1 or args.size > 2 + + interfaces = lookupvar('interfaces') + + # If we do not have any interfaces, then there are no requested attributes + return false if (interfaces == :undefined) + + interfaces = interfaces.split(',') + + if args.size == 1 + return interfaces.member?(args[0]) + end + + kind, value = args + + if lookupvar(kind) == value + return true + end + + result = false + interfaces.each do |iface| + if value == lookupvar("#{kind}_#{iface}") + result = true + break + end + end + + result + end +end diff --git a/lib/puppet/parser/functions/has_ip_address.rb b/lib/puppet/parser/functions/has_ip_address.rb new file mode 100644 index 0000000..842c8ec --- /dev/null +++ b/lib/puppet/parser/functions/has_ip_address.rb @@ -0,0 +1,25 @@ +# +# has_ip_address +# + +module Puppet::Parser::Functions + newfunction(:has_ip_address, :type => :rvalue, :doc => <<-EOS +Returns true if the client has the requested IP address on some interface. + +This function iterates through the 'interfaces' fact and checks the +'ipaddress_IFACE' facts, performing a simple string comparison. + EOS + ) do |args| + + raise(Puppet::ParseError, "has_ip_address(): Wrong number of arguments " + + "given (#{args.size} for 1)") if args.size != 1 + + Puppet::Parser::Functions.autoloader.load(:has_interface_with) \ + unless Puppet::Parser::Functions.autoloader.loaded?(:has_interface_with) + + function_has_interface_with(['ipaddress', args[0]]) + + end +end + +# vim:sts=2 sw=2 diff --git a/lib/puppet/parser/functions/has_ip_network.rb b/lib/puppet/parser/functions/has_ip_network.rb new file mode 100644 index 0000000..9ccf902 --- /dev/null +++ b/lib/puppet/parser/functions/has_ip_network.rb @@ -0,0 +1,25 @@ +# +# has_ip_network +# + +module Puppet::Parser::Functions + newfunction(:has_ip_network, :type => :rvalue, :doc => <<-EOS +Returns true if the client has an IP address within the requested network. + +This function iterates through the 'interfaces' fact and checks the +'network_IFACE' facts, performing a simple string comparision. + EOS + ) do |args| + + raise(Puppet::ParseError, "has_ip_network(): Wrong number of arguments " + + "given (#{args.size} for 1)") if args.size != 1 + + Puppet::Parser::Functions.autoloader.load(:has_interface_with) \ + unless Puppet::Parser::Functions.autoloader.loaded?(:has_interface_with) + + function_has_interface_with(['network', args[0]]) + + end +end + +# vim:sts=2 sw=2 diff --git a/lib/puppet/parser/functions/has_key.rb b/lib/puppet/parser/functions/has_key.rb index 9c1c4c3..4657cc2 100644 --- a/lib/puppet/parser/functions/has_key.rb +++ b/lib/puppet/parser/functions/has_key.rb @@ -1,16 +1,17 @@ module Puppet::Parser::Functions newfunction(:has_key, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - determine if a hash has a certain key value. + Determine if a hash has a certain key value. Example: - $my_hash = {'key_one' => 'value_one'} - if has_key($my_hash, 'key_two') { - notice('we will not reach here') - } - if has_key($my_hash, 'key_one') { - notice('this will be printed') - } + + $my_hash = {'key_one' => 'value_one'} + if has_key($my_hash, 'key_two') { + notice('we will not reach here') + } + if has_key($my_hash, 'key_one') { + notice('this will be printed') + } ENDHEREDOC diff --git a/lib/puppet/parser/functions/hash.rb b/lib/puppet/parser/functions/hash.rb index 453ba1e..8cc4823 100644 --- a/lib/puppet/parser/functions/hash.rb +++ b/lib/puppet/parser/functions/hash.rb @@ -4,7 +4,7 @@ module Puppet::Parser::Functions newfunction(:hash, :type => :rvalue, :doc => <<-EOS -This function converts and array into a hash. +This function converts an array into a hash. *Examples:* diff --git a/lib/puppet/parser/functions/intersection.rb b/lib/puppet/parser/functions/intersection.rb new file mode 100644 index 0000000..48f02e9 --- /dev/null +++ b/lib/puppet/parser/functions/intersection.rb @@ -0,0 +1,34 @@ +# +# intersection.rb +# + +module Puppet::Parser::Functions + newfunction(:intersection, :type => :rvalue, :doc => <<-EOS +This function returns an array an intersection of two. + +*Examples:* + + intersection(["a","b","c"],["b","c","d"]) + +Would return: ["b","c"] + EOS + ) do |arguments| + + # Two arguments are required + raise(Puppet::ParseError, "intersection(): Wrong number of arguments " + + "given (#{arguments.size} for 2)") if arguments.size != 2 + + first = arguments[0] + second = arguments[1] + + unless first.is_a?(Array) && second.is_a?(Array) + raise(Puppet::ParseError, 'intersection(): Requires 2 arrays') + end + + result = first & second + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_domain_name.rb b/lib/puppet/parser/functions/is_domain_name.rb index 4e92939..5826dc0 100644 --- a/lib/puppet/parser/functions/is_domain_name.rb +++ b/lib/puppet/parser/functions/is_domain_name.rb @@ -4,7 +4,7 @@ module Puppet::Parser::Functions newfunction(:is_domain_name, :type => :rvalue, :doc => <<-EOS -Returns true if the string passed to this function is a valid IP address. Support for IPv4 and IPv6 address types is included. +Returns true if the string passed to this function is a syntactically correct domain name. EOS ) do |arguments| @@ -15,11 +15,31 @@ Returns true if the string passed to this function is a valid IP address. Suppor domain = arguments[0] - if domain =~ /^(([a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\.?$/ then - return true - else - return false + # Limits (rfc1035, 3.1) + domain_max_length=255 + label_min_length=1 + label_max_length=63 + + # Allow ".", it is the top level domain + return true if domain == '.' + + # Remove the final dot, if present. + domain.chomp!('.') + + # Check the whole domain + return false if domain.empty? + return false if domain.length > domain_max_length + + # Check each label in the domain + labels = domain.split('.') + vlabels = labels.each do |label| + break if label.length < label_min_length + break if label.length > label_max_length + break if label[-1..-1] == '-' + break if label[0..0] == '-' + break unless /^[a-z\d-]+$/i.match(label) end + return vlabels == labels end end diff --git a/lib/puppet/parser/functions/is_float.rb b/lib/puppet/parser/functions/is_float.rb index 2fc05ba..911f3c2 100644 --- a/lib/puppet/parser/functions/is_float.rb +++ b/lib/puppet/parser/functions/is_float.rb @@ -15,7 +15,7 @@ Returns true if the variable passed to this function is a float. value = arguments[0] - if value != value.to_f.to_s then + if value != value.to_f.to_s and !value.is_a? Float then return false else return true 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_integer.rb b/lib/puppet/parser/functions/is_integer.rb index 8ee34f6..6b29e98 100644 --- a/lib/puppet/parser/functions/is_integer.rb +++ b/lib/puppet/parser/functions/is_integer.rb @@ -15,7 +15,7 @@ Returns true if the variable returned to this string is an integer. value = arguments[0] - if value != value.to_i.to_s then + if value != value.to_i.to_s and !value.is_a? Fixnum then return false else return true 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/is_numeric.rb b/lib/puppet/parser/functions/is_numeric.rb index ce13ece..abf0321 100644 --- a/lib/puppet/parser/functions/is_numeric.rb +++ b/lib/puppet/parser/functions/is_numeric.rb @@ -15,7 +15,7 @@ Returns true if the variable passed to this function is a number. value = arguments[0] - if value == value.to_f.to_s or value == value.to_i.to_s then + if value == value.to_f.to_s or value == value.to_i.to_s or value.is_a? Numeric then return true else return false diff --git a/lib/puppet/parser/functions/join_keys_to_values.rb b/lib/puppet/parser/functions/join_keys_to_values.rb new file mode 100644 index 0000000..e9924fe --- /dev/null +++ b/lib/puppet/parser/functions/join_keys_to_values.rb @@ -0,0 +1,47 @@ +# +# join.rb +# + +module Puppet::Parser::Functions + newfunction(:join_keys_to_values, :type => :rvalue, :doc => <<-EOS +This function joins each key of a hash to that key's corresponding value with a +separator. Keys and values are cast to strings. The return value is an array in +which each element is one joined key/value pair. + +*Examples:* + + join_keys_to_values({'a'=>1,'b'=>2}, " is ") + +Would result in: ["a is 1","b is 2"] + EOS + ) do |arguments| + + # Validate the number of arguments. + if arguments.size != 2 + raise(Puppet::ParseError, "join_keys_to_values(): Takes exactly two " + + "arguments, but #{arguments.size} given.") + end + + # Validate the first argument. + hash = arguments[0] + if not hash.is_a?(Hash) + raise(TypeError, "join_keys_to_values(): The first argument must be a " + + "hash, but a #{hash.class} was given.") + end + + # Validate the second argument. + separator = arguments[1] + if not separator.is_a?(String) + raise(TypeError, "join_keys_to_values(): The second argument must be a " + + "string, but a #{separator.class} was given.") + end + + # Join the keys to their values. + hash.map do |k,v| + String(k) + separator + String(v) + end + + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/loadyaml.rb b/lib/puppet/parser/functions/loadyaml.rb index 0f16f69..10c4005 100644 --- a/lib/puppet/parser/functions/loadyaml.rb +++ b/lib/puppet/parser/functions/loadyaml.rb @@ -1,12 +1,12 @@ module Puppet::Parser::Functions newfunction(:loadyaml, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - Load a YAML file and return the data if it contains an Array, String, or Hash - as a Puppet variable. + Load a YAML file containing an array, string, or hash, and return the data + in the corresponding native data type. For example: - $myhash = loadyaml('/etc/puppet/data/myhash.yaml') + $myhash = loadyaml('/etc/puppet/data/myhash.yaml') ENDHEREDOC unless args.length == 1 diff --git a/lib/puppet/parser/functions/max.rb b/lib/puppet/parser/functions/max.rb new file mode 100644 index 0000000..60fb94a --- /dev/null +++ b/lib/puppet/parser/functions/max.rb @@ -0,0 +1,21 @@ +module Puppet::Parser::Functions + newfunction(:max, :type => :rvalue, :doc => <<-EOS + Returns the highest value of all arguments. + Requires at least one argument. + EOS + ) do |args| + + raise(Puppet::ParseError, "max(): Wrong number of arguments " + + "need at least one") if args.size == 0 + + # Sometimes we get numbers as numerics and sometimes as strings. + # We try to compare them as numbers when possible + return args.max do |a,b| + if a.to_s =~ /\A-?\d+(.\d+)?\z/ and b.to_s =~ /\A-?\d+(.\d+)?\z/ then + a.to_f <=> b.to_f + else + a.to_s <=> b.to_s + end + end + end +end diff --git a/lib/puppet/parser/functions/merge.rb b/lib/puppet/parser/functions/merge.rb index d2dc0f9..1b39f20 100644 --- a/lib/puppet/parser/functions/merge.rb +++ b/lib/puppet/parser/functions/merge.rb @@ -4,10 +4,13 @@ module Puppet::Parser::Functions For example: - $hash1 = {'one' => 1, 'two', => 2} - $hash1 = {'two' => 2, 'three', => 2} - $merged_hash = merge($hash1, $hash2) - # merged_hash = {'one' => 1, 'two' => 2, 'three' => 2} + $hash1 = {'one' => 1, 'two', => 2} + $hash2 = {'two' => 'dos', 'three', => 'tres'} + $merged_hash = merge($hash1, $hash2) + # The resulting hash is equivalent to: + # $merged_hash = {'one' => 1, 'two' => 'dos', 'three' => 'tres'} + + When there is a duplicate key, the key in the rightmost hash will "win." ENDHEREDOC @@ -19,6 +22,7 @@ module Puppet::Parser::Functions accumulator = Hash.new # Merge into the accumulator hash args.each do |arg| + next if arg.is_a? String and arg.empty? # empty string is synonym for puppet's undef unless arg.is_a?(Hash) raise Puppet::ParseError, "merge: unexpected argument type #{arg.class}, only expects hash arguments" end diff --git a/lib/puppet/parser/functions/min.rb b/lib/puppet/parser/functions/min.rb new file mode 100644 index 0000000..6bd6ebf --- /dev/null +++ b/lib/puppet/parser/functions/min.rb @@ -0,0 +1,21 @@ +module Puppet::Parser::Functions + newfunction(:min, :type => :rvalue, :doc => <<-EOS + Returns the lowest value of all arguments. + Requires at least one argument. + EOS + ) do |args| + + raise(Puppet::ParseError, "min(): Wrong number of arguments " + + "need at least one") if args.size == 0 + + # Sometimes we get numbers as numerics and sometimes as strings. + # We try to compare them as numbers when possible + return args.min do |a,b| + if a.to_s =~ /\A^-?\d+(.\d+)?\z/ and b.to_s =~ /\A-?\d+(.\d+)?\z/ then + a.to_f <=> b.to_f + else + a.to_s <=> b.to_s + end + end + end +end diff --git a/lib/puppet/parser/functions/num2bool.rb b/lib/puppet/parser/functions/num2bool.rb 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/pick.rb b/lib/puppet/parser/functions/pick.rb new file mode 100644 index 0000000..cbc0300 --- /dev/null +++ b/lib/puppet/parser/functions/pick.rb @@ -0,0 +1,29 @@ +module Puppet::Parser::Functions + newfunction(:pick, :type => :rvalue, :doc => <<-EOS + +This function is similar to a coalesce function in SQL in that it will return +the first value in a list of values that is not undefined or an empty string +(two things in Puppet that will return a boolean false value). Typically, +this function is used to check for a value in the Puppet Dashboard/Enterprise +Console, and failover to a default value like the following: + + $real_jenkins_version = pick($::jenkins_version, '1.449') + +The value of $real_jenkins_version will first look for a top-scope variable +called 'jenkins_version' (note that parameters set in the Puppet Dashboard/ +Enterprise Console are brought into Puppet as top-scope variables), and, +failing that, will use a default value of 1.449. + +EOS +) do |args| + args = args.compact + args.delete(:undef) + args.delete(:undefined) + args.delete("") + if args[0].to_s.empty? then + fail "Must provide non empty value." + else + return args[0] + end + end +end 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/range.rb b/lib/puppet/parser/functions/range.rb index 03ab9e9..0849491 100644 --- a/lib/puppet/parser/functions/range.rb +++ b/lib/puppet/parser/functions/range.rb @@ -15,10 +15,19 @@ an array. Will return: [0,1,2,3,4,5,6,7,8,9] + range("00", "09") + +Will return: [0,1,2,3,4,5,6,7,8,9] (Zero padded strings are converted to +integers automatically) + range("a", "c") Will return: ["a","b","c"] + range("host01", "host10") + +Will return: ["host01", "host02", ..., "host09", "host10"] + Passing a third argument will cause the generated range to step by that interval, e.g. diff --git a/lib/puppet/parser/functions/reject.rb b/lib/puppet/parser/functions/reject.rb new file mode 100644 index 0000000..1953ffc --- /dev/null +++ b/lib/puppet/parser/functions/reject.rb @@ -0,0 +1,31 @@ +# +# reject.rb +# + +module Puppet::Parser::Functions + newfunction(:reject, :type => :rvalue, :doc => <<-EOS) do |args| +This function searches through an array and rejects all elements that match +the provided regular expression. + +*Examples:* + + reject(['aaa','bbb','ccc','aaaddd'], 'aaa') + +Would return: + + ['bbb','ccc'] +EOS + + if (args.size != 2) + raise Puppet::ParseError, + "reject(): Wrong number of arguments given #{args.size} for 2" + end + + ary = args[0] + pattern = Regexp.new(args[1]) + + ary.reject { |e| e =~ pattern } + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/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 c320da6..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 @@ -14,6 +14,11 @@ like: 0, f, n, false, no to 'false'. "given (#{arguments.size} for 1)") if arguments.size < 1 string = arguments[0] + + # If string is already Boolean, return it + if !!string == string + return string + end unless string.is_a?(String) raise(Puppet::ParseError, 'str2bool(): Requires either ' + diff --git a/lib/puppet/parser/functions/str2saltedsha512.rb b/lib/puppet/parser/functions/str2saltedsha512.rb new file mode 100644 index 0000000..7fe7b01 --- /dev/null +++ b/lib/puppet/parser/functions/str2saltedsha512.rb @@ -0,0 +1,32 @@ +# +# str2saltedsha512.rb +# + +module Puppet::Parser::Functions + newfunction(:str2saltedsha512, :type => :rvalue, :doc => <<-EOS +This converts a string to a salted-SHA512 password hash (which is used for +OS X versions >= 10.7). Given any simple string, you will get a hex version +of a salted-SHA512 password hash that can be inserted into your Puppet +manifests as a valid password attribute. + EOS + ) do |arguments| + require 'digest/sha2' + + raise(Puppet::ParseError, "str2saltedsha512(): Wrong number of arguments " + + "passed (#{arguments.size} but we require 1)") if arguments.size != 1 + + password = arguments[0] + + unless password.is_a?(String) + raise(Puppet::ParseError, 'str2saltedsha512(): Requires a ' + + "String argument, you passed: #{password.class}") + end + + seedint = rand(2**31 - 1) + seedstring = Array(seedint).pack("L") + saltedpass = Digest::SHA512.digest(seedstring + password) + (seedstring + saltedpass).unpack('H*')[0] + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/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/to_bytes.rb b/lib/puppet/parser/functions/to_bytes.rb new file mode 100644 index 0000000..8ff73d1 --- /dev/null +++ b/lib/puppet/parser/functions/to_bytes.rb @@ -0,0 +1,28 @@ +module Puppet::Parser::Functions + newfunction(:to_bytes, :type => :rvalue, :doc => <<-EOS + Converts the argument into bytes, for example 4 kB becomes 4096. + Takes a single string value as an argument. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "to_bytes(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + + arg = arguments[0] + + return arg if arg.is_a? Numeric + + value,prefix = */([0-9.e+-]*)\s*([^bB]?)/.match(arg)[1,2] + + value = value.to_f + case prefix + when '' then return value.to_i + when 'k' then return (value*(1<<10)).to_i + when 'M' then return (value*(1<<20)).to_i + when 'G' then return (value*(1<<30)).to_i + when 'T' then return (value*(1<<40)).to_i + when 'E' then return (value*(1<<50)).to_i + else raise Puppet::ParseError, "to_bytes(): Unknown prefix #{prefix}" + end + end +end diff --git a/lib/puppet/parser/functions/union.rb b/lib/puppet/parser/functions/union.rb new file mode 100644 index 0000000..c91bb80 --- /dev/null +++ b/lib/puppet/parser/functions/union.rb @@ -0,0 +1,34 @@ +# +# union.rb +# + +module Puppet::Parser::Functions + newfunction(:union, :type => :rvalue, :doc => <<-EOS +This function returns a union of two arrays. + +*Examples:* + + union(["a","b","c"],["b","c","d"]) + +Would return: ["a","b","c","d"] + EOS + ) do |arguments| + + # Two arguments are required + raise(Puppet::ParseError, "union(): Wrong number of arguments " + + "given (#{arguments.size} for 2)") if arguments.size != 2 + + first = arguments[0] + second = arguments[1] + + unless first.is_a?(Array) && second.is_a?(Array) + raise(Puppet::ParseError, 'union(): Requires 2 arrays') + end + + result = first | second + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/uriescape.rb b/lib/puppet/parser/functions/uriescape.rb new file mode 100644 index 0000000..67b93a6 --- /dev/null +++ b/lib/puppet/parser/functions/uriescape.rb @@ -0,0 +1,36 @@ +# +# uriescape.rb +# +require 'uri' + +module Puppet::Parser::Functions + newfunction(:uriescape, :type => :rvalue, :doc => <<-EOS + Urlencodes a string or array of strings. + Requires either a single string or an array as an input. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "uriescape(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size < 1 + + value = arguments[0] + klass = value.class + unsafe = ":/?#[]@!$&'()*+,;= " + + unless [Array, String].include?(klass) + raise(Puppet::ParseError, 'uriescape(): Requires either ' + + 'array or string to work with') + end + + if value.is_a?(Array) + # Numbers in Puppet are often string-encoded which is troublesome ... + result = value.collect { |i| i.is_a?(String) ? URI.escape(i,unsafe) : i } + else + result = URI.escape(value,unsafe) + end + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/validate_absolute_path.rb b/lib/puppet/parser/functions/validate_absolute_path.rb new file mode 100644 index 0000000..fe27974 --- /dev/null +++ b/lib/puppet/parser/functions/validate_absolute_path.rb @@ -0,0 +1,56 @@ +module Puppet::Parser::Functions + newfunction(:validate_absolute_path, :doc => <<-'ENDHEREDOC') do |args| + Validate the string represents an absolute path in the filesystem. This function works + for windows and unix style paths. + + The following values will pass: + + $my_path = "C:/Program Files (x86)/Puppet Labs/Puppet" + validate_absolute_path($my_path) + $my_path2 = "/var/lib/puppet" + validate_absolute_path($my_path2) + + + The following values will fail, causing compilation to abort: + + validate_absolute_path(true) + validate_absolute_path([ 'var/lib/puppet', '/var/foo' ]) + validate_absolute_path([ '/var/lib/puppet', 'var/foo' ]) + $undefined = undef + validate_absolute_path($undefined) + + ENDHEREDOC + + require 'puppet/util' + + unless args.length > 0 then + raise Puppet::ParseError, ("validate_absolute_path(): wrong number of arguments (#{args.length}; must be > 0)") + end + + args.each do |arg| + # This logic was borrowed from + # [lib/puppet/file_serving/base.rb](https://github.com/puppetlabs/puppet/blob/master/lib/puppet/file_serving/base.rb) + + # Puppet 2.7 and beyond will have Puppet::Util.absolute_path? Fall back to a back-ported implementation otherwise. + if Puppet::Util.respond_to?(:absolute_path?) then + unless Puppet::Util.absolute_path?(arg, :posix) or Puppet::Util.absolute_path?(arg, :windows) + raise Puppet::ParseError, ("#{arg.inspect} is not an absolute path.") + end + else + # This code back-ported from 2.7.x's lib/puppet/util.rb Puppet::Util.absolute_path? + # Determine in a platform-specific way whether a path is absolute. This + # defaults to the local platform if none is specified. + # Escape once for the string literal, and once for the regex. + slash = '[\\\\/]' + name = '[^\\\\/]+' + regexes = { + :windows => %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i, + :posix => %r!^/!, + } + + rval = (!!(arg =~ regexes[:posix])) || (!!(arg =~ regexes[:windows])) + rval or raise Puppet::ParseError, ("#{arg.inspect} is not an absolute path.") + end + end + end +end diff --git a/lib/puppet/parser/functions/validate_array.rb b/lib/puppet/parser/functions/validate_array.rb index a7a7165..34b5118 100644 --- a/lib/puppet/parser/functions/validate_array.rb +++ b/lib/puppet/parser/functions/validate_array.rb @@ -1,17 +1,15 @@ module Puppet::Parser::Functions newfunction(:validate_array, :doc => <<-'ENDHEREDOC') do |args| - Validate all passed values are a Array data structure - value does not pass the check. + Validate that all passed values are array data structures. Abort catalog + compilation if any value fails this check. - Example: - - These values validate + The following values will pass: $my_array = [ 'one', 'two' ] validate_array($my_array) - These values do NOT validate + The following values will fail, causing compilation to abort: validate_array(true) validate_array('some_string') 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_bool.rb b/lib/puppet/parser/functions/validate_bool.rb index 49e6378..62c1d88 100644 --- a/lib/puppet/parser/functions/validate_bool.rb +++ b/lib/puppet/parser/functions/validate_bool.rb @@ -1,27 +1,22 @@ module Puppet::Parser::Functions newfunction(:validate_bool, :doc => <<-'ENDHEREDOC') do |args| - Validate all passed values are true or false. Abort catalog compilation if the - value does not pass the check. + Validate that all passed values are either true or false. Abort catalog + compilation if any value fails this check. - Example: - - These booleans validate + The following values will pass: $iamtrue = true validate_bool(true) validate_bool(true, true, false, $iamtrue) - These strings do NOT validate and will abort catalog compilation + The following values will fail, causing compilation to abort: $some_array = [ true ] validate_bool("false") validate_bool("true") validate_bool($some_array) - * Jeff McCune <jeff@puppetlabs.com> - * Dan Bode <dan@puppetlabs.com> - ENDHEREDOC unless args.length > 0 then 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/lib/puppet/parser/functions/validate_hash.rb b/lib/puppet/parser/functions/validate_hash.rb index 1443318..9bdd543 100644 --- a/lib/puppet/parser/functions/validate_hash.rb +++ b/lib/puppet/parser/functions/validate_hash.rb @@ -1,25 +1,21 @@ module Puppet::Parser::Functions newfunction(:validate_hash, :doc => <<-'ENDHEREDOC') do |args| - Validate all passed values are a Hash data structure - value does not pass the check. + Validate that all passed values are hash data structures. Abort catalog + compilation if any value fails this check. - Example: - - These values validate + The following values will pass: $my_hash = { 'one' => 'two' } validate_hash($my_hash) - These values do NOT validate + The following values will fail, causing compilation to abort: validate_hash(true) validate_hash('some_string') $undefined = undef validate_hash($undefined) - * Jeff McCune <jeff@puppetlabs.com> - ENDHEREDOC unless args.length > 0 then diff --git a/lib/puppet/parser/functions/validate_re.rb b/lib/puppet/parser/functions/validate_re.rb index 583f26a..ca25a70 100644 --- a/lib/puppet/parser/functions/validate_re.rb +++ b/lib/puppet/parser/functions/validate_re.rb @@ -1,35 +1,40 @@ module Puppet::Parser::Functions - newfunction(:validate_re, :doc => <<-'ENDHEREDOC') do |args| - Perform simple validation of a string against a regular expression. The second - argument of the function should be a string regular expression (without the //'s) - or an array of regular expressions. If none of the regular expressions in the array - match the string passed in, then an exception will be raised. + Perform simple validation of a string against one or more regular + expressions. The first argument of this function should be a string to + test, and the second argument should be a stringified regular expression + (without the // delimiters) or an array of regular expressions. If none + of the regular expressions match the string passed in, compilation will + abort with a parse error. - Example: + If a third argument is specified, this will be the error message raised and + seen by the user. - These strings validate against the regular expressions + The following strings will validate against the regular expressions: validate_re('one', '^one$') validate_re('one', [ '^one', '^two' ]) - These strings do NOT validate + The following strings will fail to validate, causing compilation to abort: validate_re('one', [ '^two', '^three' ]) - Jeff McCune <jeff@puppetlabs.com> + A helpful error message can be returned like this: + + validate_re($::puppetversion, '^2.7', 'The $puppetversion fact value does not match 2.7') ENDHEREDOC - if args.length != 2 then - raise Puppet::ParseError, ("validate_re(): wrong number of arguments (#{args.length}; must be 2)") + if (args.length < 2) or (args.length > 3) then + raise Puppet::ParseError, ("validate_re(): wrong number of arguments (#{args.length}; must be 2 or 3)") end - msg = "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}" + msg = args[2] || "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}" - raise Puppet::ParseError, (msg) unless args[1].any? do |re_str| + # We're using a flattened array here because we can't call String#any? in + # Ruby 1.9 like we can in Ruby 1.8 + raise Puppet::ParseError, (msg) unless [args[1]].flatten.any? do |re_str| args[0] =~ Regexp.compile(re_str) end end - end diff --git a/lib/puppet/parser/functions/validate_slength.rb b/lib/puppet/parser/functions/validate_slength.rb new file mode 100644 index 0000000..fdcc0a2 --- /dev/null +++ b/lib/puppet/parser/functions/validate_slength.rb @@ -0,0 +1,52 @@ +module Puppet::Parser::Functions + + newfunction(:validate_slength, :doc => <<-'ENDHEREDOC') do |args| + Validate that the first argument is a string (or an array of strings), and + less/equal to than the length of the second argument. It fails if the first + argument is not a string or array of strings, and if arg 2 is not convertable + to a number. + + The following values will pass: + + validate_slength("discombobulate",17) + validate_slength(["discombobulate","moo"],17) + + The following valueis will not: + + validate_slength("discombobulate",1) + validate_slength(["discombobulate","thermometer"],5) + + ENDHEREDOC + + raise Puppet::ParseError, ("validate_slength(): Wrong number of arguments (#{args.length}; must be = 2)") unless args.length == 2 + + unless (args[0].is_a?(String) or args[0].is_a?(Array)) + raise Puppet::ParseError, ("validate_slength(): please pass a string, or an array of strings - what you passed didn't work for me at all - #{args[0].class}") + end + + begin + max_length = args[1].to_i + rescue NoMethodError => e + raise Puppet::ParseError, ("validate_slength(): Couldn't convert whatever you passed as the length parameter to an integer - sorry: " + e.message ) + end + + raise Puppet::ParseError, ("validate_slength(): please pass a positive number as max_length") unless max_length > 0 + + case args[0] + when String + raise Puppet::ParseError, ("validate_slength(): #{args[0].inspect} is #{args[0].length} characters. It should have been less than or equal to #{max_length} characters") unless args[0].length <= max_length + when Array + args[0].each do |arg| + if arg.is_a?(String) + unless ( arg.is_a?(String) and arg.length <= max_length ) + raise Puppet::ParseError, ("validate_slength(): #{arg.inspect} is #{arg.length} characters. It should have been less than or equal to #{max_length} characters") + end + else + raise Puppet::ParseError, ("validate_slength(): #{arg.inspect} is not a string, it's a #{arg.class}") + end + end + else + raise Puppet::ParseError, ("validate_slength(): please pass a string, or an array of strings - what you passed didn't work for me at all - #{args[0].class}") + end + end +end diff --git a/lib/puppet/parser/functions/validate_string.rb b/lib/puppet/parser/functions/validate_string.rb index d0e1376..e667794 100644 --- a/lib/puppet/parser/functions/validate_string.rb +++ b/lib/puppet/parser/functions/validate_string.rb @@ -1,17 +1,15 @@ module Puppet::Parser::Functions newfunction(:validate_string, :doc => <<-'ENDHEREDOC') do |args| - Validate all passed values are a string data structure - value does not pass the check. + Validate that all passed values are string data structures. Abort catalog + compilation if any value fails this check. - Example: - - These values validate + The following values will pass: $my_string = "one two" - validate_string($my_string) + validate_string($my_string, 'three') - These values do NOT validate + The following values will fail, causing compilation to abort: validate_string(true) validate_string([ 'some', 'array' ]) diff --git a/lib/puppet/provider/file_line/ruby.rb b/lib/puppet/provider/file_line/ruby.rb index 63bbd8e..a3219d3 100644 --- a/lib/puppet/provider/file_line/ruby.rb +++ b/lib/puppet/provider/file_line/ruby.rb @@ -1,15 +1,59 @@ + Puppet::Type.type(:file_line).provide(:ruby) do def exists? - File.readlines(resource[:path]).find do |line| + lines.find do |line| line.chomp == resource[:line].chomp end end def create + if resource[:match] + handle_create_with_match() + else + handle_create_without_match() + end + end + + def destroy + local_lines = lines + File.open(resource[:path],'w') do |fh| + fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join('')) + end + end + + private + def lines + # If this type is ever used with very large files, we should + # write this in a different way, using a temp + # file; for now assuming that this type is only used on + # small-ish config files that can fit into memory without + # too much trouble. + @lines ||= File.readlines(resource[:path]) + end + + def handle_create_with_match() + regex = resource[:match] ? Regexp.new(resource[:match]) : nil + match_count = lines.select { |l| regex.match(l) }.size + if match_count > 1 + raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'" + end + File.open(resource[:path], 'w') do |fh| + lines.each do |l| + fh.puts(regex.match(l) ? resource[:line] : l) + end + + if (match_count == 0) + fh.puts(resource[:line]) + end + end + end + + def handle_create_without_match File.open(resource[:path], 'a') do |fh| fh.puts resource[:line] end end + end diff --git a/lib/puppet/type/anchor.rb b/lib/puppet/type/anchor.rb index 0c28b1c..fe1e5aa 100644 --- a/lib/puppet/type/anchor.rb +++ b/lib/puppet/type/anchor.rb @@ -2,23 +2,32 @@ Puppet::Type.newtype(:anchor) do desc <<-'ENDOFDESC' A simple resource type intended to be used as an anchor in a composite class. + In Puppet 2.6, when a class declares another class, the resources in the + interior class are not contained by the exterior class. This interacts badly + with the pattern of composing complex modules from smaller classes, as it + makes it impossible for end users to specify order relationships between the + exterior class and other modules. + + The anchor type lets you work around this. By sandwiching any interior + classes between two no-op resources that _are_ contained by the exterior + class, you can ensure that all resources in the module are contained. + class ntp { + # These classes will have the correct order relationship with each + # other. However, without anchors, they won't have any order + # relationship to Class['ntp']. class { 'ntp::package': } -> class { 'ntp::config': } -> class { 'ntp::service': } - # These two resources "anchor" the composed classes - # such that the end user may use "require" and "before" - # relationships with Class['ntp'] - anchor { 'ntp::begin': } -> class { 'ntp::package': } - class { 'ntp::service': } -> anchor { 'ntp::end': } + # These two resources "anchor" the composed classes within the ntp + # class. + anchor { 'ntp::begin': } -> Class['ntp::package'] + Class['ntp::service'] -> anchor { 'ntp::end': } } - This resource allows all of the classes in the ntp module to be contained - within the ntp class from a dependency management point of view. - This allows the end user of the ntp module to establish require and before - relationships easily: + relationships with Class['ntp']: class { 'ntp': } -> class { 'mcollective': } class { 'mcollective': } -> class { 'ntp': } @@ -29,4 +38,9 @@ Puppet::Type.newtype(:anchor) do desc "The name of the anchor resource." end + def refresh + # We don't do anything with them, but we need this to + # show that we are "refresh aware" and not break the + # chain of propagation. + end end diff --git a/lib/puppet/type/file_line.rb b/lib/puppet/type/file_line.rb index aacd6d9..f71a4bc 100644 --- a/lib/puppet/type/file_line.rb +++ b/lib/puppet/type/file_line.rb @@ -1,34 +1,48 @@ Puppet::Type.newtype(:file_line) do desc <<-EOT - Type that can append whole a line to a file if it does not already contain it. + Ensures that a given line is contained within a file. The implementation + matches the full line, including whitespace at the beginning and end. If + the line is not contained in the given file, Puppet will add the line to + ensure the desired state. Multiple resources may be declared to manage + multiple lines in the same file. - Example: + Example: - file_line { 'sudo_rule': - path => '/etc/sudoers', - line => '%admin ALL=(ALL) ALL', - } + file_line { 'sudo_rule': + path => '/etc/sudoers', + line => '%sudo ALL=(ALL) ALL', + } + file_line { 'sudo_rule_nopw': + path => '/etc/sudoers', + line => '%sudonopw ALL=(ALL) NOPASSWD: ALL', + } + + In this example, Puppet will ensure both of the specified lines are + contained in the file /etc/sudoers. EOT ensurable do + defaultvalues defaultto :present - newvalue(:present) do - provider.create - end end newparam(:name, :namevar => true) do - desc 'arbitrary name used as identity' + desc 'An arbitrary name used as the identity of the resource.' + end + + newparam(:match) do + desc 'An optional regular expression to run against existing lines in the file;\n' + + 'if a match is found, we replace that line rather than adding a new line.' end newparam(:line) do - desc 'The line to be appended to the path.' + desc 'The line to be appended to the file located by the path parameter.' end newparam(:path) do - desc 'File to possibly append a line to.' + desc 'The file Puppet will ensure contains the line specified by the line parameter.' validate do |value| unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) raise(Puppet::Error, "File paths must be fully qualified, not '#{value}'") @@ -36,9 +50,21 @@ Puppet::Type.newtype(:file_line) do end end + # Autorequire the file resource if it's being managed + autorequire(:file) do + self[:path] + end + validate do unless self[:line] and self[:path] raise(Puppet::Error, "Both line and path are required attributes") end + + if (self[:match]) + unless Regexp.new(self[:match]).match(self[:line]) + raise(Puppet::Error, "When providing a 'match' parameter, the value must be a regex that matches against the value of your 'line' parameter") + end + end + end end diff --git a/manifests/init.pp b/manifests/init.pp index c804568..500ad77 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,16 +1,18 @@ # Class: stdlib # -# This module manages stdlib +# This module manages stdlib. Most of stdlib's features are automatically +# loaded by Puppet, but this class should be declared in order to use the +# standardized run stages. # -# Parameters: +# Parameters: none # # Actions: # -# Requires: +# Declares all other classes in the stdlib module. Currently, this consists +# of stdlib::stages. # -# Sample Usage: +# Requires: nothing # -# [Remember: No empty lines between comments and class definition] class stdlib { class { 'stdlib::stages': } diff --git a/manifests/stages.pp b/manifests/stages.pp index 365b905..eb15fd6 100644 --- a/manifests/stages.pp +++ b/manifests/stages.pp @@ -1,8 +1,9 @@ # Class: stdlib::stages # -# This class manages a standard set of Run Stages for Puppet. +# This class manages a standard set of run stages for Puppet. It is managed by +# the stdlib class, and should not be declared independently. # -# The high level stages are (In order): +# The high level stages are (in order): # # * setup # * main @@ -13,19 +14,19 @@ # * deploy_app # * deploy # -# Parameters: +# Parameters: none # # Actions: # # Declares various run-stages for deploying infrastructure, # language runtimes, and application layers. # -# Requires: +# Requires: nothing # # Sample Usage: # # node default { -# include stdlib::stages +# include stdlib # class { java: stage => 'runtime' } # } # diff --git a/spec/classes/anchor_spec.rb b/spec/classes/anchor_spec.rb new file mode 100644 index 0000000..2dd17de --- /dev/null +++ b/spec/classes/anchor_spec.rb @@ -0,0 +1,32 @@ +require 'puppet' +require 'rspec-puppet' + +describe "anchorrefresh" do + let(:node) { 'testhost.example.com' } + let :pre_condition do + <<-ANCHORCLASS +class anchored { + anchor { 'anchored::begin': } + ~> anchor { 'anchored::end': } +} + +class anchorrefresh { + notify { 'first': } + ~> class { 'anchored': } + ~> anchor { 'final': } +} + ANCHORCLASS + end + + def apply_catalog_and_return_exec_rsrc + catalog = subject.to_ral + transaction = catalog.apply + transaction.resource_status("Anchor[final]") + end + + it 'propagates events through the anchored class' do + resource = apply_catalog_and_return_exec_rsrc + + expect(resource.restarted).to eq(true) + end +end diff --git a/spec/functions/defined_with_params_spec.rb b/spec/functions/defined_with_params_spec.rb new file mode 100644 index 0000000..28dbab3 --- /dev/null +++ b/spec/functions/defined_with_params_spec.rb @@ -0,0 +1,37 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +require 'rspec-puppet' +describe 'defined_with_params' do + describe 'when a resource is not specified' do + it { should run.with_params().and_raise_error(ArgumentError) } + end + describe 'when compared against a resource with no attributes' do + let :pre_condition do + 'user { "dan": }' + end + it do + should run.with_params('User[dan]', {}).and_return(true) + should run.with_params('User[bob]', {}).and_return(false) + should run.with_params('User[dan]', {'foo' => 'bar'}).and_return(false) + end + end + + describe 'when compared against a resource with attributes' do + let :pre_condition do + 'user { "dan": ensure => present, shell => "/bin/csh", managehome => false}' + end + it do + should run.with_params('User[dan]', {}).and_return(true) + should run.with_params('User[dan]', '').and_return(true) + should run.with_params('User[dan]', {'ensure' => 'present'} + ).and_return(true) + should run.with_params('User[dan]', + {'ensure' => 'present', 'managehome' => false} + ).and_return(true) + should run.with_params('User[dan]', + {'ensure' => 'absent', 'managehome' => false} + ).and_return(false) + end + end +end diff --git a/spec/functions/ensure_packages_spec.rb b/spec/functions/ensure_packages_spec.rb new file mode 100644 index 0000000..1c2a328 --- /dev/null +++ b/spec/functions/ensure_packages_spec.rb @@ -0,0 +1,42 @@ +#! /usr/bin/env ruby + +require 'spec_helper' +require 'rspec-puppet' + +describe 'ensure_packages' do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + describe 'argument handling' do + it 'fails with no arguments' do + should run.with_params().and_raise_error(Puppet::ParseError) + end + it 'requires an array' do + lambda { scope.function_ensure_packages([['foo']]) }.should_not raise_error + end + it 'fails when given a string' do + should run.with_params('foo').and_raise_error(Puppet::ParseError) + end + end + + context 'given a catalog containing Package[puppet]{ensure => absent}' do + let :pre_condition do + 'package { puppet: ensure => absent }' + end + + # NOTE: should run.with_params has the side effect of making the compiler + # available to the test harness. + it 'has no effect on Package[puppet]' do + should run.with_params(['puppet']) + rsrc = compiler.catalog.resource('Package[puppet]') + rsrc.to_hash.should == {:ensure => "absent"} + end + end + + context 'given a clean catalog' do + it 'declares package resources with ensure => present' do + should run.with_params(['facter']) + rsrc = compiler.catalog.resource('Package[facter]') + rsrc.to_hash.should == {:name => "facter", :ensure => "present"} + end + end +end diff --git a/spec/functions/ensure_resource_spec.rb b/spec/functions/ensure_resource_spec.rb new file mode 100644 index 0000000..2e8aefc --- /dev/null +++ b/spec/functions/ensure_resource_spec.rb @@ -0,0 +1,64 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +require 'rspec-puppet' +describe 'ensure_resource' do + describe 'when a type or title is not specified' do + it { should run.with_params().and_raise_error(ArgumentError) } + it { should run.with_params(['type']).and_raise_error(ArgumentError) } + end + + describe 'when compared against a resource with no attributes' do + let :pre_condition do + 'user { "dan": }' + end + it "should contain the the ensured resources" do + subject.should run.with_params('user', 'dan', {}) + compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' + end + end + + describe 'when compared against a resource with attributes' do + let :pre_condition do + 'user { "dan": ensure => present, shell => "/bin/csh", managehome => false}' + end + # these first three should not fail + it { should run.with_params('User', 'dan', {}) } + it { should run.with_params('User', 'dan', '') } + it { should run.with_params('User', 'dan', {'ensure' => 'present'}) } + it { should run.with_params('User', 'dan', {'ensure' => 'present', 'managehome' => false}) } + # test that this fails + it { should run.with_params('User', 'dan', {'ensure' => 'absent', 'managehome' => false}).and_raise_error(Puppet::Error) } + end + + describe 'when an array of new resources are passed in' do + it "should contain the ensured resources" do + subject.should run.with_params('User', ['dan', 'alex'], {}) + compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' + compiler.catalog.resource('User[alex]').to_s.should == 'User[alex]' + end + end + + describe 'when an array of existing resources is compared against existing resources' do + let :pre_condition do + 'user { "dan": ensure => present; "alex": ensure => present }' + end + it "should return the existing resources" do + subject.should run.with_params('User', ['dan', 'alex'], {}) + compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' + compiler.catalog.resource('User[alex]').to_s.should == 'User[alex]' + end + end + + describe 'when compared against existing resources with attributes' do + let :pre_condition do + 'user { "dan": ensure => present; "alex": ensure => present }' + end + # These should not fail + it { should run.with_params('User', ['dan', 'alex'], {}) } + it { should run.with_params('User', ['dan', 'alex'], '') } + it { should run.with_params('User', ['dan', 'alex'], {'ensure' => 'present'}) } + # This should fail + it { should run.with_params('User', ['dan', 'alex'], {'ensure' => 'absent'}).and_raise_error(Puppet::Error) } + 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/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb deleted file mode 100755 index 30fb4fc..0000000 --- a/spec/lib/puppet_spec/files.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'fileutils' -require 'tempfile' - -# A support module for testing files. -module PuppetSpec::Files - # This code exists only to support tests that run as root, pretty much. - # Once they have finally been eliminated this can all go... --daniel 2011-04-08 - if Puppet.features.posix? then - def self.in_tmp(path) - path =~ /^\/tmp/ or path =~ /^\/var\/folders/ - end - elsif Puppet.features.microsoft_windows? - def self.in_tmp(path) - tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp")) - path =~ /^#{tempdir}/ - end - else - fail "Help! Can't find in_tmp for this platform" - end - - def self.cleanup - $global_tempfiles ||= [] - while path = $global_tempfiles.pop do - fail "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path) - - begin - FileUtils.rm_r path, :secure => true - rescue Errno::ENOENT - # nothing to do - end - end - end - - def tmpfile(name) - # Generate a temporary file, just for the name... - source = Tempfile.new(name) - path = source.path - source.close! - - # ...record it for cleanup, - $global_tempfiles ||= [] - $global_tempfiles << File.expand_path(path) - - # ...and bam. - path - end - - def tmpdir(name) - path = tmpfile(name) - FileUtils.mkdir_p(path) - path - end -end diff --git a/spec/lib/puppet_spec/fixtures.rb b/spec/lib/puppet_spec/fixtures.rb deleted file mode 100755 index 7f6bc2a..0000000 --- a/spec/lib/puppet_spec/fixtures.rb +++ /dev/null @@ -1,28 +0,0 @@ -module PuppetSpec::Fixtures - def fixtures(*rest) - File.join(PuppetSpec::FIXTURE_DIR, *rest) - end - def my_fixture_dir - callers = caller - while line = callers.shift do - next unless found = line.match(%r{/spec/(.*)_spec\.rb:}) - return fixtures(found[1]) - end - fail "sorry, I couldn't work out your path from the caller stack!" - end - def my_fixture(name) - file = File.join(my_fixture_dir, name) - unless File.readable? file then - fail Puppet::DevError, "fixture '#{name}' for #{my_fixture_dir} is not readable" - end - return file - end - def my_fixtures(glob = '*', flags = 0) - files = Dir.glob(File.join(my_fixture_dir, glob), flags) - unless files.length > 0 then - fail Puppet::DevError, "fixture '#{glob}' for #{my_fixture_dir} had no files!" - end - block_given? and files.each do |file| yield file end - files - end -end diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb deleted file mode 100644 index 77f5803..0000000 --- a/spec/lib/puppet_spec/matchers.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'stringio' - -######################################################################## -# Backward compatibility for Jenkins outdated environment. -module RSpec - module Matchers - module BlockAliases - alias_method :to, :should unless method_defined? :to - alias_method :to_not, :should_not unless method_defined? :to_not - alias_method :not_to, :should_not unless method_defined? :not_to - end - end -end - - -######################################################################## -# Custom matchers... -RSpec::Matchers.define :have_matching_element do |expected| - match do |actual| - actual.any? { |item| item =~ expected } - end -end - - -RSpec::Matchers.define :exit_with do |expected| - actual = nil - match do |block| - begin - block.call - rescue SystemExit => e - actual = e.status - end - actual and actual == expected - end - failure_message_for_should do |block| - "expected exit with code #{expected} but " + - (actual.nil? ? " exit was not called" : "we exited with #{actual} instead") - end - failure_message_for_should_not do |block| - "expected that exit would not be called with #{expected}" - end - description do - "expect exit with #{expected}" - end -end - - -RSpec::Matchers.define :have_printed do |expected| - match do |block| - $stderr = $stdout = StringIO.new - - begin - block.call - ensure - $stdout.rewind - @actual = $stdout.read - - $stdout = STDOUT - $stderr = STDERR - end - - if @actual then - case expected - when String - @actual.include? expected - when Regexp - expected.match @actual - else - raise ArgumentError, "No idea how to match a #{@actual.class.name}" - end - end - end - - failure_message_for_should do |actual| - if actual.nil? then - "expected #{expected.inspect}, but nothing was printed" - else - "expected #{expected.inspect} to be printed; got:\n#{actual}" - end - end - - description do - "expect #{expected.inspect} to be printed" - end - - diffable -end diff --git a/spec/lib/puppet_spec/verbose.rb b/spec/lib/puppet_spec/verbose.rb deleted file mode 100755 index d9834f2..0000000 --- a/spec/lib/puppet_spec/verbose.rb +++ /dev/null @@ -1,9 +0,0 @@ -# Support code for running stuff with warnings disabled. -module Kernel - def with_verbose_disabled - verbose, $VERBOSE = $VERBOSE, nil - result = yield - $VERBOSE = verbose - return result - 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/spec_helper.rb b/spec/spec_helper.rb index 87aac34..931d35c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,6 @@ dir = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift File.join(dir, 'lib') -p dir - # Don't want puppet getting the command line arguments for rake or autotest ARGV.clear @@ -12,94 +10,19 @@ require 'mocha' gem 'rspec', '>=2.0.0' require 'rspec/expectations' -# So everyone else doesn't have to include this base constant. -module PuppetSpec - FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR) -end - -require 'pathname' -require 'tmpdir' - -require 'puppet_spec/verbose' -require 'puppet_spec/files' -require 'puppet_spec/fixtures' -require 'puppet_spec/matchers' -require 'monkey_patches/alias_should_to_must' -require 'monkey_patches/publicize_methods' - -# JJM Hack to make the stdlib tests run in Puppet 2.6 (See puppet commit cf183534) -if not Puppet.constants.include? "Test" then - module Puppet::Test - class LogCollector - def initialize(logs) - @logs = logs - end - - def <<(value) - @logs << value - end - end - end - Puppet::Util::Log.newdesttype :log_collector do - match "Puppet::Test::LogCollector" - - def initialize(messages) - @messages = messages - end - - def handle(msg) - @messages << msg - end - end -end - -Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| - require behaviour.relative_path_from(Pathname.new(dir)) -end +require 'puppetlabs_spec_helper/module_spec_helper' RSpec.configure do |config| - include PuppetSpec::Fixtures - - config.mock_with :mocha - + # FIXME REVISIT - We may want to delegate to Facter like we do in + # Puppet::PuppetSpecInitializer.initialize_via_testhelper(config) because + # this behavior is a duplication of the spec_helper in Facter. config.before :each do - GC.disable - - # these globals are set by Application - $puppet_application_mode = nil - $puppet_application_name = nil - - # REVISIT: I think this conceals other bad tests, but I don't have time to - # fully diagnose those right now. When you read this, please come tell me - # I suck for letting this float. --daniel 2011-04-21 - Signal.stubs(:trap) - - # Set the confdir and vardir to gibberish so that tests - # have to be correctly mocked. - Puppet[:confdir] = "/dev/null" - Puppet[:vardir] = "/dev/null" - - # Avoid opening ports to the outside world - Puppet.settings[:bindaddress] = "127.0.0.1" - - @logs = [] - Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs)) - - @log_level = Puppet::Util::Log.level - end - - config.after :each do - Puppet.settings.clear - Puppet::Node::Environment.clear - Puppet::Util::Storage.clear - Puppet::Util::ExecutionStub.reset if Puppet::Util.constants.include? "ExecutionStub" - - PuppetSpec::Files.cleanup - - @logs.clear - Puppet::Util::Log.close_all - Puppet::Util::Log.level = @log_level - - GC.enable + # Ensure that we don't accidentally cache facts and environment between + # test cases. This requires each example group to explicitly load the + # facts being exercised with something like + # Facter.collection.loader.load(:ipaddress) + Facter::Util::Loader.any_instance.stubs(:load_all) + Facter.clear + Facter.clear_messages end end diff --git a/spec/unit/facter/pe_required_facts_spec.rb b/spec/unit/facter/pe_required_facts_spec.rb new file mode 100644 index 0000000..f219b37 --- /dev/null +++ b/spec/unit/facter/pe_required_facts_spec.rb @@ -0,0 +1,69 @@ +# Puppet Enterprise requires the following facts to be set in order to operate. +# These facts are set using the file ???? and the two facts are +# `fact_stomp_port`, and `fact_stomp_server`. +# + +require 'spec_helper' + +describe "External facts in /etc/puppetlabs/facter/facts.d/puppet_enterprise_installer.txt" do + context "With Facter 1.6.17 which does not have external facts support" do + before :each do + Facter.stubs(:version).returns("1.6.17") + # Stub out the filesystem for stdlib + Dir.stubs(:entries).with("/etc/puppetlabs/facter/facts.d"). + returns(['puppet_enterprise_installer.txt']) + Dir.stubs(:entries).with("/etc/facter/facts.d").returns([]) + File.stubs(:readlines).with('/etc/puppetlabs/facter/facts.d/puppet_enterprise_installer.txt'). + returns([ + "fact_stomp_port=61613\n", + "fact_stomp_server=puppetmaster.acme.com\n", + "fact_is_puppetagent=true\n", + "fact_is_puppetmaster=false\n", + "fact_is_puppetca=false\n", + "fact_is_puppetconsole=false\n", + ]) + if Facter.collection.respond_to? :load + Facter.collection.load(:facter_dot_d) + else + Facter.collection.loader.load(:facter_dot_d) + end + end + + it 'defines fact_stomp_port' do + Facter.fact(:fact_stomp_port).value.should == '61613' + end + it 'defines fact_stomp_server' do + Facter.fact(:fact_stomp_server).value.should == 'puppetmaster.acme.com' + end + it 'defines fact_is_puppetagent' do + Facter.fact(:fact_is_puppetagent).value.should == 'true' + end + it 'defines fact_is_puppetmaster' do + Facter.fact(:fact_is_puppetmaster).value.should == 'false' + end + it 'defines fact_is_puppetca' do + Facter.fact(:fact_is_puppetca).value.should == 'false' + end + it 'defines fact_is_puppetconsole' do + Facter.fact(:fact_is_puppetconsole).value.should == 'false' + end + end + + [ '1.7.1', '2.0.1' ].each do |v| + context "With Facter #{v} which has external facts support" do + before :each do + Facter.stubs(:version).returns(v) + end + + it 'does not call Facter::Util::DotD.new' do + Facter::Util::DotD.expects(:new).never + + if Facter.collection.respond_to? :load + Facter.collection.load(:facter_dot_d) + else + Facter.collection.loader.load(:facter_dot_d) + end + end + end + end +end diff --git a/spec/unit/facter/pe_version_spec.rb b/spec/unit/facter/pe_version_spec.rb new file mode 100644 index 0000000..931c6d4 --- /dev/null +++ b/spec/unit/facter/pe_version_spec.rb @@ -0,0 +1,76 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +describe "PE Version specs" do + before :each do + # Explicitly load the pe_version.rb file which contains generated facts + # that cannot be automatically loaded. Puppet 2.x implements + # Facter.collection.load while Facter 1.x markes Facter.collection.load as + # a private method. + if Facter.collection.respond_to? :load + Facter.collection.load(:pe_version) + else + Facter.collection.loader.load(:pe_version) + end + end + + context "If PE is installed" do + %w{ 2.6.1 2.10.300 }.each do |version| + puppetversion = "2.7.19 (Puppet Enterprise #{version})" + context "puppetversion => #{puppetversion}" do + before :each do + Facter.fact(:puppetversion).stubs(:value).returns(puppetversion) + end + + (major,minor,patch) = version.split(".") + + it "Should return true" do + Facter.fact(:is_pe).value.should == true + end + + it "Should have a version of #{version}" do + Facter.fact(:pe_version).value.should == version + end + + it "Should have a major version of #{major}" do + Facter.fact(:pe_major_version).value.should == major + end + + it "Should have a minor version of #{minor}" do + Facter.fact(:pe_minor_version).value.should == minor + end + + it "Should have a patch version of #{patch}" do + Facter.fact(:pe_patch_version).value.should == patch + end + end + end + end + + context "When PE is not installed" do + before :each do + Facter.fact(:puppetversion).stubs(:value).returns("2.7.19") + end + + it "is_pe is false" do + Facter.fact(:is_pe).value.should == false + end + + it "pe_version is nil" do + Facter.fact(:pe_version).value.should be_nil + end + + it "pe_major_version is nil" do + Facter.fact(:pe_major_version).value.should be_nil + end + + it "pe_minor_version is nil" do + Facter.fact(:pe_minor_version).value.should be_nil + end + + it "Should have a patch version" do + Facter.fact(:pe_patch_version).value.should be_nil + end + end +end diff --git a/spec/unit/facter/root_home_spec.rb b/spec/unit/facter/root_home_spec.rb index 8946d9d..ce80684 100644 --- a/spec/unit/facter/root_home_spec.rb +++ b/spec/unit/facter/root_home_spec.rb @@ -30,13 +30,11 @@ describe Facter::Util::RootHome do end end context "windows" do - let(:root_ent) { "FIXME TBD on Windows" } - let(:expected_root_home) { "FIXME TBD on Windows" } - - it "should return FIXME TBD on windows" do - pending "FIXME: TBD on windows" - Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(root_ent) - Facter::Util::RootHome.get_root_home.should == expected_root_home + before :each do + Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(nil) + end + it "should be nil on windows" do + Facter::Util::RootHome.get_root_home.should be_nil end end end diff --git a/spec/unit/facter/util/puppet_settings_spec.rb b/spec/unit/facter/util/puppet_settings_spec.rb new file mode 100644 index 0000000..c3ce6ea --- /dev/null +++ b/spec/unit/facter/util/puppet_settings_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +require 'facter/util/puppet_settings' + +describe Facter::Util::PuppetSettings do + + describe "#with_puppet" do + context "Without Puppet loaded" do + before(:each) do + Module.expects(:const_get).with("Puppet").raises(NameError) + end + + it 'should be nil' do + subject.with_puppet { Puppet[:vardir] }.should be_nil + end + it 'should not yield to the block' do + Puppet.expects(:[]).never + subject.with_puppet { Puppet[:vardir] }.should be_nil + end + end + context "With Puppet loaded" do + module Puppet; end + let(:vardir) { "/var/lib/puppet" } + + before :each do + Puppet.expects(:[]).with(:vardir).returns vardir + end + it 'should yield to the block' do + subject.with_puppet { Puppet[:vardir] } + end + it 'should return the nodes vardir' do + subject.with_puppet { Puppet[:vardir] }.should eq vardir + end + end + end +end diff --git a/spec/unit/puppet/parser/functions/abs_spec.rb b/spec/unit/puppet/parser/functions/abs_spec.rb index 65ba2e8..c0b4297 100755 --- a/spec/unit/puppet/parser/functions/abs_spec.rb +++ b/spec/unit/puppet/parser/functions/abs_spec.rb @@ -1,31 +1,25 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec + require 'spec_helper' describe "the abs function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("abs").should == "function_abs" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_abs([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_abs([]) }.should( raise_error(Puppet::ParseError)) end it "should convert a negative number into a positive" do - result = @scope.function_abs(["-34"]) + result = scope.function_abs(["-34"]) result.should(eq(34)) end it "should do nothing with a positive number" do - result = @scope.function_abs(["5678"]) + result = scope.function_abs(["5678"]) result.should(eq(5678)) 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/base64_spec.rb b/spec/unit/puppet/parser/functions/base64_spec.rb new file mode 100755 index 0000000..5faa5e6 --- /dev/null +++ b/spec/unit/puppet/parser/functions/base64_spec.rb @@ -0,0 +1,34 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the base64 function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("base64").should == "function_base64" + end + + it "should raise a ParseError if there are other than 2 arguments" do + expect { scope.function_base64([]) }.to(raise_error(Puppet::ParseError)) + expect { scope.function_base64(["asdf"]) }.to(raise_error(Puppet::ParseError)) + expect { scope.function_base64(["asdf","moo","cow"]) }.to(raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if argument 1 isn't 'encode' or 'decode'" do + expect { scope.function_base64(["bees","astring"]) }.to(raise_error(Puppet::ParseError, /first argument must be one of/)) + end + + it "should raise a ParseError if argument 2 isn't a string" do + expect { scope.function_base64(["encode",["2"]]) }.to(raise_error(Puppet::ParseError, /second argument must be a string/)) + end + + it "should encode a encoded string" do + result = scope.function_base64(["encode",'thestring']) + result.should =~ /\AdGhlc3RyaW5n\n\Z/ + end + it "should decode a base64 encoded string" do + result = scope.function_base64(["decode",'dGhlc3RyaW5n']) + result.should == 'thestring' + end +end diff --git a/spec/unit/puppet/parser/functions/bool2num_spec.rb b/spec/unit/puppet/parser/functions/bool2num_spec.rb index d5da18c..518ac85 100755 --- a/spec/unit/puppet/parser/functions/bool2num_spec.rb +++ b/spec/unit/puppet/parser/functions/bool2num_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the bool2num function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("bool2num").should == "function_bool2num" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_bool2num([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_bool2num([]) }.should( raise_error(Puppet::ParseError)) end it "should convert true to 1" do - result = @scope.function_bool2num([true]) + result = scope.function_bool2num([true]) result.should(eq(1)) end it "should convert false to 0" do - result = @scope.function_bool2num([false]) + result = scope.function_bool2num([false]) result.should(eq(0)) end - end diff --git a/spec/unit/puppet/parser/functions/capitalize_spec.rb b/spec/unit/puppet/parser/functions/capitalize_spec.rb index 1c45821..69c9758 100755 --- a/spec/unit/puppet/parser/functions/capitalize_spec.rb +++ b/spec/unit/puppet/parser/functions/capitalize_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the capitalize function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("capitalize").should == "function_capitalize" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_capitalize([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_capitalize([]) }.should( raise_error(Puppet::ParseError)) end it "should capitalize the beginning of a string" do - result = @scope.function_capitalize(["abc"]) + result = scope.function_capitalize(["abc"]) result.should(eq("Abc")) end - end diff --git a/spec/unit/puppet/parser/functions/chomp_spec.rb b/spec/unit/puppet/parser/functions/chomp_spec.rb index 0592115..e425365 100755 --- a/spec/unit/puppet/parser/functions/chomp_spec.rb +++ b/spec/unit/puppet/parser/functions/chomp_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the chomp function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("chomp").should == "function_chomp" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_chomp([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_chomp([]) }.should( raise_error(Puppet::ParseError)) end it "should chomp the end of a string" do - result = @scope.function_chomp(["abc\n"]) + result = scope.function_chomp(["abc\n"]) result.should(eq("abc")) end - end diff --git a/spec/unit/puppet/parser/functions/chop_spec.rb b/spec/unit/puppet/parser/functions/chop_spec.rb index 0c456a8..9e466de 100755 --- a/spec/unit/puppet/parser/functions/chop_spec.rb +++ b/spec/unit/puppet/parser/functions/chop_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the chop function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("chop").should == "function_chop" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_chop([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_chop([]) }.should( raise_error(Puppet::ParseError)) end it "should chop the end of a string" do - result = @scope.function_chop(["asdf\n"]) + result = scope.function_chop(["asdf\n"]) result.should(eq("asdf")) 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/count_spec.rb b/spec/unit/puppet/parser/functions/count_spec.rb new file mode 100644 index 0000000..2453815 --- /dev/null +++ b/spec/unit/puppet/parser/functions/count_spec.rb @@ -0,0 +1,31 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the count function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("count").should == "function_count" + end + + it "should raise a ArgumentError if there is more than 2 arguments" do + lambda { scope.function_count(['foo', 'bar', 'baz']) }.should( raise_error(ArgumentError)) + end + + it "should be able to count arrays" do + scope.function_count([["1","2","3"]]).should(eq(3)) + end + + it "should be able to count matching elements in arrays" do + scope.function_count([["1", "2", "2"], "2"]).should(eq(2)) + end + + it "should not count nil or empty strings" do + scope.function_count([["foo","bar",nil,""]]).should(eq(2)) + end + + it 'does not count an undefined hash key or an out of bound array index (which are both :undef)' do + expect(scope.function_count([["foo",:undef,:undef]])).to eq(1) + end +end diff --git a/spec/unit/puppet/parser/functions/delete_at_spec.rb b/spec/unit/puppet/parser/functions/delete_at_spec.rb index 27db0c8..d8d9618 100755 --- a/spec/unit/puppet/parser/functions/delete_at_spec.rb +++ b/spec/unit/puppet/parser/functions/delete_at_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the delete_at function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("delete_at").should == "function_delete_at" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_delete_at([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_delete_at([]) }.should( raise_error(Puppet::ParseError)) end it "should delete an item at specified location from an array" do - result = @scope.function_delete_at([['a','b','c'],1]) + result = scope.function_delete_at([['a','b','c'],1]) result.should(eq(['a','c'])) end - end diff --git a/spec/unit/puppet/parser/functions/delete_spec.rb b/spec/unit/puppet/parser/functions/delete_spec.rb index fab3230..2f29c93 100755 --- a/spec/unit/puppet/parser/functions/delete_spec.rb +++ b/spec/unit/puppet/parser/functions/delete_spec.rb @@ -1,26 +1,38 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the delete function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("delete").should == "function_delete" end - before :each do - @scope = Puppet::Parser::Scope.new + it "should raise a ParseError if there are fewer than 2 arguments" do + lambda { scope.function_delete([]) }.should( raise_error(Puppet::ParseError)) end - it "should exist" do - Puppet::Parser::Functions.function("delete").should == "function_delete" + it "should raise a ParseError if there are greater than 2 arguments" do + lambda { scope.function_delete([[], 'foo', 'bar']) }.should( raise_error(Puppet::ParseError)) end - it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_delete([]) }.should( raise_error(Puppet::ParseError)) + it "should raise a TypeError if a number is passed as the first argument" do + lambda { scope.function_delete([1, 'bar']) }.should( raise_error(TypeError)) end - it "should delete an item from an array" do - result = @scope.function_delete([['a','b','c'],'b']) + it "should delete all instances of an element from an array" do + result = scope.function_delete([['a','b','c','b'],'b']) result.should(eq(['a','c'])) end + it "should delete all instances of a substring from a string" do + result = scope.function_delete(['foobarbabarz','bar']) + result.should(eq('foobaz')) + end + + it "should delete a key from a hash" do + result = scope.function_delete([{ 'a' => 1, 'b' => 2, 'c' => 3 },'b']) + result.should(eq({ 'a' => 1, 'c' => 3 })) + end + end diff --git a/spec/unit/puppet/parser/functions/difference_spec.rb b/spec/unit/puppet/parser/functions/difference_spec.rb new file mode 100644 index 0000000..9feff09 --- /dev/null +++ b/spec/unit/puppet/parser/functions/difference_spec.rb @@ -0,0 +1,19 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the difference function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("difference").should == "function_difference" + end + + it "should raise a ParseError if there are fewer than 2 arguments" do + lambda { scope.function_difference([]) }.should( raise_error(Puppet::ParseError) ) + end + + it "should return the difference between two arrays" do + result = scope.function_difference([["a","b","c"],["b","c","d"]]) + result.should(eq(["a"])) + end +end diff --git a/spec/unit/puppet/parser/functions/dirname_spec.rb b/spec/unit/puppet/parser/functions/dirname_spec.rb new file mode 100755 index 0000000..fb3b4fe --- /dev/null +++ b/spec/unit/puppet/parser/functions/dirname_spec.rb @@ -0,0 +1,24 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the dirname function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("dirname").should == "function_dirname" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_dirname([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should return dirname for an absolute path" do + result = scope.function_dirname(['/path/to/a/file.ext']) + result.should(eq('/path/to/a')) + end + + it "should return dirname for a relative path" do + result = scope.function_dirname(['path/to/a/file.ext']) + result.should(eq('path/to/a')) + end +end diff --git a/spec/unit/puppet/parser/functions/downcase_spec.rb b/spec/unit/puppet/parser/functions/downcase_spec.rb index 0bccd5f..acef1f0 100755 --- a/spec/unit/puppet/parser/functions/downcase_spec.rb +++ b/spec/unit/puppet/parser/functions/downcase_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the downcase function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("downcase").should == "function_downcase" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_downcase([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_downcase([]) }.should( raise_error(Puppet::ParseError)) end it "should downcase a string" do - result = @scope.function_downcase(["ASFD"]) + result = scope.function_downcase(["ASFD"]) result.should(eq("asfd")) end it "should do nothing to a string that is already downcase" do - result = @scope.function_downcase(["asdf asdf"]) + result = scope.function_downcase(["asdf asdf"]) result.should(eq("asdf asdf")) end - end diff --git a/spec/unit/puppet/parser/functions/empty_spec.rb b/spec/unit/puppet/parser/functions/empty_spec.rb index cb0021f..7745875 100755 --- a/spec/unit/puppet/parser/functions/empty_spec.rb +++ b/spec/unit/puppet/parser/functions/empty_spec.rb @@ -1,31 +1,23 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the empty function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("empty").should == "function_empty" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_empty([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_empty([]) }.should( raise_error(Puppet::ParseError)) end it "should return a true for an empty string" do - result = @scope.function_empty(['']) + result = scope.function_empty(['']) result.should(eq(true)) end it "should return a false for a non-empty string" do - result = @scope.function_empty(['asdf']) + result = scope.function_empty(['asdf']) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/flatten_spec.rb b/spec/unit/puppet/parser/functions/flatten_spec.rb index 7bedeb2..dba7a6b 100755 --- a/spec/unit/puppet/parser/functions/flatten_spec.rb +++ b/spec/unit/puppet/parser/functions/flatten_spec.rb @@ -1,31 +1,27 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the flatten function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("flatten").should == "function_flatten" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_flatten([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_flatten([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if there is more than 1 argument" do + lambda { scope.function_flatten([[], []]) }.should( raise_error(Puppet::ParseError)) end it "should flatten a complex data structure" do - result = @scope.function_flatten([["a","b",["c",["d","e"],"f","g"]]]) + result = scope.function_flatten([["a","b",["c",["d","e"],"f","g"]]]) result.should(eq(["a","b","c","d","e","f","g"])) end it "should do nothing to a structure that is already flat" do - result = @scope.function_flatten([["a","b","c","d"]]) + result = scope.function_flatten([["a","b","c","d"]]) result.should(eq(["a","b","c","d"])) 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 new file mode 100644 index 0000000..2577723 --- /dev/null +++ b/spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb @@ -0,0 +1,33 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the fqdn_rotate function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("fqdn_rotate").should == "function_fqdn_rotate" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_fqdn_rotate([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should rotate a string and the result should be the same size" do + scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") + result = scope.function_fqdn_rotate(["asdf"]) + result.size.should(eq(4)) + 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.function_fqdn_rotate(["abcdefg"]).should eql(scope.function_fqdn_rotate(["abcdefg"])) + end + + it "should rotate a string to give different values on different hosts" do + scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") + val1 = scope.function_fqdn_rotate(["abcdefghijklmnopqrstuvwxyz01234567890987654321"]) + scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.2") + val2 = scope.function_fqdn_rotate(["abcdefghijklmnopqrstuvwxyz01234567890987654321"]) + val1.should_not eql(val2) + end +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 new file mode 100644 index 0000000..486bef6 --- /dev/null +++ b/spec/unit/puppet/parser/functions/get_module_path_spec.rb @@ -0,0 +1,46 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:get_module_path) do + Internals = PuppetlabsSpec::PuppetInternals + class StubModule + attr_reader :path + def initialize(path) + @path = path + end + end + + def scope(environment = "production") + Internals.scope(:compiler => Internals.compiler(:node => Internals.node(:environment => environment))) + end + + it 'should only allow one argument' do + 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']) }.to raise_error(Puppet::ParseError, /Could not find module/) + end + describe 'when locating a module' do + let(:modulepath) { "/tmp/does_not_exist" } + let(:path_of_module_foo) { StubModule.new("/tmp/does_not_exist/foo") } + + before(:each) { Puppet[:modulepath] = modulepath } + + it 'should be able to find module paths from the modulepath setting' do + Puppet::Module.expects(:find).with('foo', 'production').returns(path_of_module_foo) + scope.function_get_module_path(['foo']).should == path_of_module_foo.path + end + it 'should be able to find module paths when the modulepath is a list' do + Puppet[:modulepath] = modulepath + ":/tmp" + Puppet::Module.expects(:find).with('foo', 'production').returns(path_of_module_foo) + scope.function_get_module_path(['foo']).should == path_of_module_foo.path + end + it 'should respect the environment' do + pending("Disabled on Puppet 2.6.x") if Puppet.version =~ /^2\.6\b/ + Puppet.settings[:environment] = 'danstestenv' + Puppet::Module.expects(:find).with('foo', 'danstestenv').returns(path_of_module_foo) + scope('danstestenv').function_get_module_path(['foo']).should == path_of_module_foo.path + end + end +end diff --git a/spec/unit/puppet/parser/functions/getvar_spec.rb b/spec/unit/puppet/parser/functions/getvar_spec.rb index 16edd98..5ff834e 100644 --- a/spec/unit/puppet/parser/functions/getvar_spec.rb +++ b/spec/unit/puppet/parser/functions/getvar_spec.rb @@ -1,40 +1,28 @@ -require 'puppet' +#! /usr/bin/env ruby -S rspec +require 'spec_helper' -# We don't need this for the basic tests we're doing -# require 'spec_helper' - -# Dan mentioned that Nick recommended the function method call -# to return the string value for the test description. -# this will not even try the test if the function cannot be -# loaded. describe Puppet::Parser::Functions.function(:getvar) do - - # Pulled from Dan's create_resources function - def get_scope - @topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - @scope.parent = @topscope - @compiler = @scope.compiler - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling getvar from puppet' do it "should not compile when no arguments are passed" do - Puppet[:code] = 'getvar()' - get_scope - expect { @scope.compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = '$foo = getvar()' + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /wrong number of arguments/) end + it "should not compile when too many arguments are passed" do - Puppet[:code] = 'getvar("foo::bar", "baz")' - get_scope - expect { @scope.compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = '$foo = getvar("foo::bar", "baz")' + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /wrong number of arguments/) end it "should lookup variables in other namespaces" do - pending "Puppet doesn't appear to think getvar is an rvalue function... BUG?" + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ Puppet[:code] = <<-'ENDofPUPPETcode' class site::data { $foo = 'baz' } include site::data @@ -43,11 +31,7 @@ describe Puppet::Parser::Functions.function(:getvar) do fail('getvar did not return what we expect') } ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end - end - end - diff --git a/spec/unit/puppet/parser/functions/grep_spec.rb b/spec/unit/puppet/parser/functions/grep_spec.rb index b1f647c..a93b842 100755 --- a/spec/unit/puppet/parser/functions/grep_spec.rb +++ b/spec/unit/puppet/parser/functions/grep_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the grep function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("grep").should == "function_grep" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_grep([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_grep([]) }.should( raise_error(Puppet::ParseError)) end it "should grep contents from an array" do - result = @scope.function_grep([["aaabbb","bbbccc","dddeee"], "bbb"]) + result = scope.function_grep([["aaabbb","bbbccc","dddeee"], "bbb"]) result.should(eq(["aaabbb","bbbccc"])) end - end diff --git a/spec/unit/puppet/parser/functions/has_interface_with_spec.rb b/spec/unit/puppet/parser/functions/has_interface_with_spec.rb new file mode 100755 index 0000000..c5264e4 --- /dev/null +++ b/spec/unit/puppet/parser/functions/has_interface_with_spec.rb @@ -0,0 +1,64 @@ +#!/usr/bin/env ruby -S rspec +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:has_interface_with) do + + let(:scope) do + PuppetlabsSpec::PuppetInternals.scope + end + + # The subject of these examples is the method itself. + subject do + function_name = Puppet::Parser::Functions.function(:has_interface_with) + scope.method(function_name) + end + + # We need to mock out the Facts so we can specify how we expect this function + # to behave on different platforms. + context "On Mac OS X Systems" do + before :each do + scope.stubs(:lookupvar).with("interfaces").returns('lo0,gif0,stf0,en1,p2p0,fw0,en0,vmnet1,vmnet8,utun0') + end + it 'should have loopback (lo0)' do + subject.call(['lo0']).should be_true + end + it 'should not have loopback (lo)' do + subject.call(['lo']).should be_false + end + end + context "On Linux Systems" do + before :each do + scope.stubs(:lookupvar).with("interfaces").returns('eth0,lo') + scope.stubs(:lookupvar).with("ipaddress").returns('10.0.0.1') + scope.stubs(:lookupvar).with("ipaddress_lo").returns('127.0.0.1') + scope.stubs(:lookupvar).with("ipaddress_eth0").returns('10.0.0.1') + scope.stubs(:lookupvar).with('muppet').returns('kermit') + scope.stubs(:lookupvar).with('muppet_lo').returns('mspiggy') + scope.stubs(:lookupvar).with('muppet_eth0').returns('kermit') + end + it 'should have loopback (lo)' do + subject.call(['lo']).should be_true + end + it 'should not have loopback (lo0)' do + subject.call(['lo0']).should be_false + end + it 'should have ipaddress with 127.0.0.1' do + subject.call(['ipaddress', '127.0.0.1']).should be_true + end + it 'should have ipaddress with 10.0.0.1' do + subject.call(['ipaddress', '10.0.0.1']).should be_true + end + it 'should not have ipaddress with 10.0.0.2' do + subject.call(['ipaddress', '10.0.0.2']).should be_false + end + it 'should have muppet named kermit' do + subject.call(['muppet', 'kermit']).should be_true + end + it 'should have muppet named mspiggy' do + subject.call(['muppet', 'mspiggy']).should be_true + end + it 'should not have muppet named bigbird' do + subject.call(['muppet', 'bigbird']).should be_false + end + end +end diff --git a/spec/unit/puppet/parser/functions/has_ip_address_spec.rb b/spec/unit/puppet/parser/functions/has_ip_address_spec.rb new file mode 100755 index 0000000..5a68460 --- /dev/null +++ b/spec/unit/puppet/parser/functions/has_ip_address_spec.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby -S rspec +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:has_ip_address) do + + let(:scope) do + PuppetlabsSpec::PuppetInternals.scope + end + + subject do + function_name = Puppet::Parser::Functions.function(:has_ip_address) + scope.method(function_name) + end + + context "On Linux Systems" do + before :each do + scope.stubs(:lookupvar).with('interfaces').returns('eth0,lo') + scope.stubs(:lookupvar).with('ipaddress').returns('10.0.2.15') + scope.stubs(:lookupvar).with('ipaddress_eth0').returns('10.0.2.15') + scope.stubs(:lookupvar).with('ipaddress_lo').returns('127.0.0.1') + end + + it 'should have primary address (10.0.2.15)' do + subject.call(['10.0.2.15']).should be_true + end + + it 'should have lookupback address (127.0.0.1)' do + subject.call(['127.0.0.1']).should be_true + end + + it 'should not have other address' do + subject.call(['192.1681.1.1']).should be_false + end + + it 'should not have "mspiggy" on an interface' do + subject.call(['mspiggy']).should be_false + end + end +end diff --git a/spec/unit/puppet/parser/functions/has_ip_network_spec.rb b/spec/unit/puppet/parser/functions/has_ip_network_spec.rb new file mode 100755 index 0000000..c3a289e --- /dev/null +++ b/spec/unit/puppet/parser/functions/has_ip_network_spec.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby -S rspec +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:has_ip_network) do + + let(:scope) do + PuppetlabsSpec::PuppetInternals.scope + end + + subject do + function_name = Puppet::Parser::Functions.function(:has_ip_network) + scope.method(function_name) + end + + context "On Linux Systems" do + before :each do + scope.stubs(:lookupvar).with('interfaces').returns('eth0,lo') + scope.stubs(:lookupvar).with('network').returns(:undefined) + scope.stubs(:lookupvar).with('network_eth0').returns('10.0.2.0') + scope.stubs(:lookupvar).with('network_lo').returns('127.0.0.1') + end + + it 'should have primary network (10.0.2.0)' do + subject.call(['10.0.2.0']).should be_true + end + + it 'should have loopback network (127.0.0.0)' do + subject.call(['127.0.0.1']).should be_true + end + + it 'should not have other network' do + subject.call(['192.168.1.0']).should be_false + end + end +end + diff --git a/spec/unit/puppet/parser/functions/has_key_spec.rb b/spec/unit/puppet/parser/functions/has_key_spec.rb index d1dcd15..490daea 100644 --- a/spec/unit/puppet/parser/functions/has_key_spec.rb +++ b/spec/unit/puppet/parser/functions/has_key_spec.rb @@ -1,46 +1,42 @@ -require 'puppet' -require 'mocha' -describe Puppet::Parser::Functions.function(:has_key) do +#! /usr/bin/env ruby -S rspec +require 'spec_helper' - # Pulled from Dan's create_resources function - # TODO - this should be moved to spec_helper since the - # logic is likely to be applied to multiple rspec files. - let(:compiler) { - topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - topscope.parent = nil - my_scope = Puppet::Parser::Scope.new - my_scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - my_scope.parent = topscope - compiler = my_scope.compiler - } - let(:scope) { - scope = Puppet::Parser::Scope.new - scope.stubs(:environment).returns(Puppet::Node::Environment.new('production')) - scope - } +describe Puppet::Parser::Functions.function(:has_key) do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling has_key from puppet' do it "should not compile when no arguments are passed" do - Puppet[:code] = 'has_key()' - expect { compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = '$x = has_key()' + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /wrong number of arguments/) end + it "should not compile when 1 argument is passed" do - Puppet[:code] = "has_key('foo')" - expect { compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = "$x = has_key('foo')" + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /wrong number of arguments/) end + it "should require the first value to be a Hash" do - Puppet[:code] = "has_key('foo', 'bar')" - expect { compiler.compile }.should raise_error(Puppet::ParseError, /expects the first argument to be a hash/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = "$x = has_key('foo', 'bar')" + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /expects the first argument to be a hash/) end end + describe 'when calling the function has_key from a scope instance' do it 'should detect existing keys' do scope.function_has_key([{'one' => 1}, 'one']).should be_true end + it 'should detect existing keys' do scope.function_has_key([{'one' => 1}, 'two']).should be_false end end - end diff --git a/spec/unit/puppet/parser/functions/hash_spec.rb b/spec/unit/puppet/parser/functions/hash_spec.rb index 6d3d48c..7c91be9 100644 --- a/spec/unit/puppet/parser/functions/hash_spec.rb +++ b/spec/unit/puppet/parser/functions/hash_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the hash function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("hash").should == "function_hash" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_hash([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_hash([]) }.should( raise_error(Puppet::ParseError)) end it "should convert an array to a hash" do - result = @scope.function_hash([['a',1,'b',2,'c',3]]) + result = scope.function_hash([['a',1,'b',2,'c',3]]) result.should(eq({'a'=>1,'b'=>2,'c'=>3})) end - end diff --git a/spec/unit/puppet/parser/functions/intersection_spec.rb b/spec/unit/puppet/parser/functions/intersection_spec.rb new file mode 100644 index 0000000..fd44f7f --- /dev/null +++ b/spec/unit/puppet/parser/functions/intersection_spec.rb @@ -0,0 +1,19 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the intersection function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("intersection").should == "function_intersection" + end + + it "should raise a ParseError if there are fewer than 2 arguments" do + lambda { scope.function_intersection([]) }.should( raise_error(Puppet::ParseError) ) + end + + it "should return the intersection of two arrays" do + result = scope.function_intersection([["a","b","c"],["b","c","d"]]) + result.should(eq(["b","c"])) + end +end diff --git a/spec/unit/puppet/parser/functions/is_array_spec.rb b/spec/unit/puppet/parser/functions/is_array_spec.rb index 537595c..e7f4bcd 100644 --- a/spec/unit/puppet/parser/functions/is_array_spec.rb +++ b/spec/unit/puppet/parser/functions/is_array_spec.rb @@ -1,36 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_array function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_array").should == "function_is_array" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_array([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_array([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if passed an array" do - result = @scope.function_is_array([[1,2,3]]) + result = scope.function_is_array([[1,2,3]]) result.should(eq(true)) end it "should return false if passed a hash" do - result = @scope.function_is_array([{'a'=>1}]) + result = scope.function_is_array([{'a'=>1}]) result.should(eq(false)) end it "should return false if passed a string" do - result = @scope.function_is_array(["asdf"]) + result = scope.function_is_array(["asdf"]) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/is_domain_name_spec.rb b/spec/unit/puppet/parser/functions/is_domain_name_spec.rb index ec7c7f5..f2ea76d 100644 --- a/spec/unit/puppet/parser/functions/is_domain_name_spec.rb +++ b/spec/unit/puppet/parser/functions/is_domain_name_spec.rb @@ -1,56 +1,64 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_domain_name function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_domain_name").should == "function_is_domain_name" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_domain_name([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_domain_name([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should return true if a valid short domain name" do + result = scope.function_is_domain_name(["x.com"]) + result.should(be_true) + end + + it "should return true if the domain is ." do + result = scope.function_is_domain_name(["."]) + result.should(be_true) + end + + it "should return true if the domain is x.com." do + result = scope.function_is_domain_name(["x.com."]) + result.should(be_true) end it "should return true if a valid domain name" do - result = @scope.function_is_domain_name(["foo.bar.com"]) + result = scope.function_is_domain_name(["foo.bar.com"]) result.should(be_true) end it "should allow domain parts to start with numbers" do - result = @scope.function_is_domain_name(["3foo.2bar.com"]) + result = scope.function_is_domain_name(["3foo.2bar.com"]) result.should(be_true) end it "should allow domain to end with a dot" do - result = @scope.function_is_domain_name(["3foo.2bar.com."]) + result = scope.function_is_domain_name(["3foo.2bar.com."]) result.should(be_true) end it "should allow a single part domain" do - result = @scope.function_is_domain_name(["orange"]) + result = scope.function_is_domain_name(["orange"]) result.should(be_true) end it "should return false if domain parts start with hyphens" do - result = @scope.function_is_domain_name(["-3foo.2bar.com"]) + result = scope.function_is_domain_name(["-3foo.2bar.com"]) result.should(be_false) end it "should return true if domain contains hyphens" do - result = @scope.function_is_domain_name(["3foo-bar.2bar-fuzz.com"]) + result = scope.function_is_domain_name(["3foo-bar.2bar-fuzz.com"]) result.should(be_true) end it "should return false if domain name contains spaces" do - result = @scope.function_is_domain_name(["not valid"]) + result = scope.function_is_domain_name(["not valid"]) result.should(be_false) end - end diff --git a/spec/unit/puppet/parser/functions/is_float_spec.rb b/spec/unit/puppet/parser/functions/is_float_spec.rb index 55ba8cf..b7d73b0 100644 --- a/spec/unit/puppet/parser/functions/is_float_spec.rb +++ b/spec/unit/puppet/parser/functions/is_float_spec.rb @@ -1,36 +1,33 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_float function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_float").should == "function_is_float" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_float([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_float([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if a float" do - result = @scope.function_is_float(["0.12"]) + result = scope.function_is_float(["0.12"]) result.should(eq(true)) end it "should return false if a string" do - result = @scope.function_is_float(["asdf"]) + result = scope.function_is_float(["asdf"]) result.should(eq(false)) end it "should return false if an integer" do - result = @scope.function_is_float(["3"]) + result = scope.function_is_float(["3"]) result.should(eq(false)) end - + it "should return true if a float is created from an arithmetical operation" do + result = scope.function_is_float([3.2*2]) + result.should(eq(true)) + end end 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/is_hash_spec.rb b/spec/unit/puppet/parser/functions/is_hash_spec.rb index 94364f5..bbebf39 100644 --- a/spec/unit/puppet/parser/functions/is_hash_spec.rb +++ b/spec/unit/puppet/parser/functions/is_hash_spec.rb @@ -1,36 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_hash function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_hash").should == "function_is_hash" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_hash([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_hash([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if passed a hash" do - result = @scope.function_is_hash([{"a"=>1,"b"=>2}]) + result = scope.function_is_hash([{"a"=>1,"b"=>2}]) result.should(eq(true)) end it "should return false if passed an array" do - result = @scope.function_is_hash([["a","b"]]) + result = scope.function_is_hash([["a","b"]]) result.should(eq(false)) end it "should return false if passed a string" do - result = @scope.function_is_hash(["asdf"]) + result = scope.function_is_hash(["asdf"]) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/is_integer_spec.rb b/spec/unit/puppet/parser/functions/is_integer_spec.rb index faf6f2d..4335795 100644 --- a/spec/unit/puppet/parser/functions/is_integer_spec.rb +++ b/spec/unit/puppet/parser/functions/is_integer_spec.rb @@ -1,36 +1,34 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_integer function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_integer").should == "function_is_integer" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_integer([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_integer([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if an integer" do - result = @scope.function_is_integer(["3"]) + result = scope.function_is_integer(["3"]) result.should(eq(true)) end it "should return false if a float" do - result = @scope.function_is_integer(["3.2"]) + result = scope.function_is_integer(["3.2"]) result.should(eq(false)) end it "should return false if a string" do - result = @scope.function_is_integer(["asdf"]) + result = scope.function_is_integer(["asdf"]) result.should(eq(false)) end + it "should return true if an integer is created from an arithmetical operation" do + result = scope.function_is_integer([3*2]) + result.should(eq(true)) + end end diff --git a/spec/unit/puppet/parser/functions/is_ip_address_spec.rb b/spec/unit/puppet/parser/functions/is_ip_address_spec.rb index 98ce828..c0debb3 100644 --- a/spec/unit/puppet/parser/functions/is_ip_address_spec.rb +++ b/spec/unit/puppet/parser/functions/is_ip_address_spec.rb @@ -1,45 +1,39 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_ip_address function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_ip_address").should == "function_is_ip_address" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_ip_address([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_ip_address([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if an IPv4 address" do - result = @scope.function_is_ip_address(["1.2.3.4"]) + result = scope.function_is_ip_address(["1.2.3.4"]) result.should(eq(true)) end it "should return true if a full IPv6 address" do - result = @scope.function_is_ip_address(["fe80:0000:cd12:d123:e2f8:47ff:fe09:dd74"]) + result = scope.function_is_ip_address(["fe80:0000:cd12:d123:e2f8:47ff:fe09:dd74"]) result.should(eq(true)) end it "should return true if a compressed IPv6 address" do - result = @scope.function_is_ip_address(["fe00::1"]) + result = scope.function_is_ip_address(["fe00::1"]) result.should(eq(true)) end it "should return false if not valid" do - result = @scope.function_is_ip_address(["asdf"]) + result = scope.function_is_ip_address(["asdf"]) result.should(eq(false)) end it "should return false if IP octets out of range" do - result = @scope.function_is_ip_address(["1.1.1.300"]) + result = scope.function_is_ip_address(["1.1.1.300"]) result.should(eq(false)) end end diff --git a/spec/unit/puppet/parser/functions/is_mac_address_spec.rb b/spec/unit/puppet/parser/functions/is_mac_address_spec.rb index c9b9637..ca9c590 100644 --- a/spec/unit/puppet/parser/functions/is_mac_address_spec.rb +++ b/spec/unit/puppet/parser/functions/is_mac_address_spec.rb @@ -1,36 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_mac_address function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_mac_address").should == "function_is_mac_address" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_mac_address([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_mac_address([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if a valid mac address" do - result = @scope.function_is_mac_address(["00:a0:1f:12:7f:a0"]) + result = scope.function_is_mac_address(["00:a0:1f:12:7f:a0"]) result.should(eq(true)) end it "should return false if octets are out of range" do - result = @scope.function_is_mac_address(["00:a0:1f:12:7f:g0"]) + result = scope.function_is_mac_address(["00:a0:1f:12:7f:g0"]) result.should(eq(false)) end it "should return false if not valid" do - result = @scope.function_is_mac_address(["not valid"]) + result = scope.function_is_mac_address(["not valid"]) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/is_numeric_spec.rb b/spec/unit/puppet/parser/functions/is_numeric_spec.rb index 2191b7b..d7440fb 100644 --- a/spec/unit/puppet/parser/functions/is_numeric_spec.rb +++ b/spec/unit/puppet/parser/functions/is_numeric_spec.rb @@ -1,36 +1,39 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_numeric function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_numeric").should == "function_is_numeric" end it "should raise a ParseError if there is less than 1 argument" do - lambda { @scope.function_is_numeric([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_numeric([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if an integer" do - result = @scope.function_is_numeric(["3"]) + result = scope.function_is_numeric(["3"]) result.should(eq(true)) end it "should return true if a float" do - result = @scope.function_is_numeric(["3.2"]) + result = scope.function_is_numeric(["3.2"]) + result.should(eq(true)) + end + + it "should return true if an integer is created from an arithmetical operation" do + result = scope.function_is_numeric([3*2]) + result.should(eq(true)) + end + + it "should return true if a float is created from an arithmetical operation" do + result = scope.function_is_numeric([3.2*2]) result.should(eq(true)) end it "should return false if a string" do - result = @scope.function_is_numeric(["asdf"]) + result = scope.function_is_numeric(["asdf"]) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/is_string_spec.rb b/spec/unit/puppet/parser/functions/is_string_spec.rb index 4f3f5fd..3756bea 100644 --- a/spec/unit/puppet/parser/functions/is_string_spec.rb +++ b/spec/unit/puppet/parser/functions/is_string_spec.rb @@ -1,41 +1,34 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the is_string function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("is_string").should == "function_is_string" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_is_string([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_is_string([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if a string" do - result = @scope.function_is_string(["asdf"]) + result = scope.function_is_string(["asdf"]) result.should(eq(true)) end it "should return false if an integer" do - result = @scope.function_is_string(["3"]) + result = scope.function_is_string(["3"]) result.should(eq(false)) end it "should return false if a float" do - result = @scope.function_is_string(["3.23"]) + result = scope.function_is_string(["3.23"]) result.should(eq(false)) end it "should return false if an array" do - result = @scope.function_is_string([["a","b","c"]]) + result = scope.function_is_string([["a","b","c"]]) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/join_keys_to_values_spec.rb b/spec/unit/puppet/parser/functions/join_keys_to_values_spec.rb new file mode 100644 index 0000000..a52fb71 --- /dev/null +++ b/spec/unit/puppet/parser/functions/join_keys_to_values_spec.rb @@ -0,0 +1,40 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the join_keys_to_values function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("join_keys_to_values").should == "function_join_keys_to_values" + end + + it "should raise a ParseError if there are fewer than two arguments" do + lambda { scope.function_join_keys_to_values([{}]) }.should raise_error Puppet::ParseError + end + + it "should raise a ParseError if there are greater than two arguments" do + lambda { scope.function_join_keys_to_values([{}, 'foo', 'bar']) }.should raise_error Puppet::ParseError + end + + it "should raise a TypeError if the first argument is an array" do + lambda { scope.function_join_keys_to_values([[1,2], ',']) }.should raise_error TypeError + end + + it "should raise a TypeError if the second argument is an array" do + lambda { scope.function_join_keys_to_values([{}, [1,2]]) }.should raise_error TypeError + end + + it "should raise a TypeError if the second argument is a number" do + lambda { scope.function_join_keys_to_values([{}, 1]) }.should raise_error TypeError + end + + it "should return an empty array given an empty hash" do + result = scope.function_join_keys_to_values([{}, ":"]) + result.should == [] + end + + it "should join hash's keys to its values" do + result = scope.function_join_keys_to_values([{'a'=>1,2=>'foo',:b=>nil}, ":"]) + result.should =~ ['a:1','2:foo','b:'] + end +end diff --git a/spec/unit/puppet/parser/functions/join_spec.rb b/spec/unit/puppet/parser/functions/join_spec.rb index 1b3dec8..aafa1a7 100644 --- a/spec/unit/puppet/parser/functions/join_spec.rb +++ b/spec/unit/puppet/parser/functions/join_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the join function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("join").should == "function_join" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_join([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_join([]) }.should( raise_error(Puppet::ParseError)) end it "should join an array into a string" do - result = @scope.function_join([["a","b","c"], ":"]) + result = scope.function_join([["a","b","c"], ":"]) result.should(eq("a:b:c")) end - end diff --git a/spec/unit/puppet/parser/functions/keys_spec.rb b/spec/unit/puppet/parser/functions/keys_spec.rb index 927be96..fdd7a70 100644 --- a/spec/unit/puppet/parser/functions/keys_spec.rb +++ b/spec/unit/puppet/parser/functions/keys_spec.rb @@ -1,26 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the keys function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("keys").should == "function_keys" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_keys([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_keys([]) }.should( raise_error(Puppet::ParseError)) end it "should return an array of keys when given a hash" do - result = @scope.function_keys([{'a'=>1, 'b' => 2}]) - result.should(eq(['a','b'])) + result = scope.function_keys([{'a'=>1, 'b'=>2}]) + # =~ performs 'array with same elements' (set) matching + # For more info see RSpec::Matchers::MatchArray + result.should =~ ['a','b'] end - end diff --git a/spec/unit/puppet/parser/functions/lstrip_spec.rb b/spec/unit/puppet/parser/functions/lstrip_spec.rb index ac331fa..b280ae7 100644 --- a/spec/unit/puppet/parser/functions/lstrip_spec.rb +++ b/spec/unit/puppet/parser/functions/lstrip_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the lstrip function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("lstrip").should == "function_lstrip" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_lstrip([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_lstrip([]) }.should( raise_error(Puppet::ParseError)) end it "should lstrip a string" do - result = @scope.function_lstrip([" asdf"]) + result = scope.function_lstrip([" asdf"]) result.should(eq('asdf')) end - end diff --git a/spec/unit/puppet/parser/functions/max_spec.rb b/spec/unit/puppet/parser/functions/max_spec.rb new file mode 100755 index 0000000..ff6f2b3 --- /dev/null +++ b/spec/unit/puppet/parser/functions/max_spec.rb @@ -0,0 +1,27 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the max function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("max").should == "function_max" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_max([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should be able to compare strings" do + scope.function_max(["albatross","dog","horse"]).should(eq("horse")) + end + + it "should be able to compare numbers" do + scope.function_max([6,8,4]).should(eq(8)) + end + + it "should be able to compare a number with a stringified number" do + scope.function_max([1,"2"]).should(eq("2")) + end +end diff --git a/spec/unit/puppet/parser/functions/member_spec.rb b/spec/unit/puppet/parser/functions/member_spec.rb index 2cebc0d..6e9a023 100644 --- a/spec/unit/puppet/parser/functions/member_spec.rb +++ b/spec/unit/puppet/parser/functions/member_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the member function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("member").should == "function_member" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_member([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_member([]) }.should( raise_error(Puppet::ParseError)) end it "should return true if a member is in an array" do - result = @scope.function_member([["a","b","c"], "a"]) + result = scope.function_member([["a","b","c"], "a"]) result.should(eq(true)) - end + end it "should return false if a member is not in an array" do - result = @scope.function_member([["a","b","c"], "d"]) + result = scope.function_member([["a","b","c"], "d"]) result.should(eq(false)) - end - + end end diff --git a/spec/unit/puppet/parser/functions/merge_spec.rb b/spec/unit/puppet/parser/functions/merge_spec.rb index 71e1869..8a170bb 100644 --- a/spec/unit/puppet/parser/functions/merge_spec.rb +++ b/spec/unit/puppet/parser/functions/merge_spec.rb @@ -1,54 +1,52 @@ -require 'puppet' -require 'mocha' -describe Puppet::Parser::Functions.function(:merge) do +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' - # Pulled from Dan's create_resources function - # TODO - these let statements should be moved somewhere - # where they can be resued - let(:compiler) { - topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - topscope.parent = nil - my_scope = Puppet::Parser::Scope.new - my_scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - my_scope.parent = topscope - compiler = my_scope.compiler - } - let(:scope) { - scope = Puppet::Parser::Scope.new - scope.stubs(:environment).returns(Puppet::Node::Environment.new('production')) - scope - } +describe Puppet::Parser::Functions.function(:merge) do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling merge from puppet' do it "should not compile when no arguments are passed" do - Puppet[:code] = 'merge()' - expect { compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = '$x = merge()' + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /wrong number of arguments/) end + it "should not compile when 1 argument is passed" do - Puppet[:code] = "$my_hash={'one' => 1}\nmerge($my_hash)" - expect { compiler.compile }.should raise_error(Puppet::ParseError, /wrong number of arguments/) + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + Puppet[:code] = "$my_hash={'one' => 1}\n$x = merge($my_hash)" + expect { + scope.compiler.compile + }.to raise_error(Puppet::ParseError, /wrong number of arguments/) end end + 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/) + expect { new_hash = scope.function_merge([{}, 2])}.to raise_error(Puppet::ParseError, /unexpected argument type Fixnum/) + end + it 'should accept empty strings as puppet undef' do + expect { new_hash = scope.function_merge([{}, ''])}.not_to raise_error(Puppet::ParseError, /unexpected argument type String/) end + it 'should be able to merge two hashes' do new_hash = scope.function_merge([{'one' => '1', 'two' => '1'}, {'two' => '2', 'three' => '2'}]) new_hash['one'].should == '1' new_hash['two'].should == '2' new_hash['three'].should == '2' end + it 'should merge multiple hashes' do hash = scope.function_merge([{'one' => 1}, {'one' => '2'}, {'one' => '3'}]) hash['one'].should == '3' end + it 'should accept empty hashes' do scope.function_merge([{},{},{}]).should == {} end - end - end diff --git a/spec/unit/puppet/parser/functions/min_spec.rb b/spec/unit/puppet/parser/functions/min_spec.rb new file mode 100755 index 0000000..71d593e --- /dev/null +++ b/spec/unit/puppet/parser/functions/min_spec.rb @@ -0,0 +1,27 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the min function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("min").should == "function_min" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_min([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should be able to compare strings" do + scope.function_min(["albatross","dog","horse"]).should(eq("albatross")) + end + + it "should be able to compare numbers" do + scope.function_min([6,8,4]).should(eq(4)) + end + + it "should be able to compare a number with a stringified number" do + scope.function_min([1,"2"]).should(eq(1)) + end +end diff --git a/spec/unit/puppet/parser/functions/num2bool_spec.rb b/spec/unit/puppet/parser/functions/num2bool_spec.rb index 6585273..b56196d 100644 --- a/spec/unit/puppet/parser/functions/num2bool_spec.rb +++ b/spec/unit/puppet/parser/functions/num2bool_spec.rb @@ -1,31 +1,67 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the num2bool function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("num2bool").should == "function_num2bool" end - before :each do - @scope = Puppet::Parser::Scope.new + it "should raise a ParseError if there are no arguments" do + lambda { scope.function_num2bool([]) }.should( raise_error(Puppet::ParseError)) end - it "should exist" do - Puppet::Parser::Functions.function("num2bool").should == "function_num2bool" + 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 there is less than 1 arguments" do - lambda { @scope.function_num2bool([]) }.should( raise_error(Puppet::ParseError)) + 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 1" do - result = @scope.function_num2bool(["1"]) + 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 - result = @scope.function_num2bool(["0"]) + 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/parsejson_spec.rb b/spec/unit/puppet/parser/functions/parsejson_spec.rb index 26eea36..f179ac1 100644 --- a/spec/unit/puppet/parser/functions/parsejson_spec.rb +++ b/spec/unit/puppet/parser/functions/parsejson_spec.rb @@ -1,29 +1,22 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the parsejson function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("parsejson").should == "function_parsejson" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_parsejson([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_parsejson([]) }.should( raise_error(Puppet::ParseError)) end it "should convert JSON to a data structure" do json = <<-EOS ["aaa","bbb","ccc"] EOS - result = @scope.function_parsejson([json]) + result = scope.function_parsejson([json]) result.should(eq(['aaa','bbb','ccc'])) end - end diff --git a/spec/unit/puppet/parser/functions/parseyaml_spec.rb b/spec/unit/puppet/parser/functions/parseyaml_spec.rb index f9cb049..0c7aea8 100644 --- a/spec/unit/puppet/parser/functions/parseyaml_spec.rb +++ b/spec/unit/puppet/parser/functions/parseyaml_spec.rb @@ -1,21 +1,15 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the parseyaml function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("parseyaml").should == "function_parseyaml" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_parseyaml([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_parseyaml([]) }.should( raise_error(Puppet::ParseError)) end it "should convert YAML to a data structure" do @@ -24,8 +18,7 @@ describe "the parseyaml function" do - bbb - ccc EOS - result = @scope.function_parseyaml([yaml]) + result = scope.function_parseyaml([yaml]) result.should(eq(['aaa','bbb','ccc'])) end - end diff --git a/spec/unit/puppet/parser/functions/pick_spec.rb b/spec/unit/puppet/parser/functions/pick_spec.rb new file mode 100644 index 0000000..761db6b --- /dev/null +++ b/spec/unit/puppet/parser/functions/pick_spec.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the pick function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("pick").should == "function_pick" + end + + it 'should return the correct value' do + scope.function_pick(['first', 'second']).should == 'first' + end + + it 'should return the correct value if the first value is empty' do + scope.function_pick(['', 'second']).should == 'second' + end + + it 'should remove empty string values' do + scope.function_pick(['', 'first']).should == 'first' + end + + it 'should remove :undef values' do + scope.function_pick([:undef, 'first']).should == 'first' + end + + it 'should remove :undefined values' do + scope.function_pick([:undefined, 'first']).should == 'first' + end + + it 'should error if no values are passed' do + expect { scope.function_pick([]) }.to raise_error(Puppet::Error, /Must provide non empty value./) + end +end diff --git a/spec/unit/puppet/parser/functions/prefix_spec.rb b/spec/unit/puppet/parser/functions/prefix_spec.rb index a0cbcab..5cf592b 100644 --- a/spec/unit/puppet/parser/functions/prefix_spec.rb +++ b/spec/unit/puppet/parser/functions/prefix_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the prefix function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("prefix").should == "function_prefix" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_prefix([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_prefix([]) }.should( raise_error(Puppet::ParseError)) end it "should return a prefixed array" do - result = @scope.function_prefix([['a','b','c'], 'p']) + result = scope.function_prefix([['a','b','c'], 'p']) result.should(eq(['pa','pb','pc'])) end - end diff --git a/spec/unit/puppet/parser/functions/range_spec.rb b/spec/unit/puppet/parser/functions/range_spec.rb index 24cc391..5eb290f 100644 --- a/spec/unit/puppet/parser/functions/range_spec.rb +++ b/spec/unit/puppet/parser/functions/range_spec.rb @@ -1,60 +1,64 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the range function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("range").should == "function_range" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_range([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_range([]) }.should( raise_error(Puppet::ParseError)) end it "should return a letter range" do - result = @scope.function_range(["a","d"]) + result = scope.function_range(["a","d"]) result.should(eq(['a','b','c','d'])) end it "should return a letter range given a step of 1" do - result = @scope.function_range(["a","d","1"]) + result = scope.function_range(["a","d","1"]) result.should(eq(['a','b','c','d'])) end it "should return a stepped letter range" do - result = @scope.function_range(["a","d","2"]) + result = scope.function_range(["a","d","2"]) result.should(eq(['a','c'])) end it "should return a stepped letter range given a negative step" do - result = @scope.function_range(["1","4","-2"]) + result = scope.function_range(["a","d","-2"]) result.should(eq(['a','c'])) end it "should return a number range" do - result = @scope.function_range(["1","4"]) + result = scope.function_range(["1","4"]) result.should(eq([1,2,3,4])) end + it "should work with padded hostname like strings" do + expected = ("host01".."host10").to_a + scope.function_range(["host01","host10"]).should eq expected + end + + it "should coerce zero padded digits to integers" do + expected = (0..10).to_a + scope.function_range(["00", "10"]).should eq expected + end + it "should return a number range given a step of 1" do - result = @scope.function_range(["1","4","1"]) + result = scope.function_range(["1","4","1"]) result.should(eq([1,2,3,4])) end it "should return a stepped number range" do - result = @scope.function_range(["1","4","2"]) + result = scope.function_range(["1","4","2"]) result.should(eq([1,3])) end it "should return a stepped number range given a negative step" do - result = @scope.function_range(["1","4","-2"]) + result = scope.function_range(["1","4","-2"]) result.should(eq([1,3])) end diff --git a/spec/unit/puppet/parser/functions/reject_spec.rb b/spec/unit/puppet/parser/functions/reject_spec.rb new file mode 100755 index 0000000..f2cb741 --- /dev/null +++ b/spec/unit/puppet/parser/functions/reject_spec.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require 'spec_helper' + +describe "the reject function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("reject").should == "function_reject" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_reject([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should reject contents from an array" do + result = scope.function_reject([["1111", "aaabbb","bbbccc","dddeee"], "bbb"]) + result.should(eq(["1111", "dddeee"])) + end +end diff --git a/spec/unit/puppet/parser/functions/reverse_spec.rb b/spec/unit/puppet/parser/functions/reverse_spec.rb index 4fa50e4..1b59206 100644 --- a/spec/unit/puppet/parser/functions/reverse_spec.rb +++ b/spec/unit/puppet/parser/functions/reverse_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the reverse function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("reverse").should == "function_reverse" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_reverse([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_reverse([]) }.should( raise_error(Puppet::ParseError)) end it "should reverse a string" do - result = @scope.function_reverse(["asdfghijkl"]) + result = scope.function_reverse(["asdfghijkl"]) result.should(eq('lkjihgfdsa')) end - end diff --git a/spec/unit/puppet/parser/functions/rstrip_spec.rb b/spec/unit/puppet/parser/functions/rstrip_spec.rb index af8cc12..d90de1d 100644 --- a/spec/unit/puppet/parser/functions/rstrip_spec.rb +++ b/spec/unit/puppet/parser/functions/rstrip_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the rstrip function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("rstrip").should == "function_rstrip" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_rstrip([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_rstrip([]) }.should( raise_error(Puppet::ParseError)) end it "should rstrip a string" do - result = @scope.function_rstrip(["asdf "]) + result = scope.function_rstrip(["asdf "]) result.should(eq('asdf')) end it "should rstrip each element in an array" do - result = @scope.function_rstrip([["a ","b ", "c "]]) + result = scope.function_rstrip([["a ","b ", "c "]]) result.should(eq(['a','b','c'])) end - end diff --git a/spec/unit/puppet/parser/functions/shuffle_spec.rb b/spec/unit/puppet/parser/functions/shuffle_spec.rb index f04fda5..93346d5 100644 --- a/spec/unit/puppet/parser/functions/shuffle_spec.rb +++ b/spec/unit/puppet/parser/functions/shuffle_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the shuffle function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("shuffle").should == "function_shuffle" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_shuffle([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_shuffle([]) }.should( raise_error(Puppet::ParseError)) end it "should shuffle a string and the result should be the same size" do - result = @scope.function_shuffle(["asdf"]) + result = scope.function_shuffle(["asdf"]) result.size.should(eq(4)) end it "should shuffle a string but the sorted contents should still be the same" do - result = @scope.function_shuffle(["adfs"]) + result = scope.function_shuffle(["adfs"]) result.split("").sort.join("").should(eq("adfs")) end - end diff --git a/spec/unit/puppet/parser/functions/size_spec.rb b/spec/unit/puppet/parser/functions/size_spec.rb index ccaa335..b1c435a 100644 --- a/spec/unit/puppet/parser/functions/size_spec.rb +++ b/spec/unit/puppet/parser/functions/size_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the size function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("size").should == "function_size" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_size([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_size([]) }.should( raise_error(Puppet::ParseError)) end it "should return the size of a string" do - result = @scope.function_size(["asdf"]) + result = scope.function_size(["asdf"]) result.should(eq(4)) end it "should return the size of an array" do - result = @scope.function_size([["a","b","c"]]) + result = scope.function_size([["a","b","c"]]) result.should(eq(3)) end - end diff --git a/spec/unit/puppet/parser/functions/sort_spec.rb b/spec/unit/puppet/parser/functions/sort_spec.rb index fbe3073..3187a5a 100644 --- a/spec/unit/puppet/parser/functions/sort_spec.rb +++ b/spec/unit/puppet/parser/functions/sort_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the sort function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("sort").should == "function_sort" end it "should raise a ParseError if there is not 1 arguments" do - lambda { @scope.function_sort(['','']) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_sort(['','']) }.should( raise_error(Puppet::ParseError)) end it "should sort an array" do - result = @scope.function_sort([["a","c","b"]]) + result = scope.function_sort([["a","c","b"]]) result.should(eq(['a','b','c'])) end it "should sort a string" do - result = @scope.function_sort(["acb"]) + result = scope.function_sort(["acb"]) result.should(eq('abc')) end - end diff --git a/spec/unit/puppet/parser/functions/squeeze_spec.rb b/spec/unit/puppet/parser/functions/squeeze_spec.rb index 9355ad2..60e5a30 100644 --- a/spec/unit/puppet/parser/functions/squeeze_spec.rb +++ b/spec/unit/puppet/parser/functions/squeeze_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the squeeze function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("squeeze").should == "function_squeeze" end it "should raise a ParseError if there is less than 2 arguments" do - lambda { @scope.function_squeeze([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_squeeze([]) }.should( raise_error(Puppet::ParseError)) end it "should squeeze a string" do - result = @scope.function_squeeze(["aaabbbbcccc"]) + result = scope.function_squeeze(["aaabbbbcccc"]) result.should(eq('abc')) end it "should squeeze all elements in an array" do - result = @scope.function_squeeze([["aaabbbbcccc","dddfff"]]) + result = scope.function_squeeze([["aaabbbbcccc","dddfff"]]) result.should(eq(['abc','df'])) end - end diff --git a/spec/unit/puppet/parser/functions/str2bool_spec.rb b/spec/unit/puppet/parser/functions/str2bool_spec.rb index d7f0ac9..ef6350f 100644 --- a/spec/unit/puppet/parser/functions/str2bool_spec.rb +++ b/spec/unit/puppet/parser/functions/str2bool_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the str2bool function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("str2bool").should == "function_str2bool" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_str2bool([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_str2bool([]) }.should( raise_error(Puppet::ParseError)) end it "should convert string 'true' to true" do - result = @scope.function_str2bool(["true"]) + result = scope.function_str2bool(["true"]) result.should(eq(true)) end it "should convert string 'undef' to false" do - result = @scope.function_str2bool(["undef"]) + result = scope.function_str2bool(["undef"]) + result.should(eq(false)) + end + + it "should return the boolean it was called with" do + result = scope.function_str2bool([true]) + result.should(eq(true)) + result = scope.function_str2bool([false]) result.should(eq(false)) end - end diff --git a/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb b/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb new file mode 100644 index 0000000..df8fb8e --- /dev/null +++ b/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb @@ -0,0 +1,45 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the str2saltedsha512 function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("str2saltedsha512").should == "function_str2saltedsha512" + end + + it "should raise a ParseError if there is less than 1 argument" do + 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']) }.to( raise_error(Puppet::ParseError) ) + end + + it "should return a salted-sha512 password hash 136 characters in length" do + result = scope.function_str2saltedsha512(["password"]) + result.length.should(eq(136)) + end + + it "should raise an error if you pass a non-string password" do + expect { scope.function_str2saltedsha512([1234]) }.to( raise_error(Puppet::ParseError) ) + end + + it "should generate a valid password" do + # Allow the function to generate a password based on the string 'password' + password_hash = scope.function_str2saltedsha512(["password"]) + + # Separate the Salt and Password from the Password Hash + salt = password_hash[0..7] + password = password_hash[8..-1] + + # Convert the Salt and Password from Hex to Binary Data + str_salt = Array(salt.lines).pack('H*') + str_password = Array(password.lines).pack('H*') + + # Combine the Binary Salt with 'password' and compare the end result + saltedpass = Digest::SHA512.digest(str_salt + 'password') + result = (str_salt + saltedpass).unpack('H*')[0] + result.should == password_hash + end +end diff --git a/spec/unit/puppet/parser/functions/strftime_spec.rb b/spec/unit/puppet/parser/functions/strftime_spec.rb index f7a2cd9..df42b6f 100644 --- a/spec/unit/puppet/parser/functions/strftime_spec.rb +++ b/spec/unit/puppet/parser/functions/strftime_spec.rb @@ -1,36 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the strftime function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("strftime").should == "function_strftime" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_strftime([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_strftime([]) }.should( raise_error(Puppet::ParseError)) end it "using %s should be higher then when I wrote this test" do - result = @scope.function_strftime(["%s"]) + result = scope.function_strftime(["%s"]) result.to_i.should(be > 1311953157) end it "using %s should be lower then 1.5 trillion" do - result = @scope.function_strftime(["%s"]) + result = scope.function_strftime(["%s"]) result.to_i.should(be < 1500000000) end it "should return a date when given %Y-%m-%d" do - result = @scope.function_strftime(["%Y-%m-%d"]) + result = scope.function_strftime(["%Y-%m-%d"]) result.should =~ /^\d{4}-\d{2}-\d{2}$/ end - end diff --git a/spec/unit/puppet/parser/functions/strip_spec.rb b/spec/unit/puppet/parser/functions/strip_spec.rb index 48a52dd..fccdd26 100644 --- a/spec/unit/puppet/parser/functions/strip_spec.rb +++ b/spec/unit/puppet/parser/functions/strip_spec.rb @@ -1,26 +1,18 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the strip function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("strip").should == "function_strip" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_strip([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_strip([]) }.should( raise_error(Puppet::ParseError)) end it "should strip a string" do - result = @scope.function_strip([" ab cd "]) + result = scope.function_strip([" ab cd "]) result.should(eq('ab cd')) end - end 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/swapcase_spec.rb b/spec/unit/puppet/parser/functions/swapcase_spec.rb index 2686054..808b415 100644 --- a/spec/unit/puppet/parser/functions/swapcase_spec.rb +++ b/spec/unit/puppet/parser/functions/swapcase_spec.rb @@ -1,26 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the swapcase function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("swapcase").should == "function_swapcase" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_swapcase([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_swapcase([]) }.should( raise_error(Puppet::ParseError)) end it "should swapcase a string" do - result = @scope.function_swapcase(["aaBBccDD"]) + result = scope.function_swapcase(["aaBBccDD"]) result.should(eq('AAbbCCdd')) end - end diff --git a/spec/unit/puppet/parser/functions/time_spec.rb b/spec/unit/puppet/parser/functions/time_spec.rb index 666e8e0..e9fb76e 100644 --- a/spec/unit/puppet/parser/functions/time_spec.rb +++ b/spec/unit/puppet/parser/functions/time_spec.rb @@ -1,36 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the time function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("time").should == "function_time" end it "should raise a ParseError if there is more than 2 arguments" do - lambda { @scope.function_time(['','']) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_time(['','']) }.should( raise_error(Puppet::ParseError)) end it "should return a number" do - result = @scope.function_time([]) - result.class.should(eq(Fixnum)) + result = scope.function_time([]) + result.should be_an(Integer) end it "should be higher then when I wrote this test" do - result = @scope.function_time([]) + result = scope.function_time([]) result.should(be > 1311953157) end it "should be lower then 1.5 trillion" do - result = @scope.function_time([]) + result = scope.function_time([]) result.should(be < 1500000000) end - end diff --git a/spec/unit/puppet/parser/functions/to_bytes_spec.rb b/spec/unit/puppet/parser/functions/to_bytes_spec.rb new file mode 100755 index 0000000..d1ea4c8 --- /dev/null +++ b/spec/unit/puppet/parser/functions/to_bytes_spec.rb @@ -0,0 +1,58 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the to_bytes function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("to_bytes").should == "function_to_bytes" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_to_bytes([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should convert kB to B" do + result = scope.function_to_bytes(["4 kB"]) + result.should(eq(4096)) + end + + it "should work without B in unit" do + result = scope.function_to_bytes(["4 k"]) + result.should(eq(4096)) + end + + it "should work without a space before unit" do + result = scope.function_to_bytes(["4k"]) + result.should(eq(4096)) + end + + it "should work without a unit" do + result = scope.function_to_bytes(["5678"]) + result.should(eq(5678)) + end + + it "should convert fractions" do + result = scope.function_to_bytes(["1.5 kB"]) + result.should(eq(1536)) + end + + it "should convert scientific notation" do + result = scope.function_to_bytes(["1.5e2 B"]) + result.should(eq(150)) + end + + it "should do nothing with a positive number" do + result = scope.function_to_bytes([5678]) + result.should(eq(5678)) + end + + it "should should raise a ParseError if input isn't a number" do + lambda { scope.function_to_bytes(["foo"]) }.should( raise_error(Puppet::ParseError)) + end + + it "should should raise a ParseError if prefix is unknown" do + lambda { scope.function_to_bytes(["5 uB"]) }.should( raise_error(Puppet::ParseError)) + end +end diff --git a/spec/unit/puppet/parser/functions/type_spec.rb b/spec/unit/puppet/parser/functions/type_spec.rb index e3c28ed..8fec88f 100644 --- a/spec/unit/puppet/parser/functions/type_spec.rb +++ b/spec/unit/puppet/parser/functions/type_spec.rb @@ -1,51 +1,43 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the type function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("type").should == "function_type" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_type([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_type([]) }.should( raise_error(Puppet::ParseError)) end it "should return string when given a string" do - result = @scope.function_type(["aaabbbbcccc"]) + result = scope.function_type(["aaabbbbcccc"]) result.should(eq('string')) end it "should return array when given an array" do - result = @scope.function_type([["aaabbbbcccc","asdf"]]) + result = scope.function_type([["aaabbbbcccc","asdf"]]) result.should(eq('array')) end it "should return hash when given a hash" do - result = @scope.function_type([{"a"=>1,"b"=>2}]) + result = scope.function_type([{"a"=>1,"b"=>2}]) result.should(eq('hash')) end it "should return integer when given an integer" do - result = @scope.function_type(["1"]) + result = scope.function_type(["1"]) result.should(eq('integer')) end it "should return float when given a float" do - result = @scope.function_type(["1.34"]) + result = scope.function_type(["1.34"]) result.should(eq('float')) end it "should return boolean when given a boolean" do - result = @scope.function_type([true]) + result = scope.function_type([true]) result.should(eq('boolean')) end - end diff --git a/spec/unit/puppet/parser/functions/union_spec.rb b/spec/unit/puppet/parser/functions/union_spec.rb new file mode 100644 index 0000000..0d282ca --- /dev/null +++ b/spec/unit/puppet/parser/functions/union_spec.rb @@ -0,0 +1,19 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the union function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("union").should == "function_union" + end + + it "should raise a ParseError if there are fewer than 2 arguments" do + lambda { scope.function_union([]) }.should( raise_error(Puppet::ParseError) ) + end + + it "should join two arrays together" do + result = scope.function_union([["a","b","c"],["b","c","d"]]) + result.should(eq(["a","b","c","d"])) + end +end diff --git a/spec/unit/puppet/parser/functions/unique_spec.rb b/spec/unit/puppet/parser/functions/unique_spec.rb index 627dc33..5d48d49 100644 --- a/spec/unit/puppet/parser/functions/unique_spec.rb +++ b/spec/unit/puppet/parser/functions/unique_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the unique function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("unique").should == "function_unique" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_unique([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_unique([]) }.should( raise_error(Puppet::ParseError)) end it "should remove duplicate elements in a string" do - result = @scope.function_unique(["aabbc"]) + result = scope.function_unique(["aabbc"]) result.should(eq('abc')) end it "should remove duplicate elements in an array" do - result = @scope.function_unique([["a","a","b","b","c"]]) + result = scope.function_unique([["a","a","b","b","c"]]) result.should(eq(['a','b','c'])) end - end diff --git a/spec/unit/puppet/parser/functions/upcase_spec.rb b/spec/unit/puppet/parser/functions/upcase_spec.rb index 5d18846..5db5513 100644 --- a/spec/unit/puppet/parser/functions/upcase_spec.rb +++ b/spec/unit/puppet/parser/functions/upcase_spec.rb @@ -1,31 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the upcase function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("upcase").should == "function_upcase" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_upcase([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_upcase([]) }.should( raise_error(Puppet::ParseError)) end it "should upcase a string" do - result = @scope.function_upcase(["abc"]) + result = scope.function_upcase(["abc"]) result.should(eq('ABC')) end it "should do nothing if a string is already upcase" do - result = @scope.function_upcase(["ABC"]) + result = scope.function_upcase(["ABC"]) result.should(eq('ABC')) end - end diff --git a/spec/unit/puppet/parser/functions/uriescape_spec.rb b/spec/unit/puppet/parser/functions/uriescape_spec.rb new file mode 100644 index 0000000..371de46 --- /dev/null +++ b/spec/unit/puppet/parser/functions/uriescape_spec.rb @@ -0,0 +1,24 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the uriescape function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("uriescape").should == "function_uriescape" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { scope.function_uriescape([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should uriescape a string" do + result = scope.function_uriescape([":/?#[]@!$&'()*+,;= "]) + result.should(eq('%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D%20')) + end + + it "should do nothing if a string is already safe" do + result = scope.function_uriescape(["ABCdef"]) + result.should(eq('ABCdef')) + end +end diff --git a/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb new file mode 100644 index 0000000..08aaf78 --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_absolute_path) do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + # The subject of these examples is the method itself. + subject do + # This makes sure the function is loaded within each test + function_name = Puppet::Parser::Functions.function(:validate_absolute_path) + scope.method(function_name) + end + + describe "Valid Paths" do + def self.valid_paths + %w{ + C:/ + C:\\ + C:\\WINDOWS\\System32 + C:/windows/system32 + X:/foo/bar + X:\\foo\\bar + /var/tmp + /var/lib/puppet + /var/opt/../lib/puppet + } + end + + context "Without Puppet::Util.absolute_path? (e.g. Puppet <= 2.6)" do + before :each do + # The intent here is to mock Puppet to behave like Puppet 2.6 does. + # Puppet 2.6 does not have the absolute_path? method. This is only a + # convenience test, stdlib should be run with the Puppet 2.6.x in the + # $LOAD_PATH in addition to 2.7.x and master. + Puppet::Util.expects(:respond_to?).with(:absolute_path?).returns(false) + end + valid_paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should not fail" do + expect { subject.call [path] }.not_to raise_error Puppet::ParseError + end + end + end + + context "Puppet without mocking" do + valid_paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should not fail" do + expect { subject.call [path] }.not_to raise_error Puppet::ParseError + end + end + end + end + + describe 'Invalid paths' do + context 'Garbage inputs' do + [ + nil, + [ nil ], + { 'foo' => 'bar' }, + { }, + '', + ].each do |path| + it "validate_absolute_path(#{path.inspect}) should fail" do + expect { subject.call [path] }.to raise_error Puppet::ParseError + end + end + end + + context 'Relative paths' do + %w{ + relative1 + . + .. + ./foo + ../foo + etc/puppetlabs/puppet + opt/puppet/bin + }.each do |path| + it "validate_absolute_path(#{path.inspect}) should fail" do + expect { subject.call [path] }.to raise_error Puppet::ParseError + end + end + end + 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 37ae09d..4b31cfd 100644 --- a/spec/unit/puppet/parser/functions/validate_array_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_array_spec.rb @@ -1,41 +1,21 @@ -require 'puppet' +#! /usr/bin/env ruby -S rspec -# We don't need this for the basic tests we're doing -# require 'spec_helper' +require 'spec_helper' -# Dan mentioned that Nick recommended the function method call -# to return the string value for the test description. -# this will not even try the test if the function cannot be -# loaded. describe Puppet::Parser::Functions.function(:validate_array) do - - # Pulled from Dan's create_resources function - def get_scope - @topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - @scope.parent = @topscope - @compiler = @scope.compiler - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling validate_array from puppet' 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}')" - get_scope - 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})" - get_scope - 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 it "should compile when multiple array arguments are passed" do @@ -44,8 +24,7 @@ describe Puppet::Parser::Functions.function(:validate_array) do $bar = [ 'one', 'two' ] validate_array($foo, $bar) ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should not compile when an undef variable is passed" do @@ -53,11 +32,7 @@ describe Puppet::Parser::Functions.function(:validate_array) do $foo = undef validate_array($foo) ENDofPUPPETcode - get_scope - 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 e95c396..261fb23 100644 --- a/spec/unit/puppet/parser/functions/validate_bool_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_bool_spec.rb @@ -1,53 +1,33 @@ -require 'puppet' +#! /usr/bin/env/ruby -S rspec -# We don't need this for the basic tests we're doing -# require 'spec_helper' +require 'spec_helper' -# Dan mentioned that Nick recommended the function method call -# to return the string value for the test description. -# this will not even try the test if the function cannot be -# loaded. describe Puppet::Parser::Functions.function(:validate_bool) do - - # Pulled from Dan's create_resources function - def get_scope - @topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - @scope.parent = @topscope - @compiler = @scope.compiler - end - + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling validate_bool from puppet' do %w{ true false }.each do |the_string| it "should not compile when #{the_string} is a string" do Puppet[:code] = "validate_bool('#{the_string}')" - get_scope - 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 Puppet[:code] = "validate_bool(#{the_string})" - get_scope - @scope.compiler.compile + scope.compiler.compile end end it "should not compile when an arbitrary string is passed" do Puppet[:code] = 'validate_bool("jeff and dan are awesome")' - get_scope - 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()' - get_scope - 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 @@ -56,8 +36,7 @@ describe Puppet::Parser::Functions.function(:validate_bool) do $bar = false validate_bool($foo, $bar, true, false) ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should compile when multiple boolean arguments are passed" do @@ -66,11 +45,7 @@ describe Puppet::Parser::Functions.function(:validate_bool) do $bar = false validate_bool($foo, $bar, true, false, 'jeff') ENDofPUPPETcode - get_scope - 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 8cc0b3d..a0c35c2 100644 --- a/spec/unit/puppet/parser/functions/validate_hash_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_hash_spec.rb @@ -1,24 +1,9 @@ -require 'puppet' +#! /usr/bin/env ruby -S rspec -# We don't need this for the basic tests we're doing -# require 'spec_helper' +require 'spec_helper' -# Dan mentioned that Nick recommended the function method call -# to return the string value for the test description. -# this will not even try the test if the function cannot be -# loaded. describe Puppet::Parser::Functions.function(:validate_hash) do - - # Pulled from Dan's create_resources function - def get_scope - @topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - @scope.parent = @topscope - @compiler = @scope.compiler - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling validate_hash from puppet' do @@ -26,14 +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}')" - get_scope - 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})" - get_scope - 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 @@ -44,8 +27,7 @@ describe Puppet::Parser::Functions.function(:validate_hash) do $bar = { 'one' => 'two' } validate_hash($foo, $bar) ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should not compile when an undef variable is passed" do @@ -53,11 +35,9 @@ describe Puppet::Parser::Functions.function(:validate_hash) do $foo = undef validate_hash($foo) ENDofPUPPETcode - get_scope - 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_re_spec.rb b/spec/unit/puppet/parser/functions/validate_re_spec.rb new file mode 100644 index 0000000..d189efb --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_re_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_re) 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_re) + 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_re(#{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', '^/full' ], + [ '/full/path/to/something', 'full' ], + [ '/full/path/to/something', ['full', 'absent'] ], + [ '/full/path/to/something', ['full', 'absent'], 'Message to the user' ], + ] + + inputs.each do |input| + it "validate_re(#{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", [ "bye", "later", "adios" ] ], + [ "greetings", "salutations" ], + ] + + inputs.each do |input| + it "validate_re(#{input.inspect}) should fail" do + expect { subject.call input }.to raise_error /validate_re.*?does not match/ + 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_re(#{input.inspect}) should fail" do + expect { subject.call input }.to raise_error /#{input[2]}/ + end + end + end + end +end diff --git a/spec/unit/puppet/parser/functions/validate_slength_spec.rb b/spec/unit/puppet/parser/functions/validate_slength_spec.rb new file mode 100755 index 0000000..b363e7a --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_slength_spec.rb @@ -0,0 +1,48 @@ +#! /usr/bin/env ruby -S rspec + +require 'spec_helper' + +describe "the validate_slength function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("validate_slength").should == "function_validate_slength" + end + + it "should raise a ParseError if there is less than 2 arguments" do + expect { scope.function_validate_slength([]) }.to(raise_error(Puppet::ParseError)) + expect { scope.function_validate_slength(["asdf"]) }.to(raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if argument 2 doesn't convert to a fixnum" do + expect { scope.function_validate_slength(["moo",["2"]]) }.to(raise_error(Puppet::ParseError, /Couldn't convert whatever you passed/)) + end + + it "should raise a ParseError if argument 2 converted, but to 0, e.g. a string" do + expect { scope.function_validate_slength(["moo","monkey"]) }.to(raise_error(Puppet::ParseError, /please pass a positive number as max_length/)) + end + + it "should raise a ParseError if argument 2 converted, but to 0" do + expect { scope.function_validate_slength(["moo","0"]) }.to(raise_error(Puppet::ParseError, /please pass a positive number as max_length/)) + end + + it "should fail if string greater then size" do + expect { scope.function_validate_slength(["test", 2]) }.to(raise_error(Puppet::ParseError, /It should have been less than or equal to/)) + end + + it "should fail if you pass an array of something other than strings" do + expect { scope.function_validate_slength([["moo",["moo"],Hash.new["moo" => 7]], 7]) }.to(raise_error(Puppet::ParseError, /is not a string, it's a/)) + end + + it "should fail if you pass something other than a string or array" do + expect { scope.function_validate_slength([Hash.new["moo" => "7"],6]) }.to(raise_error(Puppet::ParseError, /please pass a string, or an array of strings/)) + end + + it "should not fail if string is smaller or equal to size" do + expect { scope.function_validate_slength(["test", 5]) }.to_not(raise_error(Puppet::ParseError)) + end + + it "should not fail if array of string is are all smaller or equal to size" do + expect { scope.function_validate_slength([["moo","foo","bar"], 5]) }.to_not(raise_error(Puppet::ParseError)) + 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 92392da..3b4fb3e 100644 --- a/spec/unit/puppet/parser/functions/validate_string_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_string_spec.rb @@ -1,24 +1,9 @@ -require 'puppet' +#! /usr/bin/env ruby -S rspec -# We don't need this for the basic tests we're doing -# require 'spec_helper' +require 'spec_helper' -# Dan mentioned that Nick recommended the function method call -# to return the string value for the test description. -# this will not even try the test if the function cannot be -# loaded. describe Puppet::Parser::Functions.function(:validate_string) do - - # Pulled from Dan's create_resources function - def get_scope - @topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) - @scope.parent = @topscope - @compiler = @scope.compiler - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } describe 'when calling validate_string from puppet' do @@ -26,14 +11,12 @@ describe Puppet::Parser::Functions.function(:validate_string) do it "should compile when #{the_string} is a string" do Puppet[:code] = "validate_string('#{the_string}')" - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should compile when #{the_string} is a bare word" do Puppet[:code] = "validate_string(#{the_string})" - get_scope - @scope.compiler.compile + scope.compiler.compile end end @@ -41,14 +24,12 @@ describe Puppet::Parser::Functions.function(:validate_string) do %w{ true false }.each do |the_string| it "should compile when #{the_string} is a string" do Puppet[:code] = "validate_string('#{the_string}')" - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should not compile when #{the_string} is a bare word" do Puppet[:code] = "validate_string(#{the_string})" - get_scope - 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,8 +39,7 @@ describe Puppet::Parser::Functions.function(:validate_string) do $bar = 'two' validate_string($foo, $bar) ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should compile when an explicitly undef variable is passed (NOTE THIS MAY NOT BE DESIRABLE)" do @@ -67,17 +47,14 @@ describe Puppet::Parser::Functions.function(:validate_string) do $foo = undef validate_string($foo) ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end it "should compile when an undefined variable is passed (NOTE THIS MAY NOT BE DESIRABLE)" do Puppet[:code] = <<-'ENDofPUPPETcode' validate_string($foobarbazishouldnotexist) ENDofPUPPETcode - get_scope - @scope.compiler.compile + scope.compiler.compile end end end - diff --git a/spec/unit/puppet/parser/functions/values_at_spec.rb b/spec/unit/puppet/parser/functions/values_at_spec.rb index 6c45316..08e95a5 100644 --- a/spec/unit/puppet/parser/functions/values_at_spec.rb +++ b/spec/unit/puppet/parser/functions/values_at_spec.rb @@ -1,45 +1,38 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the values_at function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("values_at").should == "function_values_at" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_values_at([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_values_at([]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if you try to use a range where stop is greater then start" do - lambda { @scope.function_values_at([['a','b'],["3-1"]]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_values_at([['a','b'],["3-1"]]) }.should( raise_error(Puppet::ParseError)) end it "should return a value at from an array" do - result = @scope.function_values_at([['a','b','c'],"1"]) + result = scope.function_values_at([['a','b','c'],"1"]) result.should(eq(['b'])) end it "should return a value at from an array when passed a range" do - result = @scope.function_values_at([['a','b','c'],"0-1"]) + result = scope.function_values_at([['a','b','c'],"0-1"]) result.should(eq(['a','b'])) end it "should return chosen values from an array when passed number of indexes" do - result = @scope.function_values_at([['a','b','c'],["0","2"]]) + result = scope.function_values_at([['a','b','c'],["0","2"]]) result.should(eq(['a','c'])) end it "should return chosen values from an array when passed ranges and multiple indexes" do - result = @scope.function_values_at([['a','b','c','d','e','f','g'],["0","2","4-5"]]) + result = scope.function_values_at([['a','b','c','d','e','f','g'],["0","2","4-5"]]) result.should(eq(['a','c','e','f'])) end - end diff --git a/spec/unit/puppet/parser/functions/values_spec.rb b/spec/unit/puppet/parser/functions/values_spec.rb index f6eb5b6..14ae417 100644 --- a/spec/unit/puppet/parser/functions/values_spec.rb +++ b/spec/unit/puppet/parser/functions/values_spec.rb @@ -1,30 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the values function" do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - before :each do - @scope = Puppet::Parser::Scope.new - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should exist" do Puppet::Parser::Functions.function("values").should == "function_values" end it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_values([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_values([]) }.should( raise_error(Puppet::ParseError)) end it "should return values from a hash" do - result = @scope.function_values([{'a'=>'1','b'=>'2','c'=>'3'}]) - result.should(eq(['1','2','3'])) + result = scope.function_values([{'a'=>'1','b'=>'2','c'=>'3'}]) + # =~ is the RSpec::Matchers::MatchArray matcher. + # A.K.A. "array with same elements" (multiset) matching + result.should =~ %w{ 1 2 3 } end - it "should return values from a hash" do - lambda { @scope.function_values([['a','b','c']]) }.should( raise_error(Puppet::ParseError)) + it "should return a multiset" do + result = scope.function_values([{'a'=>'1','b'=>'3','c'=>'3'}]) + result.should =~ %w{ 1 3 3 } + result.should_not =~ %w{ 1 3 } end + it "should raise a ParseError unless a Hash is provided" do + lambda { scope.function_values([['a','b','c']]) }.should( raise_error(Puppet::ParseError)) + end end diff --git a/spec/unit/puppet/parser/functions/zip_spec.rb b/spec/unit/puppet/parser/functions/zip_spec.rb index 074f4df..f45ab17 100644 --- a/spec/unit/puppet/parser/functions/zip_spec.rb +++ b/spec/unit/puppet/parser/functions/zip_spec.rb @@ -1,26 +1,15 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the zip 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("zip").should == "function_zip" - end + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } it "should raise a ParseError if there is less than 1 arguments" do - lambda { @scope.function_zip([]) }.should( raise_error(Puppet::ParseError)) + lambda { scope.function_zip([]) }.should( raise_error(Puppet::ParseError)) end it "should be able to zip an array" do - result = @scope.function_zip([['1','2','3'],['4','5','6']]) + result = scope.function_zip([['1','2','3'],['4','5','6']]) result.should(eq([["1", "4"], ["2", "5"], ["3", "6"]])) end - end diff --git a/spec/unit/puppet/provider/file_line/ruby_spec.rb b/spec/unit/puppet/provider/file_line/ruby_spec.rb index b03fc0e..7857d39 100644 --- a/spec/unit/puppet/provider/file_line/ruby_spec.rb +++ b/spec/unit/puppet/provider/file_line/ruby_spec.rb @@ -2,29 +2,126 @@ require 'puppet' require 'tempfile' provider_class = Puppet::Type.type(:file_line).provider(:ruby) describe provider_class do - before :each do - tmp = Tempfile.new('tmp') - @tmpfile = tmp.path - tmp.close! - @resource = Puppet::Type::File_line.new( - {:name => 'foo', :path => @tmpfile, :line => 'foo'} - ) - @provider = provider_class.new(@resource) - end - it 'should detect if the line exists in the file' do - File.open(@tmpfile, 'w') do |fh| - fh.write('foo') + context "when adding" do + before :each do + # TODO: these should be ported over to use the PuppetLabs spec_helper + # file fixtures once the following pull request has been merged: + # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files + tmp = Tempfile.new('tmp') + @tmpfile = tmp.path + tmp.close! + @resource = Puppet::Type::File_line.new( + {:name => 'foo', :path => @tmpfile, :line => 'foo'} + ) + @provider = provider_class.new(@resource) + end + it 'should detect if the line exists in the file' do + File.open(@tmpfile, 'w') do |fh| + fh.write('foo') + end + @provider.exists?.should be_true + end + it 'should detect if the line does not exist in the file' do + File.open(@tmpfile, 'w') do |fh| + fh.write('foo1') + end + @provider.exists?.should be_nil + end + it 'should append to an existing file when creating' do + @provider.create + File.read(@tmpfile).chomp.should == 'foo' end - @provider.exists?.should be_true end - it 'should detect if the line does not exist in the file' do - File.open(@tmpfile, 'w') do |fh| - fh.write('foo1') + + context "when matching" do + before :each do + # TODO: these should be ported over to use the PuppetLabs spec_helper + # file fixtures once the following pull request has been merged: + # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files + tmp = Tempfile.new('tmp') + @tmpfile = tmp.path + tmp.close! + @resource = Puppet::Type::File_line.new( + { + :name => 'foo', + :path => @tmpfile, + :line => 'foo = bar', + :match => '^foo\s*=.*$', + } + ) + @provider = provider_class.new(@resource) + end + + it 'should raise an error if more than one line matches, and should not have modified the file' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo=blah\nfoo2\nfoo=baz") + end + @provider.exists?.should be_nil + expect { @provider.create }.to raise_error(Puppet::Error, /More than one line.*matches/) + File.read(@tmpfile).should eql("foo1\nfoo=blah\nfoo2\nfoo=baz") + end + + it 'should replace a line that matches' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo=blah\nfoo2") + end + @provider.exists?.should be_nil + @provider.create + File.read(@tmpfile).chomp.should eql("foo1\nfoo = bar\nfoo2") + end + it 'should add a new line if no lines match' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo2") + end + @provider.exists?.should be_nil + @provider.create + File.read(@tmpfile).should eql("foo1\nfoo2\nfoo = bar\n") + end + it 'should do nothing if the exact line already exists' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo = bar\nfoo2") + end + @provider.exists?.should be_true + @provider.create + File.read(@tmpfile).chomp.should eql("foo1\nfoo = bar\nfoo2") end - @provider.exists?.should be_nil end - it 'should append to an existing file when creating' do - @provider.create - File.read(@tmpfile).chomp.should == 'foo' + + context "when removing" do + before :each do + # TODO: these should be ported over to use the PuppetLabs spec_helper + # file fixtures once the following pull request has been merged: + # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files + tmp = Tempfile.new('tmp') + @tmpfile = tmp.path + tmp.close! + @resource = Puppet::Type::File_line.new( + {:name => 'foo', :path => @tmpfile, :line => 'foo', :ensure => 'absent' } + ) + @provider = provider_class.new(@resource) + end + it 'should remove the line if it exists' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo\nfoo2") + end + @provider.destroy + File.read(@tmpfile).should eql("foo1\nfoo2") + end + + it 'should remove the line without touching the last new line' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo\nfoo2\n") + end + @provider.destroy + File.read(@tmpfile).should eql("foo1\nfoo2\n") + end + + it 'should remove any occurence of the line' do + File.open(@tmpfile, 'w') do |fh| + fh.write("foo1\nfoo\nfoo2\nfoo\nfoo") + end + @provider.destroy + File.read(@tmpfile).should eql("foo1\nfoo2\n") + end end end diff --git a/spec/unit/puppet/type/file_line_spec.rb b/spec/unit/puppet/type/file_line_spec.rb index 7e07c06..edc64bd 100644 --- a/spec/unit/puppet/type/file_line_spec.rb +++ b/spec/unit/puppet/type/file_line_spec.rb @@ -1,24 +1,69 @@ require 'puppet' require 'tempfile' describe Puppet::Type.type(:file_line) do - before :each do - @file_line = Puppet::Type.type(:file_line).new(:name => 'foo', :line => 'line', :path => '/tmp/path') + let :file_line do + Puppet::Type.type(:file_line).new(:name => 'foo', :line => 'line', :path => '/tmp/path') end it 'should accept a line and path' do - @file_line[:line] = 'my_line' - @file_line[:line].should == 'my_line' + file_line[:line] = 'my_line' + file_line[:line].should == 'my_line' + file_line[:path] = '/my/path' + file_line[:path].should == '/my/path' + end + it 'should accept a match regex' do + file_line[:match] = '^foo.*$' + file_line[:match].should == '^foo.*$' + end + it 'should not accept a match regex that does not match the specified line' do + expect { + Puppet::Type.type(:file_line).new( + :name => 'foo', + :path => '/my/path', + :line => 'foo=bar', + :match => '^bar=blah$' + )}.to raise_error(Puppet::Error, /the value must be a regex that matches/) + end + it 'should accept a match regex that does match the specified line' do + expect { + Puppet::Type.type(:file_line).new( + :name => 'foo', + :path => '/my/path', + :line => 'foo=bar', + :match => '^\s*foo=.*$' + )}.not_to raise_error end it 'should accept posix filenames' do - @file_line[:path] = '/tmp/path' - @file_line[:path].should == '/tmp/path' + file_line[:path] = '/tmp/path' + 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 + end + + it "should autorequire the file it manages" do + catalog = Puppet::Resource::Catalog.new + file = Puppet::Type.type(:file).new(:name => "/tmp/path") + catalog.add_resource file + catalog.add_resource file_line + + relationship = file_line.autorequire.find do |rel| + (rel.source.to_s == "File[/tmp/path]") and (rel.target.to_s == file_line.to_s) + end + relationship.should be_a Puppet::Relationship + end + + it "should not autorequire the file it manages if it is not managed" do + catalog = Puppet::Resource::Catalog.new + catalog.add_resource file_line + file_line.autorequire.should be_empty end end diff --git a/tests/has_interface_with.pp b/tests/has_interface_with.pp new file mode 100644 index 0000000..e1f1353 --- /dev/null +++ b/tests/has_interface_with.pp @@ -0,0 +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')) + diff --git a/tests/has_ip_address.pp b/tests/has_ip_address.pp new file mode 100644 index 0000000..8429a88 --- /dev/null +++ b/tests/has_ip_address.pp @@ -0,0 +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')) diff --git a/tests/has_ip_network.pp b/tests/has_ip_network.pp new file mode 100644 index 0000000..a15d8c0 --- /dev/null +++ b/tests/has_ip_network.pp @@ -0,0 +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')) + |