Merge remote-tracking branch 'pr/56' into pull-56
authorAdrien Thebo <git@somethingsinistral.net>
Tue, 28 May 2013 18:03:51 +0000 (11:03 -0700)
committerAdrien Thebo <git@somethingsinistral.net>
Tue, 28 May 2013 18:07:41 +0000 (11:07 -0700)
Conflicts:
lib/puppet/parser/functions/range.rb
spec/unit/puppet/parser/functions/range_spec.rb

185 files changed:
.gemspec [new file with mode: 0644]
.gitignore
.project [new file with mode: 0644]
.rspec [new file with mode: 0644]
.travis.yml [new file with mode: 0644]
CHANGELOG
CONTRIBUTING.md [new file with mode: 0644]
Gemfile [new file with mode: 0644]
Modulefile
README.markdown
README_DEVELOPER.markdown [new file with mode: 0644]
README_SPECS.markdown [new file with mode: 0644]
RELEASE_PROCESS.markdown
Rakefile
lib/facter/facter_dot_d.rb
lib/facter/pe_version.rb [new file with mode: 0644]
lib/facter/puppet_vardir.rb [new file with mode: 0644]
lib/facter/root_home.rb
lib/facter/util/puppet_settings.rb [new file with mode: 0644]
lib/puppet/parser/functions/abs.rb
lib/puppet/parser/functions/any2array.rb [new file with mode: 0644]
lib/puppet/parser/functions/base64.rb [new file with mode: 0644]
lib/puppet/parser/functions/chomp.rb
lib/puppet/parser/functions/chop.rb
lib/puppet/parser/functions/concat.rb [new file with mode: 0644]
lib/puppet/parser/functions/count.rb [new file with mode: 0644]
lib/puppet/parser/functions/defined_with_params.rb [new file with mode: 0644]
lib/puppet/parser/functions/delete.rb
lib/puppet/parser/functions/difference.rb [new file with mode: 0644]
lib/puppet/parser/functions/dirname.rb [new file with mode: 0644]
lib/puppet/parser/functions/ensure_packages.rb [new file with mode: 0644]
lib/puppet/parser/functions/ensure_resource.rb [new file with mode: 0644]
lib/puppet/parser/functions/flatten.rb
lib/puppet/parser/functions/floor.rb [new file with mode: 0644]
lib/puppet/parser/functions/fqdn_rotate.rb [new file with mode: 0644]
lib/puppet/parser/functions/get_module_path.rb [new file with mode: 0644]
lib/puppet/parser/functions/getparam.rb [new file with mode: 0644]
lib/puppet/parser/functions/getvar.rb
lib/puppet/parser/functions/has_interface_with.rb [new file with mode: 0644]
lib/puppet/parser/functions/has_ip_address.rb [new file with mode: 0644]
lib/puppet/parser/functions/has_ip_network.rb [new file with mode: 0644]
lib/puppet/parser/functions/has_key.rb
lib/puppet/parser/functions/hash.rb
lib/puppet/parser/functions/intersection.rb [new file with mode: 0644]
lib/puppet/parser/functions/is_domain_name.rb
lib/puppet/parser/functions/is_float.rb
lib/puppet/parser/functions/is_function_available.rb [new file with mode: 0644]
lib/puppet/parser/functions/is_integer.rb
lib/puppet/parser/functions/is_ip_address.rb
lib/puppet/parser/functions/is_numeric.rb
lib/puppet/parser/functions/join_keys_to_values.rb [new file with mode: 0644]
lib/puppet/parser/functions/loadyaml.rb
lib/puppet/parser/functions/max.rb [new file with mode: 0644]
lib/puppet/parser/functions/merge.rb
lib/puppet/parser/functions/min.rb [new file with mode: 0644]
lib/puppet/parser/functions/num2bool.rb
lib/puppet/parser/functions/parseyaml.rb
lib/puppet/parser/functions/pick.rb [new file with mode: 0644]
lib/puppet/parser/functions/prefix.rb
lib/puppet/parser/functions/range.rb
lib/puppet/parser/functions/reject.rb [new file with mode: 0644]
lib/puppet/parser/functions/squeeze.rb
lib/puppet/parser/functions/str2bool.rb
lib/puppet/parser/functions/str2saltedsha512.rb [new file with mode: 0644]
lib/puppet/parser/functions/suffix.rb [new file with mode: 0644]
lib/puppet/parser/functions/to_bytes.rb [new file with mode: 0644]
lib/puppet/parser/functions/union.rb [new file with mode: 0644]
lib/puppet/parser/functions/uriescape.rb [new file with mode: 0644]
lib/puppet/parser/functions/validate_absolute_path.rb [new file with mode: 0644]
lib/puppet/parser/functions/validate_array.rb
lib/puppet/parser/functions/validate_augeas.rb [new file with mode: 0644]
lib/puppet/parser/functions/validate_bool.rb
lib/puppet/parser/functions/validate_cmd.rb [new file with mode: 0644]
lib/puppet/parser/functions/validate_hash.rb
lib/puppet/parser/functions/validate_re.rb
lib/puppet/parser/functions/validate_slength.rb [new file with mode: 0644]
lib/puppet/parser/functions/validate_string.rb
lib/puppet/provider/file_line/ruby.rb
lib/puppet/type/anchor.rb
lib/puppet/type/file_line.rb
manifests/init.pp
manifests/stages.pp
spec/classes/anchor_spec.rb [new file with mode: 0644]
spec/functions/defined_with_params_spec.rb [new file with mode: 0644]
spec/functions/ensure_packages_spec.rb [new file with mode: 0644]
spec/functions/ensure_resource_spec.rb [new file with mode: 0644]
spec/functions/getparam_spec.rb [new file with mode: 0644]
spec/lib/puppet_spec/files.rb [deleted file]
spec/lib/puppet_spec/fixtures.rb [deleted file]
spec/lib/puppet_spec/matchers.rb [deleted file]
spec/lib/puppet_spec/verbose.rb [deleted file]
spec/monkey_patches/publicize_methods.rb
spec/spec_helper.rb
spec/unit/facter/pe_required_facts_spec.rb [new file with mode: 0644]
spec/unit/facter/pe_version_spec.rb [new file with mode: 0644]
spec/unit/facter/root_home_spec.rb
spec/unit/facter/util/puppet_settings_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/abs_spec.rb
spec/unit/puppet/parser/functions/any2array_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/base64_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/bool2num_spec.rb
spec/unit/puppet/parser/functions/capitalize_spec.rb
spec/unit/puppet/parser/functions/chomp_spec.rb
spec/unit/puppet/parser/functions/chop_spec.rb
spec/unit/puppet/parser/functions/concat_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/count_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/delete_at_spec.rb
spec/unit/puppet/parser/functions/delete_spec.rb
spec/unit/puppet/parser/functions/difference_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/dirname_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/downcase_spec.rb
spec/unit/puppet/parser/functions/empty_spec.rb
spec/unit/puppet/parser/functions/flatten_spec.rb
spec/unit/puppet/parser/functions/floor_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/get_module_path_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/getvar_spec.rb
spec/unit/puppet/parser/functions/grep_spec.rb
spec/unit/puppet/parser/functions/has_interface_with_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/has_ip_address_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/has_ip_network_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/has_key_spec.rb
spec/unit/puppet/parser/functions/hash_spec.rb
spec/unit/puppet/parser/functions/intersection_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/is_array_spec.rb
spec/unit/puppet/parser/functions/is_domain_name_spec.rb
spec/unit/puppet/parser/functions/is_float_spec.rb
spec/unit/puppet/parser/functions/is_function_available.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/is_hash_spec.rb
spec/unit/puppet/parser/functions/is_integer_spec.rb
spec/unit/puppet/parser/functions/is_ip_address_spec.rb
spec/unit/puppet/parser/functions/is_mac_address_spec.rb
spec/unit/puppet/parser/functions/is_numeric_spec.rb
spec/unit/puppet/parser/functions/is_string_spec.rb
spec/unit/puppet/parser/functions/join_keys_to_values_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/join_spec.rb
spec/unit/puppet/parser/functions/keys_spec.rb
spec/unit/puppet/parser/functions/lstrip_spec.rb
spec/unit/puppet/parser/functions/max_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/member_spec.rb
spec/unit/puppet/parser/functions/merge_spec.rb
spec/unit/puppet/parser/functions/min_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/num2bool_spec.rb
spec/unit/puppet/parser/functions/parsejson_spec.rb
spec/unit/puppet/parser/functions/parseyaml_spec.rb
spec/unit/puppet/parser/functions/pick_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/prefix_spec.rb
spec/unit/puppet/parser/functions/range_spec.rb
spec/unit/puppet/parser/functions/reject_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/reverse_spec.rb
spec/unit/puppet/parser/functions/rstrip_spec.rb
spec/unit/puppet/parser/functions/shuffle_spec.rb
spec/unit/puppet/parser/functions/size_spec.rb
spec/unit/puppet/parser/functions/sort_spec.rb
spec/unit/puppet/parser/functions/squeeze_spec.rb
spec/unit/puppet/parser/functions/str2bool_spec.rb
spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/strftime_spec.rb
spec/unit/puppet/parser/functions/strip_spec.rb
spec/unit/puppet/parser/functions/suffix_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/swapcase_spec.rb
spec/unit/puppet/parser/functions/time_spec.rb
spec/unit/puppet/parser/functions/to_bytes_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/type_spec.rb
spec/unit/puppet/parser/functions/union_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/unique_spec.rb
spec/unit/puppet/parser/functions/upcase_spec.rb
spec/unit/puppet/parser/functions/uriescape_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/validate_array_spec.rb
spec/unit/puppet/parser/functions/validate_augeas_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/validate_bool_spec.rb
spec/unit/puppet/parser/functions/validate_cmd_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/validate_hash_spec.rb
spec/unit/puppet/parser/functions/validate_re_spec.rb [new file with mode: 0644]
spec/unit/puppet/parser/functions/validate_slength_spec.rb [new file with mode: 0755]
spec/unit/puppet/parser/functions/validate_string_spec.rb
spec/unit/puppet/parser/functions/values_at_spec.rb
spec/unit/puppet/parser/functions/values_spec.rb
spec/unit/puppet/parser/functions/zip_spec.rb
spec/unit/puppet/provider/file_line/ruby_spec.rb
spec/unit/puppet/type/file_line_spec.rb
tests/has_interface_with.pp [new file with mode: 0644]
tests/has_ip_address.pp [new file with mode: 0644]
tests/has_ip_network.pp [new file with mode: 0644]

diff --git a/.gemspec b/.gemspec
new file mode 100644 (file)
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
index 481fc81..2e3ca63 100644 (file)
@@ -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 (file)
index 0000000..4e2c033
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>stdlib</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.cloudsmith.geppetto.pp.dsl.ui.modulefileBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.cloudsmith.geppetto.pp.dsl.ui.puppetNature</nature>
+               <nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
+       </natures>
+</projectDescription>
diff --git a/.rspec b/.rspec
new file mode 100644 (file)
index 0000000..7ab5f55
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,4 @@
+--color
+--format
+progress
+--backtrace
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..1bb1889
--- /dev/null
@@ -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
index dff036b..e1a095f 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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 (file)
index 0000000..bd11f63
--- /dev/null
@@ -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
diff --git a/Gemfile b/Gemfile
new file mode 100644 (file)
index 0000000..197cc6b
--- /dev/null
+++ b/Gemfile
@@ -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
index 20ddad2..9d2e8c2 100644 (file)
@@ -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'
index 4f06651..0e40f51 100644 (file)
@@ -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 (file)
index 0000000..04349ed
--- /dev/null
@@ -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 (file)
index 0000000..917b631
--- /dev/null
@@ -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)
index ea40d5d..0f9328e 100644 (file)
@@ -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.
index 01b2a31..14f1c24 100644 (file)
--- a/Rakefile
+++ b/Rakefile
@@ -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'
index 90586a9..e414b20 100644 (file)
@@ -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.
 # 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 (file)
index 0000000..0cc0f64
--- /dev/null
@@ -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 (file)
index 0000000..0e6af40
--- /dev/null
@@ -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
index 61fcf39..8249f7d 100644 (file)
@@ -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 (file)
index 0000000..1ad9452
--- /dev/null
@@ -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
index ade5462..11d2d7f 100644 (file)
@@ -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 (file)
index 0000000..e71407e
--- /dev/null
@@ -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 (file)
index 0000000..d9a590a
--- /dev/null
@@ -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
index c99d139..4564a00 100644 (file)
@@ -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
index 636b990..f242af3 100644 (file)
@@ -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 (file)
index 0000000..c86aa00
--- /dev/null
@@ -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 (file)
index 0000000..52de1b8
--- /dev/null
@@ -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 (file)
index 0000000..d7df306
--- /dev/null
@@ -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
index ab8f75b..f814344 100644 (file)
@@ -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 (file)
index 0000000..cd258f7
--- /dev/null
@@ -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 (file)
index 0000000..ea8cc1e
--- /dev/null
@@ -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 (file)
index 0000000..450ea02
--- /dev/null
@@ -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 (file)
index 0000000..a9a1733
--- /dev/null
@@ -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
index 781da78..a1ed183 100644 (file)
@@ -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 (file)
index 0000000..a401923
--- /dev/null
@@ -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 (file)
index 0000000..6558206
--- /dev/null
@@ -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 (file)
index 0000000..1421b91
--- /dev/null
@@ -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 (file)
index 0000000..6d51006
--- /dev/null
@@ -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
index ffd774d..1621149 100644 (file)
@@ -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 (file)
index 0000000..7f150a7
--- /dev/null
@@ -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 (file)
index 0000000..842c8ec
--- /dev/null
@@ -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 (file)
index 0000000..9ccf902
--- /dev/null
@@ -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
index 9c1c4c3..4657cc2 100644 (file)
@@ -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
 
index 453ba1e..8cc4823 100644 (file)
@@ -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 (file)
index 0000000..48f02e9
--- /dev/null
@@ -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 :
index 4e92939..5826dc0 100644 (file)
@@ -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
index 2fc05ba..911f3c2 100644 (file)
@@ -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 (file)
index 0000000..6cbd35c
--- /dev/null
@@ -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 :
index 8ee34f6..6b29e98 100644 (file)
@@ -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
index b4a9a15..a90adab 100644 (file)
@@ -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
index ce13ece..abf0321 100644 (file)
@@ -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 (file)
index 0000000..e9924fe
--- /dev/null
@@ -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 :
index 0f16f69..10c4005 100644 (file)
@@ -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 (file)
index 0000000..60fb94a
--- /dev/null
@@ -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
index d2dc0f9..1b39f20 100644 (file)
@@ -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 (file)
index 0000000..6bd6ebf
--- /dev/null
@@ -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
index 874db22..af0e6ed 100644 (file)
@@ -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
 
index e8ac8a4..53d54fa 100644 (file)
@@ -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 (file)
index 0000000..cbc0300
--- /dev/null
@@ -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
index 4593976..62211ae 100644 (file)
@@ -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
 
index 03ab9e9..0849491 100644 (file)
@@ -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 (file)
index 0000000..1953ffc
--- /dev/null
@@ -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 :
index 65c174a..81fadfd 100644 (file)
@@ -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
index c320da6..fece7a6 100644 (file)
@@ -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 (file)
index 0000000..7fe7b01
--- /dev/null
@@ -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 (file)
index 0000000..f7792d6
--- /dev/null
@@ -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 (file)
index 0000000..8ff73d1
--- /dev/null
@@ -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 (file)
index 0000000..c91bb80
--- /dev/null
@@ -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 (file)
index 0000000..67b93a6
--- /dev/null
@@ -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 (file)
index 0000000..fe27974
--- /dev/null
@@ -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
index a7a7165..34b5118 100644 (file)
@@ -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 (file)
index 0000000..154d660
--- /dev/null
@@ -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
index 49e6378..62c1d88 100644 (file)
@@ -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 (file)
index 0000000..344a80c
--- /dev/null
@@ -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
index 1443318..9bdd543 100644 (file)
@@ -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
index 583f26a..ca25a70 100644 (file)
@@ -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 (file)
index 0000000..fdcc0a2
--- /dev/null
@@ -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
index d0e1376..e667794 100644 (file)
@@ -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' ])
index 63bbd8e..a3219d3 100644 (file)
@@ -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
index 0c28b1c..fe1e5aa 100644 (file)
@@ -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
index aacd6d9..f71a4bc 100644 (file)
@@ -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
index c804568..500ad77 100644 (file)
@@ -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': }
index 365b905..eb15fd6 100644 (file)
@@ -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
 #  * 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 (file)
index 0000000..2dd17de
--- /dev/null
@@ -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 (file)
index 0000000..28dbab3
--- /dev/null
@@ -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 (file)
index 0000000..1c2a328
--- /dev/null
@@ -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 (file)
index 0000000..2e8aefc
--- /dev/null
@@ -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 (file)
index 0000000..d9c50a6
--- /dev/null
@@ -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 (executable)
index 30fb4fc..0000000
+++ /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 (executable)
index 7f6bc2a..0000000
+++ /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 (file)
index 77f5803..0000000
+++ /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 (executable)
index d9834f2..0000000
+++ /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
index b39e9c0..f3a1abf 100755 (executable)
@@ -8,4 +8,3 @@ class Class
         self.class_eval { private(*saved_private_instance_methods) }
     end
 end
-
index 87aac34..931d35c 100644 (file)
@@ -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 (file)
index 0000000..f219b37
--- /dev/null
@@ -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 (file)
index 0000000..931c6d4
--- /dev/null
@@ -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
index 8946d9d..ce80684 100644 (file)
@@ -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 (file)
index 0000000..c3ce6ea
--- /dev/null
@@ -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
index 65ba2e8..c0b4297 100755 (executable)
@@ -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 (file)
index 0000000..b266e84
--- /dev/null
@@ -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 (executable)
index 0000000..5faa5e6
--- /dev/null
@@ -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
index d5da18c..518ac85 100755 (executable)
@@ -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
index 1c45821..69c9758 100755 (executable)
@@ -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
index 0592115..e425365 100755 (executable)
@@ -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
index 0c456a8..9e466de 100755 (executable)
@@ -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 (file)
index 0000000..123188b
--- /dev/null
@@ -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 (file)
index 0000000..2453815
--- /dev/null
@@ -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
index 27db0c8..d8d9618 100755 (executable)
@@ -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
index fab3230..2f29c93 100755 (executable)
@@ -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 (file)
index 0000000..9feff09
--- /dev/null
@@ -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 (executable)
index 0000000..fb3b4fe
--- /dev/null
@@ -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
index 0bccd5f..acef1f0 100755 (executable)
@@ -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
index cb0021f..7745875 100755 (executable)
@@ -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
index 7bedeb2..dba7a6b 100755 (executable)
@@ -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 (file)
index 0000000..dbc8c77
--- /dev/null
@@ -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 (file)
index 0000000..2577723
--- /dev/null
@@ -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 (file)
index 0000000..486bef6
--- /dev/null
@@ -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
index 16edd98..5ff834e 100644 (file)
@@ -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
-
index b1f647c..a93b842 100755 (executable)
@@ -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 (executable)
index 0000000..c5264e4
--- /dev/null
@@ -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 (executable)
index 0000000..5a68460
--- /dev/null
@@ -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 (executable)
index 0000000..c3a289e
--- /dev/null
@@ -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
+
index d1dcd15..490daea 100644 (file)
@@ -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
index 6d3d48c..7c91be9 100644 (file)
@@ -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 (file)
index 0000000..fd44f7f
--- /dev/null
@@ -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
index 537595c..e7f4bcd 100644 (file)
@@ -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
index ec7c7f5..f2ea76d 100644 (file)
@@ -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
index 55ba8cf..b7d73b0 100644 (file)
@@ -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 (file)
index 0000000..bd40c51
--- /dev/null
@@ -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
index 94364f5..bbebf39 100644 (file)
@@ -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
index faf6f2d..4335795 100644 (file)
@@ -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
index 98ce828..c0debb3 100644 (file)
@@ -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
index c9b9637..ca9c590 100644 (file)
@@ -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
index 2191b7b..d7440fb 100644 (file)
@@ -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
index 4f3f5fd..3756bea 100644 (file)
@@ -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 (file)
index 0000000..a52fb71
--- /dev/null
@@ -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
index 1b3dec8..aafa1a7 100644 (file)
@@ -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
index 927be96..fdd7a70 100644 (file)
@@ -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
index ac331fa..b280ae7 100644 (file)
@@ -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 (executable)
index 0000000..ff6f2b3
--- /dev/null
@@ -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
index 2cebc0d..6e9a023 100644 (file)
@@ -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
index 71e1869..8a170bb 100644 (file)
@@ -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 (executable)
index 0000000..71d593e
--- /dev/null
@@ -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
index 6585273..b56196d 100644 (file)
@@ -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
index 26eea36..f179ac1 100644 (file)
@@ -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
index f9cb049..0c7aea8 100644 (file)
@@ -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 (file)
index 0000000..761db6b
--- /dev/null
@@ -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
index a0cbcab..5cf592b 100644 (file)
@@ -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
index 24cc391..5eb290f 100644 (file)
@@ -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 (executable)
index 0000000..f2cb741
--- /dev/null
@@ -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
index 4fa50e4..1b59206 100644 (file)
@@ -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
index af8cc12..d90de1d 100644 (file)
@@ -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
index f04fda5..93346d5 100644 (file)
@@ -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
index ccaa335..b1c435a 100644 (file)
@@ -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
index fbe3073..3187a5a 100644 (file)
@@ -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
index 9355ad2..60e5a30 100644 (file)
@@ -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
index d7f0ac9..ef6350f 100644 (file)
@@ -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 (file)
index 0000000..df8fb8e
--- /dev/null
@@ -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
index f7a2cd9..df42b6f 100644 (file)
@@ -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
index 48a52dd..fccdd26 100644 (file)
@@ -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 (file)
index 0000000..c28f719
--- /dev/null
@@ -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
index 2686054..808b415 100644 (file)
@@ -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
index 666e8e0..e9fb76e 100644 (file)
@@ -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 (executable)
index 0000000..d1ea4c8
--- /dev/null
@@ -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
index e3c28ed..8fec88f 100644 (file)
@@ -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 (file)
index 0000000..0d282ca
--- /dev/null
@@ -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
index 627dc33..5d48d49 100644 (file)
@@ -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
index 5d18846..5db5513 100644 (file)
@@ -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 (file)
index 0000000..371de46
--- /dev/null
@@ -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 (file)
index 0000000..08aaf78
--- /dev/null
@@ -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
index 37ae09d..4b31cfd 100644 (file)
@@ -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