changeset 26:58d1818c2ded puppet-3.6

Update MySQL module (which adds "staging" module)
author IBBoard <dev@ibboard.co.uk>
date Mon, 09 Mar 2015 01:34:59 +0000
parents 13adb555a7e2
children 25405d1350ef
files modules/mysql/CHANGELOG.md modules/mysql/CONTRIBUTING.md modules/mysql/Gemfile modules/mysql/Gemfile.lock modules/mysql/README.md modules/mysql/Rakefile modules/mysql/checksums.json modules/mysql/examples/backup.pp modules/mysql/examples/bindings.pp modules/mysql/examples/java.pp modules/mysql/examples/mysql_database.pp modules/mysql/examples/mysql_db.pp modules/mysql/examples/mysql_grant.pp modules/mysql/examples/mysql_plugin.pp modules/mysql/examples/mysql_user.pp modules/mysql/examples/perl.pp modules/mysql/examples/python.pp modules/mysql/examples/ruby.pp modules/mysql/examples/server.pp modules/mysql/examples/server/account_security.pp modules/mysql/examples/server/config.pp modules/mysql/files/mysqltuner.pl modules/mysql/lib/puppet/parser/functions/mysql_dirname.rb modules/mysql/lib/puppet/parser/functions/mysql_password.rb modules/mysql/lib/puppet/provider/database/mysql.rb modules/mysql/lib/puppet/provider/database_grant/mysql.rb modules/mysql/lib/puppet/provider/database_user/mysql.rb modules/mysql/lib/puppet/provider/mysql_database/mysql.rb modules/mysql/lib/puppet/provider/mysql_grant/mysql.rb modules/mysql/lib/puppet/provider/mysql_plugin/mysql.rb modules/mysql/lib/puppet/provider/mysql_user/mysql.rb modules/mysql/lib/puppet/type/database.rb modules/mysql/lib/puppet/type/database_grant.rb modules/mysql/lib/puppet/type/database_user.rb modules/mysql/lib/puppet/type/mysql_database.rb modules/mysql/lib/puppet/type/mysql_grant.rb modules/mysql/lib/puppet/type/mysql_plugin.rb modules/mysql/lib/puppet/type/mysql_user.rb modules/mysql/manifests/backup.pp modules/mysql/manifests/backup/mysqlbackup.pp modules/mysql/manifests/backup/mysqldump.pp modules/mysql/manifests/backup/xtrabackup.pp modules/mysql/manifests/bindings.pp modules/mysql/manifests/bindings/client_dev.pp modules/mysql/manifests/bindings/daemon_dev.pp modules/mysql/manifests/bindings/java.pp modules/mysql/manifests/bindings/perl.pp modules/mysql/manifests/bindings/php.pp modules/mysql/manifests/bindings/python.pp modules/mysql/manifests/bindings/ruby.pp modules/mysql/manifests/client.pp modules/mysql/manifests/client/install.pp modules/mysql/manifests/db.pp modules/mysql/manifests/init.pp modules/mysql/manifests/params.pp modules/mysql/manifests/server.pp modules/mysql/manifests/server/account_security.pp modules/mysql/manifests/server/backup.pp modules/mysql/manifests/server/config.pp modules/mysql/manifests/server/install.pp modules/mysql/manifests/server/mysqltuner.pp modules/mysql/manifests/server/root_password.pp modules/mysql/manifests/server/service.pp modules/mysql/metadata.json modules/mysql/spec/acceptance/mysql_account_delete_spec.rb modules/mysql/spec/acceptance/mysql_backup_spec.rb modules/mysql/spec/acceptance/mysql_bindings_spec.rb modules/mysql/spec/acceptance/mysql_db_spec.rb modules/mysql/spec/acceptance/mysql_server_config_spec.rb modules/mysql/spec/acceptance/mysql_server_monitor_spec.rb modules/mysql/spec/acceptance/mysql_server_root_password_spec.rb modules/mysql/spec/acceptance/mysql_server_spec.rb modules/mysql/spec/acceptance/nodesets/default.yml modules/mysql/spec/acceptance/types/mysql_database_spec.rb modules/mysql/spec/acceptance/types/mysql_grant_spec.rb modules/mysql/spec/acceptance/types/mysql_plugin_spec.rb modules/mysql/spec/acceptance/types/mysql_user_spec.rb modules/mysql/spec/acceptance/unsupported_spec.rb modules/mysql/spec/classes/graceful_failures_spec.rb modules/mysql/spec/classes/mycnf_template_spec.rb modules/mysql/spec/classes/mysql_bindings_spec.rb modules/mysql/spec/classes/mysql_client_spec.rb modules/mysql/spec/classes/mysql_server_account_security_spec.rb modules/mysql/spec/classes/mysql_server_backup_spec.rb modules/mysql/spec/classes/mysql_server_monitor_spec.rb modules/mysql/spec/classes/mysql_server_mysqltuner_spec.rb modules/mysql/spec/classes/mysql_server_spec.rb modules/mysql/spec/defines/mysql_db_spec.rb modules/mysql/spec/spec_helper.rb modules/mysql/spec/spec_helper_acceptance.rb modules/mysql/spec/unit/mysql_password_spec.rb modules/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb modules/mysql/spec/unit/puppet/functions/mysql_password_spec.rb modules/mysql/spec/unit/puppet/provider/database/mysql_spec.rb modules/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb modules/mysql/spec/unit/puppet/provider/database_user/mysql_spec.rb modules/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb modules/mysql/spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb modules/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb modules/mysql/spec/unit/puppet/type/mysql_database_spec.rb modules/mysql/spec/unit/puppet/type/mysql_grant_spec.rb modules/mysql/spec/unit/puppet/type/mysql_plugin_spec.rb modules/mysql/spec/unit/puppet/type/mysql_user_spec.rb modules/mysql/templates/meb.cnf.erb modules/mysql/templates/my.cnf.erb modules/mysql/templates/my.conf.cnf.erb modules/mysql/templates/mysqlbackup.sh.erb modules/mysql/tests/backup.pp modules/mysql/tests/bindings.pp modules/mysql/tests/init.pp modules/mysql/tests/java.pp modules/mysql/tests/mysql_database.pp modules/mysql/tests/mysql_db.pp modules/mysql/tests/mysql_grant.pp modules/mysql/tests/mysql_user.pp modules/mysql/tests/perl.pp modules/mysql/tests/python.pp modules/mysql/tests/ruby.pp modules/mysql/tests/server.pp modules/mysql/tests/server/account_security.pp modules/mysql/tests/server/config.pp modules/staging/Gemfile modules/staging/LICENSE modules/staging/README.md modules/staging/Rakefile modules/staging/Vagrantfile modules/staging/checksums.json modules/staging/docs/deploy.html modules/staging/docs/extract.html modules/staging/docs/file.html modules/staging/docs/init.html modules/staging/files/sample modules/staging/files/sample.tar.bz2 modules/staging/files/sample.tar.gz modules/staging/lib/facter/staging_http_get.rb modules/staging/lib/facter/staging_windir.rb modules/staging/lib/puppet/parser/functions/scope_defaults.rb modules/staging/lib/puppet/parser/functions/staging_parse.rb modules/staging/manifests/.init.pp.swp modules/staging/manifests/deploy.pp modules/staging/manifests/extract.pp modules/staging/manifests/file.pp modules/staging/manifests/init.pp modules/staging/manifests/params.pp modules/staging/metadata.json modules/staging/spec/defines/staging_deploy_spec.rb modules/staging/spec/defines/staging_extract_spec.rb modules/staging/spec/defines/staging_file_spec.rb modules/staging/spec/fixtures/hiera.yaml modules/staging/spec/spec_helper.rb modules/staging/spec/unit/puppet/parser/functions/scope_defaults_spec.rb modules/staging/spec/unit/puppet/parser/functions/staging_parse_spec.rb modules/staging/tests/deploy.pp modules/staging/tests/extract.pp modules/staging/tests/file.pp modules/staging/tests/init.pp modules/staging/tests/scope_defaults.pp modules/staging/tests/staging_parse.pp
diffstat 158 files changed, 4922 insertions(+), 3849 deletions(-) [+]
line wrap: on
line diff
--- a/modules/mysql/CHANGELOG.md	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/CHANGELOG.md	Mon Mar 09 01:34:59 2015 +0000
@@ -1,3 +1,85 @@
+##2015-03-03 - Supported Release 3.3.0
+###Summary
+This release includes major README updates, the addition of backup providers, and a fix for managing the log-bin directory.
+
+####Features
+- Add package_manage parameters to `mysql::server` and `mysql::client` (MODULES-1143)
+- README improvements
+- Add `mysqldump`, `mysqlbackup`, and `xtrabackup` backup providers.
+
+####Bugfixes
+- log-error overrides were not being properly used (MODULES-1804)
+- check for full path for log-bin to stop puppet from managing file '.'
+
+##2015-02-09 - Supported Release 3.2.0
+###Summary
+This release includes several new features and bugfixes, including support for various plugins, making the output from mysql_password more consistent when input is empty and improved username validation.
+
+####Features
+- Add type and provider to manage plugins
+- Add support for authentication plugins
+- Add support for mysql_install_db on freebsd
+- Add `create_root_user` and `create_root_my_cnf` parameters to `mysql::server`
+
+####Bugfixes
+- Remove dependency on stdlib >= 4.1.0 (MODULES-1759)
+- Make grant autorequire user
+- Remove invalid parameter 'provider' from mysql_user instance (MODULES-1731)
+- Return empty string for empty input in mysql_password
+- Fix `mysql::account_security` when fqdn==localhost
+- Update username validation (MODULES-1520)
+- Future parser fix in params.pp
+- Fix package name for debian 8
+- Don't start the service until the server package is installed and the config file is in place
+- Test fixes
+- Lint fixes
+
+##2014-12-16 - Supported Release 3.1.0
+###Summary
+
+This release includes several new features, including SLES12 support, and a number of bug fixes.
+
+####Notes
+
+`mysql::server::mysqltuner` has been refactored to fetch the mysqltuner script from github by default. If you are running on a non-network-connected system, you will need to download that file and have it available to your node at a path specified by the `source` parameter to the `mysqltuner` class.
+
+####Features
+- Add support for install_options for all package resources (MODULES-1484)
+- Add log-bin directory creation
+- Allow mysql::db to import multiple files (MODULES-1338)
+- SLES12 support
+- Improved identifier quoting detections
+- Reworked `mysql::server::mysqltuner` so that we are no longer packaging the script as it is licensed under the GPL.
+
+####Bugfixes
+- Fix regression in username validation
+- Proper containment for mysql::client in mysql::db
+- Support quoted usernames of length 15 and 16 chars
+
+##2014-11-11 - Supported Release 3.0.0
+###Summary
+
+Added several new features including MariaDB support and future parser
+
+####Backwards-incompatible Changes
+* Remove the deprecated `database`, `database_user`, and `database_grant` resources. The correct resources to use are `mysql`, `mysql_user`, and `mysql_grant` respectively.
+
+####Features
+* Add MariaDB Support
+* The mysqltuner perl script has been updated to 1.3.0 based on work at http://github.com/major/MySQLTuner-perl
+* Add future parse support, fixed issues with undef to empty string
+* Pass the backup credentials to 'SHOW DATABASES'
+* Ability to specify the Includedir for `mysql::server`
+* `mysql::db` now has an import\_timeout feature that defaults to 300
+* The `mysql` class has been removed
+* `mysql::server` now takes an `override_options` hash that will affect the installation
+* Ability to install both dev and client dev 
+
+####BugFix
+* `mysql::server::backup` now passes `ensure` param to the nested `mysql_grant`
+* `mysql::server::service` now properly requires the presence of the `log_error` file
+* `mysql::config` now occurs before `mysql::server::install_db` correctly
+
 ##2014-07-15 - Supported Release 2.3.1
 ###Summary
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/CONTRIBUTING.md	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,220 @@
+Checklist (and a short version for the impatient)
+=================================================
+
+  * Commits:
+
+    - Make commits of logical units.
+
+    - Check for unnecessary whitespace with "git diff --check" before
+      committing.
+
+    - Commit using Unix line endings (check the settings around "crlf" in
+      git-config(1)).
+
+    - Do not check in commented out code or unneeded files.
+
+    - The first line of the commit message should be a short
+      description (50 characters is the soft limit, excluding ticket
+      number(s)), and should skip the full stop.
+
+    - Associate the issue in the message. The first line should include
+      the issue number in the form "(#XXXX) Rest of message".
+
+    - The body should provide a meaningful commit message, which:
+
+      - uses the imperative, present tense: "change", not "changed" or
+        "changes".
+
+      - includes motivation for the change, and contrasts its
+        implementation with the previous behavior.
+
+    - Make sure that you have tests for the bug you are fixing, or
+      feature you are adding.
+
+    - Make sure the test suites passes after your commit:
+      `bundle exec rspec spec/acceptance` More information on [testing](#Testing) below
+
+    - When introducing a new feature, make sure it is properly
+      documented in the README.md
+
+  * Submission:
+
+    * Pre-requisites:
+
+      - Make sure you have a [GitHub account](https://github.com/join)
+
+      - [Create a ticket](https://tickets.puppetlabs.com/secure/CreateIssue!default.jspa), or [watch the ticket](https://tickets.puppetlabs.com/browse/) you are patching for.
+
+    * Preferred method:
+
+      - Fork the repository on GitHub.
+
+      - Push your changes to a topic branch in your fork of the
+        repository. (the format ticket/1234-short_description_of_change is
+        usually preferred for this project).
+
+      - Submit a pull request to the repository in the puppetlabs
+        organization.
+
+The long version
+================
+
+  1.  Make separate commits for logically separate changes.
+
+      Please break your commits down into logically consistent units
+      which include new or changed tests relevant to the rest of the
+      change.  The goal of doing this is to make the diff easier to
+      read for whoever is reviewing your code.  In general, the easier
+      your diff is to read, the more likely someone will be happy to
+      review it and get it into the code base.
+
+      If you are going to refactor a piece of code, please do so as a
+      separate commit from your feature or bug fix changes.
+
+      We also really appreciate changes that include tests to make
+      sure the bug is not re-introduced, and that the feature is not
+      accidentally broken.
+
+      Describe the technical detail of the change(s).  If your
+      description starts to get too long, that is a good sign that you
+      probably need to split up your commit into more finely grained
+      pieces.
+
+      Commits which plainly describe the things which help
+      reviewers check the patch and future developers understand the
+      code are much more likely to be merged in with a minimum of
+      bike-shedding or requested changes.  Ideally, the commit message
+      would include information, and be in a form suitable for
+      inclusion in the release notes for the version of Puppet that
+      includes them.
+
+      Please also check that you are not introducing any trailing
+      whitespace or other "whitespace errors".  You can do this by
+      running "git diff --check" on your changes before you commit.
+
+  2.  Sending your patches
+
+      To submit your changes via a GitHub pull request, we _highly_
+      recommend that you have them on a topic branch, instead of
+      directly on "master".
+      It makes things much easier to keep track of, especially if
+      you decide to work on another thing before your first change
+      is merged in.
+
+      GitHub has some pretty good
+      [general documentation](http://help.github.com/) on using
+      their site.  They also have documentation on
+      [creating pull requests](http://help.github.com/send-pull-requests/).
+
+      In general, after pushing your topic branch up to your
+      repository on GitHub, you can switch to the branch in the
+      GitHub UI and click "Pull Request" towards the top of the page
+      in order to open a pull request.
+
+
+  3.  Update the related GitHub issue.
+
+      If there is a GitHub issue associated with the change you
+      submitted, then you should update the ticket to include the
+      location of your branch, along with any other commentary you
+      may wish to make.
+
+Testing
+=======
+
+Getting Started
+---------------
+
+Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby
+package manager such as [bundler](http://bundler.io/) what Ruby packages,
+or Gems, are required to build, develop, and test this software.
+
+Please make sure you have [bundler installed](http://bundler.io/#getting-started)
+on your system, then use it to install all dependencies needed for this project,
+by running
+
+```shell
+% bundle install
+Fetching gem metadata from https://rubygems.org/........
+Fetching gem metadata from https://rubygems.org/..
+Using rake (10.1.0)
+Using builder (3.2.2)
+-- 8><-- many more --><8 --
+Using rspec-system-puppet (2.2.0)
+Using serverspec (0.6.3)
+Using rspec-system-serverspec (1.0.0)
+Using bundler (1.3.5)
+Your bundle is complete!
+Use `bundle show [gemname]` to see where a bundled gem is installed.
+```
+
+NOTE some systems may require you to run this command with sudo.
+
+If you already have those gems installed, make sure they are up-to-date:
+
+```shell
+% bundle update
+```
+
+With all dependencies in place and up-to-date we can now run the tests:
+
+```shell
+% rake spec
+```
+
+This will execute all the [rspec tests](http://rspec-puppet.com/) tests
+under [spec/defines](./spec/defines), [spec/classes](./spec/classes),
+and so on. rspec tests may have the same kind of dependencies as the
+module they are testing. While the module defines in its [Modulefile](./Modulefile),
+rspec tests define them in [.fixtures.yml](./fixtures.yml).
+
+Some puppet modules also come with [beaker](https://github.com/puppetlabs/beaker)
+tests. These tests spin up a virtual machine under
+[VirtualBox](https://www.virtualbox.org/)) with, controlling it with
+[Vagrant](http://www.vagrantup.com/) to actually simulate scripted test
+scenarios. In order to run these, you will need both of those tools
+installed on your system.
+
+You can run them by issuing the following command
+
+```shell
+% rake spec_clean
+% rspec spec/acceptance
+```
+
+This will now download a pre-fabricated image configured in the [default node-set](./spec/acceptance/nodesets/default.yml),
+install puppet, copy this module and install its dependencies per [spec/spec_helper_acceptance.rb](./spec/spec_helper_acceptance.rb)
+and then run all the tests under [spec/acceptance](./spec/acceptance).
+
+Writing Tests
+-------------
+
+XXX getting started writing tests.
+
+If you have commit access to the repository
+===========================================
+
+Even if you have commit access to the repository, you will still need to
+go through the process above, and have someone else review and merge
+in your changes.  The rule is that all changes must be reviewed by a
+developer on the project (that did not write the code) to ensure that
+all changes go through a code review process.
+
+Having someone other than the author of the topic branch recorded as
+performing the merge is the record that they performed the code
+review.
+
+
+Additional Resources
+====================
+
+* [Getting additional help](http://puppetlabs.com/community/get-help)
+
+* [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests)
+
+* [Patchwork](https://patchwork.puppetlabs.com)
+
+* [General GitHub documentation](http://help.github.com/)
+
+* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
+
--- a/modules/mysql/Gemfile	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/Gemfile	Mon Mar 09 01:34:59 2015 +0000
@@ -1,17 +1,25 @@
-source ENV['GEM_SOURCE'] || 'https://rubygems.org'
+source ENV['GEM_SOURCE'] || "https://rubygems.org"
 
-group :development, :test do
-  gem 'mime-types', '<2.0',      :require => false
-  gem 'rake',  '10.1.1',         :require => false
-  gem 'rspec', '~> 2.11',        :require => false
-  gem 'rspec-puppet',            :require => false
+group :development, :unit_tests do
+  gem 'rake',                    :require => false
+  gem 'rspec-core', '3.1.7',     :require => false
+  gem 'rspec-puppet', '~> 1.0',  :require => false
   gem 'puppetlabs_spec_helper',  :require => false
-  gem 'serverspec',              :require => false
   gem 'puppet-lint',             :require => false
-  gem 'pry',                     :require => false
   gem 'simplecov',               :require => false
-  gem 'beaker',                  :require => false
-  gem 'beaker-rspec', '>= 2.2',  :require => false
+  gem 'puppet_facts',            :require => false
+  gem 'json',                    :require => false
+end
+
+group :system_tests do
+  gem 'beaker-rspec',  :require => false
+  gem 'serverspec',    :require => false
+end
+
+if facterversion = ENV['FACTER_GEM_VERSION']
+  gem 'facter', facterversion, :require => false
+else
+  gem 'facter', :require => false
 end
 
 if puppetversion = ENV['PUPPET_GEM_VERSION']
--- a/modules/mysql/Gemfile.lock	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-GEM
-  remote: https://rubygems.org/
-  specs:
-    CFPropertyList (2.2.8)
-    addressable (2.3.6)
-    archive-tar-minitar (0.5.2)
-    autoparse (0.3.3)
-      addressable (>= 2.3.1)
-      extlib (>= 0.9.15)
-      multi_json (>= 1.0.0)
-    aws-sdk (1.42.0)
-      json (~> 1.4)
-      nokogiri (>= 1.4.4)
-    beaker (1.16.0)
-      aws-sdk (= 1.42.0)
-      blimpy (~> 0.6)
-      docker-api
-      fission (~> 0.4)
-      google-api-client (~> 0.7.1)
-      inifile (~> 2.0)
-      json (~> 1.8)
-      mime-types (~> 1.25)
-      net-scp (~> 1.1)
-      net-ssh (~> 2.6)
-      nokogiri (~> 1.5.10)
-      rbvmomi (= 1.8.1)
-      unf (~> 0.1)
-    beaker-rspec (2.2.6)
-      beaker (~> 1.10)
-      rspec
-      serverspec (~> 1.0)
-      specinfra (~> 1.0)
-    blimpy (0.6.7)
-      fog
-      minitar
-      thor
-    builder (3.2.2)
-    coderay (1.1.0)
-    diff-lcs (1.2.5)
-    docile (1.1.5)
-    docker-api (1.13.1)
-      archive-tar-minitar
-      excon (>= 0.38.0)
-      json
-    excon (0.38.0)
-    extlib (0.9.16)
-    facter (2.1.0)
-    faraday (0.9.0)
-      multipart-post (>= 1.2, < 3)
-    fission (0.5.0)
-      CFPropertyList (~> 2.2)
-    fog (1.23.0)
-      fog-brightbox
-      fog-core (~> 1.23)
-      fog-json
-      fog-softlayer
-      ipaddress (~> 0.5)
-      nokogiri (~> 1.5, >= 1.5.11)
-    fog-brightbox (0.1.1)
-      fog-core (~> 1.22)
-      fog-json
-      inflecto
-    fog-core (1.23.0)
-      builder
-      excon (~> 0.38)
-      formatador (~> 0.2)
-      mime-types
-      net-scp (~> 1.1)
-      net-ssh (>= 2.1.3)
-    fog-json (1.0.0)
-      multi_json (~> 1.0)
-    fog-softlayer (0.3.9)
-      fog-core
-      fog-json
-    formatador (0.2.5)
-    google-api-client (0.7.1)
-      addressable (>= 2.3.2)
-      autoparse (>= 0.3.3)
-      extlib (>= 0.9.15)
-      faraday (>= 0.9.0)
-      jwt (>= 0.1.5)
-      launchy (>= 2.1.1)
-      multi_json (>= 1.0.0)
-      retriable (>= 1.4)
-      signet (>= 0.5.0)
-      uuidtools (>= 2.1.0)
-    hiera (1.3.4)
-      json_pure
-    highline (1.6.21)
-    inflecto (0.0.2)
-    inifile (2.0.2)
-    ipaddress (0.8.0)
-    json (1.8.1)
-    json_pure (1.8.1)
-    jwt (1.0.0)
-    launchy (2.4.2)
-      addressable (~> 2.3)
-    metaclass (0.0.4)
-    method_source (0.8.2)
-    mime-types (1.25.1)
-    minitar (0.5.4)
-    mocha (1.1.0)
-      metaclass (~> 0.0.1)
-    multi_json (1.10.1)
-    multipart-post (2.0.0)
-    net-scp (1.2.1)
-      net-ssh (>= 2.6.5)
-    net-ssh (2.9.1)
-    nokogiri (1.5.11)
-    pry (0.10.0)
-      coderay (~> 1.1.0)
-      method_source (~> 0.8.1)
-      slop (~> 3.4)
-    puppet (3.6.2)
-      facter (> 1.6, < 3)
-      hiera (~> 1.0)
-      json_pure
-      rgen (~> 0.6.5)
-    puppet-lint (0.3.2)
-    puppetlabs_spec_helper (0.7.0)
-      mocha
-      puppet-lint
-      rake
-      rspec
-      rspec-puppet
-    rake (10.1.1)
-    rbvmomi (1.8.1)
-      builder
-      nokogiri (>= 1.4.1)
-      trollop
-    retriable (1.4.1)
-    rgen (0.6.6)
-    rspec (2.99.0)
-      rspec-core (~> 2.99.0)
-      rspec-expectations (~> 2.99.0)
-      rspec-mocks (~> 2.99.0)
-    rspec-core (2.99.1)
-    rspec-expectations (2.99.1)
-      diff-lcs (>= 1.1.3, < 2.0)
-    rspec-its (1.0.1)
-      rspec-core (>= 2.99.0.beta1)
-      rspec-expectations (>= 2.99.0.beta1)
-    rspec-mocks (2.99.1)
-    rspec-puppet (1.0.1)
-      rspec
-    serverspec (1.10.0)
-      highline
-      net-ssh
-      rspec (~> 2.99)
-      rspec-its
-      specinfra (~> 1.20)
-    signet (0.5.1)
-      addressable (>= 2.2.3)
-      faraday (>= 0.9.0.rc5)
-      jwt (>= 0.1.5)
-      multi_json (>= 1.0.0)
-    simplecov (0.8.2)
-      docile (~> 1.1.0)
-      multi_json
-      simplecov-html (~> 0.8.0)
-    simplecov-html (0.8.0)
-    slop (3.6.0)
-    specinfra (1.21.0)
-    thor (0.19.1)
-    trollop (2.0)
-    unf (0.1.4)
-      unf_ext
-    unf_ext (0.0.6)
-    uuidtools (2.1.4)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  beaker
-  beaker-rspec (>= 2.2)
-  mime-types (< 2.0)
-  pry
-  puppet
-  puppet-lint
-  puppetlabs_spec_helper
-  rake (= 10.1.1)
-  rspec (~> 2.11)
-  rspec-puppet
-  serverspec
-  simplecov
--- a/modules/mysql/README.md	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/README.md	Mon Mar 09 01:34:59 2015 +0000
@@ -4,11 +4,13 @@
 
 1. [Overview](#overview)
 2. [Module Description - What the module does and why it is useful](#module-description)
+3. [Backwards compatibility information](#backwards-compatibility)
 3. [Setup - The basics of getting started with mysql](#setup)
-    * [What mysql affects](#what-mysql-affects)
-    * [Setup requirements](#setup-requirements)
     * [Beginning with mysql](#beginning-with-mysql)
 4. [Usage - Configuration options and additional functionality](#usage)
+    * [Customizing Server Options](#customizing-server-options)
+    * [Creating a Database](#creating-a-database)
+    * [Custom Configuration](#custom-configuration)
 5. [Reference - An under-the-hood peek at what the module is doing and how](#reference)
 5. [Limitations - OS compatibility, etc.](#limitations)
 6. [Development - Guide for contributing to the module](#development)
@@ -19,20 +21,7 @@
 
 ##Module Description
 
-The mysql module manages both the installation and configuration of MySQL as
-well as extends Puppet to allow management of MySQL resources, such as
-databases, users, and grants.
-
-##Backwards Compatibility
-
-This module has just undergone a very large rewrite.  Some new classes have been added, and many previous classes and configurations work differently than before.  We've attempted to handle backwards compatibility automatically by adding a
-`attempt_compatibility_mode` parameter to the main mysql class.  If you set
-this to 'true' it will attempt to map your previous parameters into the new
-`mysql::server` class.
-
-#####WARNING
-
-Compatibility mode may fail. It may eat your MySQL server. PLEASE test it before running it live, even if the test is just a no-op and manual comparison. Please be careful!
+The mysql module manages both the installation and configuration of MySQL, as well as extending Puppet to allow management of MySQL resources, such as databases, users, and grants.
 
 ##Setup
 
@@ -44,67 +33,118 @@
 
 ###Beginning with MySQL
 
-If you just want a server installed with the default options you can run
-`include '::mysql::server'`.  
+If you want a server installed with the default options you can run
+`include '::mysql::server'`. 
 
 If you need to customize options, such as the root
 password or `/etc/my.cnf` settings, then you must also pass in an override hash:
 
-```puppet
+~~~
 class { '::mysql::server':
-  root_password    => 'strongpassword',
-  override_options => { 'mysqld' => { 'max_connections' => '1024' } }
+  root_password           => 'strongpassword',
+  remove_default_accounts => true,
+  override_options        => $override_options
 }
-```
+~~~
+
+See [**Customizing Server Options**](#customizing-server-options) below for examples of the hash structure for $override_options`.
 
 ##Usage
 
-All interaction for the server is done via `mysql::server`.  To install the
-client you use `mysql::client`, and to install bindings you can use
-`mysql::bindings`.
+All interaction for the server is done via `mysql::server`. To install the client, use `mysql::client`. To install bindings, use `mysql::bindings`.
+
+###Customizing Server Options
 
-###Overrides
+The hash structure for overrides in `mysql::server` can be structured like a hash in the my.cnf file, so:
 
-The hash structure for overrides in `mysql::server` is as follows:
-
-```puppet
+~~~
 $override_options = {
   'section' => {
-    'item'             => 'thing',
+    'item' => 'thing',
   }
 }
-```
+~~~
 
-For items that you would traditionally represent as:
+For items that you would traditionally represent as
 
-<pre>
+~~~
 [section]
 thing = X
-</pre>
+~~~
+
+you can make an entry like `thing => true`, `thing => value`, or `thing => "` in the hash. Alternatively, you can pass an array, as `thing => ['value', 'value2']`, or list each `thing => value` separately on separate lines. 
 
-You can just make an entry like `thing => true`, `thing => value`, or `thing => "` in the hash. You can also pass an array `thing => ['value', 'value2']` or even list each `thing => value` separately on separate lines. MySQL doesn't care if 'thing' is alone or set to a value; it'll happily accept both.  To keep an option out of the my.cnf file, e.g. when using override_options to revert to a default value, you can pass thing => undef.
-If an option needs multiple instances, you can pass an array. For example
+MySQL doesn't care if 'thing' is alone or set to a value; it happily accepts both. To keep an option out of the my.cnf file --- e.g., when using `override_options` to revert to a default value --- you can pass `thing => undef`.
 
-```puppet
+If an option needs multiple instances, you can pass an array. For example,
+
+~~~
 $override_options = {
   'mysqld' => {
     'replicate-do-db' => ['base1', 'base2'],
   }
 }
-```
+~~~
 
-will produce
+produces
 
-<pre>
+~~~
 [mysql]
 replicate-do-db = base1
 replicate-do-db = base2
-</pre>
+~~~
+
+### Creating a database
+
+To use `mysql::db` to create a database with a user and assign some privileges:
+
+~~~
+mysql::db { 'mydb':
+  user     => 'myuser',
+  password => 'mypass',
+  host     => 'localhost',
+  grant    => ['SELECT', 'UPDATE'],
+}
+~~~
+
+Or to use a different resource name with exported resources:
 
-###Custom configuration
+~~~
+ @@mysql::db { "mydb_${fqdn}":
+  user     => 'myuser',
+  password => 'mypass',
+  dbname   => 'mydb',
+  host     => ${fqdn},
+  grant    => ['SELECT', 'UPDATE'],
+  tag      => $domain,
+}
+~~~
+
+Then you can collect it on the remote DB server:
+
+~~~
+Mysql::Db <<| tag == $domain |>>
+~~~
+
+If you set the sql param to a file when creating a database, the file gets imported into the new database.
+
+For large sql files, you should raise the $import_timeout parameter, set by default to 300 seconds.
+
+~~~
+mysql::db { 'mydb':
+  user     => 'myuser',
+  password => 'mypass',
+  host     => 'localhost',
+  grant    => ['SELECT', 'UPDATE'],
+  sql      => '/path/to/sqlfile',
+  import_timeout => 900,
+}
+~~~
+
+###Custom Configuration
 
 To add custom MySQL configuration, drop additional files into
-`/etc/mysql/conf.d/`. Dropping files into conf.d allows you to override settings or add additional ones, which is helpful if you choose not to use `override_options` in `mysql::server`. The conf.d location is hardcoded into the my.cnf template file.
+`includedir`. Dropping files into `includedir` allows you to override settings or add additional ones, which is helpful if you choose not to use `override_options` in `mysql::server`. The `includedir` location is by default set to /etc/mysql/conf.d.
 
 ##Reference
 
@@ -123,103 +163,127 @@
 * `mysql::server::install`: Installs packages.
 * `mysql::server::config`: Configures MYSQL.
 * `mysql::server::service`: Manages service.
+* `mysql::server::account_security`: Deletes default MySQL accounts.
 * `mysql::server::root_password`: Sets MySQL root password.
 * `mysql::server::providers`: Creates users, grants, and databases.
+* `mysql::bindings::client_dev`: Installs MySQL client development package.
+* `mysql::bindings::daemon_dev`: Installs MySQL daemon development package.
 * `mysql::bindings::java`: Installs Java bindings.
 * `mysql::bindings::perl`: Installs Perl bindings.
+* `mysql::bindings::php`: Installs PHP bindings.
 * `mysql::bindings::python`: Installs Python bindings.
 * `mysql::bindings::ruby`: Installs Ruby bindings.
 * `mysql::client::install`:  Installs MySQL client.
+* `mysql::backup::mysqldump`: Implements mysqldump backups.
+* `mysql::backup::mysqlbackup`: Implements backups with Oracle MySQL Enterprise Backup.
+* `mysql::backup::xtrabackup`: Implements backups with XtraBackup from Percona.
 
 ###Parameters
 
 ####mysql::server
 
+#####`create_root_user`
+
+Specify whether root user should be created. Valid values are 'true', 'false'. Defaults to 'true'.
+
+This is useful for a cluster setup with Galera. The root user has to
+be created only once. `create_root_user` can be set to 'true' on one node while
+it is set to 'false' on the remaining nodes.
+
+#####`create_root_my_cnf`
+
+If set to 'true', creates `/root/.my.cnf`. Valid values are 'true', 'false'. Defaults to 'true'.
+
+`create_root_my_cnf` allows creation of `/root/.my.cnf` independently of `create_root_user`. This can be used for a cluster setup with Galera where you want `/root/.my.cnf` to exist on all nodes.
+
 #####`root_password`
 
-The MySQL root password.  Puppet will attempt to set the root password and update `/root/.my.cnf` with it.
+The MySQL root password. Puppet attempts to set the root password and update `/root/.my.cnf` with it.
+
+This is required if `create_root_user` or `create_root_my_cnf` are 'true'. If `root_password` is 'UNSET', then `create_root_user` and `create_root_my_cnf` are assumed to be false --- that is, the MySQL root user and `/root/.my.cnf` are not created.
 
 #####`old_root_password`
 
-The previous root password (**REQUIRED** if you wish to change the root password via Puppet.)
+The previous root password. Required if you want to change the root password via Puppet.
 
 #####`override_options`
 
-The hash of override options to pass into MySQL.  It can be structured
-like a hash in the my.cnf file, so entries look like
+The hash of override options to pass into MySQL. Structured like a hash in the my.cnf file:
 
-```puppet
+~~~
 $override_options = {
   'section' => {
     'item'             => 'thing',
   }
 }
-```
-
-For items that you would traditionally represent as:
+~~~
 
-<pre>
-[section]
-thing = X
-</pre>
-
-You can just make an entry like `thing => true`, `thing => value`, or `thing => "` in the hash. You can also pass an array `thing => ['value', 'value2']` or even list each `thing => value` separately on separate lines. MySQL doesn't care if 'thing' is alone or set to a value; it'll happily accept both.  To keep an option out of the my.cnf file, e.g. when using override_options to revert to a default value, you can pass thing => undef.
+See [**Customizing Server Options**](#customizing-server-options) above for usage details.
 
 #####`config_file`
 
-The location of the MySQL configuration file.
+The location, as a path, of the MySQL configuration file.
 
 #####`manage_config_file`
 
-Whether the MySQL configuration file should be managed.
+Whether the MySQL configuration file should be managed. Valid values are 'true', 'false'. Defaults to 'true'.
+
+#####`includedir`
+The location, as a path, of !includedir for custom configuration overrides.
+
+#####`install_options`
+Pass [install_options](https://docs.puppetlabs.com/references/latest/type.html#package-attribute-install_options) array to managed package resources. You must pass the appropriate options for the specified package manager.
 
 #####`purge_conf_dir`
 
-Whether the conf.d directory should be purged.
+Whether the `includedir` directory should be purged. Valid values are 'true', 'false'. Defaults to 'false'.
 
 #####`restart`
 
-Whether the service should be restarted when things change.
+Whether the service should be restarted when things change. Valid values are 'true', 'false'. Defaults to 'false'.
 
 #####`root_group`
 
-What is the group used for root?
+The name of the group used for root. Can be a group name or a group ID. See more about the [`group` file attribute](https://docs.puppetlabs.com/references/latest/type.html#file-attribute-group).
 
 #####`package_ensure`
 
-What to set the package to.  Can be 'present', 'absent', or 'x.y.z'.
+Whether the package exists or should be a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Defaults to 'present'.
+
+#####`package_manage`
+
+Whether to manage the mysql server package. Defaults to true.
 
 #####`package_name`
 
-The name of the mysql server package to install.
+The name of the MySQL server package to install.
 
 #####`remove_default_accounts`
 
-Boolean to decide if we should automatically include
-`mysql::server::account_security`.
+Specify whether to automatically include `mysql::server::account_security`. Valid values are 'true', 'false'. Defaults to 'false'.
 
 #####`service_enabled`
 
-Boolean to decide if the service should be enabled.
+Specify whether the service should be enabled. Valid values are 'true', 'false'. Defaults to 'true'.
 
 #####`service_manage`
 
-Boolean to decide if the service should be managed.
+Specify whether the service should be managed. Valid values are 'true', 'false'. Defaults to 'true'.
 
 #####`service_name`
 
-The name of the mysql server service.
+The name of the MySQL server service. Defaults are OS dependent, defined in params.pp.
 
 #####`service_provider`
 
-The provider to use to manage the service.
+The provider to use to manage the service. For Ubuntu, defaults to 'upstart'; otherwise, default is undefined.
 
 #####`users`
 
 Optional hash of users to create, which are passed to [mysql_user](#mysql_user). 
 
-```puppet
-$users = {
+~~~
+users => {
   'someuser@localhost' => {
     ensure                   => 'present',
     max_connections_per_hour => '0',
@@ -229,14 +293,14 @@
     password_hash            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF',
   },
 }
-```
+~~~
 
 #####`grants`
 
 Optional hash of grants, which are passed to [mysql_grant](#mysql_grant). 
 
-```puppet
-$grants = {
+~~~
+grants => {
   'someuser@localhost/somedb.*' => {
     ensure     => 'present',
     options    => ['GRANT'],
@@ -245,20 +309,20 @@
     user       => 'someuser@localhost',
   },
 }
-```
+~~~
 
 #####`databases`
 
 Optional hash of databases to create, which are passed to [mysql_database](#mysql_database).
 
-```puppet
-$databases = {
-  'somedb' => {
+~~~
+databases   => {
+  'somedb'  => {
     ensure  => 'present',
     charset => 'utf8',
   },
 }
-```
+~~~
 
 ####mysql::server::backup
 
@@ -272,7 +336,7 @@
 
 #####`backupdir`
 
-Directory to back up into.
+Directory in which to store backups.
 
 #####`backupdirmode`
 
@@ -291,39 +355,47 @@
 
 #####`backupcompress`
 
-Boolean to determine if backups should be compressed.
+Whether backups should be compressed. Valid values are 'true', 'false'. Defaults to 'true'.
 
 #####`backuprotate`
 
-How many days to keep backups for.
+How many days to keep backups. Valid value is an integer. Defaults to '30'.
 
 #####`delete_before_dump`
 
-Boolean to determine if you should cleanup before backing up or after.
+Whether to delete old .sql files before backing up. Setting to 'true' deletes old files before backing up, while setting to 'false' deletes them after backup. Valid values are 'true', 'false'. Defaults to 'false'.
 
 #####`backupdatabases`
 
-Array of databases to specifically back up.
+Specify an array of databases to back up.
 
 #####`file_per_database`
 
-Whether a separate file be used per database.
+Whether a separate file be used per database. Valid values are 'true', 'false'. Defaults to 'false'.
 
 #####`ensure`
 
-Allows you to remove the backup scripts. Can be 'present' or 'absent'.
+Allows you to remove the backup scripts. Valid values are 'present', 'absent'. Defaults to 'present'.
 
 #####`execpath`
 
-Allows you to set a custom PATH should your mysql installation be non-standard places. Defaults to `/usr/bin:/usr/sbin:/bin:/sbin`
+Allows you to set a custom PATH should your MySQL installation be non-standard places. Defaults to `/usr/bin:/usr/sbin:/bin:/sbin`.
 
 #####`time`
 
-An array of two elements to set the backup time.  Allows ['23', '5'] or ['3', '45'] for HH:MM times.
+An array of two elements to set the backup time. Allows ['23', '5'] (i.e., 23:05) or ['3', '45'] (i.e., 03:45) for HH:MM times.
 
 #####`postscript`
 
-A script that is executed at when the backup is finished. This could be used to (r)sync the backup to a central store. This script can be either a single line that is directly executed or a number of lines, when supplied as an array. It could also be one or more externally managed (executable) files.
+A script that is executed at when the backup is finished. This could be used to (r)sync the backup to a central store. This script can be either a single line that is directly executed or a number of lines supplied as an array. It could also be one or more externally managed (executable) files.
+
+#####`provider`
+
+Sets the server backup implementation. Valid values are:
+
+* `mysqldump`: Implements backups with mysqldump. Backup type: Logical. This is the default value.
+* `mysqlbackup`: Implements backups with MySQL Enterprise Backup from Oracle. Backup type: Physical. To use this type of backup, you'll need the `meb` package, which is available in RPM and TAR formats from Oracle. For Ubuntu, you can use [meb-deb](https://github.com/dveeden/meb-deb) to create a package from an official tarball.
+* `xtrabackup`: Implements backups with XtraBackup from Percona. Backup type: Physical.
 
 ####mysql::server::monitor
 
@@ -337,73 +409,133 @@
 
 #####`mysql_monitor_hostname`
 
-The hostname to allow to access the MySQL monitoring user.
+The hostname from which the monitoring user requests are allowed access. 
+
+####mysql::server::mysqltuner
+
+**Note**: If you're using this class on a non-network-connected system, you must download the mysqltuner.pl script and have it hosted somewhere accessible via `http(s)://`, `puppet://`, `ftp://`, or a fully qualified file path.
+
+##### `ensure`
+
+Ensures that the resource exists. Valid values are `present`, `absent`. Defaults to `present`.
+
+##### `version`
+
+The version to install from the major/MySQLTuner-perl github repository. Must be a valid tag. Defaults to 'v1.3.0'.
+
+##### `source`
+
+Parameter to optionally specify the source. If not specified, defaults to `https://github.com/major/MySQLTuner-perl/raw/${version}/mysqltuner.pl`
 
 ####mysql::bindings
 
-#####`java_enable`
+##### `client_dev`
+
+Specify whether `::mysql::bindings::client_dev` should be included. Valid values are true', 'false'. Defaults to 'false'.
+
+##### `daemon_dev`
 
-Boolean to decide if the Java bindings should be installed.
+Specify whether `::mysql::bindings::daemon_dev` should be included. Valid values are 'true', 'false'. Defaults to 'false'.
+
+##### `java_enable`
+
+Specify whether `::mysql::bindings::java` should be included. Valid values are 'true', 'false'. Defaults to 'false'.
+
+##### `perl_enable`
 
-#####`perl_enable`
+Specify whether `mysql::bindings::perl` should be included. Valid values are 'true', 'false'. Defaults to 'false'.
+
+##### `php_enable`
+
+Specify whether `mysql::bindings::php` should be included. Valid values are 'true', 'false'. Defaults to 'false'.
 
-Boolean to decide if the Perl bindings should be installed.
+##### `python_enable`
+
+Specify whether `mysql::bindings::python` should be included. Valid values are 'true', 'false'. Defaults to 'false'.
 
-#####`php_enable`
+##### `ruby_enable`
+
+Specify whether `mysql::bindings::ruby` should be included. Valid values are 'true', 'false'. Defaults to 'false'.
 
-Boolean to decide if the PHP bindings should be installed.
+##### `install_options`
+
+Pass `install_options` array to managed package resources. You must pass the [appropriate options](https://docs.puppetlabs.com/references/latest/type.html#package-attribute-install_options) for the package manager(s).
+
+##### `client_dev_package_ensure`
 
-#####`python_enable`
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `client_dev => true`.
+ 
+##### `client_dev_package_name`
+
+The name of the client_dev package to install. Only applies if `client_dev => true`.
+ 
+##### `client_dev_package_provider`
 
-Boolean to decide if the Python bindings should be installed.
+The provider to use to install the client_dev package. Only applies if `client_dev => true`.
+
+##### `daemon_dev_package_ensure`
+
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `daemon_dev => true`.
 
-#####`ruby_enable`
+##### `daemon_dev_package_name`
+
+The name of the daemon_dev package to install. Only applies if `daemon_dev => true`.
 
-Boolean to decide if the Ruby bindings should be installed.
+##### `daemon_dev_package_provider`
+
+The provider to use to install the daemon_dev package. Only applies if `daemon_dev => true`.
 
 #####`java_package_ensure`
 
-What to set the package to.  Can be 'present', 'absent', or 'x.y.z'.
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `java_enable => true`.
 
 #####`java_package_name`
 
-The name of the package to install.
+The name of the Java package to install. Only applies if `java_enable => true`.
 
 #####`java_package_provider`
 
-What provider should be used to install the package.
+The provider to use to install the Java package. Only applies if `java_enable => true`.
 
 #####`perl_package_ensure`
 
-What to set the package to.  Can be 'present', 'absent', or 'x.y.z'.
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `perl_enable => true`.
 
 #####`perl_package_name`
 
-The name of the package to install.
+The name of the Perl package to install. Only applies if `perl_enable => true`.
 
 #####`perl_package_provider`
 
-What provider should be used to install the package.
+The provider to use to install the Perl package. Only applies if `perl_enable => true`.
+
+##### `php_package_ensure`
+
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `php_enable => true`.
+ 
+##### `php_package_name`
+
+The name of the PHP package to install. Only applies if `php_enable => true`.
 
 #####`python_package_ensure`
 
-What to set the package to.  Can be 'present', 'absent', or 'x.y.z'.
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `python_enable => true`.
 
 #####`python_package_name`
 
-The name of the package to install.
+The name of the Python package to install. Only applies if `python_enable => true`.
 
 #####`python_package_provider`
 
-What provider should be used to install the package.
+The provider to use to install the PHP package. Only applies if `python_enable => true`.
 
 #####`ruby_package_ensure`
 
-What to set the package to.  Can be 'present', 'absent', or 'x.y.z'.
+Whether the package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'. Only applies if `ruby_enable => true`.
 
 #####`ruby_package_name`
 
-The name of the package to install.
+The name of the Ruby package to install. Only applies if `ruby_enable => true`.
 
 #####`ruby_package_provider`
 
@@ -413,57 +545,28 @@
 
 #####`bindings_enable`
 
-Boolean to automatically install all bindings.
+Whether to automatically install all bindings. Valid values are 'true', 'false'. Default to 'false'.
+
+#####`install_options`
+Array of install options for managed package resources. You must pass the appropriate options for the package manager.
 
 #####`package_ensure`
 
-What to set the package to.  Can be 'present', 'absent', or 'x.y.z'.
+Whether the MySQL package should be present, absent, or a specific version. Valid values are 'present', 'absent', or 'x.y.z'.
+
+#####`package_manage`
+
+Whether to manage the mysql client package. Defaults to true.
 
 #####`package_name`
 
-What is the name of the mysql client package to install.
+The name of the MySQL client package to install.
 
-###Defines
+###Defined Types
 
 ####mysql::db
 
-Creates a database with a user and assigns some privileges.
-
-```puppet
-    mysql::db { 'mydb':
-      user     => 'myuser',
-      password => 'mypass',
-      host     => 'localhost',
-      grant    => ['SELECT', 'UPDATE'],
-    }
-```
-
-Or using a different resource name with exported resources,
-
-```puppet
-    @@mysql::db { "mydb_${fqdn}":
-      user     => 'myuser',
-      password => 'mypass',
-      dbname   => 'mydb',
-      host     => ${fqdn},
-      grant    => ['SELECT', 'UPDATE'],
-      tag      => $domain,
-    }
-```
-
-Then collect it on the remote DB server.
-
-```puppet
-    Mysql::Db <<| tag == $domain |>>
-```
-
-###Providers
-
-####mysql_database
-
-`mysql_database` can be used to create and manage databases within MySQL.
-
-```puppet
+~~~
 mysql_database { 'information_schema':
   ensure  => 'present',
   charset => 'utf8',
@@ -474,13 +577,80 @@
   charset => 'latin1',
   collate => 'latin1_swedish_ci',
 }
-```
+~~~
+
+##### `user`
+
+The user for the database you're creating.
+ 
+##### `password`
+
+The password for $user for the database you're creating.
+
+##### `dbname`
+
+The name of the database to create. Defaults to $name.
+ 
+##### `charset`
+
+The character set for the database. Defaults to 'utf8'.
+
+##### `collate`
+
+The collation for the database. Defaults to 'utf8_general_ci'.
+ 
+##### `host`
+
+The host to use as part of user@host for grants. Defaults to 'localhost'.
+
+##### `grant`
+
+The privileges to be granted for user@host on the database. Defaults to 'ALL'.
+
+##### `sql`
+
+The path to the sqlfile you want to execute. This can be single file specified as string, or it can be an array of strings. Defaults to undef.
+
+##### `enforce_sql`
+
+Specify whether executing the sqlfiles should happen on every run. If set to 'false', sqlfiles only run once. Valid values are 'true', 'false'. Defaults to 'false'.
+ 
+##### `ensure`
+
+Specify whether to create the database. Valid values are 'present', 'absent'. Defaults to 'present'. 
+
+##### `import_timeout`
+
+Timeout, in seconds, for loading the sqlfiles. Defaults to '300'.
+
+
+###Types
+
+####mysql_database
+
+`mysql_database` creates and manages databases within MySQL.
+
+##### `ensure`
+
+Whether the resource is present. Valid values are 'present', 'absent'. Defaults to 'present'.
+
+##### `name`
+
+The name of the MySQL database to manage.
+
+##### `charset`
+
+The CHARACTER SET setting for the database. Defaults to ':utf8'.
+
+##### `collate`
+
+The COLLATE setting for the database. Defaults to ':utf8_general_ci'. 
 
 ####mysql_user
 
-`mysql_user` can be used to create and manage user grants within MySQL.
+Creates and manages user grants within MySQL.
 
-```puppet
+~~~
 mysql_user { 'root@127.0.0.1':
   ensure                   => 'present',
   max_connections_per_hour => '0',
@@ -488,15 +658,49 @@
   max_updates_per_hour     => '0',
   max_user_connections     => '0',
 }
-```
+~~~
+
+You can also specify an authentication plugin.
+
+~~~
+mysql_user{ 'myuser'@'localhost':
+  ensure                   => 'present',
+  plugin                   => 'unix_socket',
+}
+~~~
+
+##### `name`
+
+The name of the user, as 'username@hostname' or username@hostname.
+
+##### `password_hash`
+
+The user's password hash of the user. Use mysql_password() for creating such a hash.
+
+##### `max_user_connections`
+
+Maximum concurrent connections for the user. Must be an integer value. A value of '0' specifies no (or global) limit.
+
+##### `max_connections_per_hour`
+
+Maximum connections per hour for the user. Must be an integer value. A value of '0' specifies no (or global) limit.
+
+##### `max_queries_per_hour`
+
+Maximum queries per hour for the user. Must be an integer value. A value of '0' specifies no (or global) limit.
+
+##### `max_updates_per_hour`
+
+Maximum updates per hour for the user. Must be an integer value. A value of '0' specifies no (or global) limit.
+
 
 ####mysql_grant
 
-`mysql_grant` can be used to create grant permissions to access databases within
-MySQL.  To use it you must create the title of the resource as shown below,
+`mysql_grant` creates grant permissions to access databases within
+MySQL. To use it you must create the title of the resource as shown below,
 following the pattern of `username@hostname/database.table`:
 
-```puppet
+~~~
 mysql_grant { 'root@localhost/*.*':
   ensure     => 'present',
   options    => ['GRANT'],
@@ -504,18 +708,78 @@
   table      => '*.*',
   user       => 'root@localhost',
 }
-```
+~~~
+
+It is possible to specify privileges down to the column level:
+
+~~~
+mysql_grant { 'root@localhost/mysql.user':
+  ensure     => 'present',
+  privileges => ['SELECT (Host, User)'],
+  table      => 'mysql.user',
+  user       => 'root@localhost',
+}
+~~~
+
+##### `ensure`
+
+Whether the resource is present. Valid values are 'present', 'absent'. Defaults to 'present'.
+
+##### `name`
+
+Name to describe the grant. Must in a 'user/table' format. 
+
+##### `privileges`
+
+Privileges to grant the user.
+
+##### `table`
+
+The table to which privileges are applied.
+
+##### `user`
+
+User to whom privileges are granted.
+
+##### `options`
+
+MySQL options to grant. Optional.
+
+####mysql_plugin
+
+`mysql_plugin` can be used to load plugins into the MySQL Server.
+
+~~~
+mysql_plugin { 'auth_socket':
+  ensure     => 'present',
+  soname     => 'auth_socket.so',
+}
+~~~
+
+##### `ensure`
+
+Whether the resource is present. Valid values are 'present', 'absent'. Defaults to 'present'.
+
+##### `name`
+
+The name of the MySQL plugin to manage.
+
+##### `soname`
+
+The library file name.
 
 ##Limitations
 
 This module has been tested on:
 
-* RedHat Enterprise Linux 5/6
-* Debian 6/7
-* CentOS 5/6
-* Ubuntu 12.04
+* RedHat Enterprise Linux 5, 6, 7
+* Debian 6, 7
+* CentOS 5, 6, 7
+* Ubuntu 10.04, 12.04, 14.04
+* Scientific Linux 5, 6
+* SLES 11
 
-Testing on other platforms has been light and cannot be guaranteed.
+Testing on other platforms has been minimal and cannot be guaranteed.
 
 #Development
 
@@ -528,11 +792,11 @@
 modules work 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.
 
-You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing)
+Check out our the complete [module contribution guide](https://docs.puppetlabs.com/forge/contributing.html).
 
 ### Authors
 
-This module is based on work by David Schmitt. The following contributors have contributed patches to this module (beyond Puppet Labs):
+This module is based on work by David Schmitt. The following contributors have contributed to this module (beyond Puppet Labs):
 
 * Larry Ludwig
 * Christian G. Warden
@@ -543,4 +807,5 @@
 * William Van Hevelingen
 * Michael Arnold
 * Chris Weyl
+* Daniël van Eeden
 
--- a/modules/mysql/Rakefile	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/Rakefile	Mon Mar 09 01:34:59 2015 +0000
@@ -1,1 +1,10 @@
 require 'puppetlabs_spec_helper/rake_tasks'
+require 'puppet-lint/tasks/puppet-lint'
+
+PuppetLint.configuration.fail_on_warnings = true
+PuppetLint.configuration.send('relative')
+PuppetLint.configuration.send('disable_80chars')
+PuppetLint.configuration.send('disable_class_inherits_from_params_class')
+PuppetLint.configuration.send('disable_documentation')
+PuppetLint.configuration.send('disable_single_quote_string_with_variables')
+PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
--- a/modules/mysql/checksums.json	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/checksums.json	Mon Mar 09 01:34:59 2015 +0000
@@ -1,110 +1,105 @@
 {
-  "CHANGELOG.md": "8281eef9d77aa52808259900380bfe39",
-  "Gemfile": "8ce9d4a7aeab7592cac526e08c6416e4",
-  "Gemfile.lock": "f910e34764e760f216752bba4a7fe363",
+  "CHANGELOG.md": "fe4a47d4cfd7674c09acdfca74639f71",
+  "CONTRIBUTING.md": "e2b8e8e433fc76b3798b7fe435f49375",
+  "Gemfile": "bbb8d1178f386bc23c151e2779a73314",
   "LICENSE": "6089b6bd1f0d807edb8bdfd76da0b038",
-  "README.md": "9ce07a67636c662300a07f11e88904af",
-  "Rakefile": "0254db5d3fc38c67a2c160d7296a24f8",
+  "README.md": "ba602f8077366a6c04e11590fd0a5a21",
+  "Rakefile": "d953eb985f82600dc3b9ac6e1f2cfe64",
   "TODO": "88ca4024a37992b46c34cb46e4ac39e6",
-  "files/mysqltuner.pl": "65056d1386e04fdf22a1fee556c1b9fc",
+  "examples/backup.pp": "a61c6f34f153a323209faf25948737f5",
+  "examples/bindings.pp": "35a8387f5c55fa2e479c513a67918674",
+  "examples/java.pp": "0ad9de4f9f2c049642bcf08124757085",
+  "examples/mysql_database.pp": "107ee8793f7b4a12cfca32eddccc6bbd",
+  "examples/mysql_db.pp": "55d2d603f9fb8ab3c8a781d08119aa69",
+  "examples/mysql_grant.pp": "cd42336a6c7b2d27f5d5d6d0e310ee1a",
+  "examples/mysql_plugin.pp": "4f86c988a99b131e736501a309604e94",
+  "examples/mysql_user.pp": "0b76964aebb01714745548e0f2f32fd3",
+  "examples/perl.pp": "454f14dc4492fcf04afbe81b2776917e",
+  "examples/python.pp": "355a7e1ea3978a8fd290b5bc28b63808",
+  "examples/ruby.pp": "a6ae0381aacc5a8d2c403e606c6df0f0",
+  "examples/server/account_security.pp": "375442b7886c01b42fbf75a1fcb31822",
+  "examples/server/config.pp": "659b7c40e9b55634721b3c33a8c6da98",
+  "examples/server.pp": "72e22552a95b9a5e4a349dbfc13639dc",
   "lib/puppet/parser/functions/mysql_deepmerge.rb": "2b5040ee8cd75a81cf881851e922833a",
-  "lib/puppet/parser/functions/mysql_password.rb": "a4c8ec72dede069508dbc266131b06a3",
+  "lib/puppet/parser/functions/mysql_dirname.rb": "820ba09a6a0df639da560b41cb31242f",
+  "lib/puppet/parser/functions/mysql_password.rb": "100a3fa5ccb4f50c96d7f5167d43737f",
   "lib/puppet/parser/functions/mysql_strip_hash.rb": "3efe69f1eb189b2913e178b8472aaede",
-  "lib/puppet/provider/database/mysql.rb": "10c0c508f88145d0cda948f26f97d86f",
-  "lib/puppet/provider/database_grant/mysql.rb": "bfe3202efcecfe8f5fa98282594f7bed",
-  "lib/puppet/provider/database_user/mysql.rb": "26d55375b308ffa8cf9db68d0e039c5f",
   "lib/puppet/provider/mysql.rb": "881f4f76b084054f7299fe2de596e4c9",
-  "lib/puppet/provider/mysql_database/mysql.rb": "938b0602ea341e7062b011478e9fafb1",
-  "lib/puppet/provider/mysql_grant/mysql.rb": "100421e10f27f7adee4f53ec7778501c",
-  "lib/puppet/provider/mysql_user/mysql.rb": "7eda6cfab27de90e3ee8f6517d2ae3a0",
-  "lib/puppet/type/database.rb": "7b4b49b841d41541ce719d1a051ee94b",
-  "lib/puppet/type/database_grant.rb": "66fce5df0f3f4111fe37f094965f6f93",
-  "lib/puppet/type/database_user.rb": "b2a87e3854324fb0ae407a1fbad5802a",
-  "lib/puppet/type/mysql_database.rb": "e21a38611edc6cba5454889170bc0ebc",
-  "lib/puppet/type/mysql_grant.rb": "082aa9131d7e5871935da409ee16a9fa",
-  "lib/puppet/type/mysql_user.rb": "ddb054a5fd03689ae4325fbe003a41d3",
-  "manifests/backup.pp": "dfa324a48d47935a8423b102458c6516",
-  "manifests/bindings/java.pp": "6a581f1da1690d436ae14832af551ca2",
-  "manifests/bindings/perl.pp": "e765d0792afacbe72cf3e65804b78fe7",
-  "manifests/bindings/php.pp": "09017ca0adefbb8bf894393371cfad94",
-  "manifests/bindings/python.pp": "50c22f04074695f17ea383b307d01ea3",
-  "manifests/bindings/ruby.pp": "99f7c01e468136c8e699fcbb36d037fa",
-  "manifests/bindings.pp": "5976e9b74a29cc3a102f49867709a08f",
-  "manifests/client/install.pp": "381f70bfbaac921d631e3b115d8ae264",
-  "manifests/client.pp": "ab5a3ece8f5c4cc2174532472bdc5afe",
-  "manifests/db.pp": "b670eaf5e5ac1b8df6dce1a8a05f72e3",
-  "manifests/init.pp": "52ad9ac01674695edaf62cc1c48ef4f8",
-  "manifests/params.pp": "be62c4467c333032c20a9ec8adf5f03a",
-  "manifests/server/account_security.pp": "c793a434142ddaa6a529ed59739368fb",
-  "manifests/server/backup.pp": "6938ef939617b450477dc657bbf2c0f8",
-  "manifests/server/config.pp": "ba43df4df08889c8c8a052d9791b04eb",
-  "manifests/server/install.pp": "8666481a3ea12e9f76c47dfa558c09e6",
+  "lib/puppet/provider/mysql_database/mysql.rb": "5cf9b8044df4ec72a726b5f781c84f8f",
+  "lib/puppet/provider/mysql_grant/mysql.rb": "ab121816dd5a412e832418c8d8cf740c",
+  "lib/puppet/provider/mysql_plugin/mysql.rb": "49128d2a9a547c4945735bfd75fd6d6c",
+  "lib/puppet/provider/mysql_user/mysql.rb": "3af9f573524a516e2ecd6d0830a92c62",
+  "lib/puppet/type/mysql_database.rb": "cf604db80326697fcece33013f116c02",
+  "lib/puppet/type/mysql_grant.rb": "d84de534270024e090dd536c5947fd25",
+  "lib/puppet/type/mysql_plugin.rb": "0a52cead6c9283c9aa14ea71bdfa80bd",
+  "lib/puppet/type/mysql_user.rb": "7955d2cd382446966a6ee83e20806eaf",
+  "manifests/backup/mysqlbackup.pp": "a10731696220b9fc7686033497f11626",
+  "manifests/backup/mysqldump.pp": "2715fd6a480cd6aa7c5cbfc17f98f787",
+  "manifests/backup/xtrabackup.pp": "aabd9c6fb008ca368b6dec2a3f66471f",
+  "manifests/bindings/client_dev.pp": "80753f1bf48de71d2ca1a6cfda4830be",
+  "manifests/bindings/daemon_dev.pp": "94d2d74afc2b247593877cebd548ed76",
+  "manifests/bindings/java.pp": "556b743dc162d2f3264708b0b7ba0328",
+  "manifests/bindings/perl.pp": "4ecbd448ceac580a07df34b5d3e24837",
+  "manifests/bindings/php.pp": "641139f1f27085b8e72ac73fb8bc39d8",
+  "manifests/bindings/python.pp": "ef9e674237eddd7e98ab307131b11069",
+  "manifests/bindings/ruby.pp": "32b0a32161f7a5b50562cb8e154207d9",
+  "manifests/bindings.pp": "8becf04105d44910f12986a58cd566f7",
+  "manifests/client/install.pp": "5f35a8d53e60f2c20c08ccbdd8cd43ba",
+  "manifests/client.pp": "c350bd6d108acb03a87b860b14d225ef",
+  "manifests/db.pp": "41859b24225703cda1a7fdc75d710ce9",
+  "manifests/params.pp": "b0e42fe93fc84483782a9b9a65601e60",
+  "manifests/server/account_security.pp": "8b9029bfaca6d4d0579f28a7cd05e65d",
+  "manifests/server/backup.pp": "185e392bb1cbacd0e9852b18913521d0",
+  "manifests/server/config.pp": "a73a4586bbe67a6942c6f3dd89a40aef",
+  "manifests/server/install.pp": "acd23b7071feb66969e1b73e15781c25",
   "manifests/server/monitor.pp": "a63731018c171de9e441009d453dcac8",
-  "manifests/server/mysqltuner.pp": "4b19b075ecb7a7054cac237e5f50ed16",
+  "manifests/server/mysqltuner.pp": "311ce00407959117a81cc22930f18362",
   "manifests/server/providers.pp": "87a019dce5bbb6b18c9aa61b5f99134c",
-  "manifests/server/root_password.pp": "73738c1b6ee42b896db5356575c95af6",
-  "manifests/server/service.pp": "7ca5f4efbd1c89f60262c6b5ed45ab57",
-  "manifests/server.pp": "442990ade0834d54ffe30fb74c4fa9db",
-  "metadata.json": "56fe19891dbedf0e75ce4841f4746718",
-  "spec/acceptance/mysql_account_delete_spec.rb": "71a1f232dcc5a0c04e0f2c147bfb7901",
-  "spec/acceptance/mysql_backup_spec.rb": "8f700ec26a6d84e5f5fb925b03caaa06",
-  "spec/acceptance/mysql_bindings_spec.rb": "ff4172cfb4ef2eb7140773607ae8da19",
-  "spec/acceptance/mysql_db_spec.rb": "d075f2a33bacce626c41c72d8c00fa76",
-  "spec/acceptance/mysql_server_config_spec.rb": "9709a14165ff7fae246d2cebec7cc0f8",
-  "spec/acceptance/mysql_server_monitor_spec.rb": "2e38bbcb9b5a2248188b6de04a8ca140",
-  "spec/acceptance/mysql_server_root_password_spec.rb": "d689c9564aecf4d823f222daf01020fa",
-  "spec/acceptance/mysql_server_spec.rb": "2476345f9a03d84224820c8cb55c5220",
+  "manifests/server/root_password.pp": "180ca2fc27b03e575bf7dc32df9106c7",
+  "manifests/server/service.pp": "28810cee7da55a867b47d4e0d82a38f7",
+  "manifests/server.pp": "e0f27a8c3b0ac37023cda3421fc9fd2f",
+  "metadata.json": "cbd719454007b254ec727ce8e2eb7773",
+  "spec/acceptance/mysql_backup_spec.rb": "b31f2a6b400f3575ea1e89255682abe1",
+  "spec/acceptance/mysql_db_spec.rb": "a7d0d87f1d51d9914b00c849fc694083",
+  "spec/acceptance/mysql_server_spec.rb": "a09671ae9df1d70ea47762b495150003",
   "spec/acceptance/nodesets/centos-510-x64.yml": "5698f7e61292730c603e03f64fe19359",
   "spec/acceptance/nodesets/centos-59-x64.yml": "57eb3e471b9042a8ea40978c467f8151",
   "spec/acceptance/nodesets/centos-64-x64-pe.yml": "ec075d95760df3d4702abea1ce0a829b",
   "spec/acceptance/nodesets/centos-65-x64.yml": "3e5c36e6aa5a690229e720f4048bb8af",
-  "spec/acceptance/nodesets/default.yml": "092dd2c588a9f87fa1fb12997c0723ef",
+  "spec/acceptance/nodesets/default.yml": "76d6d770f3daf53ef1b9a17cd0163290",
   "spec/acceptance/nodesets/fedora-18-x64.yml": "80e41b1ee16ea489f53164bfdae58855",
   "spec/acceptance/nodesets/sles-11-x64.yml": "44e4c6c15c018333bfa9840a5e702f66",
   "spec/acceptance/nodesets/ubuntu-server-10044-x64.yml": "75e86400b7889888dc0781c0ae1a1297",
   "spec/acceptance/nodesets/ubuntu-server-12042-x64.yml": "d30d73e34cd50b043c7d14e305955269",
   "spec/acceptance/nodesets/ubuntu-server-1404-x64.yml": "5f0aed10098ac5b78e4217bb27c7aaf0",
-  "spec/acceptance/types/mysql_database_spec.rb": "20747263d874e5da4a496abad2fd7f77",
-  "spec/acceptance/types/mysql_grant_spec.rb": "f937987498c01590c666a4e12b49541f",
-  "spec/acceptance/types/mysql_user_spec.rb": "54e882b41f1664121b9f6f9a0f6b2be4",
-  "spec/acceptance/unsupported_spec.rb": "5e835e95d32823e6101966ed616c420b",
-  "spec/classes/mysql_bindings_spec.rb": "b1c61fa450b0416e791312525f0972f7",
-  "spec/classes/mysql_client_spec.rb": "1849bea122f7282153cbc46ca04aa851",
-  "spec/classes/mysql_server_account_security_spec.rb": "e223281077baa230fb6b7387f56af6d8",
-  "spec/classes/mysql_server_backup_spec.rb": "2b8ced7331a91b8bf11022f9d0d07c3f",
-  "spec/classes/mysql_server_monitor_spec.rb": "2bf20049616769424afd4a5137e25511",
-  "spec/classes/mysql_server_mysqltuner_spec.rb": "7a098808c21e3f08cd26237a96acc878",
-  "spec/classes/mysql_server_spec.rb": "f7a4bb4456636a37780847b564489283",
-  "spec/defines/mysql_db_spec.rb": "a449336350a6ed8eda5240e0adfe8702",
+  "spec/acceptance/types/mysql_database_spec.rb": "047db055103fa9782bc6714297eb2a09",
+  "spec/acceptance/types/mysql_grant_spec.rb": "f9217261e7bae6b63ea46aaa880ebfba",
+  "spec/acceptance/types/mysql_plugin_spec.rb": "e41e1df4997f57d70db4aaff8b0a1549",
+  "spec/acceptance/types/mysql_user_spec.rb": "7ae65d682141d2b6005adeafc4ce4698",
+  "spec/classes/graceful_failures_spec.rb": "47acbf0ef4b19ba26055b927b24d8a95",
+  "spec/classes/mycnf_template_spec.rb": "ea0fee62bb6f836a95eaa7382c0e6820",
+  "spec/classes/mysql_bindings_spec.rb": "fd66517c1c61afdf5a4a13c096fb7288",
+  "spec/classes/mysql_client_spec.rb": "7373be9edd90c54769a66e8af914b852",
+  "spec/classes/mysql_server_account_security_spec.rb": "22e2b1cb11bd284e02b35e86ff6704cf",
+  "spec/classes/mysql_server_backup_spec.rb": "82c7de7ad21e315bb2c551274d234371",
+  "spec/classes/mysql_server_monitor_spec.rb": "99e2b530a11e48b31b35f1a50099b8b5",
+  "spec/classes/mysql_server_mysqltuner_spec.rb": "4d5f354c4eae5a096166df1099b93f6b",
+  "spec/classes/mysql_server_spec.rb": "2dc3ee82499e9af45d9b079ceee6f676",
+  "spec/defines/mysql_db_spec.rb": "09e003a370d51c3bd22408744f3618f8",
   "spec/spec.opts": "a600ded995d948e393fbe2320ba8e51c",
-  "spec/spec_helper.rb": "92fefec2bd21423ec2aece165375678b",
-  "spec/spec_helper_acceptance.rb": "c8c15da8567f8e2a8f7361369e27bd6d",
-  "spec/unit/mysql_password_spec.rb": "7e1f9c635cb9dd4143054e096515006b",
-  "spec/unit/puppet/functions/mysql_deepmerge_spec.rb": "89741450d6098c7ed30448347c6de535",
-  "spec/unit/puppet/provider/database/mysql_spec.rb": "3bb92bdaaddfd54e7700012b2418f1ba",
-  "spec/unit/puppet/provider/database_grant/mysql_spec.rb": "261c22e57374b6651b87fcac86c9b563",
-  "spec/unit/puppet/provider/database_user/mysql_spec.rb": "50709cf2cf3f852a56de1856222b9b1f",
-  "spec/unit/puppet/provider/mysql_database/mysql_spec.rb": "ce4a0af08009f1244c97c1b4edf3fd95",
-  "spec/unit/puppet/provider/mysql_user/mysql_spec.rb": "d59edf286efa51990d0db1c0307e91ea",
-  "spec/unit/puppet/type/mysql_database_spec.rb": "0b32abc822e7613bdbb46f0a35c5b999",
-  "spec/unit/puppet/type/mysql_grant_spec.rb": "8e39370dba2a816af287f461d3b044ae",
-  "spec/unit/puppet/type/mysql_user_spec.rb": "1a20ac660f54f9976bb5a0c03c339efc",
-  "templates/my.cnf.erb": "10a4e40a3e3aacdb59748c2997a3e5df",
+  "spec/spec_helper.rb": "39901d056796699d22dad2d18622ca16",
+  "spec/spec_helper_acceptance.rb": "37523036b7df6b401e22634e5ba9607b",
+  "spec/unit/puppet/functions/mysql_deepmerge_spec.rb": "f6102e1f82fb9f4aa38cbf3955ee5973",
+  "spec/unit/puppet/functions/mysql_password_spec.rb": "388c74f0a3b46ecc008e84ec6eda70f4",
+  "spec/unit/puppet/provider/mysql_database/mysql_spec.rb": "98a799433ac10b237ac3ad27441610d4",
+  "spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb": "914bac73bb9841ec7d7041488a55d442",
+  "spec/unit/puppet/provider/mysql_user/mysql_spec.rb": "da588ef16e32e1944f9f6cc9d6fbc335",
+  "spec/unit/puppet/type/mysql_database_spec.rb": "9fa0d390c2e15c8a52fc2b981e2a63fc",
+  "spec/unit/puppet/type/mysql_grant_spec.rb": "1a003358dc88a74ef21fb33fc71debef",
+  "spec/unit/puppet/type/mysql_plugin_spec.rb": "7ebacf228dfd0c60811bdf8a28a329ff",
+  "spec/unit/puppet/type/mysql_user_spec.rb": "83839a0347ba282f796432d33e93d90f",
+  "templates/meb.cnf.erb": "b6422b19ee97b8a2883bfac44fdc0292",
+  "templates/my.cnf.erb": "701e3cd51d99fbd3ec60a68aaa1c8417",
   "templates/my.cnf.pass.erb": "30b24a3f29fcc644bd3a73929305cda0",
-  "templates/my.conf.cnf.erb": "5ebda0d5d774b2a51c25c43fbfed544a",
-  "templates/mysqlbackup.sh.erb": "2c43dc40e7aef3f2187ad7f4fbc4c4f2",
-  "tests/backup.pp": "caae4da564c1f663341bbe50915a5f7d",
-  "tests/bindings.pp": "dda8795d67098b66aa65e81ccc48ed73",
-  "tests/init.pp": "6b34827ac4731829c8a117f0b3fb8167",
-  "tests/java.pp": "0ad9de4f9f2c049642bcf08124757085",
-  "tests/mysql_database.pp": "2a85cd95a9952e3d93aa05f8f236551e",
-  "tests/mysql_db.pp": "e7ad5ffe51a7e6b508f00d6a9f371343",
-  "tests/mysql_grant.pp": "cd42336a6c7b2d27f5d5d6d0e310ee1a",
-  "tests/mysql_user.pp": "3113f355f834459954f38ca3b941bca7",
-  "tests/perl.pp": "6e496f19eaae83c90ce8b93236d44bca",
-  "tests/python.pp": "b093828acfed9c14e25ebdd60d90c282",
-  "tests/ruby.pp": "6c5071fcaf731995c9b8e31e00eaffa0",
-  "tests/server/account_security.pp": "47f79d7ae9eac2bf2134db27abf1db37",
-  "tests/server/config.pp": "619b4220138a12c6cb5f10af9867d8a1",
-  "tests/server.pp": "72e22552a95b9a5e4a349dbfc13639dc"
+  "templates/mysqlbackup.sh.erb": "fde3c2604729b60a1364ea337bdbc963"
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/backup.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,9 @@
+class { 'mysql::server':
+  root_password => 'password'
+}
+
+class { 'mysql::server::backup':
+  backupuser     => 'myuser',
+  backuppassword => 'mypassword',
+  backupdir      => '/tmp/backups',
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/bindings.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,3 @@
+class { 'mysql::bindings':
+  php_enable => true,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/java.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+class { 'mysql::java':}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/mysql_database.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,17 @@
+class { 'mysql::server':
+  root_password => 'password'
+}
+mysql::db{ ['test1', 'test2', 'test3']:
+  ensure  => present,
+  charset => 'utf8',
+  require => Class['mysql::server'],
+}
+mysql::db{ 'test4':
+  ensure  => present,
+  charset => 'latin1',
+}
+mysql::db{ 'test5':
+  ensure  => present,
+  charset => 'binary',
+  collate => 'binary',
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/mysql_db.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,17 @@
+class { 'mysql::server':
+  root_password => 'password'
+}
+mysql::db { 'mydb':
+  user     => 'myuser',
+  password => 'mypass',
+  host     => 'localhost',
+  grant    => ['SELECT', 'UPDATE'],
+}
+mysql::db { "mydb_${fqdn}":
+  user     => 'myuser',
+  password => 'mypass',
+  dbname   => 'mydb',
+  host     => $::fqdn,
+  grant    => ['SELECT', 'UPDATE'],
+  tag      => $domain,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/mysql_grant.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,5 @@
+mysql_grant{'test1@localhost/redmine.*':
+  user       => 'test1@localhost',
+  table      => 'redmine.*',
+  privileges => ['UPDATE'],
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/mysql_plugin.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,23 @@
+class { 'mysql::server':
+  root_password => 'password'
+}
+
+$validate_password_soname = $::osfamily ? {
+  windows => 'validate_password.dll',
+  default => 'validate_password.so'
+}
+
+mysql::plugin { 'validate_password':
+  ensure => present,
+  soname => $validate_password_soname,
+}
+
+$auth_socket_soname = $::osfamily ? {
+  windows => 'auth_socket.dll',
+  default => 'auth_socket.so'
+}
+
+mysql::plugin { 'auth_socket':
+  ensure => present,
+  soname => $auth_socket_soname,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/mysql_user.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,32 @@
+$mysql_root_pw = 'password'
+
+class { 'mysql::server':
+  root_password => 'password',
+}
+
+mysql_user{ 'redmine@localhost':
+  ensure        => present,
+  password_hash => mysql_password('redmine'),
+  require       => Class['mysql::server'],
+}
+
+mysql_user{ 'dan@localhost':
+  ensure        => present,
+  password_hash => mysql_password('blah')
+}
+
+mysql_user{ 'dan@%':
+  ensure        => present,
+  password_hash => mysql_password('blah'),
+}
+
+mysql_user{ 'socketplugin@%':
+  ensure => present,
+  plugin => 'unix_socket',
+}
+
+mysql_user{ 'socketplugin@%':
+  ensure        => present,
+  password_hash => mysql_password('blah'),
+  plugin        => 'mysql_native_password',
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/perl.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+include mysql::bindings::perl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/python.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+class { 'mysql::bindings::python':}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/ruby.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+include mysql::bindings::ruby
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/server.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,3 @@
+class { 'mysql::server':
+  root_password => 'password',
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/server/account_security.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,4 @@
+class { 'mysql::server':
+  root_password => 'password',
+}
+class { 'mysql::server::account_security': }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/examples/server/config.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,3 @@
+mysql::server::config { 'testfile':
+
+}
--- a/modules/mysql/files/mysqltuner.pl	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,966 +0,0 @@
-#!/usr/bin/perl -w
-# mysqltuner.pl - Version 1.2.0
-# High Performance MySQL Tuning Script
-# Copyright (C) 2006-2011 Major Hayden - major@mhtx.net
-#
-# For the latest updates, please visit http://mysqltuner.com/
-# Git repository available at http://github.com/rackerhacker/MySQLTuner-perl
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# This project would not be possible without help from:
-#   Matthew Montgomery     Paul Kehrer          Dave Burgess
-#   Jonathan Hinds         Mike Jackson         Nils Breunese
-#   Shawn Ashlee           Luuk Vosslamber      Ville Skytta
-#   Trent Hornibrook       Jason Gill           Mark Imbriaco
-#   Greg Eden              Aubin Galinotti      Giovanni Bechis
-#   Bill Bradford          Ryan Novosielski     Michael Scheidell
-#   Blair Christensen      Hans du Plooy        Victor Trac
-#   Everett Barnes         Tom Krouper          Gary Barrueto
-#   Simon Greenaway        Adam Stein           Isart Montane
-#   Baptiste M.
-#
-# Inspired by Matthew Montgomery's tuning-primer.sh script:
-# http://forge.mysql.com/projects/view.php?id=44
-#
-use strict;
-use warnings;
-use diagnostics;
-use File::Spec;
-use Getopt::Long;
-
-# Set up a few variables for use in the script
-my $tunerversion = "1.2.0";
-my (@adjvars, @generalrec);
-
-# Set defaults
-my %opt = (
-		"nobad" 		=> 0,
-		"nogood" 		=> 0,
-		"noinfo" 		=> 0,
-		"nocolor" 		=> 0,
-		"forcemem" 		=> 0,
-		"forceswap" 	=> 0,
-		"host" 			=> 0,
-		"socket" 		=> 0,
-		"port" 			=> 0,
-		"user" 			=> 0,
-		"pass"			=> 0,
-		"skipsize" 		=> 0,
-		"checkversion" 	=> 0,
-	);
-
-# Gather the options from the command line
-GetOptions(\%opt,
-		'nobad',
-		'nogood',
-		'noinfo',
-		'nocolor',
-		'forcemem=i',
-		'forceswap=i',
-		'host=s',
-		'socket=s',
-		'port=i',
-		'user=s',
-		'pass=s',
-		'skipsize',
-		'checkversion',
-		'help',
-	);
-
-if (defined $opt{'help'} && $opt{'help'} == 1) { usage(); }
-
-sub usage {
-	# Shown with --help option passed
-	print "\n".
-		"   MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n".
-		"   Bug reports, feature requests, and downloads at http://mysqltuner.com/\n".
-		"   Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n".
-		"\n".
-		"   Important Usage Guidelines:\n".
-		"      To run the script with the default options, run the script without arguments\n".
-		"      Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n".
-		"      Some routines may require root level privileges (script will provide warnings)\n".
-		"      You must provide the remote server's total memory when connecting to other servers\n".
-		"\n".
-		"   Connection and Authentication\n".
-		"      --host <hostname>    Connect to a remote host to perform tests (default: localhost)\n".
-		"      --socket <socket>    Use a different socket for a local connection\n".
-		"      --port <port>        Port to use for connection (default: 3306)\n".
-		"      --user <username>    Username to use for authentication\n".
-		"      --pass <password>    Password to use for authentication\n".
-		"\n".
-		"   Performance and Reporting Options\n".
-		"      --skipsize           Don't enumerate tables and their types/sizes (default: on)\n".
-		"                             (Recommended for servers with many tables)\n".
-		"      --checkversion       Check for updates to MySQLTuner (default: don't check)\n".
-		"      --forcemem <size>    Amount of RAM installed in megabytes\n".
-		"      --forceswap <size>   Amount of swap memory configured in megabytes\n".
-		"\n".
-		"   Output Options:\n".
-		"      --nogood             Remove OK responses\n".
-		"      --nobad              Remove negative/suggestion responses\n".
-		"      --noinfo             Remove informational responses\n".
-		"      --nocolor            Don't print output in color\n".
-		"\n";
-	exit;
-}
-
-my $devnull = File::Spec->devnull();
-
-# Setting up the colors for the print styles
-my $good = ($opt{nocolor} == 0)? "[\e[0;32mOK\e[0m]" : "[OK]" ;
-my $bad = ($opt{nocolor} == 0)? "[\e[0;31m!!\e[0m]" : "[!!]" ;
-my $info = ($opt{nocolor} == 0)? "[\e[0;34m--\e[0m]" : "[--]" ;
-
-# Functions that handle the print styles
-sub goodprint { print $good." ".$_[0] unless ($opt{nogood} == 1); }
-sub infoprint { print $info." ".$_[0] unless ($opt{noinfo} == 1); }
-sub badprint { print $bad." ".$_[0] unless ($opt{nobad} == 1); }
-sub redwrap { return ($opt{nocolor} == 0)? "\e[0;31m".$_[0]."\e[0m" : $_[0] ; }
-sub greenwrap { return ($opt{nocolor} == 0)? "\e[0;32m".$_[0]."\e[0m" : $_[0] ; }
-
-# Calculates the parameter passed in bytes, and then rounds it to one decimal place
-sub hr_bytes {
-	my $num = shift;
-	if ($num >= (1024**3)) { #GB
-		return sprintf("%.1f",($num/(1024**3)))."G";
-	} elsif ($num >= (1024**2)) { #MB
-		return sprintf("%.1f",($num/(1024**2)))."M";
-	} elsif ($num >= 1024) { #KB
-		return sprintf("%.1f",($num/1024))."K";
-	} else {
-		return $num."B";
-	}
-}
-
-# Calculates the parameter passed in bytes, and then rounds it to the nearest integer
-sub hr_bytes_rnd {
-	my $num = shift;
-	if ($num >= (1024**3)) { #GB
-		return int(($num/(1024**3)))."G";
-	} elsif ($num >= (1024**2)) { #MB
-		return int(($num/(1024**2)))."M";
-	} elsif ($num >= 1024) { #KB
-		return int(($num/1024))."K";
-	} else {
-		return $num."B";
-	}
-}
-
-# Calculates the parameter passed to the nearest power of 1000, then rounds it to the nearest integer
-sub hr_num {
-	my $num = shift;
-	if ($num >= (1000**3)) { # Billions
-		return int(($num/(1000**3)))."B";
-	} elsif ($num >= (1000**2)) { # Millions
-		return int(($num/(1000**2)))."M";
-	} elsif ($num >= 1000) { # Thousands
-		return int(($num/1000))."K";
-	} else {
-		return $num;
-	}
-}
-
-# Calculates uptime to display in a more attractive form
-sub pretty_uptime {
-	my $uptime = shift;
-	my $seconds = $uptime % 60;
-	my $minutes = int(($uptime % 3600) / 60);
-	my $hours = int(($uptime % 86400) / (3600));
-	my $days = int($uptime / (86400));
-	my $uptimestring;
-	if ($days > 0) {
-		$uptimestring = "${days}d ${hours}h ${minutes}m ${seconds}s";
-	} elsif ($hours > 0) {
-		$uptimestring = "${hours}h ${minutes}m ${seconds}s";
-	} elsif ($minutes > 0) {
-		$uptimestring = "${minutes}m ${seconds}s";
-	} else {
-		$uptimestring = "${seconds}s";
-	}
-	return $uptimestring;
-}
-
-# Retrieves the memory installed on this machine
-my ($physical_memory,$swap_memory,$duflags);
-sub os_setup {
-	sub memerror {
-		badprint "Unable to determine total memory/swap; use '--forcemem' and '--forceswap'\n";
-		exit;
-	}
-	my $os = `uname`;
-	$duflags = ($os =~ /Linux/) ? '-b' : '';
-	if ($opt{'forcemem'} > 0) {
-		$physical_memory = $opt{'forcemem'} * 1048576;
-		infoprint "Assuming $opt{'forcemem'} MB of physical memory\n";
-		if ($opt{'forceswap'} > 0) {
-			$swap_memory = $opt{'forceswap'} * 1048576;
-			infoprint "Assuming $opt{'forceswap'} MB of swap space\n";
-		} else {
-			$swap_memory = 0;
-			badprint "Assuming 0 MB of swap space (use --forceswap to specify)\n";
-		}
-	} else {
-		if ($os =~ /Linux/) {
-			$physical_memory = `free -b | grep Mem | awk '{print \$2}'` or memerror;
-			$swap_memory = `free -b | grep Swap | awk '{print \$2}'` or memerror;
-		} elsif ($os =~ /Darwin/) {
-			$physical_memory = `sysctl -n hw.memsize` or memerror;
-			$swap_memory = `sysctl -n vm.swapusage | awk '{print \$3}' | sed 's/\..*\$//'` or memerror;
-		} elsif ($os =~ /NetBSD|OpenBSD/) {
-			$physical_memory = `sysctl -n hw.physmem` or memerror;
-			if ($physical_memory < 0) {
-				$physical_memory = `sysctl -n hw.physmem64` or memerror;
-			}
-			$swap_memory = `swapctl -l | grep '^/' | awk '{ s+= \$2 } END { print s }'` or memerror;
-		} elsif ($os =~ /BSD/) {
-			$physical_memory = `sysctl -n hw.realmem`;
-			$swap_memory = `swapinfo | grep '^/' | awk '{ s+= \$2 } END { print s }'`;
-		} elsif ($os =~ /SunOS/) {
-			$physical_memory = `/usr/sbin/prtconf | grep Memory | cut -f 3 -d ' '` or memerror;
-			chomp($physical_memory);
-			$physical_memory = $physical_memory*1024*1024;
-		} elsif ($os =~ /AIX/) {
-			$physical_memory = `lsattr -El sys0 | grep realmem | awk '{print \$2}'` or memerror;
-			chomp($physical_memory);
-			$physical_memory = $physical_memory*1024;
-			$swap_memory = `lsps -as | awk -F"(MB| +)" '/MB /{print \$2}'` or memerror;
-			chomp($swap_memory);
-			$swap_memory = $swap_memory*1024*1024;
-		}
-	}
-	chomp($physical_memory);
-}
-
-# Checks to see if a MySQL login is possible
-my ($mysqllogin,$doremote,$remotestring);
-sub mysql_setup {
-	$doremote = 0;
-	$remotestring = '';
-	my $command = `which mysqladmin`;
-	chomp($command);
-	if (! -e $command) {
-		badprint "Unable to find mysqladmin in your \$PATH.  Is MySQL installed?\n";
-		exit;
-	}
-	# Are we being asked to connect via a socket?
-	if ($opt{socket} ne 0) {
-		$remotestring = " -S $opt{socket}";
-	}
-	# Are we being asked to connect to a remote server?
-	if ($opt{host} ne 0) {
-		chomp($opt{host});
-		$opt{port} = ($opt{port} eq 0)? 3306 : $opt{port} ;
-		# If we're doing a remote connection, but forcemem wasn't specified, we need to exit
-		if ($opt{'forcemem'} eq 0) {
-			badprint "The --forcemem option is required for remote connections\n";
-			exit;
-		}
-		infoprint "Performing tests on $opt{host}:$opt{port}\n";
-		$remotestring = " -h $opt{host} -P $opt{port}";
-		$doremote = 1;
-	}
-	# Did we already get a username and password passed on the command line?
-	if ($opt{user} ne 0 and $opt{pass} ne 0) {
-		$mysqllogin = "-u $opt{user} -p'$opt{pass}'".$remotestring;
-		my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`;
-		if ($loginstatus =~ /mysqld is alive/) {
-			goodprint "Logged in using credentials passed on the command line\n";
-			return 1;
-		} else {
-			badprint "Attempted to use login credentials, but they were invalid\n";
-			exit 0;
-		}
-	}
-	if ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) {
-		# It's a Plesk box, use the available credentials
-		$mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`";
-		my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`;
-		unless ($loginstatus =~ /mysqld is alive/) {
-			badprint "Attempted to use login credentials from Plesk, but they failed.\n";
-			exit 0;
-		}
-	} elsif ( -r "/etc/mysql/debian.cnf" and $doremote == 0 ){
-		# We have a debian maintenance account, use it
-		$mysqllogin = "--defaults-extra-file=/etc/mysql/debian.cnf";
-		my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`;
-		if ($loginstatus =~ /mysqld is alive/) {
-			goodprint "Logged in using credentials from debian maintenance account.\n";
-			return 1;
-		} else {
-			badprint "Attempted to use login credentials from debian maintenance account, but they failed.\n";
-			exit 0;
-		}
-	} else {
-		# It's not Plesk or debian, we should try a login
-		my $loginstatus = `mysqladmin $remotestring ping 2>&1`;
-		if ($loginstatus =~ /mysqld is alive/) {
-			# Login went just fine
-			$mysqllogin = " $remotestring ";
-			# Did this go well because of a .my.cnf file or is there no password set?
-			my $userpath = `printenv HOME`;
-			if (length($userpath) > 0) {
-				chomp($userpath);
-			}
-			unless ( -e "${userpath}/.my.cnf" ) {
-				badprint "Successfully authenticated with no password - SECURITY RISK!\n";
-			}
-			return 1;
-		} else {
-			print STDERR "Please enter your MySQL administrative login: ";
-			my $name = <>;
-			print STDERR "Please enter your MySQL administrative password: ";
-			system("stty -echo >$devnull 2>&1");
-			my $password = <>;
-			system("stty echo >$devnull 2>&1");
-			chomp($password);
-			chomp($name);
-			$mysqllogin = "-u $name";
-			if (length($password) > 0) {
-				$mysqllogin .= " -p'$password'";
-			}
-			$mysqllogin .= $remotestring;
-			my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`;
-			if ($loginstatus =~ /mysqld is alive/) {
-				print STDERR "\n";
-				if (! length($password)) {
-					# Did this go well because of a .my.cnf file or is there no password set?
-					my $userpath = `ls -d ~`;
-					chomp($userpath);
-					unless ( -e "$userpath/.my.cnf" ) {
-						badprint "Successfully authenticated with no password - SECURITY RISK!\n";
-					}
-				}
-				return 1;
-			} else {
-				print "\n".$bad." Attempted to use login credentials, but they were invalid.\n";
-				exit 0;
-			}
-			exit 0;
-		}
-	}
-}
-
-# Populates all of the variable and status hashes
-my (%mystat,%myvar,$dummyselect);
-sub get_all_vars {
-	# We need to initiate at least one query so that our data is useable
-	$dummyselect = `mysql $mysqllogin -Bse "SELECT VERSION();"`;
-	my @mysqlvarlist = `mysql $mysqllogin -Bse "SHOW /*!50000 GLOBAL */ VARIABLES;"`;
-	foreach my $line (@mysqlvarlist) {
-		$line =~ /([a-zA-Z_]*)\s*(.*)/;
-		$myvar{$1} = $2;
-	}
-	my @mysqlstatlist = `mysql $mysqllogin -Bse "SHOW /*!50000 GLOBAL */ STATUS;"`;
-	foreach my $line (@mysqlstatlist) {
-		$line =~ /([a-zA-Z_]*)\s*(.*)/;
-		$mystat{$1} = $2;
-	}
-	# Workaround for MySQL bug #59393 wrt. ignore-builtin-innodb
-	if (($myvar{'ignore_builtin_innodb'} || "") eq "ON") {
-		$myvar{'have_innodb'} = "NO";
-	}
-	# have_* for engines is deprecated and will be removed in MySQL 5.6;
-	# check SHOW ENGINES and set corresponding old style variables.
-	# Also works around MySQL bug #59393 wrt. skip-innodb
-	my @mysqlenginelist = `mysql $mysqllogin -Bse "SHOW ENGINES;" 2>$devnull`;
-	foreach my $line (@mysqlenginelist) {
-		if ($line =~ /^([a-zA-Z_]+)\s+(\S+)/) {
-			my $engine = lc($1);
-			if ($engine eq "federated" || $engine eq "blackhole") {
-				$engine .= "_engine";
-			} elsif ($engine eq "berkeleydb") {
-				$engine = "bdb";
-			}
-			my $val = ($2 eq "DEFAULT") ? "YES" : $2;
-			$myvar{"have_$engine"} = $val;
-		}
-	}
-}
-
-sub security_recommendations {
-	print "\n-------- Security Recommendations  -------------------------------------------\n";
-	my @mysqlstatlist = `mysql $mysqllogin -Bse "SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE password = '' OR password IS NULL;"`;
-	if (@mysqlstatlist) {
-		foreach my $line (sort @mysqlstatlist) {
-			chomp($line);
-			badprint "User '".$line."' has no password set.\n";
-		}
-	} else {
-		goodprint "All database users have passwords assigned\n";
-	}
-}
-
-sub get_replication_status {
-	my $slave_status = `mysql $mysqllogin -Bse "show slave status\\G"`;
-	my ($io_running) = ($slave_status =~ /slave_io_running\S*\s+(\S+)/i);
-	my ($sql_running) = ($slave_status =~ /slave_sql_running\S*\s+(\S+)/i);
-	if ($io_running eq 'Yes' && $sql_running eq 'Yes') {
-		if ($myvar{'read_only'} eq 'OFF') {
-			badprint "This replication slave is running with the read_only option disabled.";
-		} else {
-			goodprint "This replication slave is running with the read_only option enabled.";
-		}
-	}
-}
-
-# Checks for updates to MySQLTuner
-sub validate_tuner_version {
-	print "\n-------- General Statistics --------------------------------------------------\n";
-	if ($opt{checkversion} eq 0) {
-		infoprint "Skipped version check for MySQLTuner script\n";
-		return;
-	}
-	my $update;
-	my $url = "http://mysqltuner.com/versioncheck.php?v=$tunerversion";
-	if (-e "/usr/bin/curl") {
-		$update = `/usr/bin/curl --connect-timeout 5 '$url' 2>$devnull`;
-		chomp($update);
-	} elsif (-e "/usr/bin/wget") {
-		$update = `/usr/bin/wget -e timestamping=off -T 5 -O - '$url' 2>$devnull`;
-		chomp($update);
-	}
-	if ($update eq 1) {
-		badprint "There is a new version of MySQLTuner available\n";
-	} elsif ($update eq 0) {
-		goodprint "You have the latest version of MySQLTuner\n";
-	} else {
-		infoprint "Unable to check for the latest MySQLTuner version\n";
-	}
-}
-
-# Checks for supported or EOL'ed MySQL versions
-my ($mysqlvermajor,$mysqlverminor);
-sub validate_mysql_version {
-	($mysqlvermajor,$mysqlverminor) = $myvar{'version'} =~ /(\d)\.(\d)/;
-	if (!mysql_version_ge(5)) {
-		badprint "Your MySQL version ".$myvar{'version'}." is EOL software!  Upgrade soon!\n";
-	} elsif (mysql_version_ge(6)) {
-		badprint "Currently running unsupported MySQL version ".$myvar{'version'}."\n";
-	} else {
-		goodprint "Currently running supported MySQL version ".$myvar{'version'}."\n";
-	}
-}
-
-# Checks if MySQL version is greater than equal to (major, minor)
-sub mysql_version_ge {
-	my ($maj, $min) = @_;
-	return $mysqlvermajor > $maj || ($mysqlvermajor == $maj && $mysqlverminor >= ($min || 0));
-}
-
-# Checks for 32-bit boxes with more than 2GB of RAM
-my ($arch);
-sub check_architecture {
-	if ($doremote eq 1) { return; }
-	if (`uname` =~ /SunOS/ && `isainfo -b` =~ /64/) {
-		$arch = 64;
-		goodprint "Operating on 64-bit architecture\n";
-	} elsif (`uname` !~ /SunOS/ && `uname -m` =~ /64/) {
-		$arch = 64;
-		goodprint "Operating on 64-bit architecture\n";
-	} elsif (`uname` =~ /AIX/ && `bootinfo -K` =~ /64/) {
-		$arch = 64;
-		goodprint "Operating on 64-bit architecture\n";
-	} else {
-		$arch = 32;
-		if ($physical_memory > 2147483648) {
-			badprint "Switch to 64-bit OS - MySQL cannot currently use all of your RAM\n";
-		} else {
-			goodprint "Operating on 32-bit architecture with less than 2GB RAM\n";
-		}
-	}
-}
-
-# Start up a ton of storage engine counts/statistics
-my (%enginestats,%enginecount,$fragtables);
-sub check_storage_engines {
-	if ($opt{skipsize} eq 1) {
-		print "\n-------- Storage Engine Statistics -------------------------------------------\n";
-		infoprint "Skipped due to --skipsize option\n";
-		return;
-	}
-	print "\n-------- Storage Engine Statistics -------------------------------------------\n";
-	infoprint "Status: ";
-	my $engines;
-	$engines .= (defined $myvar{'have_archive'} && $myvar{'have_archive'} eq "YES")? greenwrap "+Archive " : redwrap "-Archive " ;
-	$engines .= (defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES")? greenwrap "+BDB " : redwrap "-BDB " ;
-	$engines .= (defined $myvar{'have_federated_engine'} && $myvar{'have_federated_engine'} eq "YES")? greenwrap "+Federated " : redwrap "-Federated " ;
-	$engines .= (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES")? greenwrap "+InnoDB " : redwrap "-InnoDB " ;
-	$engines .= (defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES")? greenwrap "+ISAM " : redwrap "-ISAM " ;
-	$engines .= (defined $myvar{'have_ndbcluster'} && $myvar{'have_ndbcluster'} eq "YES")? greenwrap "+NDBCluster " : redwrap "-NDBCluster " ;
-	print "$engines\n";
-	if (mysql_version_ge(5)) {
-		# MySQL 5 servers can have table sizes calculated quickly from information schema
-		my @templist = `mysql $mysqllogin -Bse "SELECT ENGINE,SUM(DATA_LENGTH),COUNT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"`;
-		foreach my $line (@templist) {
-			my ($engine,$size,$count);
-			($engine,$size,$count) = $line =~ /([a-zA-Z_]*)\s+(\d+)\s+(\d+)/;
-			if (!defined($size)) { next; }
-			$enginestats{$engine} = $size;
-			$enginecount{$engine} = $count;
-		}
-		$fragtables = `mysql $mysqllogin -Bse "SELECT COUNT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','mysql') AND Data_free > 0 AND NOT ENGINE='MEMORY';"`;
-		chomp($fragtables);
-	} else {
-		# MySQL < 5 servers take a lot of work to get table sizes
-		my @tblist;
-		# Now we build a database list, and loop through it to get storage engine stats for tables
-		my @dblist = `mysql $mysqllogin -Bse "SHOW DATABASES"`;
-		foreach my $db (@dblist) {
-			chomp($db);
-			if ($db eq "information_schema") { next; }
-			my @ixs = (1, 6, 9);
-			if (!mysql_version_ge(4, 1)) {
-				# MySQL 3.23/4.0 keeps Data_Length in the 5th (0-based) column
-				@ixs = (1, 5, 8);
-			}
-			push(@tblist, map { [ (split)[@ixs] ] } `mysql $mysqllogin -Bse "SHOW TABLE STATUS FROM \\\`$db\\\`"`);
-		}
-		# Parse through the table list to generate storage engine counts/statistics
-		$fragtables = 0;
-		foreach my $tbl (@tblist) {
-			my ($engine, $size, $datafree) = @$tbl;
-			if (defined $enginestats{$engine}) {
-				$enginestats{$engine} += $size;
-				$enginecount{$engine} += 1;
-			} else {
-				$enginestats{$engine} = $size;
-				$enginecount{$engine} = 1;
-			}
-			if ($datafree > 0) {
-				$fragtables++;
-			}
-		}
-	}
-	while (my ($engine,$size) = each(%enginestats)) {
-		infoprint "Data in $engine tables: ".hr_bytes_rnd($size)." (Tables: ".$enginecount{$engine}.")"."\n";
-	}
-	# If the storage engine isn't being used, recommend it to be disabled
-	if (!defined $enginestats{'InnoDB'} && defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES") {
-		badprint "InnoDB is enabled but isn't being used\n";
-		push(@generalrec,"Add skip-innodb to MySQL configuration to disable InnoDB");
-	}
-	if (!defined $enginestats{'BerkeleyDB'} && defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES") {
-		badprint "BDB is enabled but isn't being used\n";
-		push(@generalrec,"Add skip-bdb to MySQL configuration to disable BDB");
-	}
-	if (!defined $enginestats{'ISAM'} && defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES") {
-		badprint "ISAM is enabled but isn't being used\n";
-		push(@generalrec,"Add skip-isam to MySQL configuration to disable ISAM (MySQL > 4.1.0)");
-	}
-	# Fragmented tables
-	if ($fragtables > 0) {
-		badprint "Total fragmented tables: $fragtables\n";
-		push(@generalrec,"Run OPTIMIZE TABLE to defragment tables for better performance");
-	} else {
-		goodprint "Total fragmented tables: $fragtables\n";
-	}
-}
-
-my %mycalc;
-sub calculations {
-	if ($mystat{'Questions'} < 1) {
-		badprint "Your server has not answered any queries - cannot continue...";
-		exit 0;
-	}
-	# Per-thread memory
-	if (mysql_version_ge(4)) {
-		$mycalc{'per_thread_buffers'} = $myvar{'read_buffer_size'} + $myvar{'read_rnd_buffer_size'} + $myvar{'sort_buffer_size'} + $myvar{'thread_stack'} + $myvar{'join_buffer_size'};
-	} else {
-		$mycalc{'per_thread_buffers'} = $myvar{'record_buffer'} + $myvar{'record_rnd_buffer'} + $myvar{'sort_buffer'} + $myvar{'thread_stack'} + $myvar{'join_buffer_size'};
-	}
-	$mycalc{'total_per_thread_buffers'} = $mycalc{'per_thread_buffers'} * $myvar{'max_connections'};
-	$mycalc{'max_total_per_thread_buffers'} = $mycalc{'per_thread_buffers'} * $mystat{'Max_used_connections'};
-
-	# Server-wide memory
-	$mycalc{'max_tmp_table_size'} = ($myvar{'tmp_table_size'} > $myvar{'max_heap_table_size'}) ? $myvar{'max_heap_table_size'} : $myvar{'tmp_table_size'} ;
-	$mycalc{'server_buffers'} = $myvar{'key_buffer_size'} + $mycalc{'max_tmp_table_size'};
-	$mycalc{'server_buffers'} += (defined $myvar{'innodb_buffer_pool_size'}) ? $myvar{'innodb_buffer_pool_size'} : 0 ;
-	$mycalc{'server_buffers'} += (defined $myvar{'innodb_additional_mem_pool_size'}) ? $myvar{'innodb_additional_mem_pool_size'} : 0 ;
-	$mycalc{'server_buffers'} += (defined $myvar{'innodb_log_buffer_size'}) ? $myvar{'innodb_log_buffer_size'} : 0 ;
-	$mycalc{'server_buffers'} += (defined $myvar{'query_cache_size'}) ? $myvar{'query_cache_size'} : 0 ;
-
-	# Global memory
-	$mycalc{'max_used_memory'} = $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"};
-	$mycalc{'total_possible_used_memory'} = $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'};
-	$mycalc{'pct_physical_memory'} = int(($mycalc{'total_possible_used_memory'} * 100) / $physical_memory);
-
-	# Slow queries
-	$mycalc{'pct_slow_queries'} = int(($mystat{'Slow_queries'}/$mystat{'Questions'}) * 100);
-
-	# Connections
-	$mycalc{'pct_connections_used'} = int(($mystat{'Max_used_connections'}/$myvar{'max_connections'}) * 100);
-	$mycalc{'pct_connections_used'} = ($mycalc{'pct_connections_used'} > 100) ? 100 : $mycalc{'pct_connections_used'} ;
-
-	# Key buffers
-	if (mysql_version_ge(4, 1)) {
-		$mycalc{'pct_key_buffer_used'} = sprintf("%.1f",(1 - (($mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'}) / $myvar{'key_buffer_size'})) * 100);
-	}
-	if ($mystat{'Key_read_requests'} > 0) {
-		$mycalc{'pct_keys_from_mem'} = sprintf("%.1f",(100 - (($mystat{'Key_reads'} / $mystat{'Key_read_requests'}) * 100)));
-	} else {
-	    $mycalc{'pct_keys_from_mem'} = 0;
-	}
-	if ($doremote eq 0 and !mysql_version_ge(5)) {
-		my $size = 0;
-		$size += (split)[0] for `find $myvar{'datadir'} -name "*.MYI" 2>&1 | xargs du -L $duflags 2>&1`;
-		$mycalc{'total_myisam_indexes'} = $size;
-	} elsif (mysql_version_ge(5)) {
-		$mycalc{'total_myisam_indexes'} = `mysql $mysqllogin -Bse "SELECT IFNULL(SUM(INDEX_LENGTH),0) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema') AND ENGINE = 'MyISAM';"`;
-	}
-	if (defined $mycalc{'total_myisam_indexes'} and $mycalc{'total_myisam_indexes'} == 0) {
-		$mycalc{'total_myisam_indexes'} = "fail";
-	} elsif (defined $mycalc{'total_myisam_indexes'}) {
-		chomp($mycalc{'total_myisam_indexes'});
-	}
-
-	# Query cache
-	if (mysql_version_ge(4)) {
-		$mycalc{'query_cache_efficiency'} = sprintf("%.1f",($mystat{'Qcache_hits'} / ($mystat{'Com_select'} + $mystat{'Qcache_hits'})) * 100);
-		if ($myvar{'query_cache_size'}) {
-			$mycalc{'pct_query_cache_used'} = sprintf("%.1f",100 - ($mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'}) * 100);
-		}
-	if ($mystat{'Qcache_lowmem_prunes'} == 0) {
-			$mycalc{'query_cache_prunes_per_day'} = 0;
-		} else {
-			$mycalc{'query_cache_prunes_per_day'} = int($mystat{'Qcache_lowmem_prunes'} / ($mystat{'Uptime'}/86400));
-		}
-	}
-
-	# Sorting
-	$mycalc{'total_sorts'} = $mystat{'Sort_scan'} + $mystat{'Sort_range'};
-	if ($mycalc{'total_sorts'} > 0) {
-		$mycalc{'pct_temp_sort_table'} = int(($mystat{'Sort_merge_passes'} / $mycalc{'total_sorts'}) * 100);
-	}
-
-	# Joins
-	$mycalc{'joins_without_indexes'} = $mystat{'Select_range_check'} + $mystat{'Select_full_join'};
-	$mycalc{'joins_without_indexes_per_day'} = int($mycalc{'joins_without_indexes'} / ($mystat{'Uptime'}/86400));
-
-	# Temporary tables
-	if ($mystat{'Created_tmp_tables'} > 0) {
-		if ($mystat{'Created_tmp_disk_tables'} > 0) {
-			$mycalc{'pct_temp_disk'} = int(($mystat{'Created_tmp_disk_tables'} / ($mystat{'Created_tmp_tables'} + $mystat{'Created_tmp_disk_tables'})) * 100);
-		} else {
-			$mycalc{'pct_temp_disk'} = 0;
-		}
-	}
-
-	# Table cache
-	if ($mystat{'Opened_tables'} > 0) {
-		$mycalc{'table_cache_hit_rate'} = int($mystat{'Open_tables'}*100/$mystat{'Opened_tables'});
-	} else {
-		$mycalc{'table_cache_hit_rate'} = 100;
-	}
-
-	# Open files
-	if ($myvar{'open_files_limit'} > 0) {
-		$mycalc{'pct_files_open'} = int($mystat{'Open_files'}*100/$myvar{'open_files_limit'});
-	}
-
-	# Table locks
-	if ($mystat{'Table_locks_immediate'} > 0) {
-		if ($mystat{'Table_locks_waited'} == 0) {
-			$mycalc{'pct_table_locks_immediate'} = 100;
-		} else {
-			$mycalc{'pct_table_locks_immediate'} = int($mystat{'Table_locks_immediate'}*100/($mystat{'Table_locks_waited'} + $mystat{'Table_locks_immediate'}));
-		}
-	}
-
-	# Thread cache
-	$mycalc{'thread_cache_hit_rate'} = int(100 - (($mystat{'Threads_created'} / $mystat{'Connections'}) * 100));
-
-	# Other
-	if ($mystat{'Connections'} > 0) {
-		$mycalc{'pct_aborted_connections'} = int(($mystat{'Aborted_connects'}/$mystat{'Connections'}) * 100);
-	}
-	if ($mystat{'Questions'} > 0) {
-		$mycalc{'total_reads'} = $mystat{'Com_select'};
-		$mycalc{'total_writes'} = $mystat{'Com_delete'} + $mystat{'Com_insert'} + $mystat{'Com_update'} + $mystat{'Com_replace'};
-		if ($mycalc{'total_reads'} == 0) {
-			$mycalc{'pct_reads'} = 0;
-			$mycalc{'pct_writes'} = 100;
-		} else {
-			$mycalc{'pct_reads'} = int(($mycalc{'total_reads'}/($mycalc{'total_reads'}+$mycalc{'total_writes'})) * 100);
-			$mycalc{'pct_writes'} = 100-$mycalc{'pct_reads'};
-		}
-	}
-
-	# InnoDB
-	if ($myvar{'have_innodb'} eq "YES") {
-		$mycalc{'innodb_log_size_pct'} = ($myvar{'innodb_log_file_size'} * 100 / $myvar{'innodb_buffer_pool_size'});
-	}
-}
-
-sub mysql_stats {
-	print "\n-------- Performance Metrics -------------------------------------------------\n";
-	# Show uptime, queries per second, connections, traffic stats
-	my $qps;
-	if ($mystat{'Uptime'} > 0) { $qps = sprintf("%.3f",$mystat{'Questions'}/$mystat{'Uptime'}); }
-	if ($mystat{'Uptime'} < 86400) { push(@generalrec,"MySQL started within last 24 hours - recommendations may be inaccurate"); }
-	infoprint "Up for: ".pretty_uptime($mystat{'Uptime'})." (".hr_num($mystat{'Questions'}).
-		" q [".hr_num($qps)." qps], ".hr_num($mystat{'Connections'})." conn,".
-		" TX: ".hr_num($mystat{'Bytes_sent'}).", RX: ".hr_num($mystat{'Bytes_received'}).")\n";
-	infoprint "Reads / Writes: ".$mycalc{'pct_reads'}."% / ".$mycalc{'pct_writes'}."%\n";
-
-	# Memory usage
-	infoprint "Total buffers: ".hr_bytes($mycalc{'server_buffers'})." global + ".hr_bytes($mycalc{'per_thread_buffers'})." per thread ($myvar{'max_connections'} max threads)\n";
-	if ($mycalc{'total_possible_used_memory'} > 2*1024*1024*1024 && $arch eq 32) {
-		badprint "Allocating > 2GB RAM on 32-bit systems can cause system instability\n";
-		badprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n";
-	} elsif ($mycalc{'pct_physical_memory'} > 85) {
-		badprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n";
-		push(@generalrec,"Reduce your overall MySQL memory footprint for system stability");
-	} else {
-		goodprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n";
-	}
-
-	# Slow queries
-	if ($mycalc{'pct_slow_queries'} > 5) {
-		badprint "Slow queries: $mycalc{'pct_slow_queries'}% (".hr_num($mystat{'Slow_queries'})."/".hr_num($mystat{'Questions'}).")\n";
-	} else {
-		goodprint "Slow queries: $mycalc{'pct_slow_queries'}% (".hr_num($mystat{'Slow_queries'})."/".hr_num($mystat{'Questions'}).")\n";
-	}
-	if ($myvar{'long_query_time'} > 10) { push(@adjvars,"long_query_time (<= 10)"); }
-	if (defined($myvar{'log_slow_queries'})) {
-		if ($myvar{'log_slow_queries'} eq "OFF") { push(@generalrec,"Enable the slow query log to troubleshoot bad queries"); }
-	}
-
-	# Connections
-	if ($mycalc{'pct_connections_used'} > 85) {
-		badprint "Highest connection usage: $mycalc{'pct_connections_used'}%  ($mystat{'Max_used_connections'}/$myvar{'max_connections'})\n";
-		push(@adjvars,"max_connections (> ".$myvar{'max_connections'}.")");
-		push(@adjvars,"wait_timeout (< ".$myvar{'wait_timeout'}.")","interactive_timeout (< ".$myvar{'interactive_timeout'}.")");
-		push(@generalrec,"Reduce or eliminate persistent connections to reduce connection usage")
-	} else {
-		goodprint "Highest usage of available connections: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})\n";
-	}
-
-	# Key buffer
-	if (!defined($mycalc{'total_myisam_indexes'}) and $doremote == 1) {
-		push(@generalrec,"Unable to calculate MyISAM indexes on remote MySQL server < 5.0.0");
-	} elsif ($mycalc{'total_myisam_indexes'} =~ /^fail$/) {
-		badprint "Cannot calculate MyISAM index size - re-run script as root user\n";
-	} elsif ($mycalc{'total_myisam_indexes'} == "0") {
-		badprint "None of your MyISAM tables are indexed - add indexes immediately\n";
-	} else {
-		if ($myvar{'key_buffer_size'} < $mycalc{'total_myisam_indexes'} && $mycalc{'pct_keys_from_mem'} < 95) {
-			badprint "Key buffer size / total MyISAM indexes: ".hr_bytes($myvar{'key_buffer_size'})."/".hr_bytes($mycalc{'total_myisam_indexes'})."\n";
-			push(@adjvars,"key_buffer_size (> ".hr_bytes($mycalc{'total_myisam_indexes'}).")");
-		} else {
-			goodprint "Key buffer size / total MyISAM indexes: ".hr_bytes($myvar{'key_buffer_size'})."/".hr_bytes($mycalc{'total_myisam_indexes'})."\n";
-		}
-		if ($mystat{'Key_read_requests'} > 0) {
-			if ($mycalc{'pct_keys_from_mem'} < 95) {
-				badprint "Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% (".hr_num($mystat{'Key_read_requests'})." cached / ".hr_num($mystat{'Key_reads'})." reads)\n";
-			} else {
-				goodprint "Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% (".hr_num($mystat{'Key_read_requests'})." cached / ".hr_num($mystat{'Key_reads'})." reads)\n";
-			}
-		} else {
-			# No queries have run that would use keys
-		}
-	}
-
-	# Query cache
-	if (!mysql_version_ge(4)) {
-		# MySQL versions < 4.01 don't support query caching
-		push(@generalrec,"Upgrade MySQL to version 4+ to utilize query caching");
-	} elsif ($myvar{'query_cache_size'} < 1) {
-		badprint "Query cache is disabled\n";
-		push(@adjvars,"query_cache_size (>= 8M)");
-	} elsif ($mystat{'Com_select'} == 0) {
-		badprint "Query cache cannot be analyzed - no SELECT statements executed\n";
-	} else {
-		if ($mycalc{'query_cache_efficiency'} < 20) {
-			badprint "Query cache efficiency: $mycalc{'query_cache_efficiency'}% (".hr_num($mystat{'Qcache_hits'})." cached / ".hr_num($mystat{'Qcache_hits'}+$mystat{'Com_select'})." selects)\n";
-			push(@adjvars,"query_cache_limit (> ".hr_bytes_rnd($myvar{'query_cache_limit'}).", or use smaller result sets)");
-		} else {
-			goodprint "Query cache efficiency: $mycalc{'query_cache_efficiency'}% (".hr_num($mystat{'Qcache_hits'})." cached / ".hr_num($mystat{'Qcache_hits'}+$mystat{'Com_select'})." selects)\n";
-		}
-		if ($mycalc{'query_cache_prunes_per_day'} > 98) {
-			badprint "Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}\n";
-			if ($myvar{'query_cache_size'} > 128*1024*1024) {
-			    push(@generalrec,"Increasing the query_cache size over 128M may reduce performance");
-		        push(@adjvars,"query_cache_size (> ".hr_bytes_rnd($myvar{'query_cache_size'}).") [see warning above]");
-			} else {
-		        push(@adjvars,"query_cache_size (> ".hr_bytes_rnd($myvar{'query_cache_size'}).")");
-			}
-		} else {
-			goodprint "Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}\n";
-		}
-	}
-
-	# Sorting
-	if ($mycalc{'total_sorts'} == 0) {
-		# For the sake of space, we will be quiet here
-		# No sorts have run yet
-	} elsif ($mycalc{'pct_temp_sort_table'} > 10) {
-		badprint "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% (".hr_num($mystat{'Sort_merge_passes'})." temp sorts / ".hr_num($mycalc{'total_sorts'})." sorts)\n";
-		push(@adjvars,"sort_buffer_size (> ".hr_bytes_rnd($myvar{'sort_buffer_size'}).")");
-		push(@adjvars,"read_rnd_buffer_size (> ".hr_bytes_rnd($myvar{'read_rnd_buffer_size'}).")");
-	} else {
-		goodprint "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% (".hr_num($mystat{'Sort_merge_passes'})." temp sorts / ".hr_num($mycalc{'total_sorts'})." sorts)\n";
-	}
-
-	# Joins
-	if ($mycalc{'joins_without_indexes_per_day'} > 250) {
-		badprint "Joins performed without indexes: $mycalc{'joins_without_indexes'}\n";
-		push(@adjvars,"join_buffer_size (> ".hr_bytes($myvar{'join_buffer_size'}).", or always use indexes with joins)");
-		push(@generalrec,"Adjust your join queries to always utilize indexes");
-	} else {
-		# For the sake of space, we will be quiet here
-		# No joins have run without indexes
-	}
-
-	# Temporary tables
-	if ($mystat{'Created_tmp_tables'} > 0) {
-		if ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} < 256*1024*1024) {
-			badprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (".hr_num($mystat{'Created_tmp_disk_tables'})." on disk / ".hr_num($mystat{'Created_tmp_disk_tables'} + $mystat{'Created_tmp_tables'})." total)\n";
-			push(@adjvars,"tmp_table_size (> ".hr_bytes_rnd($myvar{'tmp_table_size'}).")");
-			push(@adjvars,"max_heap_table_size (> ".hr_bytes_rnd($myvar{'max_heap_table_size'}).")");
-			push(@generalrec,"When making adjustments, make tmp_table_size/max_heap_table_size equal");
-			push(@generalrec,"Reduce your SELECT DISTINCT queries without LIMIT clauses");
-		} elsif ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} >= 256) {
-			badprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (".hr_num($mystat{'Created_tmp_disk_tables'})." on disk / ".hr_num($mystat{'Created_tmp_disk_tables'} + $mystat{'Created_tmp_tables'})." total)\n";
-			push(@generalrec,"Temporary table size is already large - reduce result set size");
-			push(@generalrec,"Reduce your SELECT DISTINCT queries without LIMIT clauses");
-		} else {
-			goodprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (".hr_num($mystat{'Created_tmp_disk_tables'})." on disk / ".hr_num($mystat{'Created_tmp_disk_tables'} + $mystat{'Created_tmp_tables'})." total)\n";
-		}
-	} else {
-		# For the sake of space, we will be quiet here
-		# No temporary tables have been created
-	}
-
-	# Thread cache
-	if ($myvar{'thread_cache_size'} eq 0) {
-		badprint "Thread cache is disabled\n";
-		push(@generalrec,"Set thread_cache_size to 4 as a starting value");
-		push(@adjvars,"thread_cache_size (start at 4)");
-	} else {
-		if ($mycalc{'thread_cache_hit_rate'} <= 50) {
-			badprint "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% (".hr_num($mystat{'Threads_created'})." created / ".hr_num($mystat{'Connections'})." connections)\n";
-			push(@adjvars,"thread_cache_size (> $myvar{'thread_cache_size'})");
-		} else {
-			goodprint "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% (".hr_num($mystat{'Threads_created'})." created / ".hr_num($mystat{'Connections'})." connections)\n";
-		}
-	}
-
-	# Table cache
-	if ($mystat{'Open_tables'} > 0) {
-		if ($mycalc{'table_cache_hit_rate'} < 20) {
-			badprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (".hr_num($mystat{'Open_tables'})." open / ".hr_num($mystat{'Opened_tables'})." opened)\n";
-			if (mysql_version_ge(5, 1)) {
-				push(@adjvars,"table_cache (> ".$myvar{'table_open_cache'}.")");
-			} else {
-				push(@adjvars,"table_cache (> ".$myvar{'table_cache'}.")");
-			}
-			push(@generalrec,"Increase table_cache gradually to avoid file descriptor limits");
-		} else {
-			goodprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (".hr_num($mystat{'Open_tables'})." open / ".hr_num($mystat{'Opened_tables'})." opened)\n";
-		}
-	}
-
-	# Open files
-	if (defined $mycalc{'pct_files_open'}) {
-		if ($mycalc{'pct_files_open'} > 85) {
-			badprint "Open file limit used: $mycalc{'pct_files_open'}% (".hr_num($mystat{'Open_files'})."/".hr_num($myvar{'open_files_limit'}).")\n";
-			push(@adjvars,"open_files_limit (> ".$myvar{'open_files_limit'}.")");
-		} else {
-			goodprint "Open file limit used: $mycalc{'pct_files_open'}% (".hr_num($mystat{'Open_files'})."/".hr_num($myvar{'open_files_limit'}).")\n";
-		}
-	}
-
-	# Table locks
-	if (defined $mycalc{'pct_table_locks_immediate'}) {
-		if ($mycalc{'pct_table_locks_immediate'} < 95) {
-			badprint "Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}%\n";
-			push(@generalrec,"Optimize queries and/or use InnoDB to reduce lock wait");
-		} else {
-			goodprint "Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}% (".hr_num($mystat{'Table_locks_immediate'})." immediate / ".hr_num($mystat{'Table_locks_waited'}+$mystat{'Table_locks_immediate'})." locks)\n";
-		}
-	}
-
-	# Performance options
-	if (!mysql_version_ge(4, 1)) {
-		push(@generalrec,"Upgrade to MySQL 4.1+ to use concurrent MyISAM inserts");
-	} elsif ($myvar{'concurrent_insert'} eq "OFF") {
-		push(@generalrec,"Enable concurrent_insert by setting it to 'ON'");
-	} elsif ($myvar{'concurrent_insert'} eq 0) {
-		push(@generalrec,"Enable concurrent_insert by setting it to 1");
-	}
-	if ($mycalc{'pct_aborted_connections'} > 5) {
-		badprint "Connections aborted: ".$mycalc{'pct_aborted_connections'}."%\n";
-		push(@generalrec,"Your applications are not closing MySQL connections properly");
-	}
-
-	# InnoDB
-	if (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES" && defined $enginestats{'InnoDB'}) {
-		if ($myvar{'innodb_buffer_pool_size'} > $enginestats{'InnoDB'}) {
-			goodprint "InnoDB data size / buffer pool: ".hr_bytes($enginestats{'InnoDB'})."/".hr_bytes($myvar{'innodb_buffer_pool_size'})."\n";
-		} else {
-			badprint "InnoDB data size / buffer pool: ".hr_bytes($enginestats{'InnoDB'})."/".hr_bytes($myvar{'innodb_buffer_pool_size'})."\n";
-			push(@adjvars,"innodb_buffer_pool_size (>= ".hr_bytes_rnd($enginestats{'InnoDB'}).")");
-		}
-	}
-}
-
-# Take the two recommendation arrays and display them at the end of the output
-sub make_recommendations {
-	print "\n-------- Recommendations -----------------------------------------------------\n";
-	if (@generalrec > 0) {
-		print "General recommendations:\n";
-		foreach (@generalrec) { print "    ".$_."\n"; }
-	}
-	if (@adjvars > 0) {
-		print "Variables to adjust:\n";
-		if ($mycalc{'pct_physical_memory'} > 90) {
-			print "  *** MySQL's maximum memory usage is dangerously high ***\n".
-				  "  *** Add RAM before increasing MySQL buffer variables ***\n";
-		}
-		foreach (@adjvars) { print "    ".$_."\n"; }
-	}
-	if (@generalrec == 0 && @adjvars ==0) {
-		print "No additional performance recommendations are available.\n"
-	}
-	print "\n";
-}
-
-# ---------------------------------------------------------------------------
-# BEGIN 'MAIN'
-# ---------------------------------------------------------------------------
-print	"\n >>  MySQLTuner $tunerversion - Major Hayden <major\@mhtx.net>\n".
-		" >>  Bug reports, feature requests, and downloads at http://mysqltuner.com/\n".
-		" >>  Run with '--help' for additional options and output filtering\n";
-mysql_setup;					# Gotta login first
-os_setup;						# Set up some OS variables
-get_all_vars;					# Toss variables/status into hashes
-validate_tuner_version;			# Check current MySQLTuner version
-validate_mysql_version;			# Check current MySQL version
-check_architecture;				# Suggest 64-bit upgrade
-check_storage_engines;			# Show enabled storage engines
-security_recommendations;		# Display some security recommendations
-calculations;					# Calculate everything we need
-mysql_stats;					# Print the server stats
-make_recommendations;			# Make recommendations based on stats
-# ---------------------------------------------------------------------------
-# END 'MAIN'
-# ---------------------------------------------------------------------------
-
-# Local variables:
-# indent-tabs-mode: t
-# cperl-indent-level: 8
-# perl-indent-level: 8
-# End:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/lib/puppet/parser/functions/mysql_dirname.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,15 @@
+module Puppet::Parser::Functions
+  newfunction(:mysql_dirname, :type => :rvalue, :doc => <<-EOS
+    Returns the dirname of a path.
+    EOS
+  ) do |arguments|
+
+    raise(Puppet::ParseError, "mysql_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 :
--- a/modules/mysql/lib/puppet/parser/functions/mysql_password.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/parser/functions/mysql_password.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -10,6 +10,7 @@
     raise(Puppet::ParseError, 'mysql_password(): Wrong number of arguments ' +
       "given (#{args.size} for 1)") if args.size != 1
 
+    return '' if args[0].empty?
     '*' + Digest::SHA1.hexdigest(Digest::SHA1.digest(args[0])).upcase
   end
 end
--- a/modules/mysql/lib/puppet/provider/database/mysql.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
-Puppet::Type.type(:database).provide(:mysql, :parent => Puppet::Provider::Mysql) do
-  desc 'Manages MySQL database.'
-
-  defaultfor :kernel => 'Linux'
-
-  optional_commands :mysql      => 'mysql'
-  optional_commands :mysqladmin => 'mysqladmin'
-
-  def self.instances
-    mysql([defaults_file, '-NBe', 'show databases'].compact).split("\n").collect do |name|
-      new(:name => name)
-    end
-  end
-
-  def create
-    mysql([defaults_file, '-NBe', "create database `#{@resource[:name]}` character set #{resource[:charset]}"].compact)
-  end
-
-  def destroy
-    mysqladmin([defaults_file, '-f', 'drop', @resource[:name]].compact)
-  end
-
-  def charset
-    mysql([defaults_file, '-NBe', "show create database `#{resource[:name]}`"].compact).match(/.*?(\S+)\s(?:COLLATE.*)?\*\//)[1]
-  end
-
-  def charset=(value)
-    mysql([defaults_file, '-NBe', "alter database `#{resource[:name]}` CHARACTER SET #{value}"].compact)
-  end
-
-  def exists?
-    begin
-      mysql([defaults_file, '-NBe', 'show databases'].compact).match(/^#{@resource[:name]}$/)
-    rescue => e
-      debug(e.message)
-      return nil
-    end
-  end
-
-end
--- a/modules/mysql/lib/puppet/provider/database_grant/mysql.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-# A grant is either global or per-db. This can be distinguished by the syntax
-# of the name:
-#   user@host => global
-#   user@host/db => per-db
-
-require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
-Puppet::Type.type(:database_grant).provide(:mysql, :parent => Puppet::Provider::Mysql) do
-
-  desc 'Uses mysql as database.'
-
-  defaultfor :kernel => 'Linux'
-
-  optional_commands :mysql      => 'mysql'
-  optional_commands :mysqladmin => 'mysqladmin'
-
-  def self.prefetch(resources)
-    @user_privs = query_user_privs
-    @db_privs = query_db_privs
-  end
-
-  def self.user_privs
-    @user_privs || query_user_privs
-  end
-
-  def self.db_privs
-    @db_privs || query_db_privs
-  end
-
-  def user_privs
-    self.class.user_privs
-  end
-
-  def db_privs
-    self.class.db_privs
-  end
-
-  def self.query_user_privs
-    results = mysql([defaults_file, 'mysql', '-Be', 'describe user'].compact)
-    column_names = results.split(/\n/).map { |l| l.chomp.split(/\t/)[0] }
-    @user_privs = column_names.delete_if { |e| !( e =~/_priv$/) }
-  end
-
-  def self.query_db_privs
-    results = mysql([defaults_file, 'mysql', '-Be', 'describe db'].compact)
-    column_names = results.split(/\n/).map { |l| l.chomp.split(/\t/)[0] }
-    @db_privs = column_names.delete_if { |e| !(e =~/_priv$/) }
-  end
-
-  def mysql_flush
-    mysqladmin([defaults_file, 'flush-privileges'].compact)
-  end
-
-  # this parses the
-  def split_name(string)
-    matches = /^([^@]*)@([^\/]*)(\/(.*))?$/.match(string).captures.compact
-    case matches.length
-    when 2
-      {
-        :type => :user,
-        :user => matches[0],
-        :host => matches[1]
-      }
-    when 4
-      {
-        :type => :db,
-        :user => matches[0],
-        :host => matches[1],
-        :db => matches[3]
-      }
-    end
-  end
-
-  def create_row
-    unless @resource.should(:privileges).empty?
-      name = split_name(@resource[:name])
-      case name[:type]
-      when :user
-        mysql([defaults_file, 'mysql', '-e', "INSERT INTO user (host, user) VALUES ('%s', '%s')" % [
-          name[:host], name[:user],
-        ]].compact)
-      when :db
-        mysql([defaults_file, 'mysql', '-e', "INSERT INTO db (host, user, db) VALUES ('%s', '%s', '%s')" % [
-          name[:host], name[:user], name[:db],
-        ]].compact)
-      end
-      mysql_flush
-    end
-  end
-
-  def destroy
-    mysql([defaults_file, 'mysql', '-e', "REVOKE ALL ON '%s'.* FROM '%s@%s'" % [ @resource[:privileges], @resource[:database], @resource[:name], @resource[:host] ]].compact)
-  end
-
-  def row_exists?
-    name = split_name(@resource[:name])
-    fields = [:user, :host]
-    if name[:type] == :db
-      fields << :db
-    end
-    not mysql([defaults_file, 'mysql', '-NBe', "SELECT '1' FROM %s WHERE %s" % [ name[:type], fields.map do |f| "%s='%s'" % [f, name[f]] end.join(' AND ')]].compact).empty?
-  end
-
-  def all_privs_set?
-    all_privs = case split_name(@resource[:name])[:type]
-                when :user
-                  user_privs
-                when :db
-                  db_privs
-                end
-    all_privs = all_privs.collect do |p| p.downcase end.sort.join('|')
-    privs = privileges.collect do |p| p.downcase end.sort.join('|')
-
-    all_privs == privs
-  end
-
-  def privileges
-    name = split_name(@resource[:name])
-    privs = ''
-
-    case name[:type]
-    when :user
-      privs = mysql([defaults_file, 'mysql', '-Be', "select * from mysql.user where user='%s' and host='%s'" % [ name[:user], name[:host] ]].compact)
-    when :db
-      privs = mysql([defaults_file, 'mysql', '-Be', "select * from mysql.db where user='%s' and host='%s' and db='%s'" % [ name[:user], name[:host], name[:db] ]].compact)
-    end
-
-    if privs.match(/^$/)
-      privs = [] # no result, no privs
-    else
-      # returns a line with field names and a line with values, each tab-separated
-      privs = privs.split(/\n/).map! do |l| l.chomp.split(/\t/) end
-      # transpose the lines, so we have key/value pairs
-      privs = privs[0].zip(privs[1])
-      privs = privs.select do |p| p[0].match(/_priv$/) and p[1] == 'Y' end
-    end
-
-    privs.collect do |p| p[0] end
-  end
-
-  def privileges=(privs)
-    unless row_exists?
-      create_row
-    end
-
-    # puts "Setting privs: ", privs.join(", ")
-    name = split_name(@resource[:name])
-    stmt = ''
-    where = ''
-    all_privs = []
-    case name[:type]
-    when :user
-      stmt = 'update user set '
-      where = " where user='%s' and host='%s'" % [ name[:user], name[:host] ]
-      all_privs = user_privs
-    when :db
-      stmt = 'update db set '
-      where = " where user='%s' and host='%s' and db='%s'" % [ name[:user], name[:host], name[:db] ]
-      all_privs = db_privs
-    end
-
-    if privs[0].downcase == 'all'
-      privs = all_privs
-    end
-
-    # Downcase the requested priviliges for case-insensitive selection
-    # we don't map! here because the all_privs object has to remain in
-    # the same case the DB gave it to us in
-    privs = privs.map { |p| p.downcase }
-
-    # puts "stmt:", stmt
-    set = all_privs.collect do |p| "%s = '%s'" % [p, privs.include?(p.downcase) ? 'Y' : 'N'] end.join(', ')
-    # puts "set:", set
-    stmt = stmt << set << where
-
-    validate_privs privs, all_privs
-    mysql([defaults_file, 'mysql', '-Be', stmt].compact)
-    mysql_flush
-  end
-
-  def validate_privs(set_privs, all_privs)
-    all_privs = all_privs.collect { |p| p.downcase }
-    set_privs = set_privs.collect { |p| p.downcase }
-    invalid_privs = Array.new
-    hints = Array.new
-    # Test each of the user provided privs to see if they exist in all_privs
-    set_privs.each do |priv|
-      invalid_privs << priv unless all_privs.include?(priv)
-      hints << "#{priv}_priv" if all_privs.include?("#{priv}_priv")
-    end
-    unless invalid_privs.empty?
-      # Print a decently helpful and gramatically correct error message
-      hints = "Did you mean '#{hints.join(',')}'?" unless hints.empty?
-      p = invalid_privs.size > 1 ? ['s', 'are not valid'] : ['', 'is not valid']
-      detail = ["The privilege#{p[0]} '#{invalid_privs.join(',')}' #{p[1]}."]
-      fail [detail, hints].join(' ')
-    end
-  end
-
-end
--- a/modules/mysql/lib/puppet/provider/database_user/mysql.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
-Puppet::Type.type(:database_user).provide(:mysql, :parent => Puppet::Provider::Mysql) do
-
-  desc 'manage users for a mysql database.'
-
-  defaultfor :kernel => 'Linux'
-
-  commands :mysql      => 'mysql'
-  commands :mysqladmin => 'mysqladmin'
-
-  def self.instances
-    users = mysql([defaults_file, 'mysql', '-BNe' "select concat(User, '@',Host) as User from mysql.user"].compact).split("\n")
-    users.select{ |user| user =~ /.+@/ }.collect do |name|
-      new(:name => name)
-    end
-  end
-
-  def create
-    merged_name          = self.class.cmd_user(@resource[:name])
-    password_hash        = @resource.value(:password_hash)
-    max_user_connections = @resource.value(:max_user_connections) || 0
-
-    mysql([defaults_file, 'mysql', '-e', "grant usage on *.* to #{merged_name} identified by PASSWORD
-      '#{password_hash}' with max_user_connections #{max_user_connections}"].compact)
-
-    exists? ? (return true) : (return false)
-  end
-
-  def destroy
-    merged_name   = self.class.cmd_user(@resource[:name])
-    mysql([defaults_file, 'mysql', '-e', "drop user #{merged_name}"].compact)
-
-    exists? ? (return false) : (return true)
-  end
-
-  def password_hash
-    mysql([defaults_file, 'mysql', '-NBe', "select password from mysql.user where CONCAT(user, '@', host) = '#{@resource[:name]}'"].compact).chomp
-  end
-
-  def password_hash=(string)
-    mysql([defaults_file, 'mysql', '-e', "SET PASSWORD FOR #{self.class.cmd_user(@resource[:name])} = '#{string}'"].compact)
-
-    password_hash == string ? (return true) : (return false)
-  end
-
-  def max_user_connections
-    mysql([defaults_file, "mysql", "-NBe", "select max_user_connections from mysql.user where CONCAT(user, '@', host) = '#{@resource[:name]}'"].compact).chomp
-  end
-
-  def max_user_connections=(int)
-    mysql([defaults_file, "mysql", "-e", "grant usage on *.* to %s with max_user_connections #{int}" % [ self.class.cmd_user(@resource[:name])] ].compact).chomp
-
-    max_user_connections == int ? (return true) : (return false)
-  end
-
-  def exists?
-    not mysql([defaults_file, 'mysql', '-NBe', "select '1' from mysql.user where CONCAT(user, '@', host) = '%s'" % @resource.value(:name)].compact).empty?
-  end
-
-  def flush
-    @property_hash.clear
-    mysqladmin([defaults_file, 'flush-privileges'].compact)
-  end
-
-end
--- a/modules/mysql/lib/puppet/provider/mysql_database/mysql.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/provider/mysql_database/mysql.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -31,7 +31,7 @@
   end
 
   def create
-    mysql([defaults_file, '-NBe', "create database if not exists `#{@resource[:name]}` character set #{@resource[:charset]} collate #{@resource[:collate]}"].compact)
+    mysql([defaults_file, '-NBe', "create database if not exists `#{@resource[:name]}` character set `#{@resource[:charset]}` collate `#{@resource[:collate]}`"].compact)
 
     @property_hash[:ensure]  = :present
     @property_hash[:charset] = @resource[:charset]
@@ -41,7 +41,7 @@
   end
 
   def destroy
-    mysql([defaults_file, '-NBe', "drop database `#{@resource[:name]}`"].compact)
+    mysql([defaults_file, '-NBe', "drop database if exists `#{@resource[:name]}`"].compact)
 
     @property_hash.clear
     exists? ? (return false) : (return true)
--- a/modules/mysql/lib/puppet/provider/mysql_grant/mysql.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/provider/mysql_grant/mysql.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -8,21 +8,47 @@
     users.select{ |user| user =~ /.+@/ }.collect do |user|
       user_string = self.cmd_user(user)
       query = "SHOW GRANTS FOR #{user_string};"
-      grants = mysql([defaults_file, "-NBe", query].compact)
+      begin
+        grants = mysql([defaults_file, "-NBe", query].compact)
+      rescue Puppet::ExecutionFailure => e
+        # Silently ignore users with no grants. Can happen e.g. if user is
+        # defined with fqdn and server is run with skip-name-resolve. Example:
+        # Default root user created by mysql_install_db on a host with fqdn
+        # of myhost.mydomain.my: root@myhost.mydomain.my, when MySQL is started
+        # with --skip-name-resolve.
+        if e.inspect =~ /There is no such grant defined for user/
+          next
+        else
+          raise Puppet::Error, "#mysql had an error ->  #{e.inspect}"
+        end
+      end
       # Once we have the list of grants generate entries for each.
       grants.each_line do |grant|
         # Match the munges we do in the type.
         munged_grant = grant.delete("'").delete("`")
         # Matching: GRANT (SELECT, UPDATE) PRIVILEGES ON (*.*) TO ('root')@('127.0.0.1') (WITH GRANT OPTION)
-        if match = munged_grant.match(/^GRANT\s(.+)\sON\s(.+)\sTO\s(.*)@(.*?)(\s.*)$/)
+        if match = munged_grant.match(/^GRANT\s(.+)\sON\s(.+)\sTO\s(.*)@(.*?)(\s.*)?$/)
           privileges, table, user, host, rest = match.captures
-          # Once we split privileges up on the , we need to make sure we
-          # shortern ALL PRIVILEGES to just all.
-          stripped_privileges = privileges.split(',').map do |priv|
-            priv == 'ALL PRIVILEGES' ? 'ALL' : priv.lstrip.rstrip
+          table.gsub!('\\\\', '\\')
+
+          # split on ',' if it is not a non-'('-containing string followed by a
+          # closing parenthesis ')'-char - e.g. only split comma separated elements not in
+          # parentheses
+          stripped_privileges = privileges.strip.split(/\s*,\s*(?![^(]*\))/).map do |priv|
+            # split and sort the column_privileges in the parentheses and rejoin
+            if priv.include?('(')
+              type, col=priv.strip.split(/\s+|\b/,2)
+              type.upcase + " (" + col.slice(1...-1).strip.split(/\s*,\s*/).sort.join(', ') + ")"
+            else
+              # Once we split privileges up on the , we need to make sure we
+              # shortern ALL PRIVILEGES to just all.
+              priv == 'ALL PRIVILEGES' ? 'ALL' : priv.strip
+            end
           end
           # Same here, but to remove OPTION leaving just GRANT.
           options = ['GRANT'] if rest.match(/WITH\sGRANT\sOPTION/)
+          # fix double backslash that MySQL prints, so resources match
+          table.gsub!("\\\\", "\\")
           # We need to return an array of instances so capture these
           instances << new(
               :name       => "#{user}@#{host}/#{table}",
@@ -74,13 +100,15 @@
     user_string = self.class.cmd_user(user)
     table_string = self.class.cmd_table(table)
 
-    query = "REVOKE ALL ON #{table_string} FROM #{user_string}"
-    mysql([defaults_file, '-e', query].compact)
     # revoke grant option needs to be a extra query, because
     # "REVOKE ALL PRIVILEGES, GRANT OPTION [..]" is only valid mysql syntax
     # if no ON clause is used.
+    # It hast to be executed before "REVOKE ALL [..]" since a GRANT has to
+    # exist to be executed successfully
     query = "REVOKE GRANT OPTION ON #{table_string} FROM #{user_string}"
     mysql([defaults_file, '-e', query].compact)
+    query = "REVOKE ALL ON #{table_string} FROM #{user_string}"
+    mysql([defaults_file, '-e', query].compact)
   end
 
   def destroy
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/lib/puppet/provider/mysql_plugin/mysql.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,53 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
+Puppet::Type.type(:mysql_plugin).provide(:mysql, :parent => Puppet::Provider::Mysql) do
+  desc 'Manages MySQL plugins.'
+
+  commands :mysql => 'mysql'
+
+  def self.instances
+    mysql([defaults_file, '-NBe', 'show plugins'].compact).split("\n").collect do |line|
+      name, status, type, library, license = line.split(/\t/)
+      new(:name    => name,
+          :ensure  => :present,
+          :soname  => library
+         )
+    end
+  end
+
+  # We iterate over each mysql_plugin entry in the catalog and compare it against
+  # the contents of the property_hash generated by self.instances
+  def self.prefetch(resources)
+    plugins = instances
+    resources.keys.each do |plugin|
+      if provider = plugins.find { |pl| pl.name == plugin }
+        resources[plugin].provider = provider
+      end
+    end
+  end
+
+  def create
+    # Use plugin_name.so as soname if it's not specified. This won't work on windows as
+    # there it should be plugin_name.dll
+    @resource[:soname].nil? ? (soname=@resource[:name] + '.so') : (soname=@resource[:soname])
+    mysql([defaults_file, '-NBe', "install plugin #{@resource[:name]} soname '#{soname}'"].compact)
+
+    @property_hash[:ensure]  = :present
+    @property_hash[:soname] = @resource[:soname]
+
+    exists? ? (return true) : (return false)
+  end
+
+  def destroy
+    mysql([defaults_file, '-NBe', "uninstall plugin #{@resource[:name]}"].compact)
+
+    @property_hash.clear
+    exists? ? (return false) : (return true)
+  end
+
+  def exists?
+    @property_hash[:ensure] == :present || false
+  end
+
+  mk_resource_methods
+
+end
--- a/modules/mysql/lib/puppet/provider/mysql_user/mysql.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/provider/mysql_user/mysql.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -12,13 +12,14 @@
     # To reduce the number of calls to MySQL we collect all the properties in
     # one big swoop.
     users.collect do |name|
-      query = "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD FROM mysql.user WHERE CONCAT(user, '@', host) = '#{name}'"
+      query = "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{name}'"
       @max_user_connections, @max_connections_per_hour, @max_queries_per_hour,
-      @max_updates_per_hour, @password = mysql([defaults_file, "-NBe", query].compact).split(/\s/)
+      @max_updates_per_hour, @password, @plugin = mysql([defaults_file, "-NBe", query].compact).split(/\s/)
 
       new(:name                     => name,
           :ensure                   => :present,
           :password_hash            => @password,
+          :plugin                   => @plugin,
           :max_user_connections     => @max_user_connections,
           :max_connections_per_hour => @max_connections_per_hour,
           :max_queries_per_hour     => @max_queries_per_hour,
@@ -39,17 +40,26 @@
   end
 
   def create
-    merged_name             = @resource[:name].sub('@', "'@'")
+    merged_name              = @resource[:name].sub('@', "'@'")
     password_hash            = @resource.value(:password_hash)
+    plugin                   = @resource.value(:plugin)
     max_user_connections     = @resource.value(:max_user_connections) || 0
     max_connections_per_hour = @resource.value(:max_connections_per_hour) || 0
     max_queries_per_hour     = @resource.value(:max_queries_per_hour) || 0
     max_updates_per_hour     = @resource.value(:max_updates_per_hour) || 0
 
-    mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO '#{merged_name}' IDENTIFIED BY PASSWORD '#{password_hash}' WITH MAX_USER_CONNECTIONS #{max_user_connections} MAX_CONNECTIONS_PER_HOUR #{max_connections_per_hour} MAX_QUERIES_PER_HOUR #{max_queries_per_hour} MAX_UPDATES_PER_HOUR #{max_updates_per_hour}"].compact)
-
-    @property_hash[:ensure] = :present
-    @property_hash[:password_hash] = password_hash
+    # Use CREATE USER to be compatible with NO_AUTO_CREATE_USER sql_mode
+    # This is also required if you want to specify a authentication plugin
+    if !plugin.nil?
+      mysql([defaults_file, '-e', "CREATE USER '#{merged_name}' IDENTIFIED WITH '#{plugin}'"].compact)
+      @property_hash[:ensure] = :present
+      @property_hash[:plugin] = plugin
+    else
+      mysql([defaults_file, '-e', "CREATE USER '#{merged_name}' IDENTIFIED BY PASSWORD '#{password_hash}'"].compact)
+      @property_hash[:ensure] = :present
+      @property_hash[:password_hash] = password_hash
+    end
+    mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO '#{merged_name}' WITH MAX_USER_CONNECTIONS #{max_user_connections} MAX_CONNECTIONS_PER_HOUR #{max_connections_per_hour} MAX_QUERIES_PER_HOUR #{max_queries_per_hour} MAX_UPDATES_PER_HOUR #{max_updates_per_hour}"].compact)
     @property_hash[:max_user_connections] = max_user_connections
     @property_hash[:max_connections_per_hour] = max_connections_per_hour
     @property_hash[:max_queries_per_hour] = max_queries_per_hour
--- a/modules/mysql/lib/puppet/type/database.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-# This has to be a separate type to enable collecting
-Puppet::Type.newtype(:database) do
-  @doc = 'Manage databases.'
-
-  ensurable
-
-  newparam(:name, :namevar=>true) do
-    desc 'The name of the database.'
-    validate do |value|
-      Puppet.warning("database has been deprecated in favor of mysql_database.")
-      true
-    end
-  end
-
-  newproperty(:charset) do
-    desc 'The characterset to use for a database'
-    defaultto :utf8
-    newvalue(/^\S+$/)
-  end
-
-end
--- a/modules/mysql/lib/puppet/type/database_grant.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-# This has to be a separate type to enable collecting
-Puppet::Type.newtype(:database_grant) do
-  @doc = "Manage a database user's rights."
-  #ensurable
-
-  autorequire :database do
-    # puts "Starting db autoreq for %s" % self[:name]
-    reqs = []
-    matches = self[:name].match(/^([^@]+)@([^\/]+)\/(.+)$/)
-    unless matches.nil?
-      reqs << matches[3]
-    end
-    # puts "Autoreq: '%s'" % reqs.join(" ")
-    reqs
-  end
-
-  autorequire :database_user do
-    # puts "Starting user autoreq for %s" % self[:name]
-    reqs = []
-    matches = self[:name].match(/^([^@]+)@([^\/]+).*$/)
-    unless matches.nil?
-      reqs << '%s@%s' % [ matches[1], matches[2] ]
-    end
-    # puts "Autoreq: '%s'" % reqs.join(" ")
-    reqs
-  end
-
-  newparam(:name, :namevar=>true) do
-    desc 'The primary key: either user@host for global privilges or user@host/database for database specific privileges'
-    validate do |value|
-      Puppet.warning("database_grant has been deprecated in favor of mysql_grant.")
-      true
-    end
-  end
-
-  newproperty(:privileges, :array_matching => :all) do
-    desc 'The privileges the user should have. The possible values are implementation dependent.'
-
-    def should_to_s(newvalue = @should)
-      if newvalue
-        unless newvalue.is_a?(Array)
-          newvalue = [ newvalue ]
-        end
-        newvalue.collect do |v| v.downcase end.sort.join ', '
-      else
-        nil
-      end
-    end
-
-    def is_to_s(currentvalue = @is)
-      if currentvalue
-        unless currentvalue.is_a?(Array)
-          currentvalue = [ currentvalue ]
-        end
-        currentvalue.collect do |v| v.downcase end.sort.join ', '
-      else
-        nil
-      end
-    end
-
-    # use the sorted outputs for comparison
-    def insync?(is)
-      if defined? @should and @should
-        case self.should_to_s
-        when 'all'
-          self.provider.all_privs_set?
-        when self.is_to_s(is)
-          true
-        else
-          false
-        end
-      else
-        true
-      end
-    end
-  end
-
-end
-
--- a/modules/mysql/lib/puppet/type/database_user.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-# This has to be a separate type to enable collecting
-Puppet::Type.newtype(:database_user) do
-  @doc = 'Manage a database user. This includes management of users password as well as privileges'
-
-  ensurable
-
-  newparam(:name, :namevar=>true) do
-    desc "The name of the user. This uses the 'username@hostname' or username@hostname."
-    validate do |value|
-      Puppet.warning("database has been deprecated in favor of mysql_user.")
-      # https://dev.mysql.com/doc/refman/5.1/en/account-names.html
-      # Regex should problably be more like this: /^[`'"]?[^`'"]*[`'"]?@[`'"]?[\w%\.]+[`'"]?$/
-      raise(ArgumentError, "Invalid database user #{value}") unless value =~ /[\w-]*@[\w%\.:]+/
-      username = value.split('@')[0]
-      if username.size > 16
-        raise ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters'
-      end
-    end
-  end
-
-  newproperty(:password_hash) do
-    desc 'The password hash of the user. Use mysql_password() for creating such a hash.'
-    newvalue(/\w+/)
-  end
-
-  newproperty(:max_user_connections) do
-    desc "Max concurrent connections for the user. 0 means no (or global) limit."
-    newvalue(/\d+/)
-  end
-
-end
--- a/modules/mysql/lib/puppet/type/mysql_database.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/type/mysql_database.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -3,6 +3,8 @@
 
   ensurable
 
+  autorequire(:file) { '/root/.my.cnf' }
+
   newparam(:name, :namevar => true) do
     desc 'The name of the MySQL database to manage.'
   end
--- a/modules/mysql/lib/puppet/type/mysql_grant.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/type/mysql_grant.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -4,6 +4,7 @@
   ensurable
 
   autorequire(:file) { '/root/.my.cnf' }
+  autorequire(:mysql_user) { self[:user] }
 
   def initialize(*args)
     super
@@ -17,13 +18,22 @@
     # Sort the privileges array in order to ensure the comparision in the provider
     # self.instances method match.  Otherwise this causes it to keep resetting the
     # privileges.
-    self[:privileges] = Array(self[:privileges]).map(&:upcase).uniq.reject{|k| k == 'GRANT' or k == 'GRANT OPTION'}.sort!
+    self[:privileges] = Array(self[:privileges]).map{ |priv|
+       # split and sort the column_privileges in the parentheses and rejoin
+       if priv.include?('(')
+         type, col=priv.strip.split(/\s+|\b/,2)
+         type.upcase + " (" + col.slice(1...-1).strip.split(/\s*,\s*/).sort.join(', ') + ")"
+       else
+         priv.strip.upcase
+       end
+     }.uniq.reject{|k| k == 'GRANT' or k == 'GRANT OPTION'}.sort!
   end
 
   validate do
     fail('privileges parameter is required.') if self[:ensure] == :present and self[:privileges].nil?
     fail('table parameter is required.') if self[:ensure] == :present and self[:table].nil?
     fail('user parameter is required.') if self[:ensure] == :present and self[:user].nil?
+    fail('name must match user and table parameters') if self[:name] != "#{self[:user]}/#{self[:table]}"
   end
 
   newparam(:name, :namevar => true) do
@@ -36,10 +46,6 @@
 
   newproperty(:privileges, :array_matching => :all) do
     desc 'Privileges for user'
-
-    munge do |value|
-      value.upcase
-    end
   end
 
   newproperty(:table) do
@@ -55,13 +61,24 @@
   newproperty(:user) do
     desc 'User to operate on.'
     validate do |value|
-      # https://dev.mysql.com/doc/refman/5.1/en/account-names.html
-      # Regex should problably be more like this: /^[`'"]?[^`'"]*[`'"]?@[`'"]?[\w%\.]+[`'"]?$/
-      raise(ArgumentError, "Invalid user #{value}") unless value =~ /[\w-]*@[\w%\.:]+/
-      username = value.split('@')[0]
-      if username.size > 16
-        raise ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters'
+      # http://dev.mysql.com/doc/refman/5.5/en/identifiers.html
+      # If at least one special char is used, string must be quoted
+
+      # http://stackoverflow.com/questions/8055727/negating-a-backreference-in-regular-expressions/8057827#8057827
+      if matches = /^(['`"])((?!\1).)*\1@([\w%\.:\-]+)/.match(value)
+        user_part = matches[2]
+        host_part = matches[3]
+      elsif matches = /^([0-9a-zA-Z$_]*)@([\w%\.:\-]+)/.match(value)
+        user_part = matches[1]
+        host_part = matches[2]
+      elsif matches = /^((?!['`"]).*[^0-9a-zA-Z$_].*)@(.+)$/.match(value)
+        user_part = matches[1]
+        host_part = matches[2]
+      else
+        raise(ArgumentError, "Invalid database user #{value}")
       end
+
+      raise(ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters') unless user_part.size <= 16
     end
   end
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/lib/puppet/type/mysql_plugin.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,17 @@
+Puppet::Type.newtype(:mysql_plugin) do
+  @doc = 'Manage MySQL plugins.'
+
+  ensurable
+
+  autorequire(:file) { '/root/.my.cnf' }
+
+  newparam(:name, :namevar => true) do
+    desc 'The name of the MySQL plugin to manage.'
+  end
+
+  newproperty(:soname) do
+    desc 'The name of the library'
+    newvalue(/^\w+\.\w+$/)
+  end
+
+end
--- a/modules/mysql/lib/puppet/type/mysql_user.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/lib/puppet/type/mysql_user.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -4,21 +4,44 @@
 
   ensurable
 
+  autorequire(:file) { '/root/.my.cnf' }
+
   newparam(:name, :namevar => true) do
     desc "The name of the user. This uses the 'username@hostname' or username@hostname."
     validate do |value|
-      # https://dev.mysql.com/doc/refman/5.1/en/account-names.html
-      # Regex should problably be more like this: /^[`'"]?[^`'"]*[`'"]?@[`'"]?[\w%\.]+[`'"]?$/
-      raise(ArgumentError, "Invalid database user #{value}") unless value =~ /[\w-]*@[\w%\.:]+/
-      username = value.split('@')[0]
-      if username.size > 16
-        raise ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters'
+      # http://dev.mysql.com/doc/refman/5.5/en/identifiers.html
+      # If at least one special char is used, string must be quoted
+
+      # http://stackoverflow.com/questions/8055727/negating-a-backreference-in-regular-expressions/8057827#8057827
+      if matches = /^(['`"])((?:(?!\1).)*)\1@([\w%\.:\-]+)/.match(value)
+        user_part = matches[2]
+        host_part = matches[3]
+      elsif matches = /^([0-9a-zA-Z$_]*)@([\w%\.:\-]+)/.match(value)
+        user_part = matches[1]
+        host_part = matches[2]
+      elsif matches = /^((?!['`"]).*[^0-9a-zA-Z$_].*)@(.+)$/.match(value)
+        user_part = matches[1]
+        host_part = matches[2]
+      else
+        raise(ArgumentError, "Invalid database user #{value}")
       end
+
+      raise(ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters') if user_part.size > 16
+    end
+
+    munge do |value|
+      matches = /^((['`"]?).*\2)@([\w%\.:\-]+)/.match(value)
+      "#{matches[1]}@#{matches[3].downcase}"
     end
   end
 
   newproperty(:password_hash) do
     desc 'The password hash of the user. Use mysql_password() for creating such a hash.'
+    newvalue(/\w*/)
+  end
+
+  newproperty(:plugin) do
+    desc 'The authentication plugin of the user.'
     newvalue(/\w+/)
   end
 
--- a/modules/mysql/manifests/backup.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-# Deprecated class
-class mysql::backup (
-  $backupuser,
-  $backuppassword,
-  $backupdir,
-  $backupcompress = true,
-  $backuprotate = 30,
-  $delete_before_dump = false,
-  $backupdatabases = [],
-  $file_per_database = false,
-  $ensure = 'present',
-  $time = ['23', '5'],
-) {
-
-  crit("This class has been deprecated and callers should directly call
-  mysql::server::backup now.")
-
-  class { 'mysql::server::backup':
-    ensure             => $ensure,
-    backupuser         => $backupuser,
-    backuppassword     => $backuppassword,
-    backupdir          => $backupdir,
-    backupcompress     => $backupcompress,
-    backuprotate       => $backuprotate,
-    delete_before_dump => $delete_before_dump,
-    backupdatabases    => $backupdatabases,
-    file_per_database  => $file_per_database,
-    time               => $time,
-  }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/manifests/backup/mysqlbackup.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,103 @@
+# See README.me for usage.
+class mysql::backup::mysqlbackup (
+  $backupuser,
+  $backuppassword,
+  $backupdir,
+  $backupdirmode = '0700',
+  $backupdirowner = 'root',
+  $backupdirgroup = 'root',
+  $backupcompress = true,
+  $backuprotate = 30,
+  $ignore_events = true,
+  $delete_before_dump = false,
+  $backupdatabases = [],
+  $file_per_database = false,
+  $ensure = 'present',
+  $time = ['23', '5'],
+  $postscript = false,
+  $execpath   = '/usr/bin:/usr/sbin:/bin:/sbin',
+) {
+
+  mysql_user { "${backupuser}@localhost":
+    ensure        => $ensure,
+    password_hash => mysql_password($backuppassword),
+    provider      => 'mysql',
+    require       => Class['mysql::server::root_password'],
+  }
+
+  package { 'meb':
+    ensure    => $ensure,
+  }
+
+  # http://dev.mysql.com/doc/mysql-enterprise-backup/3.11/en/mysqlbackup.privileges.html
+  mysql_grant { "${backupuser}@localhost/*.*":
+    ensure     => $ensure,
+    user       => "${backupuser}@localhost",
+    table      => '*.*',
+    privileges => [ 'RELOAD', 'SUPER', 'REPLICATION CLIENT' ],
+    require    => Mysql_user["${backupuser}@localhost"],
+  }
+
+  mysql_grant { "${backupuser}@localhost/mysql.backup_progress":
+    ensure     => $ensure,
+    user       => "${backupuser}@localhost",
+    table      => 'mysql.backup_progress',
+    privileges => [ 'CREATE', 'INSERT', 'DROP', 'UPDATE' ],
+    require    => Mysql_user["${backupuser}@localhost"],
+  }
+
+  mysql_grant { "${backupuser}@localhost/mysql.backup_history":
+    ensure     => $ensure,
+    user       => "${backupuser}@localhost",
+    table      => 'mysql.backup_history',
+    privileges => [ 'CREATE', 'INSERT', 'SELECT', 'DROP', 'UPDATE' ],
+    require    => Mysql_user["${backupuser}@localhost"],
+  }
+
+  cron { 'mysqlbackup-weekly':
+    ensure  => $ensure,
+    command => 'mysqlbackup backup',
+    user    => 'root',
+    hour    => $time[0],
+    minute  => $time[1],
+    weekday => 0,
+    require => Package['meb'],
+  }
+
+  cron { 'mysqlbackup-daily':
+    ensure  => $ensure,
+    command => 'mysqlbackup --incremental backup',
+    user    => 'root',
+    hour    => $time[0],
+    minute  => $time[1],
+    weekday => 1-6,
+    require => Package['meb'],
+  }
+
+  $default_options = {
+    'mysqlbackup' => {
+      'backup-dir'             => $backupdir,
+      'with-timestamp'         => true,
+      'incremental_base'       => 'history:last_backup',
+      'incremental_backup_dir' => $backupdir,
+      'user'                   => $backupuser,
+      'password'               => $backuppassword,
+    }
+  }
+  $options = mysql_deepmerge($default_options, $mysql::server::override_options)
+
+  file { 'mysqlbackup-config-file':
+    path    => '/etc/mysql/conf.d/meb.cnf',
+    content => template('mysql/meb.cnf.erb'),
+    mode    => '0600',
+  }
+
+  file { 'mysqlbackupdir':
+    ensure => 'directory',
+    path   => $backupdir,
+    mode   => $backupdirmode,
+    owner  => $backupdirowner,
+    group  => $backupdirgroup,
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/manifests/backup/mysqldump.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,62 @@
+# See README.me for usage.
+class mysql::backup::mysqldump (
+  $backupuser,
+  $backuppassword,
+  $backupdir,
+  $backupdirmode = '0700',
+  $backupdirowner = 'root',
+  $backupdirgroup = 'root',
+  $backupcompress = true,
+  $backuprotate = 30,
+  $ignore_events = true,
+  $delete_before_dump = false,
+  $backupdatabases = [],
+  $file_per_database = false,
+  $ensure = 'present',
+  $time = ['23', '5'],
+  $postscript = false,
+  $execpath   = '/usr/bin:/usr/sbin:/bin:/sbin',
+) {
+
+  mysql_user { "${backupuser}@localhost":
+    ensure        => $ensure,
+    password_hash => mysql_password($backuppassword),
+    provider      => 'mysql',
+    require       => Class['mysql::server::root_password'],
+  }
+
+  mysql_grant { "${backupuser}@localhost/*.*":
+    ensure     => $ensure,
+    user       => "${backupuser}@localhost",
+    table      => '*.*',
+    privileges => [ 'SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS' ],
+    require    => Mysql_user["${backupuser}@localhost"],
+  }
+
+  cron { 'mysql-backup':
+    ensure  => $ensure,
+    command => '/usr/local/sbin/mysqlbackup.sh',
+    user    => 'root',
+    hour    => $time[0],
+    minute  => $time[1],
+    require => File['mysqlbackup.sh'],
+  }
+
+  file { 'mysqlbackup.sh':
+    ensure  => $ensure,
+    path    => '/usr/local/sbin/mysqlbackup.sh',
+    mode    => '0700',
+    owner   => 'root',
+    group   => 'root',
+    content => template('mysql/mysqlbackup.sh.erb'),
+  }
+
+  file { 'mysqlbackupdir':
+    ensure => 'directory',
+    path   => $backupdir,
+    mode   => $backupdirmode,
+    owner  => $backupdirowner,
+    group  => $backupdirgroup,
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/manifests/backup/xtrabackup.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,59 @@
+# See README.me for usage.
+class mysql::backup::xtrabackup (
+  $backupuser,
+  $backuppassword,
+  $backupdir,
+  $backupmethod = 'mysqldump',
+  $backupdirmode = '0700',
+  $backupdirowner = 'root',
+  $backupdirgroup = 'root',
+  $backupcompress = true,
+  $backuprotate = 30,
+  $ignore_events = true,
+  $delete_before_dump = false,
+  $backupdatabases = [],
+  $file_per_database = false,
+  $ensure = 'present',
+  $time = ['23', '5'],
+  $postscript = false,
+  $execpath   = '/usr/bin:/usr/sbin:/bin:/sbin',
+) {
+
+  mysql_user { "${backupuser}@localhost":
+    ensure        => $ensure,
+    password_hash => mysql_password($backuppassword),
+    provider      => 'mysql',
+    require       => Class['mysql::server::root_password'],
+  }
+
+  package{ 'percona-xtrabackup':
+    ensure  => $ensure,
+  }
+  cron { 'xtrabackup-weekly':
+    ensure  => $ensure,
+    command => 'innobackupex $backupdir',
+    user    => 'root',
+    hour    => $time[0],
+    minute  => $time[1],
+    weekday => 0,
+    require => Package['percona-xtrabackup'],
+  }
+  cron { 'xtrabackup-daily':
+    ensure  => $ensure,
+    command => 'innobackupex --incremental $backupdir',
+    user    => 'root',
+    hour    => $time[0],
+    minute  => $time[1],
+    weekday => 1-6,
+    require => Package['percona-xtrabackup'],
+  }
+
+  file { 'mysqlbackupdir':
+    ensure => 'directory',
+    path   => $backupdir,
+    mode   => $backupdirmode,
+    owner  => $backupdirowner,
+    group  => $backupdirgroup,
+  }
+
+}
--- a/modules/mysql/manifests/bindings.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/bindings.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,33 +1,57 @@
 # See README.md.
 class mysql::bindings (
+  $install_options = undef,
   # Boolean to determine if we should include the classes.
-  $java_enable   = false,
-  $perl_enable   = false,
-  $php_enable    = false,
-  $python_enable = false,
-  $ruby_enable   = false,
+  $java_enable     = false,
+  $perl_enable     = false,
+  $php_enable      = false,
+  $python_enable   = false,
+  $ruby_enable     = false,
+  $client_dev      = false,
+  $daemon_dev      = false,
   # Settings for the various classes.
-  $java_package_ensure     = $mysql::params::java_package_ensure,
-  $java_package_name       = $mysql::params::java_package_name,
-  $java_package_provider   = $mysql::params::java_package_provider,
-  $perl_package_ensure     = $mysql::params::perl_package_ensure,
-  $perl_package_name       = $mysql::params::perl_package_name,
-  $perl_package_provider   = $mysql::params::perl_package_provider,
-  $php_package_ensure      = $mysql::params::php_package_ensure,
-  $php_package_name        = $mysql::params::php_package_name,
-  $php_package_provider    = $mysql::params::php_package_provider,
-  $python_package_ensure   = $mysql::params::python_package_ensure,
-  $python_package_name     = $mysql::params::python_package_name,
-  $python_package_provider = $mysql::params::python_package_provider,
-  $ruby_package_ensure     = $mysql::params::ruby_package_ensure,
-  $ruby_package_name       = $mysql::params::ruby_package_name,
-  $ruby_package_provider   = $mysql::params::ruby_package_provider
+  $java_package_ensure         = $mysql::params::java_package_ensure,
+  $java_package_name           = $mysql::params::java_package_name,
+  $java_package_provider       = $mysql::params::java_package_provider,
+  $perl_package_ensure         = $mysql::params::perl_package_ensure,
+  $perl_package_name           = $mysql::params::perl_package_name,
+  $perl_package_provider       = $mysql::params::perl_package_provider,
+  $php_package_ensure          = $mysql::params::php_package_ensure,
+  $php_package_name            = $mysql::params::php_package_name,
+  $php_package_provider        = $mysql::params::php_package_provider,
+  $python_package_ensure       = $mysql::params::python_package_ensure,
+  $python_package_name         = $mysql::params::python_package_name,
+  $python_package_provider     = $mysql::params::python_package_provider,
+  $ruby_package_ensure         = $mysql::params::ruby_package_ensure,
+  $ruby_package_name           = $mysql::params::ruby_package_name,
+  $ruby_package_provider       = $mysql::params::ruby_package_provider,
+  $client_dev_package_ensure   = $mysql::params::client_dev_package_ensure,
+  $client_dev_package_name     = $mysql::params::client_dev_package_name,
+  $client_dev_package_provider = $mysql::params::client_dev_package_provider,
+  $daemon_dev_package_ensure   = $mysql::params::daemon_dev_package_ensure,
+  $daemon_dev_package_name     = $mysql::params::daemon_dev_package_name,
+  $daemon_dev_package_provider = $mysql::params::daemon_dev_package_provider
 ) inherits mysql::params {
 
-  if $java_enable   { include '::mysql::bindings::java' }
-  if $perl_enable   { include '::mysql::bindings::perl' }
-  if $php_enable    { include '::mysql::bindings::php' }
-  if $python_enable { include '::mysql::bindings::python' }
-  if $ruby_enable   { include '::mysql::bindings::ruby' }
+  case $::osfamily {
+    'Archlinux': {
+      if $java_enable   { fail("::mysql::bindings::java cannot be managed by puppet on ${::osfamily} as it is not in official repositories. Please disable java mysql binding.") }
+      if $perl_enable   { include '::mysql::bindings::perl' }
+      if $php_enable    { warning("::mysql::bindings::php does not need to be managed by puppet on ${::osfamily} as it is included in mysql package by default.") }
+      if $python_enable { include '::mysql::bindings::python' }
+      if $ruby_enable   { fail("::mysql::bindings::ruby cannot be managed by puppet on ${::osfamily} as it is not in official repositories. Please disable ruby mysql binding.") }
+    }
+
+    default: {
+      if $java_enable   { include '::mysql::bindings::java' }
+      if $perl_enable   { include '::mysql::bindings::perl' }
+      if $php_enable    { include '::mysql::bindings::php' }
+      if $python_enable { include '::mysql::bindings::python' }
+      if $ruby_enable   { include '::mysql::bindings::ruby' }
+    }
+  }
+
+  if $client_dev    { include '::mysql::bindings::client_dev' }
+  if $daemon_dev    { include '::mysql::bindings::daemon_dev' }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/manifests/bindings/client_dev.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,15 @@
+# Private class
+class mysql::bindings::client_dev {
+
+  if $mysql::bindings::client_dev_package_name {
+    package { 'mysql-client_dev':
+      ensure          => $mysql::bindings::client_dev_package_ensure,
+      install_options => $mysql::bindings::install_options,
+      name            => $mysql::bindings::client_dev_package_name,
+      provider        => $mysql::bindings::client_dev_package_provider,
+    }
+  } else {
+    warning("No MySQL client development package configured for ${::operatingsystem}.")
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/manifests/bindings/daemon_dev.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,15 @@
+# Private class
+class mysql::bindings::daemon_dev {
+
+  if $mysql::bindings::daemon_dev_package_name {
+    package { 'mysql-daemon_dev':
+      ensure          => $mysql::bindings::daemon_dev_package_ensure,
+      install_options => $mysql::bindings::install_options,
+      name            => $mysql::bindings::daemon_dev_package_name,
+      provider        => $mysql::bindings::daemon_dev_package_provider,
+    }
+  } else {
+    warning("No MySQL daemon development package configured for ${::operatingsystem}.")
+  }
+
+}
--- a/modules/mysql/manifests/bindings/java.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/bindings/java.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,9 +2,10 @@
 class mysql::bindings::java {
 
   package { 'mysql-connector-java':
-    ensure   => $mysql::bindings::java_package_ensure,
-    name     => $mysql::bindings::java_package_name,
-    provider => $mysql::bindings::java_package_provider,
+    ensure          => $mysql::bindings::java_package_ensure,
+    install_options => $mysql::bindings::install_options,
+    name            => $mysql::bindings::java_package_name,
+    provider        => $mysql::bindings::java_package_provider,
   }
 
 }
--- a/modules/mysql/manifests/bindings/perl.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/bindings/perl.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,9 +2,10 @@
 class mysql::bindings::perl {
 
   package{ 'perl_mysql':
-    ensure   => $mysql::bindings::perl_package_ensure,
-    name     => $mysql::bindings::perl_package_name,
-    provider => $mysql::bindings::perl_package_provider,
+    ensure          => $mysql::bindings::perl_package_ensure,
+    install_options => $mysql::bindings::install_options,
+    name            => $mysql::bindings::perl_package_name,
+    provider        => $mysql::bindings::perl_package_provider,
   }
 
 }
--- a/modules/mysql/manifests/bindings/php.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/bindings/php.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,9 +2,10 @@
 class mysql::bindings::php {
 
   package { 'php-mysql':
-    ensure   => $mysql::bindings::php_package_ensure,
-    name     => $mysql::bindings::php_package_name,
-    provider => $mysql::bindings::php_package_provider,
+    ensure          => $mysql::bindings::php_package_ensure,
+    install_options => $mysql::bindings::install_options,
+    name            => $mysql::bindings::php_package_name,
+    provider        => $mysql::bindings::php_package_provider,
   }
 
 }
--- a/modules/mysql/manifests/bindings/python.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/bindings/python.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,9 +2,10 @@
 class mysql::bindings::python {
 
   package { 'python-mysqldb':
-    ensure   => $mysql::bindings::python_package_ensure,
-    name     => $mysql::bindings::python_package_name,
-    provider => $mysql::bindings::python_package_provider,
+    ensure          => $mysql::bindings::python_package_ensure,
+    install_options => $mysql::bindings::install_options,
+    name            => $mysql::bindings::python_package_name,
+    provider        => $mysql::bindings::python_package_provider,
   }
 
 }
--- a/modules/mysql/manifests/bindings/ruby.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/bindings/ruby.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,9 +2,10 @@
 class mysql::bindings::ruby {
 
   package{ 'ruby_mysql':
-    ensure   => $mysql::bindings::ruby_package_ensure,
-    name     => $mysql::bindings::ruby_package_name,
-    provider => $mysql::bindings::ruby_package_provider,
+    ensure          => $mysql::bindings::ruby_package_ensure,
+    install_options => $mysql::bindings::install_options,
+    name            => $mysql::bindings::ruby_package_name,
+    provider        => $mysql::bindings::ruby_package_provider,
   }
 
 }
--- a/modules/mysql/manifests/client.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/client.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,7 +1,9 @@
 #
 class mysql::client (
   $bindings_enable = $mysql::params::bindings_enable,
+  $install_options = undef,
   $package_ensure  = $mysql::params::client_package_ensure,
+  $package_manage  = $mysql::params::client_package_manage,
   $package_name    = $mysql::params::client_package_name,
 ) inherits mysql::params {
 
--- a/modules/mysql/manifests/client/install.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/client/install.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,8 +1,13 @@
 class mysql::client::install {
 
-  package { 'mysql_client':
-    ensure => $mysql::client::package_ensure,
-    name   => $mysql::client::package_name,
+  if $mysql::client::package_manage {
+
+    package { 'mysql_client':
+      ensure          => $mysql::client::package_ensure,
+      install_options => $mysql::client::install_options,
+      name            => $mysql::client::package_name,
+    }
+
   }
 
 }
--- a/modules/mysql/manifests/db.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/db.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,22 +2,33 @@
 define mysql::db (
   $user,
   $password,
-  $dbname      = $name,
-  $charset     = 'utf8',
-  $collate     = 'utf8_general_ci',
-  $host        = 'localhost',
-  $grant       = 'ALL',
-  $sql         = '',
-  $enforce_sql = false,
-  $ensure      = 'present'
+  $dbname         = $name,
+  $charset        = 'utf8',
+  $collate        = 'utf8_general_ci',
+  $host           = 'localhost',
+  $grant          = 'ALL',
+  $sql            = undef,
+  $enforce_sql    = false,
+  $ensure         = 'present',
+  $import_timeout = 300,
 ) {
   #input validation
   validate_re($ensure, '^(present|absent)$',
   "${ensure} is not supported for ensure. Allowed values are 'present' and 'absent'.")
   $table = "${dbname}.*"
 
+  if !(is_array($sql) or is_string($sql)) {
+    fail('$sql must be either a string or an array.')
+  }
+
+  $sql_inputs = join([$sql], ' ')
+
   include '::mysql::client'
 
+  anchor{"mysql::db_${name}::begin": }->
+  Class['::mysql::client']->
+  anchor{"mysql::db_${name}::end": }
+
   $db_resource = {
     ensure   => $ensure,
     charset  => $charset,
@@ -48,12 +59,14 @@
 
     if $sql {
       exec{ "${dbname}-import":
-        command     => "/usr/bin/mysql ${dbname} < ${sql}",
+        command     => "cat ${sql_inputs} | mysql ${dbname}",
         logoutput   => true,
         environment => "HOME=${::root_home}",
         refreshonly => $refresh,
+        path        => '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin',
         require     => Mysql_grant["${user}@${host}/${table}"],
         subscribe   => Mysql_database[$dbname],
+        timeout     => $import_timeout,
       }
     }
   }
--- a/modules/mysql/manifests/init.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-#
-class mysql(
-  $basedir               = '',
-  $bind_address          = '',
-  $client_package_ensure = '',
-  $client_package_name   = '',
-  $config_file           = '',
-  $config_template       = '',
-  $datadir               = '',
-  $default_engine        = '',
-  $etc_root_password     = '',
-  $log_error             = '',
-  $manage_config_file    = '',
-  $manage_service        = '',
-  $max_allowed_packet    = '',
-  $max_connections       = '',
-  $old_root_password     = '',
-  $package_ensure        = '',
-  $php_package_name      = '',
-  $pidfile               = '',
-  $port                  = '',
-  $purge_conf_dir        = '',
-  $restart               = '',
-  $root_group            = '',
-  $root_password         = '',
-  $server_package_name   = '',
-  $service_name          = '',
-  $service_provider      = '',
-  $socket                = '',
-  $ssl                   = '',
-  $ssl_ca                = '',
-  $ssl_cert              = '',
-  $ssl_key               = '',
-  $tmpdir                = '',
-  $attempt_compatibility_mode = false,
-) {
-
-  if $attempt_compatibility_mode {
-    notify { "An attempt has been made below to automatically apply your custom
-    settings to mysql::server. Please verify this works in a safe test
-    environment.": }
-
-    $override_options = {
-      'client'                 => {
-        'port'                 => $port,
-        'socket'               => $socket
-      },
-      'mysqld_safe'            => {
-        'log_error'            => $log_error,
-        'socket'               => $socket,
-      },
-      'mysqld'               => {
-        'basedir'            => $basedir,
-        'bind_address'       => $bind_address,
-        'datadir'            => $datadir,
-        'log_error'          => $log_error,
-        'max_allowed_packet' => $max_allowed_packet,
-        'max_connections'    => $max_connections,
-        'pid_file'           => $pidfile,
-        'port'               => $port,
-        'socket'             => $socket,
-        'ssl-ca'             => $ssl_ca,
-        'ssl-cert'           => $ssl_cert,
-        'ssl-key'            => $ssl_key,
-        'tmpdir'             => $tmpdir,
-      },
-      'mysqldump'              => {
-        'max_allowed_packet'  => $max_allowed_packet,
-      },
-      'config_file'          => $config_file,
-      'etc_root_password'    => $etc_root_password,
-      'manage_config_file'   => $manage_config_file,
-      'old_root_password'    => $old_root_password,
-      'purge_conf_dir'       => $purge_conf_dir,
-      'restart'              => $restart,
-      'root_group'           => $root_group,
-      'root_password'        => $root_password,
-      'service_name'         => $service_name,
-      'ssl'                  => $ssl
-    }
-    $filtered_options = mysql_strip_hash($override_options)
-    validate_hash($filtered_options)
-    notify { $filtered_options: }
-    class { 'mysql::server':
-      override_options => $filtered_options,
-    }
-
-  } else {
-    fail("ERROR:  This class has been deprecated and the functionality moved
-    into mysql::server.  If you run mysql::server without correctly calling
-    mysql:: server with the new override_options hash syntax you will revert
-    your MySQL to the stock settings.  Do not proceed without removing this
-    class and using mysql::server correctly.
-
-    If you are brave you may set attempt_compatibility_mode in this class which
-    attempts to automap the previous settings to appropriate calls to
-    mysql::server")
-  }
-
-}
--- a/modules/mysql/manifests/params.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/params.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -7,34 +7,43 @@
   $restart                = false
   $root_password          = 'UNSET'
   $server_package_ensure  = 'present'
+  $server_package_manage  = true
   $server_service_manage  = true
   $server_service_enabled = true
+  $client_package_ensure  = 'present'
+  $client_package_manage  = true
+  $create_root_user       = true
+  $create_root_my_cnf     = true
   # mysql::bindings
-  $bindings_enable         = false
-  $java_package_ensure     = 'present'
-  $java_package_provider   = undef
-  $perl_package_ensure     = 'present'
-  $perl_package_provider   = undef
-  $php_package_ensure      = 'present'
-  $php_package_provider    = undef
-  $python_package_ensure   = 'present'
-  $python_package_provider = undef
-  $ruby_package_ensure     = 'present'
-  $ruby_package_provider   = undef
+  $bindings_enable             = false
+  $java_package_ensure         = 'present'
+  $java_package_provider       = undef
+  $perl_package_ensure         = 'present'
+  $perl_package_provider       = undef
+  $php_package_ensure          = 'present'
+  $php_package_provider        = undef
+  $python_package_ensure       = 'present'
+  $python_package_provider     = undef
+  $ruby_package_ensure         = 'present'
+  $ruby_package_provider       = undef
+  $client_dev_package_ensure   = 'present'
+  $client_dev_package_provider = undef
+  $daemon_dev_package_ensure   = 'present'
+  $daemon_dev_package_provider = undef
 
 
   case $::osfamily {
     'RedHat': {
       case $::operatingsystem {
         'Fedora': {
-          if is_integer($::operatingsystemrelease) and $::operatingsystemrelease >= 19 or $::operatingsystemrelease == "Rawhide" {
+          if versioncmp($::operatingsystemrelease, '19') >= 0 or $::operatingsystemrelease == 'Rawhide' {
             $provider = 'mariadb'
           } else {
             $provider = 'mysql'
           }
         }
-        'RedHat': {
-          if $::operatingsystemrelease >= 7 {
+        /^(RedHat|CentOS|Scientific|OracleLinux)$/: {
+          if versioncmp($::operatingsystemmajrelease, '7') >= 0 {
             $provider = 'mariadb'
           } else {
             $provider = 'mysql'
@@ -50,7 +59,9 @@
         $server_package_name = 'mariadb-server'
         $server_service_name = 'mariadb'
         $log_error           = '/var/log/mariadb/mariadb.log'
-        $config_file         = '/etc/my.cnf'
+        $config_file         = '/etc/my.cnf.d/server.cnf'
+        # mariadb package by default has !includedir set in my.cnf to /etc/my.cnf.d
+        $includedir          = undef
         $pidfile             = '/var/run/mariadb/mariadb.pid'
       } else {
         $client_package_name = 'mysql'
@@ -58,36 +69,52 @@
         $server_service_name = 'mysqld'
         $log_error           = '/var/log/mysqld.log'
         $config_file         = '/etc/my.cnf'
+        $includedir          = '/etc/my.cnf.d'
         $pidfile             = '/var/run/mysqld/mysqld.pid'
       }
 
-      $basedir             = '/usr'
-      $datadir             = '/var/lib/mysql'
-      $root_group          = 'root'
-      $socket              = '/var/lib/mysql/mysql.sock'
-      $ssl_ca              = '/etc/mysql/cacert.pem'
-      $ssl_cert            = '/etc/mysql/server-cert.pem'
-      $ssl_key             = '/etc/mysql/server-key.pem'
-      $tmpdir              = '/tmp'
+      $basedir                 = '/usr'
+      $datadir                 = '/var/lib/mysql'
+      $root_group              = 'root'
+      $socket                  = '/var/lib/mysql/mysql.sock'
+      $ssl_ca                  = '/etc/mysql/cacert.pem'
+      $ssl_cert                = '/etc/mysql/server-cert.pem'
+      $ssl_key                 = '/etc/mysql/server-key.pem'
+      $tmpdir                  = '/tmp'
       # mysql::bindings
-      $java_package_name   = 'mysql-connector-java'
-      $perl_package_name   = 'perl-DBD-MySQL'
-      $php_package_name    = 'php-mysql'
-      $python_package_name = 'MySQL-python'
-      $ruby_package_name   = 'ruby-mysql'
+      $java_package_name       = 'mysql-connector-java'
+      $perl_package_name       = 'perl-DBD-MySQL'
+      $php_package_name        = 'php-mysql'
+      $python_package_name     = 'MySQL-python'
+      $ruby_package_name       = 'ruby-mysql'
+      $client_dev_package_name = undef
+      $daemon_dev_package_name = 'mysql-devel'
     }
 
     'Suse': {
-      $client_package_name   = $::operatingsystem ? {
-        /OpenSuSE/           => 'mysql-community-server-client',
-        /(SLES|SLED)/        => 'mysql-client',
+      case $::operatingsystem {
+        'OpenSuSE': {
+          $client_package_name = 'mysql-community-server-client'
+          $server_package_name = 'mysql-community-server'
+          $basedir             = '/usr'
+        }
+        'SLES','SLED': {
+          if versioncmp($::operatingsystemrelease, '12') >= 0 {
+            $client_package_name = 'mariadb-client'
+            $server_package_name = 'mariadb'
+            $basedir             = undef
+          } else {
+            $client_package_name = 'mysql-client'
+            $server_package_name = 'mysql'
+            $basedir             = '/usr'
+          }
+        }
+        default: {
+          fail("Unsupported platform: puppetlabs-${module_name} currently doesn't support ${::operatingsystem}")
+        }
       }
-      $server_package_name   = $::operatingsystem ? {
-        /OpenSuSE/           => 'mysql-community-server',
-        /(SLES|SLED)/        => 'mysql',
-      }
-      $basedir             = '/usr'
       $config_file         = '/etc/my.cnf'
+      $includedir          = '/etc/my.cnf.d'
       $datadir             = '/var/lib/mysql'
       $log_error           = $::operatingsystem ? {
         /OpenSuSE/         => '/var/log/mysql/mysqld.log',
@@ -116,24 +143,27 @@
         /OpenSuSE/         => 'rubygem-mysql',
         /(SLES|SLED)/      => 'ruby-mysql',
       }
+      $client_dev_package_name = 'libmysqlclient-devel'
+      $daemon_dev_package_name = 'mysql-devel'
     }
 
     'Debian': {
-      $client_package_name = 'mysql-client'
-      $server_package_name = 'mysql-server'
+      $client_package_name     = 'mysql-client'
+      $server_package_name     = 'mysql-server'
 
-      $basedir             = '/usr'
-      $config_file         = '/etc/mysql/my.cnf'
-      $datadir             = '/var/lib/mysql'
-      $log_error           = '/var/log/mysql/error.log'
-      $pidfile             = '/var/run/mysqld/mysqld.pid'
-      $root_group          = 'root'
-      $server_service_name = 'mysql'
-      $socket              = '/var/run/mysqld/mysqld.sock'
-      $ssl_ca              = '/etc/mysql/cacert.pem'
-      $ssl_cert            = '/etc/mysql/server-cert.pem'
-      $ssl_key             = '/etc/mysql/server-key.pem'
-      $tmpdir              = '/tmp'
+      $basedir                 = '/usr'
+      $config_file             = '/etc/mysql/my.cnf'
+      $includedir              = '/etc/mysql/conf.d'
+      $datadir                 = '/var/lib/mysql'
+      $log_error               = '/var/log/mysql/error.log'
+      $pidfile                 = '/var/run/mysqld/mysqld.pid'
+      $root_group              = 'root'
+      $server_service_name     = 'mysql'
+      $socket                  = '/var/run/mysqld/mysqld.sock'
+      $ssl_ca                  = '/etc/mysql/cacert.pem'
+      $ssl_cert                = '/etc/mysql/server-cert.pem'
+      $ssl_key                 = '/etc/mysql/server-key.pem'
+      $tmpdir                  = '/tmp'
       # mysql::bindings
       $java_package_name   = 'libmysql-java'
       $perl_package_name   = 'libdbd-mysql-perl'
@@ -141,8 +171,57 @@
       $python_package_name = 'python-mysqldb'
       $ruby_package_name   = $::lsbdistcodename ? {
         'trusty'           => 'ruby-mysql',
+        'jessie'           => 'ruby-mysql',
         default            => 'libmysql-ruby',
       }
+      $client_dev_package_name = 'libmysqlclient-dev'
+      $daemon_dev_package_name = 'libmysqld-dev'
+    }
+
+    'Archlinux': {
+      $client_package_name = 'mariadb-clients'
+      $server_package_name = 'mariadb'
+      $basedir             = '/usr'
+      $config_file         = '/etc/mysql/my.cnf'
+      $datadir             = '/var/lib/mysql'
+      $log_error           = '/var/log/mysqld.log'
+      $pidfile             = '/var/run/mysqld/mysqld.pid'
+      $root_group          = 'root'
+      $server_service_name = 'mysqld'
+      $socket              = '/var/lib/mysql/mysql.sock'
+      $ssl_ca              = '/etc/mysql/cacert.pem'
+      $ssl_cert            = '/etc/mysql/server-cert.pem'
+      $ssl_key             = '/etc/mysql/server-key.pem'
+      $tmpdir              = '/tmp'
+      # mysql::bindings
+      $java_package_name   = 'mysql-connector-java'
+      $perl_package_name   = 'perl-dbd-mysql'
+      $php_package_name    = undef
+      $python_package_name = 'mysql-python'
+      $ruby_package_name   = 'mysql-ruby'
+    }
+
+    'Gentoo': {
+      $client_package_name = 'virtual/mysql'
+      $server_package_name = 'virtual/mysql'
+      $basedir             = '/usr'
+      $config_file         = '/etc/mysql/my.cnf'
+      $datadir             = '/var/lib/mysql'
+      $log_error           = '/var/log/mysql/mysqld.err'
+      $pidfile             = '/run/mysqld/mysqld.pid'
+      $root_group          = 'root'
+      $server_service_name = 'mysql'
+      $socket              = '/run/mysqld/mysqld.sock'
+      $ssl_ca              = '/etc/mysql/cacert.pem'
+      $ssl_cert            = '/etc/mysql/server-cert.pem'
+      $ssl_key             = '/etc/mysql/server-key.pem'
+      $tmpdir              = '/tmp'
+      # mysql::bindings
+      $java_package_name   = 'dev-java/jdbc-mysql'
+      $perl_package_name   = 'dev-perl/DBD-mysql'
+      $php_package_name    = undef
+      $python_package_name = 'dev-python/mysql-python'
+      $ruby_package_name   = 'dev-ruby/mysql-ruby'
     }
 
     'FreeBSD': {
@@ -150,6 +229,7 @@
       $server_package_name = 'databases/mysql55-server'
       $basedir             = '/usr/local'
       $config_file         = '/var/db/mysql/my.cnf'
+      $includedir          = '/var/db/mysql/my.cnf.d'
       $datadir             = '/var/db/mysql'
       $log_error           = "/var/db/mysql/${::hostname}.err"
       $pidfile             = '/var/db/mysql/mysql.pid'
@@ -166,6 +246,9 @@
       $php_package_name    = 'php5-mysql'
       $python_package_name = 'databases/py-MySQLdb'
       $ruby_package_name   = 'databases/ruby-mysql'
+      # The libraries installed by these packages are included in client and server packages, no installation required.
+      $client_dev_package_name     = undef
+      $daemon_dev_package_name     = undef
     }
 
     default: {
@@ -175,6 +258,7 @@
           $server_package_name = 'mysql-server'
           $basedir             = '/usr'
           $config_file         = '/etc/my.cnf'
+          $includedir          = '/etc/my.cnf.d'
           $datadir             = '/var/lib/mysql'
           $log_error           = '/var/log/mysqld.log'
           $pidfile             = '/var/run/mysqld/mysqld.pid'
@@ -191,10 +275,13 @@
           $php_package_name    = 'php-mysql'
           $python_package_name = 'MySQL-python'
           $ruby_package_name   = 'ruby-mysql'
+          # The libraries installed by these packages are included in client and server packages, no installation required.
+          $client_dev_package_name     = undef
+          $daemon_dev_package_name     = undef
         }
 
         default: {
-          fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat, Debian, and FreeBSD, or operatingsystem Amazon")
+          fail("Unsupported platform: puppetlabs-${module_name} currently doesn't support ${::osfamily} or ${::operatingsystem}")
         }
       }
     }
@@ -256,4 +343,8 @@
     },
   }
 
+  ## Additional graceful failures
+  if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '4' {
+    fail("Unsupported platform: puppetlabs-${module_name} only supports RedHat 5.0 and beyond")
+  }
 }
--- a/modules/mysql/manifests/server.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,10 +1,13 @@
 # Class: mysql::server:  See README.md for documentation.
 class mysql::server (
   $config_file             = $mysql::params::config_file,
+  $includedir              = $mysql::params::includedir,
+  $install_options         = undef,
   $manage_config_file      = $mysql::params::manage_config_file,
   $old_root_password       = $mysql::params::old_root_password,
   $override_options        = {},
   $package_ensure          = $mysql::params::server_package_ensure,
+  $package_manage          = $mysql::params::server_package_manage,
   $package_name            = $mysql::params::server_package_name,
   $purge_conf_dir          = $mysql::params::purge_conf_dir,
   $remove_default_accounts = false,
@@ -15,6 +18,8 @@
   $service_manage          = $mysql::params::server_service_manage,
   $service_name            = $mysql::params::server_service_name,
   $service_provider        = $mysql::params::server_service_provider,
+  $create_root_user        = $mysql::params::create_root_user,
+  $create_root_my_cnf      = $mysql::params::create_root_my_cnf,
   $users                   = {},
   $grants                  = {},
   $databases               = {},
--- a/modules/mysql/manifests/server/account_security.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/account_security.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,20 +1,36 @@
 class mysql::server::account_security {
   mysql_user {
-    [ "root@${::fqdn}",
-      'root@127.0.0.1',
+    [ 'root@127.0.0.1',
       'root@::1',
-      "@${::fqdn}",
       '@localhost',
       '@%']:
     ensure  => 'absent',
     require => Anchor['mysql::server::end'],
   }
-  if ($::fqdn != $::hostname) {
-    mysql_user { ["root@${::hostname}", "@${::hostname}"]:
+  if ($::fqdn != 'localhost.localdomain') {
+    mysql_user {
+      [ 'root@localhost.localdomain',
+        '@localhost.localdomain']:
       ensure  => 'absent',
       require => Anchor['mysql::server::end'],
     }
   }
+  if ($::fqdn != 'localhost') {
+    mysql_user {
+      [ "root@${::fqdn}",
+        "@${::fqdn}"]:
+      ensure  => 'absent',
+      require => Anchor['mysql::server::end'],
+    }
+  }
+  if ($::fqdn != $::hostname) {
+    if ($::hostname != 'localhost') {
+      mysql_user { ["root@${::hostname}", "@${::hostname}"]:
+        ensure  => 'absent',
+        require => Anchor['mysql::server::end'],
+      }
+    }
+  }
   mysql_database { 'test':
     ensure  => 'absent',
     require => Anchor['mysql::server::end'],
--- a/modules/mysql/manifests/server/backup.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/backup.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,62 +1,43 @@
 # See README.me for usage.
 class mysql::server::backup (
-  $backupuser,
-  $backuppassword,
-  $backupdir,
-  $backupdirmode = '0700',
-  $backupdirowner = 'root',
-  $backupdirgroup = 'root',
-  $backupcompress = true,
-  $backuprotate = 30,
-  $ignore_events = true,
+  $backupuser         = undef,
+  $backuppassword     = undef,
+  $backupdir          = undef,
+  $backupdirmode      = '0700',
+  $backupdirowner     = 'root',
+  $backupdirgroup     = 'root',
+  $backupcompress     = true,
+  $backuprotate       = 30,
+  $ignore_events      = true,
   $delete_before_dump = false,
-  $backupdatabases = [],
-  $file_per_database = false,
-  $ensure = 'present',
-  $time = ['23', '5'],
-  $postscript = false,
-  $execpath   = '/usr/bin:/usr/sbin:/bin:/sbin',
+  $backupdatabases    = [],
+  $file_per_database  = false,
+  $ensure             = 'present',
+  $time               = ['23', '5'],
+  $postscript         = false,
+  $execpath           = '/usr/bin:/usr/sbin:/bin:/sbin',
+  $provider           = 'mysqldump',
 ) {
 
-  mysql_user { "${backupuser}@localhost":
-    ensure        => $ensure,
-    password_hash => mysql_password($backuppassword),
-    provider      => 'mysql',
-    require       => Class['mysql::server::root_password'],
-  }
-
-  mysql_grant { "${backupuser}@localhost/*.*":
-    ensure     => present,
-    user       => "${backupuser}@localhost",
-    table      => '*.*',
-    privileges => [ 'SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS' ],
-    require    => Mysql_user["${backupuser}@localhost"],
-  }
-
-  cron { 'mysql-backup':
-    ensure  => $ensure,
-    command => '/usr/local/sbin/mysqlbackup.sh',
-    user    => 'root',
-    hour    => $time[0],
-    minute  => $time[1],
-    require => File['mysqlbackup.sh'],
-  }
-
-  file { 'mysqlbackup.sh':
-    ensure  => $ensure,
-    path    => '/usr/local/sbin/mysqlbackup.sh',
-    mode    => '0700',
-    owner   => 'root',
-    group   => 'root',
-    content => template('mysql/mysqlbackup.sh.erb'),
-  }
-
-  file { 'mysqlbackupdir':
-    ensure => 'directory',
-    path   => $backupdir,
-    mode   => $backupdirmode,
-    owner  => $backupdirowner,
-    group  => $backupdirgroup,
-  }
+  create_resources('class', {
+    "mysql::backup::${provider}" => {
+      'backupuser'         => $backupuser,
+      'backuppassword'     => $backuppassword,
+      'backupdir'          => $backupdir,
+      'backupdirmode'      => $backupdirmode,
+      'backupdirowner'     => $backupdirowner,
+      'backupdirgroup'     => $backupdirgroup,
+      'backupcompress'     => $backupcompress,
+      'backuprotate'       => $backuprotate,
+      'ignore_events'      => $ignore_events,
+      'delete_before_dump' => $delete_before_dump,
+      'backupdatabases'    => $backupdatabases,
+      'file_per_database'  => $file_per_database,
+      'ensure'             => $ensure,
+      'time'               => $time,
+      'postscript'         => $postscript,
+      'execpath'           => $execpath,
+    }
+  })
 
 }
--- a/modules/mysql/manifests/server/config.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/config.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -2,6 +2,7 @@
 class mysql::server::config {
 
   $options = $mysql::server::options
+  $includedir = $mysql::server::includedir
 
   File {
     owner  => 'root',
@@ -9,20 +10,34 @@
     mode   => '0400',
   }
 
-  file { '/etc/mysql':
-    ensure => directory,
-    mode   => '0755',
+  if $includedir and $includedir != '' {
+    file { $includedir:
+      ensure  => directory,
+      mode    => '0755',
+      recurse => $mysql::server::purge_conf_dir,
+      purge   => $mysql::server::purge_conf_dir,
+    }
   }
 
-  file { '/etc/mysql/conf.d':
-    ensure  => directory,
-    mode    => '0755',
-    recurse => $mysql::server::purge_conf_dir,
-    purge   => $mysql::server::purge_conf_dir,
+  $logbin = pick($options['mysqld']['log-bin'], $options['mysqld']['log_bin'], false)
+
+  if $logbin {
+    $logbindir = mysql_dirname($logbin)
+
+    #Stop puppet from managing directory if just a filename/prefix is specified
+    if $logbindir != '.' {
+      file { $logbindir:
+        ensure => directory,
+        mode   => '0755',
+        owner  => $options['mysqld']['user'],
+        group  => $options['mysqld']['user'],
+      }
+    }
   }
 
   if $mysql::server::manage_config_file  {
-    file { $mysql::server::config_file:
+    file { 'mysql-config-file':
+      path    => $mysql::server::config_file,
       content => template('mysql/my.cnf.erb'),
       mode    => '0644',
     }
--- a/modules/mysql/manifests/server/install.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/install.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,9 +1,39 @@
 #
 class mysql::server::install {
 
-  package { 'mysql-server':
-    ensure => $mysql::server::package_ensure,
-    name   => $mysql::server::package_name,
+  if $mysql::server::package_manage {
+
+    package { 'mysql-server':
+      ensure          => $mysql::server::package_ensure,
+      install_options => $mysql::server::install_options,
+      name            => $mysql::server::package_name,
+    }
+
+    # Build the initial databases.
+    $mysqluser = $mysql::server::options['mysqld']['user']
+    $datadir = $mysql::server::options['mysqld']['datadir']
+    $basedir = $mysql::server::options['mysqld']['basedir']
+    $config_file = $mysql::server::config_file
+
+    if $mysql::server::manage_config_file {
+      $install_db_args = "--basedir=${basedir} --defaults-extra-file=${config_file} --datadir=${datadir} --user=${mysqluser}"
+    } else {
+      $install_db_args = "--basedir=${basedir} --datadir=${datadir} --user=${mysqluser}"
+    }
+
+    exec { 'mysql_install_db':
+      command   => "mysql_install_db ${install_db_args}",
+      creates   => "${datadir}/mysql",
+      logoutput => on_failure,
+      path      => '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin',
+      require   => Package['mysql-server'],
+    }
+
+    if $mysql::server::restart {
+      Exec['mysql_install_db'] {
+        notify => Class['mysql::server::service'],
+      }
+    }
   }
 
 }
--- a/modules/mysql/manifests/server/mysqltuner.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/mysqltuner.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,9 +1,31 @@
 #
-class mysql::server::mysqltuner($ensure='present') {
-  # mysql performance tester
-  file { '/usr/local/bin/mysqltuner':
-    ensure  => $ensure,
-    mode    => '0550',
-    source  => 'puppet:///modules/mysql/mysqltuner.pl',
+class mysql::server::mysqltuner(
+  $ensure  = 'present',
+  $version = 'v1.3.0',
+  $source  = undef,
+) {
+
+  if $source {
+    $_version = $source
+    $_source  = $source
+  } else {
+    $_version = $version
+    $_source  = "https://github.com/major/MySQLTuner-perl/raw/${version}/mysqltuner.pl"
+  }
+
+  if $ensure == 'present' {
+    class { 'staging': }
+    staging::file { "mysqltuner-${_version}":
+      source => $_source,
+    }
+    file { '/usr/local/bin/mysqltuner':
+      ensure => $ensure,
+      mode   => '0550',
+      source => "${::staging::path}/mysql/mysqltuner-${_version}",
+    }
+  } else {
+    file { '/usr/local/bin/mysqltuner':
+      ensure => $ensure,
+    }
   }
 }
--- a/modules/mysql/manifests/server/root_password.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/root_password.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -4,17 +4,21 @@
   $options = $mysql::server::options
 
   # manage root password if it is set
-  if $mysql::server::root_password != 'UNSET' {
+  if $mysql::server::create_root_user == true and $mysql::server::root_password != 'UNSET' {
     mysql_user { 'root@localhost':
       ensure        => present,
       password_hash => mysql_password($mysql::server::root_password),
     }
+  }
 
+  if $mysql::server::create_root_my_cnf == true and $mysql::server::root_password != 'UNSET' {
     file { "${::root_home}/.my.cnf":
       content => template('mysql/my.cnf.pass.erb'),
       owner   => 'root',
       mode    => '0600',
-      require => Mysql_user['root@localhost'],
+    }
+    if $mysql::server::create_root_user == true {
+      Mysql_user['root@localhost'] -> File["${::root_home}/.my.cnf"]
     }
   }
 
--- a/modules/mysql/manifests/server/service.pp	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/manifests/server/service.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -1,23 +1,27 @@
 #
 class mysql::server::service {
-
-  if $mysql::server::real_service_enabled {
-    $service_ensure = 'running'
-  } else {
-    $service_ensure = 'stopped'
-  }
+  $options = $mysql::server::options
 
   if $mysql::server::real_service_manage {
-    file { $mysql::params::log_error:
-      owner => 'mysql',
-      group => 'mysql',
-    }
-    service { 'mysqld':
-      ensure   => $service_ensure,
-      name     => $mysql::server::service_name,
-      enable   => $mysql::server::real_service_enabled,
-      provider => $mysql::server::service_provider,
+    if $mysql::server::real_service_enabled {
+      $service_ensure = 'running'
+    } else {
+      $service_ensure = 'stopped'
     }
   }
 
+  file { $options['mysqld']['log-error']:
+    ensure => present,
+    owner  => 'mysql',
+    group  => 'mysql',
+  }
+
+  service { 'mysqld':
+    ensure   => $service_ensure,
+    name     => $mysql::server::service_name,
+    enable   => $mysql::server::real_service_enabled,
+    provider => $mysql::server::service_provider,
+    require  => [ File['mysql-config-file'], Package['mysql-server'] ]
+  }
+
 }
--- a/modules/mysql/metadata.json	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/metadata.json	Mon Mar 09 01:34:59 2015 +0000
@@ -1,12 +1,12 @@
 {
   "name": "puppetlabs-mysql",
-  "version": "2.3.1",
+  "version": "3.3.0",
   "author": "Puppet Labs",
-  "summary": "Mysql module",
-  "license": "Apache 2.0",
+  "summary": "Installs, configures, and manages the MySQL service.",
+  "license": "Apache-2.0",
   "source": "git://github.com/puppetlabs/puppetlabs-mysql.git",
   "project_page": "http://github.com/puppetlabs/puppetlabs-mysql",
-  "issues_url": "https://github.com/puppetlabs/puppetlabs-mysql/issues",
+  "issues_url": "https://tickets.puppetlabs.com/browse/MODULES",
   "operatingsystem_support": [
     {
       "operatingsystem": "RedHat",
@@ -43,7 +43,9 @@
     {
       "operatingsystem": "SLES",
       "operatingsystemrelease": [
-        "11 SP1"
+        "10 SP4",
+        "11 SP1",
+        "12"
       ]
     },
     {
@@ -65,7 +67,7 @@
   "requirements": [
     {
       "name": "pe",
-      "version_requirement": ">= 3.2.0 < 3.4.0"
+      "version_requirement": "3.x"
     },
     {
       "name": "puppet",
@@ -74,9 +76,7 @@
   ],
   "description": "Mysql module",
   "dependencies": [
-    {
-      "name": "puppetlabs/stdlib",
-      "version_requirement": ">= 3.2.0"
-    }
+    {"name":"puppetlabs/stdlib","version_requirement":">= 3.2.0 < 5.0.0"},
+    {"name":"nanliu/staging","version_requirement":"1.x"}
   ]
 }
--- a/modules/mysql/spec/acceptance/mysql_account_delete_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-require 'spec_helper_acceptance'
-
-describe 'mysql::server::account_security class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  describe 'running puppet code' do
-    it 'should work with no errors' do
-      pp = <<-EOS
-        class { 'mysql::server': remove_default_accounts => true }
-      EOS
-
-      # Run it twice and test for idempotency
-      apply_manifest(pp, :catch_failures => true)
-      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-    end
-
-    describe 'accounts' do
-      it 'should delete accounts' do
-        shell("mysql -e 'show grants for root@127.0.0.1;'", :acceptable_exit_codes => 1)
-      end
-
-      it 'should delete databases' do
-        shell("mysql -e 'show databases;' |grep test", :acceptable_exit_codes => 1)
-      end
-    end
-  end
-end
--- a/modules/mysql/spec/acceptance/mysql_backup_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/mysql_backup_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,6 +1,6 @@
 require 'spec_helper_acceptance'
 
-describe 'mysql::server::backup class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
+describe 'mysql::server::backup class' do
   context 'should work with no errors' do
     it 'when configuring mysql backups' do
       pp = <<-EOS
@@ -13,10 +13,6 @@
           password => 'secret',
         }
 
-        package { 'bzip2':
-          ensure => present,
-        }
-
         class { 'mysql::server::backup':
           backupuser     => 'myuser',
           backuppassword => 'mypassword',
@@ -29,7 +25,6 @@
             'touch /var/tmp/mysqlbackups.done',
           ],
           execpath      => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin',
-          require       => Package['bzip2'],
         }
       EOS
 
@@ -66,4 +61,72 @@
       end
     end
   end
+
+  context 'with one file per database' do
+    context 'should work with no errors' do
+      it 'when configuring mysql backups' do
+        pp = <<-EOS
+          class { 'mysql::server': root_password => 'password' }
+          mysql::db { [
+            'backup1',
+            'backup2'
+          ]:
+            user     => 'backup',
+            password => 'secret',
+          }
+
+          class { 'mysql::server::backup':
+            backupuser        => 'myuser',
+            backuppassword    => 'mypassword',
+            backupdir         => '/tmp/backups',
+            backupcompress    => true,
+            file_per_database => true,
+            postscript        => [
+              'rm -rf /var/tmp/mysqlbackups',
+              'rm -f /var/tmp/mysqlbackups.done',
+              'cp -r /tmp/backups /var/tmp/mysqlbackups',
+              'touch /var/tmp/mysqlbackups.done',
+            ],
+            execpath          => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+        apply_manifest(pp, :catch_failures => true)
+      end
+    end
+
+    describe 'mysqlbackup.sh' do
+      it 'should run mysqlbackup.sh with no errors without root credentials' do
+        shell("HOME=/tmp/dontreadrootcredentials /usr/local/sbin/mysqlbackup.sh") do |r|
+          expect(r.stderr).to eq("")
+        end
+      end
+
+      it 'should create one file per database' do
+        ['backup1', 'backup2'].each do |database|
+          shell("ls -l /tmp/backups/mysql_backup_#{database}_*-*.sql.bz2 | wc -l") do |r|
+            expect(r.stdout).to match(/1/)
+            expect(r.exit_code).to be_zero
+          end
+        end
+      end
+
+      context 'should create one file per database per run' do
+        it 'executes mysqlbackup.sh a second time' do
+          shell('sleep 1')
+          shell('HOME=/tmp/dontreadrootcredentials /usr/local/sbin/mysqlbackup.sh')
+        end
+
+        it 'has one file per database per run' do
+          ['backup1', 'backup2'].each do |database|
+            shell("ls -l /tmp/backups/mysql_backup_#{database}_*-*.sql.bz2 | wc -l") do |r|
+              expect(r.stdout).to match(/2/)
+              expect(r.exit_code).to be_zero
+            end
+          end
+        end
+      end
+    end
+  end
 end
--- a/modules/mysql/spec/acceptance/mysql_bindings_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-require 'spec_helper_acceptance'
-
-osfamily = fact('osfamily')
-operatingsystem = fact('operatingsystem')
-ruby_package_provider = 'undef'
-
-case osfamily
-when 'RedHat'
-  java_package   = 'mysql-connector-java'
-  perl_package   = 'perl-DBD-MySQL'
-  php_package    = 'php-mysql'
-  python_package = 'MySQL-python'
-  ruby_package   = 'ruby-mysql'
-  if fact('operatingsystemmajrelease') == '7'
-    ruby_package_provider = 'gem'
-  end
-when 'Suse'
-  java_package   = 'mysql-connector-java'
-  perl_package   = 'perl-DBD-mysql'
-  php_package    = 'apache2-mod_php53'
-  python_package = 'python-mysql'
-  case operatingsystem
-  when /OpenSuSE/
-    ruby_package = 'rubygem-mysql'
-  when /(SLES|SLED)/
-    ruby_package = 'ruby-mysql'
-  end
-when 'Debian'
-  java_package = 'libmysql-java'
-  perl_package     = 'libdbd-mysql-perl'
-  php_package      = 'php5-mysql'
-  python_package   = 'python-mysqldb'
-  if fact('lsbdistcodename') == 'trusty'
-    ruby_package   = 'ruby-mysql'
-  else
-    ruby_package   = 'libmysql-ruby'
-  end
-when 'FreeBSD'
-  java_package = 'databases/mysql-connector-java'
-  perl_package   = 'p5-DBD-mysql'
-  php_package    = 'php5-mysql'
-  python_package = 'databases/py-MySQLdb'
-  ruby_package   = 'ruby-mysql'
-else
-  case operatingsystem
-  when 'Amazon'
-    java_package = 'mysql-connector-java'
-    perl_package   = 'perl-DBD-MySQL'
-    php_package    = 'php5-mysql'
-    python_package = 'MySQL-python'
-    ruby_package   = 'ruby-mysql'
-  end
-end
-
-describe 'mysql::bindings class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-
-  if fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') =~ /(5|6)/
-    it 'adds epel' do
-      pp = "class { 'epel': }"
-      apply_manifest(pp, :catch_failures => true)
-    end
-  end
-
-  describe 'running puppet code' do
-    it 'should work with no errors' do
-      pp = <<-EOS
-        class { 'mysql::bindings': }
-      EOS
-
-      # Run it twice and test for idempotency
-      apply_manifest(pp, :catch_failures => true)
-      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-    end
-  end
-
-  describe 'all parameters' do
-    it 'should work with no errors' do
-      pp = <<-EOS
-        class { 'mysql::bindings':
-          java_enable             => true,
-          perl_enable             => true,
-          php_enable              => true,
-          python_enable           => true,
-          ruby_enable             => true,
-          java_package_ensure     => present,
-          perl_package_ensure     => present,
-          php_package_ensure      => present,
-          python_package_ensure   => present,
-          ruby_package_ensure     => present,
-          java_package_name       => #{java_package},
-          perl_package_name       => #{perl_package},
-          php_package_name        => #{php_package},
-          python_package_name     => #{python_package},
-          ruby_package_name       => #{ruby_package},
-          java_package_provider   => undef,
-          perl_package_provider   => undef,
-          php_package_provider    => undef,
-          python_package_provider => undef,
-          ruby_package_provider   => #{ruby_package_provider},
-        }
-      EOS
-
-      # Run it twice and test for idempotency
-      apply_manifest(pp, :catch_failures => true)
-      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-    end
-
-    describe package(java_package) do
-      it { should be_installed }
-    end
-
-    describe package(perl_package) do
-      it { should be_installed }
-    end
-
-    # This package is not available out of the box and adding in other repos
-    # is a bit much for the scope of this test.
-    unless fact('osfamily') == 'RedHat'
-      describe package(php_package) do
-        it { should be_installed }
-      end
-    end
-
-    describe package(python_package) do
-      it { should be_installed }
-    end
-
-    # ruby-mysql is installed via gem on RHEL7, be_installed doesn't know how to check that
-    if fact('osfamily') == 'RedHat' && fact('operatingsystemmajrelease') == '7'
-      describe package(ruby_package) do
-        it { should_not be_installed }
-      end
-    else
-      describe package(ruby_package) do
-        it { should be_installed }
-      end
-    end
-  end
-end
--- a/modules/mysql/spec/acceptance/mysql_db_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/mysql_db_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,6 +1,6 @@
 require 'spec_helper_acceptance'
 
-describe 'mysql::db define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
+describe 'mysql::db define' do
   describe 'creating a database' do
     # Using puppet_apply as a helper
     it 'should work with no errors' do
@@ -55,7 +55,7 @@
         mysql::db { 'spec1':
           user     => 'root1',
           password => 'password',
-	  dbname   => 'realdb',
+          dbname   => 'realdb',
         }
       EOS
 
--- a/modules/mysql/spec/acceptance/mysql_server_config_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-require 'spec_helper_acceptance'
-
-describe 'config location', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  it 'creates the file elsewhere' do
-    pp = <<-EOS
-        class { 'mysql::server':
-          config_file => '/etc/testmy.cnf',
-        }
-    EOS
-    apply_manifest(pp, :catch_failures => true)
-  end
-
-  describe file('/etc/testmy.cnf') do
-    it { should be_file }
-  end
-end
-
-describe 'manage_config_file', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  it 'wont reset the location of my.cnf' do
-    pp = <<-EOS
-      class { 'mysql::server':
-        config_file        => '/etc/my.cnf',
-        manage_config_file => false,
-        service_manage     => false,
-      }
-    EOS
-    # Make sure this doesn't exist so we can test if puppet
-    # readded it.  It may not exist in the first place on
-    # some platforms.
-    shell('rm /etc/my.cnf', :acceptable_exit_codes => [0,1,2])
-    apply_manifest(pp, :catch_failures => true)
-  end
-
-  describe file('/etc/my.cnf') do
-    it { should_not be_file }
-  end
-end
-
-describe 'resets', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  it 'cleans up' do
-    pp = <<-EOS
-        class { 'mysql::server': }
-    EOS
-    apply_manifest(pp, :catch_failures => true)
-    shell('rm /etc/testmy.cnf')
-  end
-end
--- a/modules/mysql/spec/acceptance/mysql_server_monitor_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-require 'spec_helper_acceptance'
-
-describe 'mysql::server::monitor class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  it 'should work with no errors' do
-    pp = <<-EOS
-      class { 'mysql::server': root_password => 'password' }
-
-      class { 'mysql::server::monitor':
-        mysql_monitor_username => 'monitoruser',
-        mysql_monitor_password => 'monitorpass',
-        mysql_monitor_hostname => 'localhost',
-      }
-    EOS
-
-    apply_manifest(pp, :catch_failures => true)
-    expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-  end
-
-  it 'should run mysqladmin ping with no errors' do
-    expect(shell("mysqladmin -u monitoruser -pmonitorpass -h localhost ping").stdout).to match(/mysqld is alive/)
-  end
-end
--- a/modules/mysql/spec/acceptance/mysql_server_root_password_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-require 'spec_helper_acceptance'
-
-describe 'mysql::server::root_password class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-
-  describe 'reset' do
-    it 'shuts down mysql' do
-      pp = <<-EOS
-      class { 'mysql::server': service_enabled => false }
-      EOS
-
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    it 'deletes the /root/.my.cnf password' do
-      shell('rm -rf /root/.my.cnf')
-    end
-
-    it 'deletes all databases' do
-      case fact('osfamily')
-      when 'RedHat', 'Suse'
-        shell('grep -q datadir /etc/my.cnf && rm -rf `grep datadir /etc/my.cnf | cut -d" " -f 3`/*')
-      when 'Debian'
-        shell('rm -rf `grep datadir /etc/mysql/my.cnf | cut -d" " -f 3`/*')
-        shell('mysql_install_db')
-      end
-    end
-
-    it 'starts up mysql' do
-      pp = <<-EOS
-      class { 'mysql::server': service_enabled => true }
-      EOS
-
-      puppet_apply(pp, :catch_failures => true)
-    end
-  end
-
-  describe 'when unset' do
-    it 'should work' do
-      pp = <<-EOS
-        class { 'mysql::server': root_password => 'test' }
-      EOS
-
-      # Run it twice and test for idempotency
-      apply_manifest(pp, :catch_failures => true)
-      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-    end
-  end
-
-  describe 'when set' do
-    it 'should work' do
-      pp = <<-EOS
-        class { 'mysql::server': root_password => 'new', old_root_password => 'test' }
-      EOS
-
-      # Run it twice and test for idempotency
-      apply_manifest(pp, :catch_failures => true)
-      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-    end
-  end
-end
-
-# Debian relies on a debian-sys-maint@ account to do almost everything.
-# Without recreating this account we can't even stop the service in future
-# tests.
-if fact('osfamily') == 'Debian'
-  describe 'readd debian user' do
-    it 'readds the user' do
-      shell("MYSQL_PASSWORD=`head -5 /etc/mysql/debian.cnf | grep password | cut -d' ' -f 3`; mysql -NBe \"GRANT ALL PRIVILEGES ON *.* to 'debian-sys-maint'@'localhost' IDENTIFIED BY '${MYSQL_PASSWORD}' WITH GRANT OPTION;\"")
-    end
-  end
-end
--- a/modules/mysql/spec/acceptance/mysql_server_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/mysql_server_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,288 +1,55 @@
 require 'spec_helper_acceptance'
 
-describe 'mysql class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  case fact('osfamily')
-  when 'RedHat'
-    if fact('operatingsystemmajrelease') == '7'
-      package_name     = 'mariadb-server'
-      service_name     = 'mariadb'
-      service_provider = 'undef'
-      mycnf            = '/etc/my.cnf'
-    else
-      package_name     = 'mysql-server'
-      service_name     = 'mysqld'
-      service_provider = 'undef'
-      mycnf            = '/etc/my.cnf'
-    end
-  when 'Suse'
-    case fact('operatingsystem')
-    when 'OpenSuSE'
-      package_name     = 'mysql-community-server'
-      service_name     = 'mysql'
-      service_provider = 'undef'
-      mycnf            = '/etc/my.cnf'
-    when 'SLES'
-      package_name     = 'mysql'
-      service_name     = 'mysql'
-      service_provider = 'undef'
-      mycnf            = '/etc/my.cnf'
-    end
-  when 'Debian'
-    package_name     = 'mysql-server'
-    service_name     = 'mysql'
-    service_provider = 'undef'
-    mycnf            = '/etc/mysql/my.cnf'
-  when 'Ubuntu'
-    package_name     = 'mysql-server'
-    service_name     = 'mysql'
-    service_provider = 'upstart'
-    mycnf            = '/etc/mysql/my.cnf'
-  end
+describe 'mysql class' do
 
   describe 'running puppet code' do
     # Using puppet_apply as a helper
     it 'should work with no errors' do
-      pp = <<-EOS
-        class { 'mysql::server': }
-      EOS
-
-      # Run it twice and test for idempotency
-      apply_manifest(pp, :catch_failures => true)
-      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
-    end
-
-    describe package(package_name) do
-      it { should be_installed }
-    end
-
-    describe service(service_name) do
-      it { should be_running }
-      it { should be_enabled }
-    end
-  end
-
-  describe 'mycnf' do
-    it 'should contain sensible values' do
-      pp = <<-EOS
-        class { 'mysql::server': }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe file(mycnf) do
-      it { should contain 'key_buffer_size = 16M' }
-      it { should contain 'max_binlog_size = 100M' }
-      it { should contain 'query_cache_size = 16M' }
-    end
-  end
-
-  describe 'my.cnf changes' do
-    it 'sets values' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          override_options => { 'mysqld' => 
-            { 'key_buffer'       => '32M',
-              'max_binlog_size'  => '200M',
-              'query_cache_size' => '32M',
-            }
-          }  
-        }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe file(mycnf) do
-      it { should contain 'key_buffer = 32M' }
-      it { should contain 'max_binlog_size = 200M' }
-      it { should contain 'query_cache_size = 32M' }
-    end
-  end
-
-  describe 'my.cnf should contain multiple instances of the same option' do
-    it 'sets multiple values' do
+      tmpdir = default.tmpdir('mysql')
       pp = <<-EOS
         class { 'mysql::server':
-          override_options => { 'mysqld' => 
-            { 'replicate-do-db' => ['base1', 'base2', 'base3'], }
-          }  
-        }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe file(mycnf) do
-      it { should contain 'replicate-do-db = base1' }
-      it { should contain 'replicate-do-db = base2' }
-      it { should contain 'replicate-do-db = base3' }
-    end
-  end
-
-  describe 'package_ensure => absent' do
-    it 'uninstalls mysql' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          service_enabled => false,
-          package_ensure  => absent,
-          package_name    => #{package_name}
-        }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe package(package_name) do
-      it { should_not be_installed }
-    end
-  end
-
-  describe 'package_ensure => present' do
-    it 'installs mysql' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          package_ensure => present,
-          package_name   => #{package_name}
-        }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe package(package_name) do
-      it { should be_installed }
-    end
-  end
-
-  describe 'purge_conf_dir' do
-
-    it 'purges the conf dir' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          purge_conf_dir => true
-        }
-      EOS
-      shell('touch /etc/mysql/conf.d/test.conf')
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe file('/etc/mysql/conf.d/test.conf') do
-      it { should_not be_file }
-    end
-  end
-
-  describe 'restart' do
-    it 'restart => true' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          restart          => true,
-          override_options => { 'mysqldump' => { 'default-character-set' => 'UTF-8' } }
+          config_file             => '#{tmpdir}/my.cnf',
+          includedir              => '#{tmpdir}/include',
+          manage_config_file      => 'true',
+          override_options        => { 'mysqld' => { 'key_buffer_size' => '32M' }},
+          package_ensure          => 'present',
+          purge_conf_dir          => 'true',
+          remove_default_accounts => 'true',
+          restart                 => 'true',
+          root_group              => 'root',
+          root_password           => 'test',
+          service_enabled         => 'true',
+          service_manage          => 'true',
+          users                   => {
+            'someuser@localhost' => {
+              ensure                   => 'present',
+              max_connections_per_hour => '0',
+              max_queries_per_hour     => '0',
+              max_updates_per_hour     => '0',
+              max_user_connections     => '0',
+              password_hash            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF',
+            }},
+          grants                  => {
+            'someuser@localhost/somedb.*' => {
+              ensure     => 'present',
+              options    => ['GRANT'],
+              privileges => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
+              table      => 'somedb.*',
+              user       => 'someuser@localhost',
+            },
+          },
+          databases => {
+            'somedb' => {
+              ensure  => 'present',
+              charset => 'utf8',
+            },
+          }
         }
       EOS
 
-      apply_manifest(pp, :catch_failures => true) do |r|
-        expect(r.exit_code).to eq(2)
-        expect(r.stdout).to match(/Scheduling refresh/)
-      end
-    end
-    it 'restart => false' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          restart          => false,
-          override_options => { 'mysqldump' => { 'default-character-set' => 'UTF-16' } }
-        }
-      EOS
-
-      apply_manifest(pp, :catch_failures => true) do |r|
-        expect(r.exit_code).to eq(2)
-        expect(r.stdout).to_not match(/Scheduling refresh/)
-      end
-    end
-  end
-
-  describe 'root_group' do
-    it 'creates a group' do
-      pp = "group { 'test': ensure => present }"
       apply_manifest(pp, :catch_failures => true)
-    end
-
-    it 'sets the group of files' do
-      pp = <<-EOS
-        class { 'mysql::server':
-          root_group => 'test',
-          config_file => '/tmp/mysql_group_test',
-        }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    describe file('/tmp/mysql_group_test') do
-      it { should be_grouped_into 'test' }
-    end
-  end
-
-  describe 'service parameters' do
-    it 'calls all parameters' do
-      pp = <<-EOS
-      class { 'mysql::server':
-        service_enabled  => true,
-        service_manage   => true,
-        service_name     => #{service_name},
-        service_provider => #{service_provider}
-      }
-      EOS
-      apply_manifest(pp, :catch_failures => true)
+      apply_manifest(pp, :catch_changes => true)
     end
   end
+end
 
-  describe 'users, grants, and databases' do
-    it 'are created' do
-      pp = <<-EOS
-      class { 'mysql::server':
-        users => {
-          'zerg1@localhost' => {
-            ensure                   => 'present',
-            max_connections_per_hour => '0',
-            max_queries_per_hour     => '0',
-            max_updates_per_hour     => '0',
-            max_user_connections     => '0',
-            password_hash            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF',
-          }
-        },
-        grants => {
-          'zerg1@localhost/zergdb.*' => {
-            ensure     => 'present',
-            options    => ['GRANT'],
-            privileges => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
-            table      => 'zergdb.*',
-            user       => 'zerg1@localhost',
-          }
-        },
-        databases => {
-          'zergdb' => {
-            ensure  => 'present',
-            charset => 'utf8',
-          }
-        },
-      }
-    EOS
-      apply_manifest(pp, :catch_failures => true)
-    end
-
-    it 'has a user' do
-      shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'zerg1@localhost'\"") do |r|
-        expect(r.stdout).to match(/^1$/)
-        expect(r.stderr).to be_empty
-      end
-    end
-    it 'has a grant' do
-      shell("mysql -NBe \"SHOW GRANTS FOR zerg1@localhost\"") do |r|
-        expect(r.stdout).to match(/GRANT SELECT, INSERT, UPDATE, DELETE ON `zergdb`.* TO 'zerg1'@'localhost' WITH GRANT OPTION/)
-        expect(r.stderr).to be_empty
-      end
-    end
-    it 'has a database' do
-      shell("mysql -NBe \"SHOW DATABASES LIKE 'zergdb'\"") do |r|
-        expect(r.stdout).to match(/zergdb/)
-        expect(r.stderr).to be_empty
-      end
-    end
-  end
-
-end
--- a/modules/mysql/spec/acceptance/nodesets/default.yml	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/nodesets/default.yml	Mon Mar 09 01:34:59 2015 +0000
@@ -2,6 +2,7 @@
   centos-64-x64:
     roles:
       - master
+      - default
     platform: el-6-x86_64
     box : centos-64-x64-vbox4210-nocm
     box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
--- a/modules/mysql/spec/acceptance/types/mysql_database_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/types/mysql_database_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,6 +1,6 @@
 require 'spec_helper_acceptance'
 
-describe 'mysql_database', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
+describe 'mysql_database' do
   describe 'setup' do
     it 'should work with no errors' do
       pp = <<-EOS
--- a/modules/mysql/spec/acceptance/types/mysql_grant_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/types/mysql_grant_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,6 +1,6 @@
 require 'spec_helper_acceptance'
 
-describe 'mysql_grant', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
+describe 'mysql_grant' do
 
   describe 'setup' do
     it 'setup mysql::server' do
@@ -70,6 +70,43 @@
     end
   end
 
+  describe 'adding privileges with special character in name' do
+    it 'should work without errors' do
+      pp = <<-EOS
+        mysql_grant { 'test-2@tester/test.*':
+          ensure     => 'present',
+          table      => 'test.*',
+          user       => 'test-2@tester',
+          privileges => ['SELECT', 'UPDATE'],
+        }
+      EOS
+
+      apply_manifest(pp, :catch_failures => true)
+    end
+
+    it 'should find the user' do
+      shell("mysql -NBe \"SHOW GRANTS FOR 'test-2'@tester\"") do |r|
+        expect(r.stdout).to match(/GRANT SELECT, UPDATE.*TO 'test-2'@'tester'/)
+        expect(r.stderr).to be_empty
+      end
+    end
+  end
+
+  describe 'adding privileges with invalid name' do
+    it 'should fail' do
+      pp = <<-EOS
+        mysql_grant { 'test':
+          ensure     => 'present',
+          table      => 'test.*',
+          user       => 'test2@tester',
+          privileges => ['SELECT', 'UPDATE'],
+        }
+      EOS
+
+      expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/name must match user and table parameters/)
+    end
+  end
+
   describe 'adding option' do
     it 'should work without errors' do
       pp = <<-EOS
@@ -305,4 +342,75 @@
       end
     end
   end
+
+  describe 'grants with skip-name-resolve specified' do
+    it 'setup mysql::server' do
+      pp = <<-EOS
+        class { 'mysql::server':
+          override_options => {
+            'mysqld' => {'skip-name-resolve' => true}
+          },
+          restart          => true,
+        }
+      EOS
+
+      apply_manifest(pp, :catch_failures => true)
+    end
+
+    it 'should apply' do
+      pp = <<-EOS
+        mysql_grant { 'test@fqdn.com/test.*':
+          ensure     => 'present',
+          table      => 'test.*',
+          user       => 'test@fqdn.com',
+          privileges => 'ALL',
+        }
+        mysql_grant { 'test@192.168.5.7/test.*':
+          ensure     => 'present',
+          table      => 'test.*',
+          user       => 'test@192.168.5.7',
+          privileges => 'ALL',
+        }
+      EOS
+
+      apply_manifest(pp, :catch_failures => true)
+    end
+
+    it 'should fail with fqdn' do
+      expect(shell("mysql -NBe \"SHOW GRANTS FOR test@fqdn.com\"", { :acceptable_exit_codes => 1}).stderr).to match(/There is no such grant defined for user 'test' on host 'fqdn.com'/)
+    end
+    it 'finds ipv4' do
+      shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.7'\"") do |r|
+        expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'192.168.5.7'/)
+        expect(r.stderr).to be_empty
+      end
+    end
+
+    it 'should fail to execute while applying' do
+      pp = <<-EOS
+        mysql_grant { 'test@fqdn.com/test.*':
+          ensure     => 'present',
+          table      => 'test.*',
+          user       => 'test@fqdn.com',
+          privileges => 'ALL',
+        }
+      EOS
+
+      mysql_cmd = shell('which mysql').stdout.chomp
+      shell("mv #{mysql_cmd} #{mysql_cmd}.bak")
+      expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/Command mysql is missing/)
+      shell("mv #{mysql_cmd}.bak #{mysql_cmd}")
+    end
+
+    it 'reset mysql::server config' do
+      pp = <<-EOS
+        class { 'mysql::server':
+          restart          => true,
+        }
+      EOS
+
+      apply_manifest(pp, :catch_failures => true)
+    end
+  end
+
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/spec/acceptance/types/mysql_plugin_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,72 @@
+require 'spec_helper_acceptance'
+
+# Different operating systems (and therefore different versions/forks
+# of mysql) have varying levels of support for plugins and have
+# different plugins available. Choose a plugin that works or don't try
+# to test plugins if not available.
+if fact('osfamily') =~ /RedHat/
+  if fact('operatingsystemrelease') =~ /^5\./
+    plugin = nil # Plugins not supported on mysql on RHEL 5
+  elsif fact('operatingsystemrelease') =~ /^6\./
+    plugin     = 'example'
+    plugin_lib = 'ha_example.so'
+  elsif fact('operatingsystemrelease') =~ /^7\./
+    plugin     = 'pam'
+    plugin_lib = 'auth_pam.so'
+  end
+elsif fact('osfamily') =~ /Debian/
+  if fact('operatingsystem') =~ /Debian/
+    if fact('operatingsystemrelease') =~ /^6\./
+      # Only available plugin is innodb which is already loaded and not unload- or reload-able
+      plugin = nil
+    elsif fact('operatingsystemrelease') =~ /^7\./
+      plugin     = 'example'
+      plugin_lib = 'ha_example.so'
+    end
+  elsif fact('operatingsystem') =~ /Ubuntu/
+    if fact('operatingsystemrelease') =~ /^10\.04/
+      # Only available plugin is innodb which is already loaded and not unload- or reload-able
+      plugin = nil
+    else
+      plugin     = 'example'
+      plugin_lib = 'ha_example.so'
+    end
+  end
+elsif fact('osfamily') =~ /Suse/
+  plugin = nil # Plugin library path is broken on Suse http://lists.opensuse.org/opensuse-bugs/2013-08/msg01123.html
+end
+
+describe 'mysql_plugin' do
+  if plugin # if plugins are supported
+    describe 'setup' do
+      it 'should work with no errors' do
+        pp = <<-EOS
+          class { 'mysql::server': }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+    end
+
+    describe 'load plugin' do
+      it 'should work without errors' do
+        pp = <<-EOS
+          mysql_plugin { #{plugin}:
+            ensure => present,
+            soname => '#{plugin_lib}',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should find the plugin' do
+        shell("mysql -NBe \"select plugin_name from information_schema.plugins where plugin_name='#{plugin}'\"") do |r|
+          expect(r.stdout).to match(/^#{plugin}$/i)
+          expect(r.stderr).to be_empty
+        end
+      end
+    end
+  end
+
+end
--- a/modules/mysql/spec/acceptance/types/mysql_user_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/acceptance/types/mysql_user_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,6 +1,6 @@
 require 'spec_helper_acceptance'
 
-describe 'mysql_user', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
+describe 'mysql_user' do
   describe 'setup' do
     it 'should work with no errors' do
       pp = <<-EOS
@@ -11,22 +11,76 @@
     end
   end
 
-  describe 'adding user' do
-    it 'should work without errors' do
-      pp = <<-EOS
-        mysql_user { 'ashp@localhost':
-          password_hash => '6f8c114b58f2ce9e',
-        }
-      EOS
+  context 'using ashp@localhost' do
+    describe 'adding user' do
+      it 'should work without errors' do
+        pp = <<-EOS
+          mysql_user { 'ashp@localhost':
+            password_hash => '6f8c114b58f2ce9e',
+          }
+        EOS
 
-      apply_manifest(pp, :catch_failures => true)
-    end
+        apply_manifest(pp, :catch_failures => true)
+      end
 
-    it 'should find the user' do
-      shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r|
-        expect(r.stdout).to match(/^1$/)
-        expect(r.stderr).to be_empty
+      it 'should find the user' do
+        shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r|
+          expect(r.stdout).to match(/^1$/)
+          expect(r.stderr).to be_empty
+        end
       end
     end
   end
+
+  context 'using ashp-dash@localhost' do
+    describe 'adding user' do
+      it 'should work without errors' do
+        pp = <<-EOS
+          mysql_user { 'ashp-dash@localhost':
+            password_hash => '6f8c114b58f2ce9e',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should find the user' do
+        shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp-dash@localhost'\"") do |r|
+          expect(r.stdout).to match(/^1$/)
+          expect(r.stderr).to be_empty
+        end
+      end
+    end
+  end
+
+  context 'using ashp@LocalHost' do
+    describe 'adding user' do
+      it 'should work without errors' do
+        pp = <<-EOS
+          mysql_user { 'ashp@LocalHost':
+            password_hash => '6f8c114b58f2ce9e',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should find the user' do
+        shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r|
+          expect(r.stdout).to match(/^1$/)
+          expect(r.stderr).to be_empty
+        end
+      end
+    end
+  end
+  context 'using resource should throw no errors' do
+    describe 'find users' do
+      it {
+        on default, puppet('resource mysql_user'), {:catch_failures => true} do |r|
+          expect(r.stdout).to_not match(/Error:/)
+          expect(r.stdout).to_not match(/must be properly quoted, invalid character:/)
+        end
+      }
+    end
+  end
 end
--- a/modules/mysql/spec/acceptance/unsupported_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-require 'spec_helper_acceptance'
-
-describe 'unsupported distributions and OSes', :if => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
-  it 'should fail' do
-    pp = <<-EOS
-    class { 'mysql::server': }
-    EOS
-    expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/unsupported osfamily/i)
-  end
-end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/spec/classes/graceful_failures_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe 'mysql::server' do
+  on_pe_unsupported_platforms.each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
+
+        context 'should gracefully fail' do
+          it { expect { is_expected.to compile}.to raise_error(Puppet::Error, /Unsupported platform:/) }
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/spec/classes/mycnf_template_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,78 @@
+require 'spec_helper'
+
+describe 'mysql::server' do
+  context 'my.cnf template' do
+    on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+      pe_platforms.each do |pe_platform,facts|
+        describe "on #{pe_version} #{pe_platform}" do
+          let(:facts) { facts }
+
+          context 'normal entry' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'socket' => '/var/lib/mysql/mysql.sock' } } }}
+            it do
+              is_expected.to contain_file('mysql-config-file').with({
+                :mode => '0644',
+              }).with_content(/socket = \/var\/lib\/mysql\/mysql.sock/)
+            end
+          end
+
+          describe 'array entry' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'replicate-do-db' => ['base1', 'base2'], } }}}
+            it do
+              is_expected.to contain_file('mysql-config-file').with_content(
+                /.*replicate-do-db = base1\nreplicate-do-db = base2.*/
+              )
+            end
+          end
+
+          describe 'ssl set to true' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'ssl' => true }}}}
+            it { is_expected.to contain_file('mysql-config-file').with_content(/ssl/) }
+            it { is_expected.to contain_file('mysql-config-file').without_content(/ssl = true/) }
+          end
+
+          describe 'ssl set to false' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'ssl' => false }}}}
+            it { is_expected.to contain_file('mysql-config-file').with_content(/ssl = false/) }
+          end
+
+          # ssl-disable (and ssl) are special cased within mysql.
+          describe 'possibility of disabling ssl completely' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'ssl' => true, 'ssl-disable' => true }}}}
+            it { is_expected.to contain_file('mysql-config-file').without_content(/ssl = true/) }
+          end
+
+          describe 'a non ssl option set to true' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'test' => true }}}}
+            it { is_expected.to contain_file('mysql-config-file').with_content(/^test$/) }
+            it { is_expected.to contain_file('mysql-config-file').without_content(/test = true/) }
+          end
+
+          context 'with includedir' do
+            let(:params) {{ :includedir => '/etc/my.cnf.d' }}
+            it 'makes the directory' do
+              is_expected.to contain_file('/etc/my.cnf.d').with({
+                :ensure => :directory,
+                :mode   => '0755',
+              })
+            end
+
+            it { is_expected.to contain_file('mysql-config-file').with_content(/!includedir/) }
+          end
+
+          context 'without includedir' do
+            let(:params) {{ :includedir => '' }}
+            it 'shouldnt contain the directory' do
+              is_expected.not_to contain_file('mysql-config-file').with({
+                :ensure => :directory,
+                :mode   => '0755',
+              })
+            end
+
+            it { is_expected.to contain_file('mysql-config-file').without_content(/!includedir/) }
+          end
+        end
+      end
+    end
+  end
+end
--- a/modules/mysql/spec/classes/mysql_bindings_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_bindings_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,60 +1,30 @@
 require 'spec_helper'
 
 describe 'mysql::bindings' do
-  let(:params) {{
-    'java_enable'   => true,
-    'perl_enable'   => true,
-    'php_enable'    => true,
-    'python_enable' => true,
-    'ruby_enable'   => true,
-  }}
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
 
-  shared_examples 'bindings' do |osfamily, operatingsystem, operatingsystemrelease, java_name, perl_name, php_name, python_name, ruby_name|
-    let :facts do
-      { :osfamily => osfamily, :operatingsystem => operatingsystem,
-        :operatingsystemrelease => operatingsystemrelease, :root_home => '/root',
-      }
-    end
-    it { should contain_package('mysql-connector-java').with(
-      :name   => java_name,
-      :ensure => 'present'
-    )}
-    it { should contain_package('perl_mysql').with(
-      :name     => perl_name,
-      :ensure   => 'present'
-    )}
-    it { should contain_package('python-mysqldb').with(
-      :name   => python_name,
-      :ensure => 'present'
-    )}
-    it { should contain_package('ruby_mysql').with(
-      :name     => ruby_name,
-      :ensure   => 'present'
-    )}
-  end
+        let(:params) {{
+          'java_enable'             => true,
+          'perl_enable'             => true,
+          'php_enable'              => true,
+          'python_enable'           => true,
+          'ruby_enable'             => true,
+          'client_dev'              => true,
+          'daemon_dev'              => true,
+          'client_dev_package_name' => 'libmysqlclient-devel',
+          'daemon_dev_package_name' => 'mysql-devel',
+        }}
 
-  context 'Debian' do
-    it_behaves_like 'bindings', 'Debian', 'Debian', '7.4','libmysql-java', 'libdbd-mysql-perl', 'php5-mysql', 'python-mysqldb', 'libmysql-ruby'
-    it_behaves_like 'bindings', 'Debian', 'Ubuntu', '14.04', 'libmysql-java', 'libdbd-mysql-perl', 'php5-mysql', 'python-mysqldb', 'libmysql-ruby'
-  end
-
-  context 'freebsd' do
-    it_behaves_like 'bindings', 'FreeBSD', 'FreeBSD', '10.0', 'databases/mysql-connector-java', 'p5-DBD-mysql', 'databases/php5-mysql', 'databases/py-MySQLdb', 'databases/ruby-mysql'
-  end
-
-  context 'redhat' do
-    it_behaves_like 'bindings', 'RedHat', 'RedHat', '6.5', 'mysql-connector-java', 'perl-DBD-MySQL', 'php-mysql', 'MySQL-python', 'ruby-mysql'
-    it_behaves_like 'bindings', 'RedHat', 'OpenSuSE', '11.3', 'mysql-connector-java', 'perl-DBD-MySQL', 'php-mysql', 'MySQL-python', 'ruby-mysql'
-  end
-
-  describe 'on any other os' do
-    let :facts do
-      {:osfamily => 'foo', :root_home => '/root'}
-    end
-
-    it 'should fail' do
-      expect { subject }.to raise_error(/Unsupported osfamily: foo/)
+        it { is_expected.to contain_package('mysql-connector-java') }
+        it { is_expected.to contain_package('perl_mysql') }
+        it { is_expected.to contain_package('python-mysqldb') }
+        it { is_expected.to contain_package('ruby_mysql') }
+        it { is_expected.to contain_package('mysql-client_dev') }
+        it { is_expected.to contain_package('mysql-daemon_dev') }
+      end
     end
   end
-
 end
--- a/modules/mysql/spec/classes/mysql_client_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_client_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,16 +1,36 @@
+require 'spec_helper'
+
 describe 'mysql::client' do
-  let(:facts) {{ :osfamily => 'RedHat' }}
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
 
-  context 'with defaults' do
-    it { should_not contain_class('mysql::bindings') }
-    it { should contain_package('mysql_client') }
-  end
+        context 'with defaults' do
+          it { is_expected.not_to contain_class('mysql::bindings') }
+          it { is_expected.to contain_package('mysql_client') }
+        end
+
+        context 'with bindings enabled' do
+          let(:params) {{ :bindings_enable => true }}
 
-  context 'with bindings enabled' do
-    let(:params) {{ :bindings_enable => true }}
+          it { is_expected.to contain_class('mysql::bindings') }
+          it { is_expected.to contain_package('mysql_client') }
+        end
+
+        context 'with package_manage set to true' do
+          let(:params) {{ :package_manage => true }}
+
+          it { is_expected.to contain_package('mysql_client') }
+        end
 
-    it { should contain_class('mysql::bindings') }
-    it { should contain_package('mysql_client') }
+        context 'with package_manage set to false' do
+          let(:params) {{ :package_manage => false }}
+
+          it { is_expected.not_to contain_package('mysql_client') }
+        end
+
+      end
+    end
   end
-
 end
--- a/modules/mysql/spec/classes/mysql_server_account_security_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_server_account_security_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,41 +1,68 @@
 require 'spec_helper'
 
 describe 'mysql::server::account_security' do
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts.merge({:fqdn => 'myhost.mydomain', :hostname => 'myhost'}) }
 
-  let :facts do {
-    :fqdn     => 'myhost.mydomain',
-    :hostname => 'myhost',
-    :root_home => '/root'
-  }
-  end
+        [ 'root@myhost.mydomain',
+          'root@127.0.0.1',
+          'root@::1',
+          '@myhost.mydomain',
+          '@localhost',
+          '@%',
+        ].each do |user|
+          it "removes Mysql_User[#{user}]" do
+            is_expected.to contain_mysql_user(user).with_ensure('absent')
+          end
+        end
+
+        # When the hostname doesn't match the fqdn we also remove these.
+        # We don't need to test the inverse as when they match they are
+        # covered by the above list.
+        [ 'root@myhost', '@myhost' ].each do |user|
+          it "removes Mysql_User[#{user}]" do
+            is_expected.to contain_mysql_user(user).with_ensure('absent')
+          end
+        end
+
+        it 'should remove Mysql_database[test]' do
+          is_expected.to contain_mysql_database('test').with_ensure('absent')
+        end
+      end
 
-  it 'should remove Mysql_User[root@myhost.mydomain]' do
-    should contain_mysql_user('root@myhost.mydomain').with_ensure('absent')
-  end
-  it 'should remove Mysql_User[root@myhost]' do
-    should contain_mysql_user('root@myhost').with_ensure('absent')
-  end
-  it 'should remove Mysql_User[root@127.0.0.1]' do
-    should contain_mysql_user('root@127.0.0.1').with_ensure('absent')
+      describe "on #{pe_version} #{pe_platform} with fqdn==localhost" do
+        let(:facts) { facts.merge({:fqdn => 'localhost', :hostname => 'localhost'}) }
+
+        [ 'root@127.0.0.1',
+          'root@::1',
+          '@localhost',
+          'root@localhost.localdomain',
+          '@localhost.localdomain',
+          '@%',
+        ].each do |user|
+          it "removes Mysql_User[#{user}]" do
+            is_expected.to contain_mysql_user(user).with_ensure('absent')
+          end
+        end
+      end
+
+      describe "on #{pe_version} #{pe_platform} with fqdn==localhost.localdomain" do
+        let(:facts) { facts.merge({:fqdn => 'localhost.localdomain', :hostname => 'localhost'}) }
+
+        [ 'root@127.0.0.1',
+          'root@::1',
+          '@localhost',
+          'root@localhost.localdomain',
+          '@localhost.localdomain',
+          '@%',
+        ].each do |user|
+          it "removes Mysql_User[#{user}]" do
+            is_expected.to contain_mysql_user(user).with_ensure('absent')
+          end
+        end
+      end
+    end
   end
-  it 'should remove Mysql_User[root@::1]' do
-    should contain_mysql_user('root@::1').with_ensure('absent')
-  end
-  it 'should remove Mysql_User[@myhost.mydomain]' do
-    should contain_mysql_user('@myhost.mydomain').with_ensure('absent')
-  end
-  it 'should remove Mysql_User[@myhost]' do
-    should contain_mysql_user('@myhost').with_ensure('absent')
-  end
-  it 'should remove Mysql_User[@localhost]' do
-    should contain_mysql_user('@localhost').with_ensure('absent')
-  end
-  it 'should remove Mysql_User[@%]' do
-    should contain_mysql_user('@%').with_ensure('absent')
-  end
-
-  it 'should remove Mysql_database[test]' do
-    should contain_mysql_database('test').with_ensure('absent')
-  end
-
 end
--- a/modules/mysql/spec/classes/mysql_server_backup_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_server_backup_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,188 +1,189 @@
 require 'spec_helper'
 
 describe 'mysql::server::backup' do
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
 
-    let(:default_params) {
-        { 'backupuser'         => 'testuser',
+        let(:default_params) {
+          { 'backupuser'         => 'testuser',
             'backuppassword'     => 'testpass',
             'backupdir'          => '/tmp',
             'backuprotate'       => '25',
             'delete_before_dump' => true,
             'execpath'           => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin',
+          }
         }
-    }
-    context 'standard conditions' do
-        let(:params) { default_params }
+
+        context 'standard conditions' do
+          let(:params) { default_params }
 
-        it { should contain_mysql_user('testuser@localhost').with(
-            :require => 'Class[Mysql::Server::Root_password]'
-        )}
+          # Cannot use that_requires here, doesn't work on classes.
+          it { is_expected.to contain_mysql_user('testuser@localhost').with(
+            :require => 'Class[Mysql::Server::Root_password]') }
 
-        it { should contain_mysql_grant('testuser@localhost/*.*').with(
-            :privileges => ["SELECT", "RELOAD", "LOCK TABLES", "SHOW VIEW", "PROCESS"]
-        )}
+          it { is_expected.to contain_mysql_grant('testuser@localhost/*.*').with(
+            :privileges => ['SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS']
+          ).that_requires('Mysql_user[testuser@localhost]') }
 
-        it { should contain_cron('mysql-backup').with(
+          it { is_expected.to contain_cron('mysql-backup').with(
             :command => '/usr/local/sbin/mysqlbackup.sh',
             :ensure  => 'present'
-        )}
+          )}
 
-        it { should contain_file('mysqlbackup.sh').with(
+          it { is_expected.to contain_file('mysqlbackup.sh').with(
             :path   => '/usr/local/sbin/mysqlbackup.sh',
             :ensure => 'present'
-        ) }
+          ) }
 
-        it { should contain_file('mysqlbackupdir').with(
+          it { is_expected.to contain_file('mysqlbackupdir').with(
             :path   => '/tmp',
             :ensure => 'directory'
-        )}
+          )}
 
-        it 'should have compression by default' do
-            verify_contents(subject, 'mysqlbackup.sh', [
-                            ' --all-databases | bzcat -zc > ${DIR}/${PREFIX}`date +%Y%m%d-%H%M%S`.sql.bz2',
-            ])
-        end
-        it 'should skip backing up events table by default' do
-            verify_contents(subject, 'mysqlbackup.sh', [
-                            'EVENTS="--ignore-table=mysql.event"',
-            ])
+          it 'should have compression by default' do
+            is_expected.to contain_file('mysqlbackup.sh').with(
+              :content => /bzcat -zc/
+            )
+          end
+          it 'should skip backing up events table by default' do
+            is_expected.to contain_file('mysqlbackup.sh').with(
+              :content => /EVENTS="--ignore-table=mysql.event"/
+            )
+          end
+
+          it 'should have 25 days of rotation' do
+            # MySQL counts from 0
+            is_expected.to contain_file('mysqlbackup.sh').with_content(/.*ROTATE=24.*/)
+          end
+
+          it 'should have a standard PATH' do
+            is_expected.to contain_file('mysqlbackup.sh').with_content(%r{PATH=/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin})
+          end
         end
 
-        it 'should have 25 days of rotation' do
-            # MySQL counts from 0 I guess.
-            should contain_file('mysqlbackup.sh').with_content(/.*ROTATE=24.*/)
-        end
-
-        it 'should have a standard PATH' do
-            should contain_file('mysqlbackup.sh').with_content(%r{PATH=/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin})
-        end
-    end
+        context 'custom ownership and mode for backupdir' do
+          let(:params) do
+            { :backupdirmode => '0750',
+              :backupdirowner => 'testuser',
+              :backupdirgroup => 'testgrp',
+            }.merge(default_params)
+          end
 
-    context 'custom ownership and mode for backupdir' do
-        let(:params) do
-            { :backupdirmode => '0750',
-                :backupdirowner => 'testuser',
-                :backupdirgroup => 'testgrp',
-            }.merge(default_params)
-        end
-
-        it { should contain_file('mysqlbackupdir').with(
+          it { is_expected.to contain_file('mysqlbackupdir').with(
             :path => '/tmp',
             :ensure => 'directory',
             :mode => '0750',
             :owner => 'testuser',
             :group => 'testgrp'
-        ) }
-    end
-
-    context 'with compression disabled' do
-        let(:params) do
-            { :backupcompress => false }.merge(default_params)
-        end
-
-        it { should contain_file('mysqlbackup.sh').with(
-            :path   => '/usr/local/sbin/mysqlbackup.sh',
-            :ensure => 'present'
-        ) }
-
-        it 'should be able to disable compression' do
-            verify_contents(subject, 'mysqlbackup.sh', [
-                            ' --all-databases > ${DIR}/${PREFIX}`date +%Y%m%d-%H%M%S`.sql',
-            ])
-        end
-    end
-
-    context 'with mysql.events backedup' do
-        let(:params) do
-            { :ignore_events => false }.merge(default_params)
-        end
-
-        it { should contain_file('mysqlbackup.sh').with(
-            :path   => '/usr/local/sbin/mysqlbackup.sh',
-            :ensure => 'present'
-        ) }
-
-        it 'should be able to backup events table' do
-            verify_contents(subject, 'mysqlbackup.sh', [
-                            'EVENTS="--events"',
-            ])
-        end
-    end
-
-
-    context 'with database list specified' do
-        let(:params) do
-            { :backupdatabases => ['mysql'] }.merge(default_params)
-        end
-
-        it { should contain_file('mysqlbackup.sh').with(
-            :path   => '/usr/local/sbin/mysqlbackup.sh',
-            :ensure => 'present'
-        ) }
-
-        it 'should have a backup file for each database' do
-            content = subject.resource('file','mysqlbackup.sh').send(:parameters)[:content]
-            content.should match(' mysql | bzcat -zc \${DIR}\\\${PREFIX}mysql_`date')
-            #      verify_contents(subject, 'mysqlbackup.sh', [
-            #        ' mysql | bzcat -zc ${DIR}/${PREFIX}mysql_`date +%Y%m%d-%H%M%S`.sql',
-            #      ])
-        end 
-    end
-
-    context 'with file per database' do
-        let(:params) do
-            default_params.merge({ :file_per_database => true })
-        end
-
-        it 'should loop through backup all databases' do
-            verify_contents(subject, 'mysqlbackup.sh', [
-                            'mysql -s -r -N -e \'SHOW DATABASES\' | while read dbname',
-                            'do',
-                            '  mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \\',
-                            '    ${EVENTS} \\',
-                            '    ${dbname} | bzcat -zc > ${DIR}/${PREFIX}${dbname}_`date +%Y%m%d-%H%M%S`.sql.bz2',
-                            'done',
-            ])
+          ) }
         end
 
         context 'with compression disabled' do
+          let(:params) do
+            { :backupcompress => false }.merge(default_params)
+          end
+
+          it { is_expected.to contain_file('mysqlbackup.sh').with(
+            :path   => '/usr/local/sbin/mysqlbackup.sh',
+            :ensure => 'present'
+          ) }
+
+          it 'should be able to disable compression' do
+            is_expected.to contain_file('mysqlbackup.sh').without_content(
+              /.*bzcat -zc.*/
+            )
+          end
+        end
+
+        context 'with mysql.events backedup' do
+          let(:params) do
+            { :ignore_events => false }.merge(default_params)
+          end
+
+          it { is_expected.to contain_file('mysqlbackup.sh').with(
+            :path   => '/usr/local/sbin/mysqlbackup.sh',
+            :ensure => 'present'
+          ) }
+
+          it 'should be able to backup events table' do
+            is_expected.to contain_file('mysqlbackup.sh').with_content(
+              /EVENTS="--events"/
+            )
+          end
+        end
+
+        context 'with database list specified' do
+          let(:params) do
+            { :backupdatabases => ['mysql'] }.merge(default_params)
+          end
+
+          it { is_expected.to contain_file('mysqlbackup.sh').with(
+            :path   => '/usr/local/sbin/mysqlbackup.sh',
+            :ensure => 'present'
+            )
+          }
+
+          it 'should have a backup file for each database' do
+            is_expected.to contain_file('mysqlbackup.sh').with_content(
+              /mysql | bzcat -zc \${DIR}\\\${PREFIX}mysql_`date'/
+            )
+          end
+        end
+
+        context 'with file per database' do
+          let(:params) do
+            default_params.merge({ :file_per_database => true })
+          end
+
+          it 'should loop through backup all databases' do
+            is_expected.to contain_file('mysqlbackup.sh').with_content(/.*SHOW DATABASES.*/)
+          end
+
+          context 'with compression disabled' do
             let(:params) do
-                default_params.merge({ :file_per_database => true, :backupcompress => false })
+              default_params.merge({ :file_per_database => true, :backupcompress => false })
             end
 
             it 'should loop through backup all databases without compression' do
-                verify_contents(subject, 'mysqlbackup.sh', [
-                                '    ${dbname} > ${DIR}/${PREFIX}${dbname}_`date +%Y%m%d-%H%M%S`.sql',
-                ])
+              is_expected.to contain_file('mysqlbackup.sh').with_content(
+                /.*SHOW DATABASES.*/
+              )
+              is_expected.to contain_file('mysqlbackup.sh').without_content(
+                /.*bzcat -zc.*/
+              )
             end
+          end
         end
-    end
 
-		context 'with postscript' do
-			let(:params) do
-				default_params.merge({ :postscript => 'rsync -a /tmp backup01.local-lan:' })
-			end
+        context 'with postscript' do
+          let(:params) do
+            default_params.merge({ :postscript => 'rsync -a /tmp backup01.local-lan:' })
+          end
 
-			it 'should be add postscript' do
-				verify_contents(subject, 'mysqlbackup.sh', [
-					'rsync -a /tmp backup01.local-lan:',
-				])
-			end
-		end
+          it 'should be add postscript' do
+            is_expected.to contain_file('mysqlbackup.sh').with_content(
+              /rsync -a \/tmp backup01.local-lan:/
+            )
+          end
+        end
 
-		context 'with postscripts' do
-			let(:params) do
-				default_params.merge({ :postscript => [
-					'rsync -a /tmp backup01.local-lan:',
-					'rsync -a /tmp backup02.local-lan:',
-				]})
-			end
+        context 'with postscripts' do
+          let(:params) do
+            default_params.merge({ :postscript => [
+              'rsync -a /tmp backup01.local-lan:',
+              'rsync -a /tmp backup02.local-lan:',
+            ]})
+          end
 
-			it 'should be add postscript' do
-				verify_contents(subject, 'mysqlbackup.sh', [
-					'rsync -a /tmp backup01.local-lan:',
-					'rsync -a /tmp backup02.local-lan:',
-				])
-			end
-		end
+          it 'should be add postscript' do
+            is_expected.to contain_file('mysqlbackup.sh').with_content(
+              /.*rsync -a \/tmp backup01.local-lan:\n\nrsync -a \/tmp backup02.local-lan:.*/
+            )
+          end
+        end
+      end
+    end
+  end
 end
--- a/modules/mysql/spec/classes/mysql_server_monitor_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_server_monitor_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,31 +1,35 @@
 require 'spec_helper'
 describe 'mysql::server::monitor' do
-  let :facts do
-    { :osfamily => 'Debian', :root_home => '/root' }
-  end
-  let :pre_condition do
-    "include 'mysql::server'"
-  end
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
+        let :pre_condition do
+          "include 'mysql::server'"
+        end
 
-  let :default_params do
-    {
-      :mysql_monitor_username   => 'monitoruser',
-      :mysql_monitor_password   => 'monitorpass',
-      :mysql_monitor_hostname   => 'monitorhost',
-    }
-  end
+        let :default_params do
+          {
+            :mysql_monitor_username   => 'monitoruser',
+            :mysql_monitor_password   => 'monitorpass',
+            :mysql_monitor_hostname   => 'monitorhost',
+          }
+        end
 
-  let :params do
-    default_params
-  end
+        let :params do
+          default_params
+        end
 
-  it { should contain_mysql_user('monitoruser@monitorhost')}
+        it { is_expected.to contain_mysql_user('monitoruser@monitorhost')}
 
-  it { should contain_mysql_grant('monitoruser@monitorhost/*.*').with(
-    :ensure     => 'present',
-    :user       => 'monitoruser@monitorhost',
-    :table      => '*.*',
-    :privileges => ["PROCESS", "SUPER"],
-    :require    => 'Mysql_user[monitoruser@monitorhost]'
-  )}
+        it { is_expected.to contain_mysql_grant('monitoruser@monitorhost/*.*').with(
+          :ensure     => 'present',
+          :user       => 'monitoruser@monitorhost',
+          :table      => '*.*',
+          :privileges => ["PROCESS", "SUPER"],
+          :require    => 'Mysql_user[monitoruser@monitorhost]'
+        )}
+      end
+    end
+  end
 end
--- a/modules/mysql/spec/classes/mysql_server_mysqltuner_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_server_mysqltuner_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,5 +1,43 @@
+require 'spec_helper'
+
 describe 'mysql::server::mysqltuner' do
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
+
+        context 'ensure => present' do
+          it { is_expected.to compile }
+          it { is_expected.to contain_staging__file('mysqltuner-v1.3.0').with({
+            :source => 'https://github.com/major/MySQLTuner-perl/raw/v1.3.0/mysqltuner.pl',
+          })
+          }
+        end
+
+        context 'ensure => absent' do
+          let(:params) {{ :ensure => 'absent' }}
+          it { is_expected.to compile }
+          it { is_expected.to contain_file('/usr/local/bin/mysqltuner').with(:ensure => 'absent') }
+        end
 
-  it { should contain_file('/usr/local/bin/mysqltuner') }
+        context 'custom version' do
+          let(:params) {{ :version => 'v1.2.0' }}
+          it { is_expected.to compile }
+          it { is_expected.to contain_staging__file('mysqltuner-v1.2.0').with({
+            :source => 'https://github.com/major/MySQLTuner-perl/raw/v1.2.0/mysqltuner.pl',
+          })
+          }
+        end
 
+        context 'custom source' do
+          let(:params) {{ :source => '/tmp/foo' }}
+          it { is_expected.to compile }
+          it { is_expected.to contain_staging__file('mysqltuner-/tmp/foo').with({
+            :source => '/tmp/foo',
+          })
+          }
+        end
+      end
+    end
+  end
 end
--- a/modules/mysql/spec/classes/mysql_server_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/classes/mysql_server_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,202 +1,163 @@
 require 'spec_helper'
+
 describe 'mysql::server' do
-  let(:facts) {{:osfamily => 'RedHat', :root_home => '/root'}}
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
 
-  context 'with defaults' do
-    it { should contain_class('mysql::server::install') }
-    it { should contain_class('mysql::server::config') }
-    it { should contain_class('mysql::server::service') }
-    it { should contain_class('mysql::server::root_password') }
-    it { should contain_class('mysql::server::providers') }
-  end
+        context 'with defaults' do
+          it { is_expected.to contain_class('mysql::server::install') }
+          it { is_expected.to contain_class('mysql::server::config') }
+          it { is_expected.to contain_class('mysql::server::service') }
+          it { is_expected.to contain_class('mysql::server::root_password') }
+          it { is_expected.to contain_class('mysql::server::providers') }
+        end
 
-  # make sure that overriding the mysqld settings keeps the defaults for everything else
-  context 'with overrides' do
-    let(:params) {{ :override_options => { 'mysqld' => { 'socket' => '/var/lib/mysql/mysql.sock' } } }}
-    it do
-      should contain_file('/etc/my.cnf').with({
-        :mode => '0644',
-      }).with_content(/basedir/)
-    end
-  end
+        context 'with remove_default_accounts set' do
+          let(:params) {{ :remove_default_accounts => true }}
+          it { is_expected.to contain_class('mysql::server::account_security') }
+        end
 
-  describe 'with multiple instance of an option' do
-    let(:params) {{ :override_options => { 'mysqld' => { 'replicate-do-db' => ['base1', 'base2', 'base3'], } }}}
-    it do
-      should contain_file('/etc/my.cnf').with_content(
-        /^replicate-do-db = base1$/
-      ).with_content(
-        /^replicate-do-db = base2$/
-      ).with_content(
-        /^replicate-do-db = base3$/
-      )
-    end
-  end
+        context 'mysql::server::install' do
+          it 'contains the package by default' do
+            is_expected.to contain_package('mysql-server').with({
+              :ensure => :present,
+            })
+          end
+          context 'with package_manage set to true' do
+            let(:params) {{ :package_manage => true }}
+            it { is_expected.to contain_package('mysql-server') }
+          end
+          context 'with package_manage set to false' do
+            let(:params) {{ :package_manage => false }}
+            it { is_expected.not_to contain_package('mysql-server') }
+          end
+          context 'with datadir overridden' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'datadir' => '/tmp' }} }}
+            it { is_expected.to contain_exec('mysql_install_db') }
+          end
+        end
 
-  describe 'an option set to true' do
-    let(:params) {
-      { :override_options => { 'mysqld' => { 'ssl' => true } }}
-    }
-    it do
-      should contain_file('/etc/my.cnf').with_content(/^\s*ssl\s*(?:$|= true)/m)
-    end
-  end
+        context 'mysql::server::service' do
+          context 'with defaults' do
+            it { is_expected.to contain_service('mysqld') }
+          end
+
+          context 'service_enabled set to false' do
+            let(:params) {{ :service_enabled => false }}
 
-  describe 'an option set to false' do
-    let(:params) {
-      { :override_options => { 'mysqld' => { 'ssl' => false } }}
-    }
-    it do
-      should contain_file('/etc/my.cnf').with_content(/^\s*ssl = false/m)
-    end
-  end
+            it do
+              is_expected.to contain_service('mysqld').with({
+                :ensure => :stopped
+              })
+            end
+          end
+          context 'with log-error overridden' do
+            let(:params) {{ :override_options => { 'mysqld' => { 'log-error' => '/tmp/error.log' }} }}
+            it { is_expected.to contain_file('/tmp/error.log') }
+          end
+        end
 
-  context 'with remove_default_accounts set' do
-    let (:params) {{ :remove_default_accounts => true }}
-    it { should contain_class('mysql::server::account_security') }
-  end
-
-  describe 'possibility of disabling ssl completely' do
-    let(:params) {
-      { :override_options => { 'mysqld' => { 'ssl' => true, 'ssl-disable' => true } }}
-    }
-    it do
-      should contain_file('/etc/my.cnf').without_content(/^\s*ssl\s*(?:$|= true)/m)
-    end
-  end
-
-  context 'mysql::server::install' do
-    let(:params) {{ :package_ensure => 'present', :name => 'mysql-server' }}
-    it do
-      should contain_package('mysql-server').with({
-      :ensure => :present,
-      :name   => 'mysql-server',
-    })
-    end
-  end
+        context 'mysql::server::root_password' do
+          describe 'when defaults' do
+            it { is_expected.not_to contain_mysql_user('root@localhost') }
+            it { is_expected.not_to contain_file('/root/.my.cnf') }
+          end
+          describe 'when root_password set' do
+            let(:params) {{:root_password => 'SET' }}
+            it { is_expected.to contain_mysql_user('root@localhost') }
+            it { is_expected.to contain_file('/root/.my.cnf').that_requires('Mysql_user[root@localhost]') }
+          end
+          describe 'when root_password set, create_root_user set to false' do
+            let(:params) {{ :root_password => 'SET', :create_root_user => false }}
+            it { is_expected.not_to contain_mysql_user('root@localhost') }
+            it { is_expected.to contain_file('/root/.my.cnf') }
+          end
+          describe 'when root_password set, create_root_my_cnf set to false' do
+            let(:params) {{ :root_password => 'SET', :create_root_my_cnf => false }}
+            it { is_expected.to contain_mysql_user('root@localhost') }
+            it { is_expected.not_to contain_file('/root/.my.cnf') }
+          end
+          describe 'when root_password set, create_root_user and create_root_my_cnf set to false' do
+            let(:params) {{ :root_password => 'SET', :create_root_user => false, :create_root_my_cnf => false }}
+            it { is_expected.not_to contain_mysql_user('root@localhost') }
+            it { is_expected.not_to contain_file('/root/.my.cnf') }
+          end
+        end
 
-  context 'mysql::server::config' do
-    it do
-      should contain_file('/etc/mysql').with({
-        :ensure => :directory,
-        :mode   => '0755',
-      })
-    end
-
-    it do
-      should contain_file('/etc/mysql/conf.d').with({
-        :ensure => :directory,
-        :mode   => '0755',
-      })
-    end
+        context 'mysql::server::providers' do
+          describe 'with users' do
+            let(:params) {{:users => {
+              'foo@localhost' => {
+                'max_connections_per_hour' => '1',
+                'max_queries_per_hour'     => '2',
+                'max_updates_per_hour'     => '3',
+                'max_user_connections'     => '4',
+                'password_hash'            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF'
+              },
+              'foo2@localhost' => {}
+            }}}
+            it { is_expected.to contain_mysql_user('foo@localhost').with(
+              :max_connections_per_hour => '1',
+              :max_queries_per_hour     => '2',
+              :max_updates_per_hour     => '3',
+              :max_user_connections     => '4',
+              :password_hash            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF'
+            )}
+            it { is_expected.to contain_mysql_user('foo2@localhost').with(
+              :max_connections_per_hour => nil,
+              :max_queries_per_hour     => nil,
+              :max_updates_per_hour     => nil,
+              :max_user_connections     => nil,
+              :password_hash            => ''
+            )}
+          end
 
-    it do
-      should contain_file('/etc/my.cnf').with({
-        :mode => '0644',
-      })
-    end
-  end
+          describe 'with grants' do
+            let(:params) {{:grants => {
+              'foo@localhost/somedb.*' => {
+                'user'       => 'foo@localhost',
+                'table'      => 'somedb.*',
+                'privileges' => ["SELECT", "UPDATE"],
+                'options'    => ["GRANT"],
+              },
+              'foo2@localhost/*.*' => {
+                'user'       => 'foo2@localhost',
+                'table'      => '*.*',
+                'privileges' => ["SELECT"],
+              },
+            }}}
+            it { is_expected.to contain_mysql_grant('foo@localhost/somedb.*').with(
+              :user       => 'foo@localhost',
+              :table      => 'somedb.*',
+              :privileges => ["SELECT", "UPDATE"],
+              :options    => ["GRANT"]
+            )}
+            it { is_expected.to contain_mysql_grant('foo2@localhost/*.*').with(
+              :user       => 'foo2@localhost',
+              :table      => '*.*',
+              :privileges => ["SELECT"],
+              :options    => nil
+            )}
+          end
 
-  context 'mysql::server::service' do
-    context 'with defaults' do
-      it { should contain_service('mysqld') }
-    end
-
-    context 'service_enabled set to false' do
-      let(:params) {{ :service_enabled => false }}
-
-      it do
-        should contain_service('mysqld').with({
-          :ensure => :stopped
-        })
+          describe 'with databases' do
+            let(:params) {{:databases => {
+              'somedb' => {
+                'charset' => 'latin1',
+                'collate' => 'latin1',
+              },
+              'somedb2' => {}
+            }}}
+            it { is_expected.to contain_mysql_database('somedb').with(
+              :charset => 'latin1',
+              :collate => 'latin1'
+            )}
+            it { is_expected.to contain_mysql_database('somedb2')}
+          end
+        end
       end
     end
   end
-
-  context 'mysql::server::root_password' do
-    describe 'when defaults' do
-      it { should_not contain_mysql_user('root@localhost') }
-      it { should_not contain_file('/root/.my.cnf') }
-    end
-    describe 'when set' do
-      let(:params) {{:root_password => 'SET' }}
-      it { should contain_mysql_user('root@localhost') }
-      it { should contain_file('/root/.my.cnf') }
-    end
-
-  end
-
-  context 'mysql::server::providers' do
-    describe 'with users' do
-      let(:params) {{:users => {
-        'foo@localhost' => {
-          'max_connections_per_hour' => '1',
-          'max_queries_per_hour'     => '2',
-          'max_updates_per_hour'     => '3',
-          'max_user_connections'     => '4',
-          'password_hash'            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF'
-        },
-        'foo2@localhost' => {}
-      }}}
-      it { should contain_mysql_user('foo@localhost').with(
-        :max_connections_per_hour => '1',
-        :max_queries_per_hour     => '2',
-        :max_updates_per_hour     => '3',
-        :max_user_connections     => '4',
-        :password_hash            => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF'
-      )}
-      it { should contain_mysql_user('foo2@localhost').with(
-        :max_connections_per_hour => nil,
-        :max_queries_per_hour     => nil,
-        :max_updates_per_hour     => nil,
-        :max_user_connections     => nil,
-        :password_hash            => ''
-      )}
-    end
-
-    describe 'with grants' do
-      let(:params) {{:grants => {
-        'foo@localhost/somedb.*' => {
-          'user'       => 'foo@localhost',
-          'table'      => 'somedb.*',
-          'privileges' => ["SELECT", "UPDATE"],
-          'options'    => ["GRANT"],
-        },
-        'foo2@localhost/*.*' => {
-          'user'       => 'foo2@localhost',
-          'table'      => '*.*',
-          'privileges' => ["SELECT"],
-        },
-      }}}
-      it { should contain_mysql_grant('foo@localhost/somedb.*').with(
-        :user       => 'foo@localhost',
-        :table      => 'somedb.*',
-        :privileges => ["SELECT", "UPDATE"],
-        :options    => ["GRANT"]
-      )}
-      it { should contain_mysql_grant('foo2@localhost/*.*').with(
-        :user       => 'foo2@localhost',
-        :table      => '*.*',
-        :privileges => ["SELECT"],
-        :options    => nil
-      )}
-    end
-
-    describe 'with databases' do
-      let(:params) {{:databases => {
-        'somedb' => {
-          'charset' => 'latin1',
-          'collate' => 'latin1',
-        },
-        'somedb2' => {}
-      }}}
-      it { should contain_mysql_database('somedb').with(
-        :charset => 'latin1',
-        :collate => 'latin1'
-      )}
-      it { should contain_mysql_database('somedb2')}
-    end
-
-  end
-
 end
--- a/modules/mysql/spec/defines/mysql_db_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/defines/mysql_db_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,56 +1,75 @@
 require 'spec_helper'
 
 describe 'mysql::db', :type => :define do
-  let(:facts) {{ :osfamily => 'RedHat' }}
-  let(:title) { 'test_db' }
+  on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms|
+    pe_platforms.each do |pe_platform,facts|
+      describe "on #{pe_version} #{pe_platform}" do
+        let(:facts) { facts }
 
-  let(:params) {
-    { 'user'     => 'testuser',
-      'password' => 'testpass',
-    }
-  }
+        let(:title) { 'test_db' }
+
+        let(:params) {
+          { 'user'     => 'testuser',
+            'password' => 'testpass',
+          }
+        }
 
-  it 'should report an error when ensure is not present or absent' do
-    params.merge!({'ensure' => 'invalid_val'})
-    expect { subject }.to raise_error(Puppet::Error,
-      /invalid_val is not supported for ensure\. Allowed values are 'present' and 'absent'\./)
-  end
+        it 'should report an error when ensure is not present or absent' do
+          params.merge!({'ensure' => 'invalid_val'})
+          expect { subject }.to raise_error(Puppet::Error,
+                                            /invalid_val is not supported for ensure\. Allowed values are 'present' and 'absent'\./)
+        end
+
+        it 'should not notify the import sql exec if no sql script was provided' do
+          is_expected.to contain_mysql_database('test_db').without_notify
+        end
 
-  it 'should not notify the import sql exec if no sql script was provided' do
-    should contain_mysql_database('test_db').without_notify
-  end
+        it 'should subscribe to database if sql script is given' do
+          params.merge!({'sql' => 'test_sql'})
+          is_expected.to contain_exec('test_db-import').with_subscribe('Mysql_database[test_db]')
+        end
 
-  it 'should subscribe to database if sql script is given' do
-    params.merge!({'sql' => 'test_sql'})
-    should contain_exec('test_db-import').with_subscribe('Mysql_database[test_db]')
-  end
+        it 'should only import sql script on creation if not enforcing' do
+          params.merge!({'sql' => 'test_sql', 'enforce_sql' => false})
+          is_expected.to contain_exec('test_db-import').with_refreshonly(true)
+        end
 
-  it 'should only import sql script on creation if not enforcing' do
-    params.merge!({'sql' => 'test_sql', 'enforce_sql' => false})
-    should contain_exec('test_db-import').with_refreshonly(true)
-  end
+        it 'should import sql script on creation if enforcing' do
+          params.merge!({'sql' => 'test_sql', 'enforce_sql' => true})
+          is_expected.to contain_exec('test_db-import').with_refreshonly(false)
+          is_expected.to contain_exec('test_db-import').with_command("cat test_sql | mysql test_db")
+        end
 
-  it 'should import sql script on creation if enforcing' do
-    params.merge!({'sql' => 'test_sql', 'enforce_sql' => true})
-    should contain_exec('test_db-import').with_refreshonly(false)
-  end
+        it 'should import sql scripts when more than one is specified' do
+          params.merge!({'sql' => ['test_sql', 'test_2_sql']})
+          is_expected.to contain_exec('test_db-import').with_command('cat test_sql test_2_sql | mysql test_db')
+        end
+
+        it 'should report an error if sql isn\'t a string or an array' do
+          params.merge!({'sql' => {'foo' => 'test_sql', 'bar' => 'test_2_sql'}})
+          expect { subject }.to raise_error(Puppet::Error,
+                                            /\$sql must be either a string or an array\./)
+        end
 
-  it 'should not create database and database user' do
-    params.merge!({'ensure' => 'absent', 'host' => 'localhost'})
-    should contain_mysql_database('test_db').with_ensure('absent')
-    should contain_mysql_user('testuser@localhost').with_ensure('absent')
-  end
+        it 'should not create database and database user' do
+          params.merge!({'ensure' => 'absent', 'host' => 'localhost'})
+          is_expected.to contain_mysql_database('test_db').with_ensure('absent')
+          is_expected.to contain_mysql_user('testuser@localhost').with_ensure('absent')
+        end
 
-  it 'should create with an appropriate collate and charset' do
-    params.merge!({'charset' => 'utf8', 'collate' => 'utf8_danish_ci'})
-    should contain_mysql_database('test_db').with({
-      'charset' => 'utf8',
-      'collate' => 'utf8_danish_ci',
-    })
-  end
+        it 'should create with an appropriate collate and charset' do
+          params.merge!({'charset' => 'utf8', 'collate' => 'utf8_danish_ci'})
+          is_expected.to contain_mysql_database('test_db').with({
+            'charset' => 'utf8',
+            'collate' => 'utf8_danish_ci',
+          })
+        end
 
-  it 'should use dbname parameter as database name instead of name' do
-    params.merge!({'dbname' => 'real_db'})
-    should contain_mysql_database('real_db')
+        it 'should use dbname parameter as database name instead of name' do
+          params.merge!({'dbname' => 'real_db'})
+          is_expected.to contain_mysql_database('real_db')
+        end
+      end
+    end
   end
 end
--- a/modules/mysql/spec/spec_helper.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/spec_helper.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,5 +1,10 @@
-require 'simplecov'
-SimpleCov.start do
-  add_filter "/spec/"
+require 'puppetlabs_spec_helper/module_spec_helper'
+require 'puppet_facts'
+include PuppetFacts
+RSpec.configure do |c|
+  c.formatter = :documentation
 end
-require 'puppetlabs_spec_helper/module_spec_helper'
+
+# The default set of platforms to test again.
+ENV['UNIT_TEST_PLATFORMS'] = 'centos-6-x86_64 ubuntu-1404-x86_64'
+PLATFORMS = ENV['UNIT_TEST_PLATFORMS'].split(' ')
--- a/modules/mysql/spec/spec_helper_acceptance.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/spec_helper_acceptance.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -3,11 +3,12 @@
 UNSUPPORTED_PLATFORMS = [ 'Windows', 'Solaris', 'AIX' ]
 
 unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no'
-  if hosts.first.is_pe?
-    install_pe
-  else
-    install_puppet
-  end
+  # This will install the latest available package on el and deb based
+  # systems fail on windows and osx, and install via gem on other *nixes
+  foss_opts = { :default_action => 'gem_install' }
+
+  if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end
+
   hosts.each do |host|
     on hosts, "mkdir -p #{host['distmoduledir']}"
   end
@@ -29,10 +30,13 @@
       if fact('osfamily') == 'RedHat'
         version = fact("operatingsystemmajrelease")
         shell("yum localinstall -y http://yum.puppetlabs.com/puppetlabs-release-el-#{version}.noarch.rpm")
+        if fact('operatingsystemmajrelease') =~ /7/ || fact('operatingsystem') =~ /Fedora/
+          shell("yum install -y bzip2")
+        end
       end
 
       shell("/bin/touch #{default['puppetpath']}/hiera.yaml")
-      shell('puppet module install puppetlabs-stdlib --version 3.2.0', { :acceptable_exit_codes => [0,1] })
+      on host, puppet('module install puppetlabs-stdlib --version 3.2.0'), { :acceptable_exit_codes => [0,1] }
       on host, puppet('module','install','stahnma/epel'), { :acceptable_exit_codes => [0,1] }
     end
   end
--- a/modules/mysql/spec/unit/mysql_password_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-require 'spec_helper'
-
-describe 'the mysql_password function' do
-  before :all do
-    Puppet::Parser::Functions.autoloader.loadall
-  end
-
-  let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
-
-  it 'should exist' do
-    Puppet::Parser::Functions.function('mysql_password').should == 'function_mysql_password'
-  end
-
-  it 'should raise a ParseError if there is less than 1 arguments' do
-    lambda { scope.function_mysql_password([]) }.should( raise_error(Puppet::ParseError))
-  end
-
-  it 'should raise a ParseError if there is more than 1 arguments' do
-    lambda { scope.function_mysql_password(%w(foo bar)) }.should( raise_error(Puppet::ParseError))
-  end
-
-  it 'should convert password into a hash' do
-    result = scope.function_mysql_password(%w(password))
-    result.should(eq('*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19'))
-  end
-
-end
--- a/modules/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -7,7 +7,7 @@
 
   describe 'when calling mysql_deepmerge from puppet' do
     it "should not compile when no arguments are passed" do
-      pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./
+      skip("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./
       Puppet[:code] = '$x = mysql_deepmerge()'
       expect {
         scope.compiler.compile
@@ -15,7 +15,7 @@
     end
 
     it "should not compile when 1 argument is passed" do
-      pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./
+      skip("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./
       Puppet[:code] = "$my_hash={'one' => 1}\n$x = mysql_deepmerge($my_hash)"
       expect {
         scope.compiler.compile
@@ -35,57 +35,57 @@
 
     it 'should be able to mysql_deepmerge two hashes' do
       new_hash = scope.function_mysql_deepmerge([{'one' => '1', 'two' => '1'}, {'two' => '2', 'three' => '2'}])
-      new_hash['one'].should   == '1'
-      new_hash['two'].should   == '2'
-      new_hash['three'].should == '2'
+      expect(new_hash['one']).to   eq('1')
+      expect(new_hash['two']).to   eq('2')
+      expect(new_hash['three']).to eq('2')
     end
 
     it 'should mysql_deepmerge multiple hashes' do
       hash = scope.function_mysql_deepmerge([{'one' => 1}, {'one' => '2'}, {'one' => '3'}])
-      hash['one'].should == '3'
+      expect(hash['one']).to eq('3')
     end
 
     it 'should accept empty hashes' do
-      scope.function_mysql_deepmerge([{},{},{}]).should == {}
+      expect(scope.function_mysql_deepmerge([{},{},{}])).to eq({})
     end
 
     it 'should mysql_deepmerge subhashes' do
       hash = scope.function_mysql_deepmerge([{'one' => 1}, {'two' => 2, 'three' => { 'four' => 4 } }])
-      hash['one'].should == 1
-      hash['two'].should == 2
-      hash['three'].should == { 'four' => 4 }
+      expect(hash['one']).to eq(1)
+      expect(hash['two']).to eq(2)
+      expect(hash['three']).to eq({ 'four' => 4 })
     end
 
     it 'should append to subhashes' do
       hash = scope.function_mysql_deepmerge([{'one' => { 'two' => 2 } }, { 'one' => { 'three' => 3 } }])
-      hash['one'].should == { 'two' => 2, 'three' => 3 }
+      expect(hash['one']).to eq({ 'two' => 2, 'three' => 3 })
     end
 
     it 'should append to subhashes 2' do
       hash = scope.function_mysql_deepmerge([{'one' => 1, 'two' => 2, 'three' => { 'four' => 4 } }, {'two' => 'dos', 'three' => { 'five' => 5 } }])
-      hash['one'].should == 1
-      hash['two'].should == 'dos'
-      hash['three'].should == { 'four' => 4, 'five' => 5 }
+      expect(hash['one']).to eq(1)
+      expect(hash['two']).to eq('dos')
+      expect(hash['three']).to eq({ 'four' => 4, 'five' => 5 })
     end
 
     it 'should append to subhashes 3' do
       hash = scope.function_mysql_deepmerge([{ 'key1' => { 'a' => 1, 'b' => 2 }, 'key2' => { 'c' => 3 } }, { 'key1' => { 'b' => 99 } }])
-      hash['key1'].should == { 'a' => 1, 'b' => 99 }
-      hash['key2'].should == { 'c' => 3 }
+      expect(hash['key1']).to eq({ 'a' => 1, 'b' => 99 })
+      expect(hash['key2']).to eq({ 'c' => 3 })
     end
 
     it 'should equate keys mod dash and underscore' do
       hash = scope.function_mysql_deepmerge([{  'a-b-c' => 1 } , { 'a_b_c' => 10 }])
-      hash['a_b_c'].should == 10
-      hash.should_not have_key('a-b-c')
+      expect(hash['a_b_c']).to eq(10)
+      expect(hash).not_to have_key('a-b-c')
     end
 
     it 'should keep style of the last when keys are euqal mod dash and underscore' do
       hash = scope.function_mysql_deepmerge([{  'a-b-c' => 1,  'b_c_d' => { 'c-d-e' => 2, 'e-f-g' => 3 }} , { 'a_b_c' => 10, 'b-c-d' => { 'c_d_e' => 12 } }])
-      hash['a_b_c'].should == 10
-      hash.should_not have_key('a-b-c')
-      hash['b-c-d'].should == { 'e-f-g' => 3, 'c_d_e' => 12 }
-      hash.should_not have_key('b_c_d')
+      expect(hash['a_b_c']).to eq(10)
+      expect(hash).not_to have_key('a-b-c')
+      expect(hash['b-c-d']).to eq({ 'e-f-g' => 3, 'c_d_e' => 12 })
+      expect(hash).not_to have_key('b_c_d')
     end
   end
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/spec/unit/puppet/functions/mysql_password_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe 'the mysql_password function' do
+  before :all do
+    Puppet::Parser::Functions.autoloader.loadall
+  end
+
+  let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
+
+  it 'should exist' do
+    expect(Puppet::Parser::Functions.function('mysql_password')).to eq('function_mysql_password')
+  end
+
+  it 'should raise a ParseError if there is less than 1 arguments' do
+    expect { scope.function_mysql_password([]) }.to( raise_error(Puppet::ParseError))
+  end
+
+  it 'should raise a ParseError if there is more than 1 arguments' do
+    expect { scope.function_mysql_password(%w(foo bar)) }.to( raise_error(Puppet::ParseError))
+  end
+
+  it 'should convert password into a hash' do
+    result = scope.function_mysql_password(%w(password))
+    expect(result).to(eq('*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19'))
+  end
+  
+  it 'should convert an empty password into a empty string' do
+    result = scope.function_mysql_password([""])
+    expect(result).to(eq(''))
+  end
+
+end
--- a/modules/mysql/spec/unit/puppet/provider/database/mysql_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-require 'spec_helper'
-
-provider_class = Puppet::Type.type(:database).provider(:mysql)
-
-describe provider_class do
-  subject { provider_class }
-
-  let(:root_home) { '/root' }
-  let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' }
-
-  let(:raw_databases) do
-    <<-SQL_OUTPUT
-information_schema
-mydb
-mysql
-performance_schema
-test
-    SQL_OUTPUT
-  end
-
-  let(:parsed_databases) { %w(information_schema mydb mysql performance_schema test) }
-
-  before :each do
-    @resource = Puppet::Type::Database.new(
-      { :charset => 'utf8', :name => 'new_database' }
-    )
-    @provider = provider_class.new(@resource)
-    Facter.stubs(:value).with(:root_home).returns(root_home)
-    Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql')
-    subject.stubs(:which).with('mysql').returns('/usr/bin/mysql')
-    subject.stubs(:defaults_file).returns('--defaults-extra-file=/root/.my.cnf')
-  end
-
-  describe 'self.instances' do
-    it 'returns an array of databases' do
-      subject.stubs(:mysql).with([defaults_file, '-NBe', 'show databases']).returns(raw_databases)
-
-      databases = subject.instances.collect {|x| x.name }
-      parsed_databases.should match_array(databases)
-    end
-  end
-
-  describe 'create' do
-    it 'makes a user' do
-      subject.expects(:mysql).with([defaults_file, '-NBe', "create database `#{@resource[:name]}` character set #{@resource[:charset]}"])
-      @provider.create
-    end
-  end
-
-  describe 'destroy' do
-    it 'removes a user if present' do
-      subject.expects(:mysqladmin).with([defaults_file, '-f', 'drop', "#{@resource[:name]}"])
-      @provider.destroy
-    end
-  end
-
-  describe 'charset' do
-    it 'returns a charset' do
-      subject.expects(:mysql).with([defaults_file, '-NBe', "show create database `#{@resource[:name]}`"]).returns('mydbCREATE DATABASE `mydb` /*!40100 DEFAULT CHARACTER SET utf8 */')
-      @provider.charset.should == 'utf8'
-    end
-  end
-
-  describe 'charset=' do
-    it 'changes the charset' do
-      subject.expects(:mysql).with([defaults_file, '-NBe', "alter database `#{@resource[:name]}` CHARACTER SET blah"]).returns('0')
-
-      @provider.charset=('blah')
-    end
-  end
-
-  describe 'exists?' do
-    it 'checks if user exists' do
-      subject.expects(:mysql).with([defaults_file, '-NBe', 'show databases']).returns('information_schema\nmydb\nmysql\nperformance_schema\ntest')
-      @provider.exists?
-    end
-  end
-
-  describe 'self.defaults_file' do
-    it 'sets --defaults-extra-file' do
-      File.stubs(:file?).with('#{root_home}/.my.cnf').returns(true)
-      @provider.defaults_file.should == '--defaults-extra-file=/root/.my.cnf'
-    end
-  end
-
-end
--- a/modules/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-require 'puppet'
-require 'mocha/api'
-require 'spec_helper'
-RSpec.configure do |config|
-  config.mock_with :mocha
-end
-provider_class = Puppet::Type.type(:database_grant).provider(:mysql)
-describe provider_class do
-  let(:root_home) { '/root' }
-
-  before :each do
-    @resource = Puppet::Type::Database_grant.new(
-      { :privileges => 'all', :provider => 'mysql', :name => 'user@host'}
-    )
-    @provider = provider_class.new(@resource)
-    Facter.stubs(:value).with(:root_home).returns(root_home)
-    File.stubs(:file?).with("#{root_home}/.my.cnf").returns(true)
-  end
-
-  it 'should query privileges from the database' do
-    provider_class.expects(:mysql) .with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'describe user']).returns <<-EOT
-Field	Type	Null	Key	Default	Extra
-Host	char(60)	NO	PRI		
-User	char(16)	NO	PRI		
-Password	char(41)	NO			
-Select_priv	enum('N','Y')	NO		N	
-Insert_priv	enum('N','Y')	NO		N	
-Update_priv	enum('N','Y')	NO		N
-EOT
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'describe db']).returns <<-EOT
-Field	Type	Null	Key	Default	Extra
-Host	char(60)	NO	PRI		
-Db	char(64)	NO	PRI		
-User	char(16)	NO	PRI		
-Select_priv	enum('N','Y')	NO		N	
-Insert_priv	enum('N','Y')	NO		N	
-Update_priv	enum('N','Y')	NO		N
-EOT
-    provider_class.user_privs.should == %w(Select_priv Insert_priv Update_priv)
-    provider_class.db_privs.should == %w(Select_priv Insert_priv Update_priv)
-  end
-
-  it 'should query set privileges' do
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "select * from mysql.user where user='user' and host='host'"]).returns <<-EOT
-Host	User	Password	Select_priv	Insert_priv	Update_priv
-host	user		Y	N	Y
-EOT
-    @provider.privileges.should == %w(Select_priv Update_priv)
-  end
-
-  it 'should recognize when all privileges are set' do
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "select * from mysql.user where user='user' and host='host'"]).returns <<-EOT
-Host	User	Password	Select_priv	Insert_priv	Update_priv
-host	user		Y	Y	Y
-EOT
-    @provider.all_privs_set?.should == true
-  end
-
-  it 'should recognize when all privileges are not set' do
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "select * from mysql.user where user='user' and host='host'"]).returns <<-EOT
-Host	User	Password	Select_priv	Insert_priv	Update_priv
-host	user		Y	N	Y
-EOT
-    @provider.all_privs_set?.should == false
-  end
-
-  it 'should be able to set all privileges' do
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n"
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'Y', Update_priv = 'Y' where user='user' and host='host'"])
-    provider_class.expects(:mysqladmin).with(%W(--defaults-extra-file=#{root_home}/.my.cnf flush-privileges))
-    @provider.privileges=(%w(all))
-  end
-
-  it 'should be able to set partial privileges' do
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n"
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'N', Update_priv = 'Y' where user='user' and host='host'"])
-    provider_class.expects(:mysqladmin).with(%W(--defaults-extra-file=#{root_home}/.my.cnf flush-privileges))
-    @provider.privileges=(%w(Select_priv Update_priv))
-  end
-
-  it 'should be case insensitive' do
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n"
-    provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'Y', Update_priv = 'Y' where user='user' and host='host'"])
-    provider_class.expects(:mysqladmin).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'flush-privileges'])
-    @provider.privileges=(%w(SELECT_PRIV insert_priv UpDaTe_pRiV))
-  end
-
-  it 'should not pass --defaults-extra-file if $root_home/.my.cnf is absent' do
-    File.stubs(:file?).with("#{root_home}/.my.cnf").returns(false)
-    provider_class.expects(:mysql).with(['mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n"
-    provider_class.expects(:mysql).with(['mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'N', Update_priv = 'Y' where user='user' and host='host'"])
-    provider_class.expects(:mysqladmin).with(%w(flush-privileges))
-    @provider.privileges=(%w(Select_priv Update_priv))
-  end
-end
--- a/modules/mysql/spec/unit/puppet/provider/database_user/mysql_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-require 'spec_helper'
-
-provider_class = Puppet::Type.type(:database_user).provider(:mysql)
-
-describe provider_class do
-  subject { provider_class }
-
-  let(:root_home) { '/root' }
-  let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' }
-  let(:newhash) { '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' }
-
-  let(:raw_users) do
-    <<-SQL_OUTPUT
-root@127.0.0.1
-root@::1
-@localhost
-debian-sys-maint@localhost
-root@localhost
-usvn_user@localhost
-@vagrant-ubuntu-raring-64
-    SQL_OUTPUT
-  end
-
-  let(:parsed_users) { %w(root@127.0.0.1 root@::1 debian-sys-maint@localhost root@localhost usvn_user@localhost) }
-
-  before :each do
-    # password hash = mypass
-    @resource = Puppet::Type::Database_user.new(
-      { :password_hash => '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4',
-        :name => 'joe@localhost',
-        :max_user_connections => '10'
-      }
-    )
-    @provider = provider_class.new(@resource)
-    Facter.stubs(:value).with(:root_home).returns(root_home)
-    Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql')
-    subject.stubs(:which).with('mysql').returns('/usr/bin/mysql')
-    subject.stubs(:defaults_file).returns('--defaults-extra-file=/root/.my.cnf')
-  end
-
-  describe 'self.instances' do
-    it 'returns an array of users' do
-      subject.stubs(:mysql).with([defaults_file, 'mysql', "-BNeselect concat(User, '@',Host) as User from mysql.user"]).returns(raw_users)
-
-      usernames = subject.instances.collect {|x| x.name }
-      parsed_users.should match_array(usernames)
-    end
-  end
-
-  describe 'create' do
-    it 'makes a user' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "grant usage on *.* to 'joe'@'localhost' identified by PASSWORD
-      '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' with max_user_connections 10"])
-      @provider.expects(:exists?).returns(true)
-      @provider.create.should be_true
-    end
-  end
-
-  describe 'destroy' do
-    it 'removes a user if present' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "drop user 'joe'@'localhost'"])
-      @provider.expects(:exists?).returns(false)
-      @provider.destroy.should be_true
-    end
-  end
-
-  describe 'password_hash' do
-    it 'returns a hash' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-NBe', "select password from mysql.user where CONCAT(user, '@', host) = 'joe@localhost'"]).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4')
-      @provider.password_hash.should == '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'
-    end
-  end
-
-  describe 'password_hash=' do
-    it 'changes the hash' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'"]).returns('0')
-
-      @provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5')
-      @provider.password_hash=('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5')
-    end
-  end
-
-  describe 'max_user_connections' do
-    it 'returns max user connections' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-NBe', "select max_user_connections from mysql.user where CONCAT(user, '@', host) = 'joe@localhost'"]).returns('10')
-      @provider.max_user_connections.should == '10'
-    end
-  end
-
-  describe 'max_user_connections=' do
-    it 'changes max user connections' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "grant usage on *.* to 'joe'@'localhost' with max_user_connections 42"]).returns('0')
-      @provider.expects(:max_user_connections).returns('42')
-      @provider.max_user_connections=('42')
-    end
-  end
-
-  describe 'exists?' do
-    it 'checks if user exists' do
-      subject.expects(:mysql).with([defaults_file, 'mysql', '-NBe', "select '1' from mysql.user where CONCAT(user, '@', host) = 'joe@localhost'"]).returns('1')
-      @provider.exists?.should be_true
-    end
-  end
-
-  describe 'flush' do
-    it 'removes cached privileges' do
-      subject.expects(:mysqladmin).with([defaults_file, 'flush-privileges'])
-      @provider.flush
-    end
-  end
-
-  describe 'self.defaults_file' do
-    it 'sets --defaults-extra-file' do
-      File.stubs(:file?).with('#{root_home}/.my.cnf').returns(true)
-      @provider.defaults_file.should == '--defaults-extra-file=/root/.my.cnf'
-    end
-  end
-
-end
--- a/modules/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -43,7 +43,7 @@
         provider.class.stubs(:mysql).with([defaults_file, '-NBe', "show variables like '%_database'", db.chomp]).returns("character_set_database latin1\ncollation_database  latin1_swedish_ci\nskip_show_database  OFF")
       end
       databases = provider.class.instances.collect {|x| x.name }
-      parsed_databases.should match_array(databases)
+      expect(parsed_databases).to match_array(databases)
     end
   end
 
@@ -56,40 +56,40 @@
 
   describe 'create' do
     it 'makes a database' do
-      provider.expects(:mysql).with([defaults_file, '-NBe', "create database if not exists `#{resource[:name]}` character set #{resource[:charset]} collate #{resource[:collate]}"])
+      provider.expects(:mysql).with([defaults_file, '-NBe', "create database if not exists `#{resource[:name]}` character set `#{resource[:charset]}` collate `#{resource[:collate]}`"])
       provider.expects(:exists?).returns(true)
-      provider.create.should be_true
+      expect(provider.create).to be_truthy
     end
   end
 
   describe 'destroy' do
     it 'removes a database if present' do
-      provider.expects(:mysql).with([defaults_file, '-NBe', "drop database `#{resource[:name]}`"])
+      provider.expects(:mysql).with([defaults_file, '-NBe', "drop database if exists `#{resource[:name]}`"])
       provider.expects(:exists?).returns(false)
-      provider.destroy.should be_true
+      expect(provider.destroy).to be_truthy
     end
   end
 
   describe 'exists?' do
     it 'checks if database exists' do
-      instance.exists?.should be_true
+      expect(instance.exists?).to be_truthy
     end
   end
 
   describe 'self.defaults_file' do
     it 'sets --defaults-extra-file' do
       File.stubs(:file?).with('/root/.my.cnf').returns(true)
-      provider.defaults_file.should eq '--defaults-extra-file=/root/.my.cnf'
+      expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf'
     end
     it 'fails if file missing' do
       File.stubs(:file?).with('/root/.my.cnf').returns(false)
-      provider.defaults_file.should be_nil
+      expect(provider.defaults_file).to be_nil
     end
   end
 
   describe 'charset' do
     it 'returns a charset' do
-      instance.charset.should == 'latin1'
+      expect(instance.charset).to eq('latin1')
     end
   end
 
@@ -103,7 +103,7 @@
 
   describe 'collate' do
     it 'returns a collate' do
-      instance.collate.should == 'latin1_swedish_ci'
+      expect(instance.collate).to eq('latin1_swedish_ci')
     end
   end
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe Puppet::Type.type(:mysql_plugin).provider(:mysql) do
+
+  let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' }
+
+  let(:resource) { Puppet::Type.type(:mysql_plugin).new(
+    { :ensure   => :present,
+      :soname   => 'auth_socket.so',
+      :name     => 'auth_socket',
+      :provider => described_class.name
+    }
+  )}
+  let(:provider) { resource.provider }
+
+  before :each do
+    Facter.stubs(:value).with(:root_home).returns('/root')
+    Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql')
+    File.stubs(:file?).with('/root/.my.cnf').returns(true)
+    provider.class.stubs(:mysql).with([defaults_file, '-NBe', 'show plugins']).returns('auth_socket	ACTIVE	AUTHENTICATION	auth_socket.so	GPL')
+  end
+
+  let(:instance) { provider.class.instances.first }
+
+  describe 'self.prefetch' do
+    it 'exists' do
+      provider.class.instances
+      provider.class.prefetch({})
+    end
+  end
+
+  describe 'create' do
+    it 'loads a plugin' do
+      provider.expects(:mysql).with([defaults_file, '-NBe', "install plugin #{resource[:name]} soname '#{resource[:soname]}'"])
+      provider.expects(:exists?).returns(true)
+      expect(provider.create).to be_truthy
+    end
+  end
+
+  describe 'destroy' do
+    it 'unloads a plugin if present' do
+      provider.expects(:mysql).with([defaults_file, '-NBe', "uninstall plugin #{resource[:name]}"])
+      provider.expects(:exists?).returns(false)
+      expect(provider.destroy).to be_truthy
+    end
+  end
+
+  describe 'exists?' do
+    it 'checks if plugin exists' do
+      expect(instance.exists?).to be_truthy
+    end
+  end
+
+  describe 'self.defaults_file' do
+    it 'sets --defaults-extra-file' do
+      File.stubs(:file?).with('/root/.my.cnf').returns(true)
+      expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf'
+    end
+    it 'fails if file missing' do
+      File.stubs(:file?).with('/root/.my.cnf').returns(false)
+      expect(provider.defaults_file).to be_nil
+    end
+  end
+
+  describe 'soname' do
+    it 'returns a soname' do
+      expect(instance.soname).to eq('auth_socket.so')
+    end
+  end
+
+end
--- a/modules/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -37,7 +37,7 @@
     Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql')
     File.stubs(:file?).with('/root/.my.cnf').returns(true)
     provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT CONCAT(User, '@',Host) AS User FROM mysql.user"]).returns('joe@localhost')
-    provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'"]).returns('10 10 10 10 *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4')
+    provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'"]).returns('10 10 10 10 *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4')
   end
 
   let(:instance) { provider.class.instances.first }
@@ -46,11 +46,11 @@
     it 'returns an array of users' do
       provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT CONCAT(User, '@',Host) AS User FROM mysql.user"]).returns(raw_users)
       parsed_users.each do |user|
-        provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'"]).returns('10 10 10 10 ')
+        provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'"]).returns('10 10 10 10 ')
       end
 
       usernames = provider.class.instances.collect {|x| x.name }
-      parsed_users.should match_array(usernames)
+      expect(parsed_users).to match_array(usernames)
     end
   end
 
@@ -63,9 +63,10 @@
 
   describe 'create' do
     it 'makes a user' do
-      provider.expects(:mysql).with([defaults_file, '-e', "GRANT USAGE ON *.* TO 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10"])
+      provider.expects(:mysql).with([defaults_file, '-e', "CREATE USER 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'"])
+      provider.expects(:mysql).with([defaults_file, '-e', "GRANT USAGE ON *.* TO 'joe'@'localhost' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10"])
       provider.expects(:exists?).returns(true)
-      provider.create.should be_true
+      expect(provider.create).to be_truthy
     end
   end
 
@@ -73,30 +74,30 @@
     it 'removes a user if present' do
       provider.expects(:mysql).with([defaults_file, '-e', "DROP USER 'joe'@'localhost'"])
       provider.expects(:exists?).returns(false)
-      provider.destroy.should be_true
+      expect(provider.destroy).to be_truthy
     end
   end
 
   describe 'exists?' do
     it 'checks if user exists' do
-      instance.exists?.should be_true
+      expect(instance.exists?).to be_truthy
     end
   end
 
   describe 'self.defaults_file' do
     it 'sets --defaults-extra-file' do
       File.stubs(:file?).with('/root/.my.cnf').returns(true)
-      provider.defaults_file.should eq '--defaults-extra-file=/root/.my.cnf'
+      expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf'
     end
     it 'fails if file missing' do
       File.expects(:file?).with('/root/.my.cnf').returns(false)
-      provider.defaults_file.should be_nil
+      expect(provider.defaults_file).to be_nil
     end
   end
 
   describe 'password_hash' do
     it 'returns a hash' do
-      instance.password_hash.should == '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'
+      expect(instance.password_hash).to eq('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4')
     end
   end
 
@@ -114,7 +115,7 @@
 
     describe property do
       it "returns #{property}" do
-        instance.send("#{property}".to_sym).should == '10'
+        expect(instance.send("#{property}".to_sym)).to eq('10')
       end
     end
 
--- a/modules/mysql/spec/unit/puppet/type/mysql_database_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/unit/puppet/type/mysql_database_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -7,17 +7,17 @@
   end
 
   it 'should accept a database name' do
-    @user[:name].should == 'test'
+    expect(@user[:name]).to eq('test')
   end
 
   it 'should accept a charset' do
     @user[:charset] = 'latin1'
-    @user[:charset].should == 'latin1'
+    expect(@user[:charset]).to eq('latin1')
   end
 
   it 'should accept a collate' do
     @user[:collate] = 'latin1_swedish_ci'
-    @user[:collate].should == 'latin1_swedish_ci'
+    expect(@user[:collate]).to eq('latin1_swedish_ci')
   end
 
   it 'should require a name' do
--- a/modules/mysql/spec/unit/puppet/type/mysql_grant_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/unit/puppet/type/mysql_grant_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -7,32 +7,32 @@
   end
 
   it 'should accept a grant name' do
-    @user[:name].should == 'foo@localhost/*.*'
+    expect(@user[:name]).to eq('foo@localhost/*.*')
   end
   
   it 'should accept ALL privileges' do
     @user[:privileges] = 'ALL'
-    @user[:privileges].should == ['ALL']
+    expect(@user[:privileges]).to eq(['ALL'])
   end
 
   it 'should accept PROXY privilege' do
     @user[:privileges] = 'PROXY'
-    @user[:privileges].should == ['PROXY']
+    expect(@user[:privileges]).to eq(['PROXY'])
   end
   
   it 'should accept a table' do
     @user[:table] = '*.*'
-    @user[:table].should == '*.*'
+    expect(@user[:table]).to eq('*.*')
   end
   
   it 'should accept @ for table' do
     @user[:table] = '@'
-    @user[:table].should == '@'
+    expect(@user[:table]).to eq('@')
   end
   
   it 'should accept a user' do
     @user[:user] = 'foo@localhost'
-    @user[:user].should == 'foo@localhost'
+    expect(@user[:user]).to eq('foo@localhost')
   end
   
   it 'should require a name' do
@@ -41,4 +41,34 @@
     }.to raise_error(Puppet::Error, 'Title or name must be provided')
   end
 
-end
\ No newline at end of file
+  it 'should require the name to match the user and table' do
+    expect {
+      Puppet::Type.type(:mysql_grant).new(:name => 'foo', :privileges => ['ALL', 'PROXY'], :table => ['*.*','@'], :user => 'foo@localhost')
+    }.to raise_error /name must match user and table parameters/
+  end
+
+  describe 'it should munge privileges' do
+
+    it 'to just ALL' do
+      @user = Puppet::Type.type(:mysql_grant).new(
+        :name => 'foo@localhost/*.*',  :table => ['*.*','@'], :user => 'foo@localhost',
+        :privileges => ['ALL', 'PROXY'] )
+      expect(@user[:privileges]).to eq(['ALL'])
+    end
+
+    it 'to upcase and ordered' do
+      @user = Puppet::Type.type(:mysql_grant).new(
+        :name => 'foo@localhost/*.*',  :table => ['*.*','@'], :user => 'foo@localhost',
+        :privileges => ['select', 'Insert'] )
+      expect(@user[:privileges]).to eq(['INSERT', 'SELECT'])
+    end
+
+    it 'ordered including column privileges' do
+      @user = Puppet::Type.type(:mysql_grant).new(
+        :name => 'foo@localhost/*.*',  :table => ['*.*','@'], :user => 'foo@localhost',
+        :privileges => ['SELECT(Host,Address)', 'Insert'] )
+      expect(@user[:privileges]).to eq(['INSERT', 'SELECT (Address, Host)'])
+    end
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/spec/unit/puppet/type/mysql_plugin_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,24 @@
+require 'puppet'
+require 'puppet/type/mysql_plugin'
+describe Puppet::Type.type(:mysql_plugin) do
+
+  before :each do
+    @plugin = Puppet::Type.type(:mysql_plugin).new(:name => 'test', :soname => 'test.so')
+  end
+
+  it 'should accept a plugin name' do
+    expect(@plugin[:name]).to eq('test')
+  end
+
+  it 'should accept a library name' do
+    @plugin[:soname] = 'test.so'
+    expect(@plugin[:soname]).to eq('test.so')
+  end
+
+  it 'should require a name' do
+    expect {
+      Puppet::Type.type(:mysql_plugin).new({})
+    }.to raise_error(Puppet::Error, 'Title or name must be provided')
+  end
+
+end
--- a/modules/mysql/spec/unit/puppet/type/mysql_user_spec.rb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/spec/unit/puppet/type/mysql_user_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -2,23 +2,10 @@
 require 'puppet/type/mysql_user'
 describe Puppet::Type.type(:mysql_user) do
 
-  before :each do
-    @user = Puppet::Type.type(:mysql_user).new(:name => 'foo@localhost', :password_hash => 'pass')
-  end
-
-  it 'should accept a user name' do
-    @user[:name].should == 'foo@localhost'
-  end
-
   it 'should fail with a long user name' do
     expect {
       Puppet::Type.type(:mysql_user).new({:name => '12345678901234567@localhost', :password_hash => 'pass'})
-      }.to raise_error /MySQL usernames are limited to a maximum of 16 characters/
-  end
-
-  it 'should accept a password' do
-    @user[:password_hash] = 'foo'
-    @user[:password_hash].should == 'foo'
+    }.to raise_error /MySQL usernames are limited to a maximum of 16 characters/
   end
 
   it 'should require a name' do
@@ -27,4 +14,94 @@
     }.to raise_error(Puppet::Error, 'Title or name must be provided')
   end
 
+  context 'using foo@localhost' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => 'foo@localhost', :password_hash => 'pass')
+    end
+
+    it 'should accept a user name' do
+      expect(@user[:name]).to eq('foo@localhost')
+    end
+
+    it 'should accept a password' do
+      @user[:password_hash] = 'foo'
+      expect(@user[:password_hash]).to eq('foo')
+    end
+  end
+
+  context 'using foo@LocalHost' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => 'foo@LocalHost', :password_hash => 'pass')
+    end
+
+    it 'should lowercase the user name' do
+      expect(@user[:name]).to eq('foo@localhost')
+    end
+  end
+
+  context 'using allo_wed$char@localhost' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => 'allo_wed$char@localhost', :password_hash => 'pass')
+    end
+
+    it 'should accept a user name' do
+      expect(@user[:name]).to eq('allo_wed$char@localhost')
+    end
+  end
+
+  context 'ensure the default \'debian-sys-main\'@localhost user can be parsed' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => '\'debian-sys-maint\'@localhost', :password_hash => 'pass')
+    end
+
+    it 'should accept a user name' do
+      expect(@user[:name]).to eq('\'debian-sys-maint\'@localhost')
+    end
+  end
+
+  context 'using a quoted 16 char username' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => '"debian-sys-maint"@localhost', :password_hash => 'pass')
+    end
+
+    it 'should accept a user name' do
+      expect(@user[:name]).to eq('"debian-sys-maint"@localhost')
+    end
+  end
+
+  context 'using a quoted username that is too long ' do
+    it 'should fail with a size error' do
+      expect {
+        Puppet::Type.type(:mysql_user).new(:name => '"debian-sys-maint2"@localhost', :password_hash => 'pass')
+      }.to raise_error /MySQL usernames are limited to a maximum of 16 characters/
+    end
+  end
+
+  context 'using `speci!al#`@localhost' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => '`speci!al#`@localhost', :password_hash => 'pass')
+    end
+
+    it 'should accept a quoted user name with special chatracters' do
+      expect(@user[:name]).to eq('`speci!al#`@localhost')
+    end
+  end
+
+  context 'using in-valid@localhost' do
+    before :each do
+      @user = Puppet::Type.type(:mysql_user).new(:name => 'in-valid@localhost', :password_hash => 'pass')
+    end
+
+    it 'should accept a user name with special chatracters' do
+      expect(@user[:name]).to eq('in-valid@localhost')
+    end
+  end
+
+  context 'using "misquoted@localhost' do
+    it 'should fail with a misquoted username is used' do
+      expect {
+        Puppet::Type.type(:mysql_user).new(:name => '"misquoted@localhost', :password_hash => 'pass')
+      }.to raise_error /Invalid database user "misquoted@localhost/
+    end
+  end
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/mysql/templates/meb.cnf.erb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,18 @@
+### MANAGED BY PUPPET ###
+
+<% @options.sort.map do |k,v| -%>
+<%   if v.is_a?(Hash) -%>
+[<%=   k %>]
+<%     v.sort.map do |ki, vi| -%>
+<%       if vi == true or v == '' -%>
+<%=        ki %>
+<%       elsif vi.is_a?(Array) -%>
+<%         vi.each do |vii| -%>
+<%=          ki %> = <%= vii %>
+<%         end -%>
+<%       elsif ![nil, '', :undef].include?(vi) -%>
+<%=        ki %> = <%= vi %>
+<%       end -%>
+<%     end -%>
+<%   end %>
+<% end -%>
--- a/modules/mysql/templates/my.cnf.erb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/templates/my.cnf.erb	Mon Mar 09 01:34:59 2015 +0000
@@ -1,3 +1,5 @@
+### MANAGED BY PUPPET ###
+
 <% @options.sort.map do |k,v| -%>
 <%   if v.is_a?(Hash) -%>
 [<%=   k %>]
@@ -10,11 +12,13 @@
 <%         vi.each do |vii| -%>
 <%=          ki %> = <%= vii %>
 <%         end -%>
-<%       elsif vi != :undef -%>
+<%       elsif ![nil, '', :undef].include?(vi) -%>
 <%=        ki %> = <%= vi %>
 <%       end -%>
 <%     end -%>
 <%   end %>
 <% end -%>
 
-!includedir /etc/mysql/conf.d/
+<% if @includedir and @includedir != '' %>
+!includedir <%= @includedir %>
+<% end %>
--- a/modules/mysql/templates/my.conf.cnf.erb	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-### MANAGED BY PUPPET ###
-<% @settings.sort.each do |section, content|      -%>
-[<%= section %>]
-<%   content.sort.each do |key, values|          -%>
-<%     [values].flatten.sort.each do |value|     -%>
-<%= !value ? '#' : '' %><%= key -%><%=
-         case value
-         when true, false
-           ''
-         else
-           " = #{value}"
-         end
-%>
-<%     end                                       -%>
-<%   end                                         -%>
-
-<% end                                           -%>
--- a/modules/mysql/templates/mysqlbackup.sh.erb	Mon Mar 09 00:58:19 2015 +0000
+++ b/modules/mysql/templates/mysqlbackup.sh.erb	Mon Mar 09 01:34:59 2015 +0000
@@ -40,7 +40,7 @@
 <% end -%>
 <% if @backupdatabases.empty? -%>
 <% if @file_per_database -%>
-mysql -s -r -N -e 'SHOW DATABASES' | while read dbname
+mysql -u${USER} -p${PASS} -s -r -N -e 'SHOW DATABASES' | while read dbname
 do
   mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \
     ${EVENTS} \
--- a/modules/mysql/tests/backup.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-class { 'mysql::server':
-  config_hash => {'root_password' => 'password'}
-}
-class { 'mysql::backup':
-  backupuser     => 'myuser',
-  backuppassword => 'mypassword',
-  backupdir      => '/tmp/backups',
-}
--- a/modules/mysql/tests/bindings.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-class { 'mysql::bindings':
-  php_enable => 'true',
-}
--- a/modules/mysql/tests/init.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-include mysql
--- a/modules/mysql/tests/java.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-class { 'mysql::java':}
--- a/modules/mysql/tests/mysql_database.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-class { 'mysql::server':
-  config_hash => {'root_password' => 'password'}
-}
-database{ ['test1', 'test2', 'test3']:
-  ensure  => present,
-  charset => 'utf8',
-  require => Class['mysql::server'],
-}
-database{ 'test4':
-  ensure  => present,
-  charset => 'latin1',
-}
--- a/modules/mysql/tests/mysql_db.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-class { 'mysql::server':
-  config_hash => {'root_password' => 'password'}
-}
-mysql::db { 'mydb':
-  user     => 'myuser',
-  password => 'mypass',
-  host     => 'localhost',
-  grant    => ['SELECT', 'UPDATE'],
-}
-mysql::db { "mydb_${fqdn}":
-  user     => 'myuser',
-  password => 'mypass',
-  dbname   => 'mydb',
-  host     => $::fqdn,
-  grant    => ['SELECT', 'UPDATE'],
-  tag      => $domain,
-}
--- a/modules/mysql/tests/mysql_grant.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-mysql_grant{'test1@localhost/redmine.*':
-  user       => 'test1@localhost',
-  table      => 'redmine.*',
-  privileges => ['UPDATE'],
-}
--- a/modules/mysql/tests/mysql_user.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-$mysql_root_pw = 'password'
-
-class { 'mysql::server':
-  config_hash => {
-    root_password => 'password',
-  }
-}
-
-mysql_user{ 'redmine@localhost':
-  ensure        => present,
-  password_hash => mysql_password('redmine'),
-  require       => Class['mysql::server'],
-}
-
-mysql_user{ 'dan@localhost':
-  ensure        => present,
-  password_hash => mysql_password('blah')
-}
-
-mysql_user{ 'dan@%':
-  ensure        => present,
-  password_hash => mysql_password('blah'),
-}
--- a/modules/mysql/tests/perl.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-include mysql::perl
--- a/modules/mysql/tests/python.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-class { 'mysql::python':}
--- a/modules/mysql/tests/ruby.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-include mysql::ruby
--- a/modules/mysql/tests/server.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-class { 'mysql::server':
-  root_password => 'password',
-}
--- a/modules/mysql/tests/server/account_security.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-class { 'mysql::server':
-  config_hash => { 'root_password' => 'password', },
-}
-class { 'mysql::server::account_security': }
--- a/modules/mysql/tests/server/config.pp	Mon Mar 09 00:58:19 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-mysql::server::config { 'testfile':
-  settings => {
-    'mysqld' => {
-      'bind-address' => '0.0.0.0',
-      'read-only'    => true,
-    },
-    'client' => {
-      'port' => '3306'
-    }
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/Gemfile	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,7 @@
+source "https://rubygems.org"
+
+group :development, :test do
+  gem 'bodeco_module_helper', :git => 'https://github.com/bodeco/bodeco_module_helper.git'
+  gem 'rspec', "~> 2.11.0", :require => false
+  gem 'mocha', "~> 0.10.5", :require => false
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/LICENSE	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,11 @@
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/README.md	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,70 @@
+# Staging module for Puppet
+
+Manages staging directory, along with download/extraction of compressed files.
+
+[![Build Status](https://secure.travis-ci.org/nanliu/puppet-staging.png?branch=master)](http://travis-ci.org/nanliu/puppet-staging)
+
+WARNING: Version 0.2.0 no longer uses hiera functions. The same behavior should be available in Puppet 3.0.
+
+NOTE: Version 1.0.0 will be the last feature release. New functionality such as checksum will be implemented in a type/provider module [puppet-archive](https://www.github.com/nanliu/puppet-archive).
+
+## Usage
+
+Specify a different default staging path (must be declared before using resource):
+```puppet
+class { 'staging':
+  path  => '/var/staging',
+  owner => 'puppet',
+  group => 'puppet',
+}
+```
+
+Staging files from various sources:
+```puppet
+staging::file { 'sample':
+  source => 'puppet://modules/staging/sample',
+}
+
+staging::file { 'apache-tomcat-6.0.35':
+  source => 'http://apache.cs.utah.edu/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz',
+}
+```
+
+Staging and extracting files:
+```puppet
+staging::file { 'sample.tar.gz':
+  source => 'puppet:///modules/staging/sample.tar.gz'
+}
+
+staging::extract { 'sample.tar.gz':
+  target  => '/tmp/staging',
+  creates => '/tmp/staging/sample',
+  require => Staging::File['sample.tar.gz'],
+}
+```
+
+Deploying a file (combining staging and extract):
+```puppet
+staging::deploy { 'sample.tar.gz':
+  source => 'puppet:///modules/staging/sample.tar.gz',
+  target => '/usr/local',
+}
+```
+
+Staging files currently support the following source:
+
+* http(s)://
+* puppet://
+* ftp://
+* s3:// (requires aws cli to be installed and configured.)
+* local (though this doesn't serve any real purpose.)
+
+## Contributor
+
+* Adrien Thebo
+* gizero
+* Harald Skoglund
+* Hunter Haugen
+* Justin Clayton
+* Owen Jacobson
+* Reid Vandewiele
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/Rakefile	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+require 'bodeco_module_helper/rake_tasks'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/Vagrantfile	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,29 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+Vagrant.configure("2") do |config|
+  # All Vagrant configuration is done here. The most common configuration
+  # options are documented and commented below. For a complete reference,
+  # please see the online documentation at vagrantup.com.
+
+  #config.vm.synced_folder "manifests", "/tmp/manifests", "tests"
+  config.vm.synced_folder "./", "/etc/puppet/modules/staging"
+
+  config.vm.define :staging do |m|
+    m.vm.box = "centos63"
+    m.vm.box_url = "https://dl.dropbox.com/s/eqdrqnla4na8qax/centos63.box"
+
+    m.vm.hostname = 'staging'
+    m.vm.provider :vmware_fusion do |v|
+      v.vmx["displayName"] = "staging"
+      v.vmx["memsize"] = 512
+      v.vmx["numvcpus"] = 4
+    end
+
+    m.vm.provision :puppet do |puppet|
+      puppet.manifests_path = "tests"
+      puppet.module_path    = "spec/fixtures/modules/"
+      puppet.manifest_file  = "init.pp"
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/checksums.json	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,37 @@
+{
+  "Gemfile": "270871f3fe4a93fd7c2df1666717cabb",
+  "LICENSE": "cdbf8d74b765504fbdf8e154bb4458a1",
+  "README.md": "af449710d996f056cf718289ff800af7",
+  "Rakefile": "a56b311c2311136cccbc2fdc3490dc77",
+  "Vagrantfile": "eef71b5e6febf751053145cc86752758",
+  "docs/deploy.html": "d9342a62d811e8d331c83a9521d7cb60",
+  "docs/extract.html": "ddfc7718ed639b24861b5a1d68cf0b0c",
+  "docs/file.html": "879efe671f96a8eaeb5fb023ced5b7ab",
+  "docs/init.html": "c16ebedfb85ab41558ad2fdea679177d",
+  "files/sample": "c668e2074cc870f9ef59b6b33ffda097",
+  "files/sample.tar.bz2": "d063a08e52797236df1b769dbdbd2c69",
+  "files/sample.tar.gz": "eba24f899d8b439f5c461f728563ece6",
+  "lib/facter/staging_http_get.rb": "1f89d73e82135c4c9533119e2a95690d",
+  "lib/facter/staging_windir.rb": "6897a62c26ff15041cae6b62bf9b7a35",
+  "lib/puppet/parser/functions/scope_defaults.rb": "da916d46f3ff3be8359f75c93c2b5532",
+  "lib/puppet/parser/functions/staging_parse.rb": "cf6a6648b567b6efd3d0741e5f690ca8",
+  "manifests/deploy.pp": "f2a3e783e69ab878ac2e115fdf897e52",
+  "manifests/extract.pp": "b4ac5a23a1e307c46764bca61c28e21c",
+  "manifests/file.pp": "9488fefca4c3aca6d293ae0d76db84aa",
+  "manifests/init.pp": "8363cfb8b0ec3c378a9b58e4749c57ee",
+  "manifests/params.pp": "229aaeafe695fa94e8c5b5602f4a9f77",
+  "metadata.json": "85161c511984d3151e884c448e7aaae8",
+  "spec/defines/staging_deploy_spec.rb": "d4310153779c65fb168ab275c81c7f36",
+  "spec/defines/staging_extract_spec.rb": "1aa0aa8419fcf98f374e3a62e2d01958",
+  "spec/defines/staging_file_spec.rb": "a11f653feeb70cd8631de5f69aaa4dcb",
+  "spec/fixtures/hiera.yaml": "324e9873e29c1970850f01b0f64c0269",
+  "spec/spec_helper.rb": "050ea9cc153b1c6da1db53cf6cf214a8",
+  "spec/unit/puppet/parser/functions/scope_defaults_spec.rb": "16a2af56511497feb7417e226161221f",
+  "spec/unit/puppet/parser/functions/staging_parse_spec.rb": "1a811cf7ca52b4b6fe3fe59ae0ebd38b",
+  "tests/deploy.pp": "27e2deca3fd5353dc8aa37c9162b2368",
+  "tests/extract.pp": "8076b72133e9240500b8e04eea023918",
+  "tests/file.pp": "8588e9c8b5e2ca430271ba2045dfb286",
+  "tests/init.pp": "d889d06d4cb307c38cba9267aaac978f",
+  "tests/scope_defaults.pp": "b620b367fc13aad2ff6f09f0c18463ba",
+  "tests/staging_parse.pp": "bec31082b383ef5bf6f81e4e82ec3b94"
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/docs/deploy.html	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=utf-8">
+  <title>deploy.pp</title>
+  <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
+</head>
+<body>
+<div id='container'>
+  <div id="background"></div>
+  <div id="jump_to">
+    Jump To &hellip;
+    <div id="jump_wrapper">
+      <div id="jump_page">
+          <a class="source" href="deploy.html">deploy.pp</a>
+          <a class="source" href="extract.html">extract.pp</a>
+          <a class="source" href="file.html">file.pp</a>
+          <a class="source" href="init.html">init.pp</a>
+      </div>
+    </div>
+  </div>
+  <table cellspacing=0 cellpadding=0>
+  <thead>
+    <tr>
+      <th class=docs><h1>deploy.pp</h1></th>
+      <th class=code></th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr id='section-Define:_staging::deploy'>
+      <td class=docs>
+        <div class="pilwrap">
+          <a class="pilcrow" href="#section-Define:_staging::deploy">&#182;</a>
+        </div>
+        <h3>Define: staging::deploy</h3>
+
+<p>The define resource extracts compressed file to a staging location.</p>
+
+<h3>Parameters:</h3>
+
+<ul>
+<li>[<em>source</em>]:  the source file location, supports local files, puppet://, http://, https://, ftp:// (default: )</li>
+<li>[<em>target</em>]:  the target extraction directory (default: )</li>
+<li>[<em>staging_path</em>]:  the staging location for compressed file. defaults to ${staging::path}/${caller_module_name} (default: undef)</li>
+<li>[<em>username</em>]:  https or ftp username (default: undef)</li>
+<li>[<em>certificate</em>]:  https certifcate file (default: undef)</li>
+<li>[<em>password</em>]:  https or ftp user password or https certificate password (default: undef)</li>
+<li>[<em>environment</em>]:  environment variable for settings such as http_proxy (default: undef)</li>
+<li>[<em>timeout</em>]:  the time to wait for the file transfer to complete (default: undef)</li>
+<li>[<em>user</em>]:  extract file as this user (default: undef)</li>
+<li>[<em>group</em>]:  extract group as this group (default: undef)</li>
+<li>[<em>creates</em>]:  the file/folder created after extraction. if unspecified defaults to ${target}/${name} (default: undef)</li>
+<li>[<em>unless</em>]:  alternative way to conditionally extract file (default: undef)</li>
+<li>[<em>onlyif</em>]:  alternative way to conditionally extract file (default: undef)</li>
+</ul>
+
+
+<h3>Usage:</h3>
+
+<pre><code>staging::deploy { 'sample.tar.gz':
+  source =&gt; 'puppet:///modules/staging/sample.tar.gz',
+  target =&gt; '/usr/local',
+}
+</code></pre>
+
+      </td>
+      <td class=code>
+        <div class='highlight'><pre><span class="n">define</span> <span class="n">staging</span><span class="o">::</span><span class="n">deploy</span> <span class="p">(</span>
+  <span class="vg">$source</span><span class="p">,</span>               
+  <span class="vg">$target</span><span class="p">,</span>               
+  <span class="vg">$staging_path</span> <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$username</span>     <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$certificate</span>  <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$password</span>     <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$environment</span>  <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$timeout</span>      <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$user</span>         <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$group</span>        <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$creates</span>      <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$unless</span>       <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$onlyif</span>       <span class="o">=</span> <span class="k">undef</span>  
+<span class="p">)</span> <span class="p">{</span>
+
+  <span class="n">staging</span><span class="o">::</span><span class="n">file</span> <span class="p">{</span> <span class="vg">$name</span><span class="p">:</span>
+    <span class="n">source</span>      <span class="o">=&gt;</span> <span class="vg">$source</span><span class="p">,</span>
+    <span class="n">target</span>      <span class="o">=&gt;</span> <span class="vg">$staging_path</span><span class="p">,</span>
+    <span class="n">username</span>    <span class="o">=&gt;</span> <span class="vg">$username</span><span class="p">,</span>
+    <span class="n">certificate</span> <span class="o">=&gt;</span> <span class="vg">$certificate</span><span class="p">,</span>
+    <span class="n">password</span>    <span class="o">=&gt;</span> <span class="vg">$password</span><span class="p">,</span>
+    <span class="n">environment</span> <span class="o">=&gt;</span> <span class="vg">$environment</span><span class="p">,</span>
+    <span class="n">subdir</span>      <span class="o">=&gt;</span> <span class="vg">$caller_module_name</span><span class="p">,</span>
+    <span class="n">timeout</span>     <span class="o">=&gt;</span> <span class="vg">$timeout</span><span class="p">,</span>
+  <span class="p">}</span>
+
+  <span class="n">staging</span><span class="o">::</span><span class="n">extract</span> <span class="p">{</span> <span class="vg">$name</span><span class="p">:</span>
+    <span class="n">target</span>      <span class="o">=&gt;</span> <span class="vg">$target</span><span class="p">,</span>
+    <span class="n">source</span>      <span class="o">=&gt;</span> <span class="vg">$staging_path</span><span class="p">,</span>
+    <span class="n">user</span>        <span class="o">=&gt;</span> <span class="vg">$user</span><span class="p">,</span>
+    <span class="n">group</span>       <span class="o">=&gt;</span> <span class="vg">$group</span><span class="p">,</span>
+    <span class="n">environment</span> <span class="o">=&gt;</span> <span class="vg">$environment</span><span class="p">,</span>
+    <span class="n">subdir</span>      <span class="o">=&gt;</span> <span class="vg">$caller_module_name</span><span class="p">,</span>
+    <span class="n">creates</span>     <span class="o">=&gt;</span> <span class="vg">$creates</span><span class="p">,</span>
+    <span class="k">unless</span>      <span class="o">=&gt;</span> <span class="vg">$unless</span><span class="p">,</span>
+    <span class="n">onlyif</span>      <span class="o">=&gt;</span> <span class="vg">$onlyif</span><span class="p">,</span>
+    <span class="nb">require</span>     <span class="o">=&gt;</span> <span class="no">Staging</span><span class="o">::</span><span class="no">File</span><span class="o">[</span><span class="vg">$name</span><span class="o">]</span><span class="p">,</span>
+  <span class="p">}</span>
+
+<span class="p">}</span></pre></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</body>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/docs/extract.html	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=utf-8">
+  <title>extract.pp</title>
+  <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
+</head>
+<body>
+<div id='container'>
+  <div id="background"></div>
+  <div id="jump_to">
+    Jump To &hellip;
+    <div id="jump_wrapper">
+      <div id="jump_page">
+          <a class="source" href="deploy.html">deploy.pp</a>
+          <a class="source" href="extract.html">extract.pp</a>
+          <a class="source" href="file.html">file.pp</a>
+          <a class="source" href="init.html">init.pp</a>
+      </div>
+    </div>
+  </div>
+  <table cellspacing=0 cellpadding=0>
+  <thead>
+    <tr>
+      <th class=docs><h1>extract.pp</h1></th>
+      <th class=code></th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr id='section-Define:_staging::extract'>
+      <td class=docs>
+        <div class="pilwrap">
+          <a class="pilcrow" href="#section-Define:_staging::extract">&#182;</a>
+        </div>
+        <h3>Define: staging::extract</h3>
+
+<p>Define resource to extract files from staging directories to target directories.</p>
+
+<h3>Parameters:</h3>
+
+<ul>
+<li>[<em>target</em>]:  the target extraction directory (default: )</li>
+<li>[<em>source</em>]:  the source compression file, supports tar, tar.gz, zip, war (default: undef)</li>
+<li>[<em>creates</em>]:  the file created after extraction. if unspecified defaults ${staging::path}/${caller_module_name}/${name} ${target}/${name} (default: undef)</li>
+<li>[<em>unless</em>]:  alternative way to conditionally check whether to extract file. (default: undef)</li>
+<li>[<em>onlyif</em>]:  alternative way to conditionally check whether to extract file. (default: undef)</li>
+<li>[<em>user</em>]:  extract file as this user. (default: undef)</li>
+<li>[<em>group</em>]:   extract file as this group. (default: undef)</li>
+<li>[<em>environment</em>]:  environment variables. (default: undef)</li>
+<li>[<em>subdir</em>]:  subdir per module in staging directory. (default: $caller_module_name)</li>
+</ul>
+
+
+<h3>Usage:</h3>
+
+<pre><code>$caller_module_name = 'demo'
+
+class { 'staging':
+  path =&gt; '/tmp/staging',
+}
+
+staging::file { 'sample.tar.gz':
+  source =&gt; 'puppet:///modules/staging/sample.tar.gz'
+}
+
+staging::extract { 'sample.tar.gz':
+  target  =&gt; '/tmp/staging',
+  creates =&gt; '/tmp/staging/sample',
+  require =&gt; Staging::File['sample.tar.gz'],
+}
+</code></pre>
+      </td>
+      <td class=code>
+        <div class='highlight'><pre><span class="n">define</span> <span class="n">staging</span><span class="o">::</span><span class="n">extract</span> <span class="p">(</span>
+  <span class="vg">$target</span><span class="p">,</span>              
+  <span class="vg">$source</span>      <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$creates</span>     <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$unless</span>      <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$onlyif</span>      <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$user</span>        <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$group</span>       <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$environment</span> <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$subdir</span>      <span class="o">=</span> <span class="vg">$caller_module_name</span> 
+<span class="p">)</span> <span class="p">{</span>
+
+  <span class="kp">include</span> <span class="n">staging</span>
+
+  <span class="k">if</span> <span class="vg">$source</span> <span class="p">{</span>
+    <span class="vg">$source_path</span> <span class="o">=</span> <span class="vg">$source</span>
+  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+    <span class="vg">$source_path</span> <span class="o">=</span> <span class="s2">&quot;${staging::path}/${subdir}/${name}&quot;</span>
+  <span class="p">}</span></pre></div>
+      </td>
+    </tr>
+    <tr id='section-2'>
+      <td class=docs>
+        <div class="pilwrap">
+          <a class="pilcrow" href="#section-2">&#182;</a>
+        </div>
+        <p>Use user supplied creates path, set default value if creates, unless or
+onlyif is not supplied.</p>
+
+      </td>
+      <td class=code>
+        <div class='highlight'><pre>  <span class="k">if</span> <span class="vg">$creates</span> <span class="p">{</span>
+    <span class="vg">$creates_path</span> <span class="o">=</span> <span class="vg">$creates</span>
+  <span class="p">}</span> <span class="k">elsif</span> <span class="o">!</span> <span class="p">(</span><span class="vg">$unless</span> <span class="ow">or</span> <span class="vg">$onlyif</span><span class="p">)</span> <span class="p">{</span>
+    <span class="k">if</span> <span class="vg">$name</span> <span class="o">=~</span> <span class="sr">/.tar.gz$/</span> <span class="p">{</span>
+      <span class="vg">$folder</span>       <span class="o">=</span> <span class="n">staging_parse</span><span class="p">(</span><span class="vg">$name</span><span class="p">,</span> <span class="s1">&#39;basename&#39;</span><span class="p">,</span> <span class="s1">&#39;.tar.gz&#39;</span><span class="p">)</span>
+      <span class="vg">$creates_path</span> <span class="o">=</span> <span class="s2">&quot;${target}/${folder}&quot;</span>
+    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+      <span class="vg">$folder</span>       <span class="o">=</span> <span class="n">staging_parse</span><span class="p">(</span><span class="vg">$name</span><span class="p">,</span> <span class="s1">&#39;basename&#39;</span><span class="p">)</span>
+      <span class="vg">$creates_path</span> <span class="o">=</span> <span class="s2">&quot;${target}/${folder}&quot;</span>
+    <span class="p">}</span>
+  <span class="p">}</span>
+
+  <span class="k">if</span> <span class="n">scope_defaults</span><span class="p">(</span><span class="s1">&#39;Exec&#39;</span><span class="p">,</span><span class="s1">&#39;path&#39;</span><span class="p">)</span> <span class="p">{</span>
+    <span class="no">Exec</span><span class="p">{</span>
+      <span class="n">cwd</span>         <span class="o">=&gt;</span> <span class="vg">$target</span><span class="p">,</span>
+      <span class="n">user</span>        <span class="o">=&gt;</span> <span class="vg">$user</span><span class="p">,</span>
+      <span class="n">group</span>       <span class="o">=&gt;</span> <span class="vg">$group</span><span class="p">,</span>
+      <span class="n">environment</span> <span class="o">=&gt;</span> <span class="vg">$environment</span><span class="p">,</span>
+      <span class="n">creates</span>     <span class="o">=&gt;</span> <span class="vg">$creates_path</span><span class="p">,</span>
+      <span class="k">unless</span>      <span class="o">=&gt;</span> <span class="vg">$unless</span><span class="p">,</span>
+      <span class="n">onlyif</span>      <span class="o">=&gt;</span> <span class="vg">$onlyif</span><span class="p">,</span>
+      <span class="n">logoutput</span>   <span class="o">=&gt;</span> <span class="n">on_failure</span><span class="p">,</span>
+    <span class="p">}</span>
+  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+    <span class="no">Exec</span><span class="p">{</span>
+      <span class="n">path</span>        <span class="o">=&gt;</span> <span class="vg">$:</span><span class="ss">:path</span><span class="p">,</span>
+      <span class="n">cwd</span>         <span class="o">=&gt;</span> <span class="vg">$target</span><span class="p">,</span>
+      <span class="n">user</span>        <span class="o">=&gt;</span> <span class="vg">$user</span><span class="p">,</span>
+      <span class="n">group</span>       <span class="o">=&gt;</span> <span class="vg">$group</span><span class="p">,</span>
+      <span class="n">environment</span> <span class="o">=&gt;</span> <span class="vg">$environment</span><span class="p">,</span>
+      <span class="n">creates</span>     <span class="o">=&gt;</span> <span class="vg">$creates_path</span><span class="p">,</span>
+      <span class="k">unless</span>      <span class="o">=&gt;</span> <span class="vg">$unless</span><span class="p">,</span>
+      <span class="n">onlyif</span>      <span class="o">=&gt;</span> <span class="vg">$onlyif</span><span class="p">,</span>
+      <span class="n">logoutput</span>   <span class="o">=&gt;</span> <span class="n">on_failure</span><span class="p">,</span>
+    <span class="p">}</span>
+  <span class="p">}</span>
+
+  <span class="k">case</span> <span class="vg">$name</span> <span class="p">{</span>
+    <span class="sr">/.tar$/</span><span class="p">:</span> <span class="p">{</span>
+      <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;tar xf ${source_path}&quot;</span>
+    <span class="p">}</span>
+
+    <span class="sr">/(.tgz|.tar.gz)$/</span><span class="p">:</span> <span class="p">{</span>
+      <span class="k">if</span> <span class="vg">$:</span><span class="ss">:osfamily</span> <span class="o">==</span> <span class="s1">&#39;Solaris&#39;</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;gunzip -dc &lt; ${source_path} | tar xf - &quot;</span>
+      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;tar xzf ${source_path}&quot;</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+
+    <span class="sr">/.zip$/</span><span class="p">:</span> <span class="p">{</span>
+      <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;unzip ${source_path}&quot;</span>
+    <span class="p">}</span>
+
+    <span class="sr">/.war$/</span><span class="p">:</span> <span class="p">{</span>
+      <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;jar xf ${source_path}&quot;</span>
+    <span class="p">}</span>
+
+    <span class="n">default</span><span class="p">:</span> <span class="p">{</span>
+      <span class="nb">fail</span><span class="p">(</span><span class="s2">&quot;staging::extract: unsupported file format ${name}.&quot;</span><span class="p">)</span>
+    <span class="p">}</span>
+  <span class="p">}</span>
+
+  <span class="nb">exec</span> <span class="p">{</span> <span class="s2">&quot;extract ${name}&quot;</span><span class="p">:</span>
+    <span class="n">command</span> <span class="o">=&gt;</span> <span class="vg">$command</span><span class="p">,</span>
+  <span class="p">}</span>
+<span class="p">}</span></pre></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</body>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/docs/file.html	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=utf-8">
+  <title>file.pp</title>
+  <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
+</head>
+<body>
+<div id='container'>
+  <div id="background"></div>
+  <div id="jump_to">
+    Jump To &hellip;
+    <div id="jump_wrapper">
+      <div id="jump_page">
+          <a class="source" href="deploy.html">deploy.pp</a>
+          <a class="source" href="extract.html">extract.pp</a>
+          <a class="source" href="file.html">file.pp</a>
+          <a class="source" href="init.html">init.pp</a>
+      </div>
+    </div>
+  </div>
+  <table cellspacing=0 cellpadding=0>
+  <thead>
+    <tr>
+      <th class=docs><h1>file.pp</h1></th>
+      <th class=code></th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr id='section-Define:_staging::file'>
+      <td class=docs>
+        <div class="pilwrap">
+          <a class="pilcrow" href="#section-Define:_staging::file">&#182;</a>
+        </div>
+        <h3>Define: staging::file</h3>
+
+<h4>Overview:</h4>
+
+<p>Define resource to retrieve files to staging directories. It is
+intententionally not replacing files, as these intend to be large binaries
+that are versioned.</p>
+
+<h4>Notes:</h4>
+
+<p>  If you specify a different staging location, please manage the file
+  resource as necessary.</p>
+
+<h3>Parameters:</h3>
+
+<ul>
+<li>[<em>source</em>]:  the source file location, supports local files, puppet://, http://, https://, ftp:// (default: )</li>
+<li>[<em>target</em>]:  the target staging directory, if unspecified ${staging::path}/${caller_module_name} (default: undef)</li>
+<li>[<em>username</em>]:  https or ftp username (default: undef)</li>
+<li>[<em>certificate</em>]:  https certificate file (default: undef)</li>
+<li>[<em>password</em>]:  https or ftp user password or https certificate password (default: undef)</li>
+<li>[<em>environment</em>]:  environment variable for settings such as http_proxy, https_proxy, of ftp_proxy (default: undef)</li>
+<li>[<em>timeout</em>]:  the the time to wait for the file transfer to complete (default: undef)</li>
+<li>[<em>subdir</em>]:  (default: $caller_module_name)</li>
+</ul>
+
+
+<h3>Usage:</h3>
+
+<pre><code>$caller_module_name = 'demo'
+
+class { 'staging':
+  path =&gt; '/tmp/staging',
+}
+
+staging::file { 'sample':
+  source =&gt; 'puppet:///modules/staging/sample',
+}
+
+staging::file { 'passwd':
+  source =&gt; '/etc/passwd',
+}
+
+staging::file { 'manpage.html':
+  source =&gt; 'http://curl.haxx.se/docs/manpage.html',
+}
+</code></pre>
+
+      </td>
+      <td class=code>
+        <div class='highlight'><pre><span class="n">define</span> <span class="n">staging</span><span class="o">::</span><span class="n">file</span> <span class="p">(</span>
+  <span class="vg">$source</span><span class="p">,</span>              
+  <span class="vg">$target</span>      <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$username</span>    <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$certificate</span> <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$password</span>    <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$environment</span> <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$timeout</span>     <span class="o">=</span> <span class="k">undef</span><span class="p">,</span> 
+  <span class="vg">$subdir</span>      <span class="o">=</span> <span class="vg">$caller_module_name</span>
+<span class="p">)</span> <span class="p">{</span>
+
+  <span class="kp">include</span> <span class="n">staging</span>
+
+  <span class="k">if</span> <span class="vg">$target</span> <span class="p">{</span>
+    <span class="vg">$target_file</span> <span class="o">=</span> <span class="vg">$target</span>
+    <span class="vg">$staging_dir</span> <span class="o">=</span> <span class="n">staging_parse</span><span class="p">(</span><span class="vg">$target</span><span class="p">,</span> <span class="s1">&#39;parent&#39;</span><span class="p">)</span>
+  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+    <span class="vg">$staging_dir</span> <span class="o">=</span> <span class="s2">&quot;${staging::path}/${subdir}&quot;</span>
+    <span class="vg">$target_file</span> <span class="o">=</span> <span class="s2">&quot;${staging_dir}/${name}&quot;</span>
+
+    <span class="k">if</span> <span class="o">!</span> <span class="n">defined</span><span class="p">(</span><span class="no">File</span><span class="o">[</span><span class="vg">$staging_dir</span><span class="o">]</span><span class="p">)</span> <span class="p">{</span>
+      <span class="n">file</span> <span class="p">{</span> <span class="vg">$staging_dir</span><span class="p">:</span>
+        <span class="k">ensure</span><span class="o">=&gt;</span><span class="n">directory</span><span class="p">,</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+  <span class="p">}</span>
+
+  <span class="no">Exec</span> <span class="p">{</span>
+    <span class="n">path</span>        <span class="o">=&gt;</span> <span class="s1">&#39;/usr/local/bin:/usr/bin:/bin&#39;</span><span class="p">,</span>
+    <span class="n">environment</span> <span class="o">=&gt;</span> <span class="vg">$environment</span><span class="p">,</span>
+    <span class="n">cwd</span>         <span class="o">=&gt;</span> <span class="vg">$staging_dir</span><span class="p">,</span>
+    <span class="n">creates</span>     <span class="o">=&gt;</span> <span class="vg">$target_file</span><span class="p">,</span>
+    <span class="n">timeout</span>     <span class="o">=&gt;</span> <span class="vg">$timeout</span><span class="p">,</span>
+    <span class="n">logoutput</span>   <span class="o">=&gt;</span> <span class="n">on_failure</span><span class="p">,</span>
+  <span class="p">}</span>
+
+  <span class="k">case</span> <span class="vg">$source</span> <span class="p">{</span>
+    <span class="sr">/^\//</span><span class="p">:</span> <span class="p">{</span>
+      <span class="n">file</span> <span class="p">{</span> <span class="vg">$target_file</span><span class="p">:</span>
+        <span class="n">source</span>  <span class="o">=&gt;</span> <span class="vg">$source</span><span class="p">,</span>
+        <span class="n">replace</span> <span class="o">=&gt;</span> <span class="kp">false</span><span class="p">,</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+
+    <span class="sr">/^puppet:\/\//</span><span class="p">:</span> <span class="p">{</span>
+      <span class="n">file</span> <span class="p">{</span> <span class="vg">$target_file</span><span class="p">:</span>
+        <span class="n">source</span>  <span class="o">=&gt;</span> <span class="vg">$source</span><span class="p">,</span>
+        <span class="n">replace</span> <span class="o">=&gt;</span> <span class="kp">false</span><span class="p">,</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+
+    <span class="sr">/^http:\/\//</span><span class="p">:</span> <span class="p">{</span>
+      <span class="nb">exec</span> <span class="p">{</span> <span class="vg">$target_file</span><span class="p">:</span>
+        <span class="n">command</span>     <span class="o">=&gt;</span> <span class="s2">&quot;curl -L -o ${name} ${source}&quot;</span><span class="p">,</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+
+    <span class="sr">/^https:\/\//</span><span class="p">:</span> <span class="p">{</span>
+      <span class="k">if</span> <span class="vg">$username</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;curl -L -o ${name} -u ${username}:${password} ${source}&quot;</span>
+      <span class="p">}</span> <span class="k">elsif</span> <span class="vg">$certificate</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;curl -L -o ${name} -E ${certificate}:${password} ${source}&quot;</span>
+      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;curl -L -o ${name} ${source}&quot;</span>
+      <span class="p">}</span>
+
+      <span class="nb">exec</span> <span class="p">{</span> <span class="vg">$target_file</span><span class="p">:</span>
+        <span class="n">command</span>     <span class="o">=&gt;</span> <span class="vg">$command</span><span class="p">,</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+
+    <span class="sr">/^ftp:\/\//</span><span class="p">:</span> <span class="p">{</span>
+      <span class="k">if</span> <span class="vg">$username</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;curl -o ${name} -u ${username}:${password} ${source}&quot;</span>
+      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+        <span class="vg">$command</span> <span class="o">=</span> <span class="s2">&quot;curl -o ${name} ${source}&quot;</span>
+      <span class="p">}</span>
+
+      <span class="nb">exec</span> <span class="p">{</span> <span class="vg">$target_file</span><span class="p">:</span>
+        <span class="n">command</span>     <span class="o">=&gt;</span> <span class="vg">$command</span><span class="p">,</span>
+      <span class="p">}</span>
+    <span class="p">}</span>
+
+    <span class="n">default</span><span class="p">:</span> <span class="p">{</span>
+      <span class="nb">fail</span><span class="p">(</span><span class="s2">&quot;stage::file: do not recognize source ${source}.&quot;</span><span class="p">)</span>
+    <span class="p">}</span>
+  <span class="p">}</span>
+
+<span class="p">}</span></pre></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</body>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/docs/init.html	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=utf-8">
+  <title>init.pp</title>
+  <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
+</head>
+<body>
+<div id='container'>
+  <div id="background"></div>
+  <div id="jump_to">
+    Jump To &hellip;
+    <div id="jump_wrapper">
+      <div id="jump_page">
+          <a class="source" href="deploy.html">deploy.pp</a>
+          <a class="source" href="extract.html">extract.pp</a>
+          <a class="source" href="file.html">file.pp</a>
+          <a class="source" href="init.html">init.pp</a>
+      </div>
+    </div>
+  </div>
+  <table cellspacing=0 cellpadding=0>
+  <thead>
+    <tr>
+      <th class=docs><h1>init.pp</h1></th>
+      <th class=code></th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr id='section-Class:_staging'>
+      <td class=docs>
+        <div class="pilwrap">
+          <a class="pilcrow" href="#section-Class:_staging">&#182;</a>
+        </div>
+        <h3>Class: staging</h3>
+
+<p>This module manages staging and extraction of files from various sources.</p>
+
+<h4>Actions:</h4>
+
+<p>Creates the root staging directory. By default files will be created in a subdirectory matching the caller_module_name.</p>
+
+<p>   /opt/staging/</p>
+
+<pre><code>          |-- puppet
+          |   `-- puppet.enterprise.2.0.tar.gz
+          `-- tomcat
+              `-- tomcat.5.0.tar.gz
+</code></pre>
+
+<h3>Parameters:</h3>
+
+<ul>
+<li>[<em>path</em>]:  staging directory filepath (default: &lsquo;/opt/staging&rsquo;)</li>
+<li>[<em>owner</em>]:  staging directory owner (default: &lsquo;0&rsquo;)</li>
+<li>[<em>group</em>]:  staging directory group (default: &lsquo;0&rsquo;)</li>
+<li>[<em>mode</em>]:  staging directory permission (default: &lsquo;0755&rsquo;)</li>
+</ul>
+
+
+<h3>Usage:</h3>
+
+<pre><code>include staging
+</code></pre>
+
+      </td>
+      <td class=code>
+        <div class='highlight'><pre><span class="k">class</span> <span class="n">staging</span> <span class="p">(</span>
+  <span class="vg">$path</span>  <span class="o">=</span> <span class="s1">&#39;/opt/staging&#39;</span><span class="p">,</span> 
+  <span class="vg">$owner</span> <span class="o">=</span> <span class="s1">&#39;0&#39;</span><span class="p">,</span>            
+  <span class="vg">$group</span> <span class="o">=</span> <span class="s1">&#39;0&#39;</span><span class="p">,</span>            
+  <span class="vg">$mode</span>  <span class="o">=</span> <span class="s1">&#39;0755&#39;</span>          
+<span class="p">)</span> <span class="p">{</span>
+
+  <span class="n">file</span> <span class="p">{</span> <span class="vg">$path</span><span class="p">:</span>
+    <span class="k">ensure</span> <span class="o">=&gt;</span> <span class="n">directory</span><span class="p">,</span>
+    <span class="n">owner</span>  <span class="o">=&gt;</span> <span class="vg">$owner</span><span class="p">,</span>
+    <span class="n">group</span>  <span class="o">=&gt;</span> <span class="vg">$group</span><span class="p">,</span>
+    <span class="n">mode</span>   <span class="o">=&gt;</span> <span class="vg">$mode</span><span class="p">,</span>
+  <span class="p">}</span>
+
+<span class="p">}</span></pre></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</body>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/files/sample	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+sample file to test module.
Binary file modules/staging/files/sample.tar.bz2 has changed
Binary file modules/staging/files/sample.tar.gz has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/lib/facter/staging_http_get.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,29 @@
+Facter.add("staging_http_get") do
+  setcode do
+
+    fact = nil
+
+    which = lambda do |cmd|
+      result = nil
+      exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
+      ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+        exts.each do |ext|
+          exe = File.join(path, "#{cmd}#{ext}")
+          result = exe if File.executable? exe
+          break if result
+        end
+        break if result
+      end
+      result
+    end
+
+    ['powershell', 'curl', 'wget'].each do |cmd|
+      available = which.call(cmd)
+      fact = available ? cmd : nil
+      break if fact
+    end
+
+    fact
+
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/lib/facter/staging_windir.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,11 @@
+Facter.add(:staging_windir) do
+  confine :osfamily => :windows
+  setcode do
+    program_data = `echo %SYSTEMDRIVE%\\ProgramData`.chomp
+    if File.directory? program_data
+      "#{program_data}\\staging"
+    else
+      "C:\\staging"
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/lib/puppet/parser/functions/scope_defaults.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,17 @@
+module Puppet::Parser::Functions
+  newfunction(:scope_defaults, :type => :rvalue, :doc => <<-EOS
+Determine if specified resource defaults have a attribute defined in
+current scope.
+EOS
+  ) do |arguments|
+
+    raise(Puppet::ParseError, "scope_defaults(): Wrong number of arguments " +
+      "given (#{arguments.size} for 2)") if arguments.size != 2
+
+    # auto capitalize puppet resource for lookup:
+    res_type = arguments[0].split('::').collect{ |x| x.capitalize }.join('::')
+    res_attr = arguments[1]
+
+    return self.lookupdefaults(res_type).has_key?(res_attr.to_sym)
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/lib/puppet/parser/functions/staging_parse.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,36 @@
+require 'uri'
+
+module Puppet::Parser::Functions
+  newfunction(:staging_parse, :type => :rvalue, :doc => <<-EOS
+Parse filepath to retrieve information about the file.
+    EOS
+  ) do |arguments|
+
+    raise(Puppet::ParseError, "staging_parse(): Wrong number of arguments " +
+      "given (#{arguments.size} for 1, 2, 3)") if arguments.size < 1 || arguments.size > 3
+
+    source    = arguments[0]
+    path      = URI.parse(source.gsub('\\', '/')).path
+
+    raise Puppet::ParseError, "staging_parse(): #{source.inspect} has no URI " +
+      "'path' component" if path.nil?
+
+    info      = arguments[1] ? arguments[1] : 'filename'
+    extension = arguments[2] ? arguments[2] : File.extname(path)
+
+    case info
+    when 'filename'
+      result = File.basename(path)
+    when 'basename'
+      result = File.basename(path, extension)
+    when 'extname'
+      result = File.extname(path)
+    when 'parent'
+      result = File.expand_path(File.join(path, '..'))
+    else
+      raise Puppet::ParseError, "staging_parse(), unknown parse info #{info}."
+    end
+
+    return result
+  end
+end
Binary file modules/staging/manifests/.init.pp.swp has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/manifests/deploy.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,42 @@
+# The define resource extracts compressed file to a staging location.
+define staging::deploy (
+  $source,               #: the source file location, supports local files, puppet://, http://, https://, ftp://
+  $target,               #: the target extraction directory
+  $staging_path = undef, #: the staging location for compressed file. defaults to ${staging::path}/${caller_module_name}
+  $username     = undef, #: https or ftp username
+  $certificate  = undef, #: https certifcate file
+  $password     = undef, #: https or ftp user password or https certificate password
+  $environment  = undef, #: environment variable for settings such as http_proxy
+  $timeout      = undef, #: the time to wait for the file transfer to complete
+  $user         = undef, #: extract file as this user
+  $group        = undef, #: extract group as this group
+  $creates      = undef, #: the file/folder created after extraction. if unspecified defaults to ${target}/${name}
+  $unless       = undef, #: alternative way to conditionally extract file
+  $onlyif       = undef  #: alternative way to conditionally extract file
+) {
+
+  staging::file { $name:
+    source      => $source,
+    target      => $staging_path,
+    username    => $username,
+    certificate => $certificate,
+    password    => $password,
+    environment => $environment,
+    subdir      => $caller_module_name,
+    timeout     => $timeout,
+  }
+
+  staging::extract { $name:
+    target      => $target,
+    source      => $staging_path,
+    user        => $user,
+    group       => $group,
+    environment => $environment,
+    subdir      => $caller_module_name,
+    creates     => $creates,
+    unless      => $unless,
+    onlyif      => $onlyif,
+    require     => Staging::File[$name],
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/manifests/extract.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,109 @@
+# Define resource to extract files from staging directories to target directories.
+define staging::extract (
+  $target,              #: the target extraction directory
+  $source      = undef, #: the source compression file, supports tar, tar.gz, zip, war
+  $creates     = undef, #: the file created after extraction. if unspecified defaults ${staging::path}/${caller_module_name}/${name} ${target}/${name}
+  $unless      = undef, #: alternative way to conditionally check whether to extract file.
+  $onlyif      = undef, #: alternative way to conditionally check whether to extract file.
+  $user        = undef, #: extract file as this user.
+  $group       = undef, #:  extract file as this group.
+  $environment = undef, #: environment variables.
+  $strip       = undef, #: extract file with the --strip=X option. Only works with GNU tar.
+  $subdir      = $caller_module_name #: subdir per module in staging directory.
+) {
+
+  include staging
+
+  if $source {
+    $source_path = $source
+  } else {
+    $source_path = "${staging::path}/${subdir}/${name}"
+  }
+
+  # Use user supplied creates path, set default value if creates, unless or
+  # onlyif is not supplied.
+  if $creates {
+    $creates_path = $creates
+  } elsif ! ($unless or $onlyif) {
+    if $name =~ /.tar.gz$/ {
+      $folder       = staging_parse($name, 'basename', '.tar.gz')
+    } elsif $name =~ /.tar.bz2$/ {
+      $folder       = staging_parse($name, 'basename', '.tar.bz2')
+    } else {
+      $folder       = staging_parse($name, 'basename')
+    }
+    $creates_path = "${target}/${folder}"
+  } else {
+    $creates_path = undef
+  }
+
+  if scope_defaults('Exec','path') {
+    Exec{
+      cwd         => $target,
+      user        => $user,
+      group       => $group,
+      environment => $environment,
+      creates     => $creates_path,
+      unless      => $unless,
+      onlyif      => $onlyif,
+      logoutput   => on_failure,
+    }
+  } else {
+    Exec{
+      path        => $::path,
+      cwd         => $target,
+      user        => $user,
+      group       => $group,
+      environment => $environment,
+      creates     => $creates_path,
+      unless      => $unless,
+      onlyif      => $onlyif,
+      logoutput   => on_failure,
+    }
+  }
+
+  if $strip {
+    if $::osfamily == 'Solaris' or $name !~ /(.tar|.tgz|.tar.gz|.tar.bz2)$/ {
+      warning('strip is only supported with GNU tar, ignoring the parameter')
+      $strip_opt = ''
+    } else {
+      $strip_opt = " --strip=${strip}"
+    }
+  } else {
+    $strip_opt = ''
+  }
+
+  case $name {
+    /.tar$/: {
+      $command = "tar xf ${source_path}${strip_opt}"
+    }
+
+    /(.tgz|.tar.gz)$/: {
+      if $::osfamily == 'Solaris' {
+        $command = "gunzip -dc < ${source_path} | tar xf - "
+      } else {
+        $command = "tar xzf ${source_path}${strip_opt}"
+      }
+    }
+
+    /.tar.bz2$/: {
+      $command = "tar xjf ${source_path}${strip_opt}"
+    }
+
+    /.zip$/: {
+      $command = "unzip ${source_path}"
+    }
+
+    /(.war|.jar)$/: {
+      $command = "jar xf ${source_path}"
+    }
+
+    default: {
+      fail("staging::extract: unsupported file format ${name}.")
+    }
+  }
+
+  exec { "extract ${name}":
+    command => $command,
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/manifests/file.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,125 @@
+# #### Overview:
+#
+# Define resource to retrieve files to staging directories. It is
+# intententionally not replacing files, as these intend to be large binaries
+# that are versioned.
+#
+# #### Notes:
+#
+#   If you specify a different staging location, please manage the file
+#   resource as necessary.
+#
+define staging::file (
+  $source,              #: the source file location, supports local files, puppet://, http://, https://, ftp://, s3://
+  $target      = undef, #: the target file location, if unspecified ${staging::path}/${subdir}/${name}
+  $username    = undef, #: https or ftp username
+  $certificate = undef, #: https certificate file
+  $password    = undef, #: https or ftp user password or https certificate password
+  $environment = undef, #: environment variable for settings such as http_proxy, https_proxy, of ftp_proxy
+  $timeout     = undef, #: the the time to wait for the file transfer to complete
+  $curl_option = undef, #: options to pass to curl
+  $wget_option = undef, #: options to pass to wget
+  $tries       = undef, #: amount of retries for the file transfer when non transient connection errors exist
+  $try_sleep   = undef, #: time to wait between retries for the file transfer
+  $subdir      = $caller_module_name
+) {
+
+  include staging
+
+  $quoted_source = shellquote($source)
+
+  if $target {
+    $target_file = $target
+    $staging_dir = staging_parse($target, 'parent')
+  } else {
+    $staging_dir = "${staging::path}/${subdir}"
+    $target_file = "${staging_dir}/${name}"
+
+    if ! defined(File[$staging_dir]) {
+      file { $staging_dir:
+        ensure=>directory,
+      }
+    }
+  }
+
+  Exec {
+    path        => $staging::exec_path,
+    environment => $environment,
+    cwd         => $staging_dir,
+    creates     => $target_file,
+    timeout     => $timeout,
+    try_sleep   => $try_sleep,
+    tries       => $tries,
+    logoutput   => on_failure,
+  }
+
+  case $::staging_http_get {
+    'curl', default: {
+      $http_get        = "curl ${curl_option} -f -L -o ${target_file} ${quoted_source}"
+      $http_get_passwd = "curl ${curl_option} -f -L -o ${target_file} -u ${username}:${password} ${quoted_source}"
+      $http_get_cert   = "curl ${curl_option} -f -L -o ${target_file} -E ${certificate}:${password} ${quoted_source}"
+      $ftp_get         = "curl ${curl_option} -o ${target_file} ${quoted_source}"
+      $ftp_get_passwd  = "curl ${curl_option} -o ${target_file} -u ${username}:${password} ${quoted_source}"
+    }
+    'wget': {
+      $http_get        = "wget ${wget_option} -O ${target_file} ${quoted_source}"
+      $http_get_passwd = "wget ${wget_option} -O ${target_file} --user=${username} --password=${password} ${quoted_source}"
+      $http_get_cert   = "wget ${wget_option} -O ${target_file} --user=${username} --certificate=${certificate} ${quoted_source}"
+      $ftp_get         = $http_get
+      $ftp_get_passwd  = $http_get_passwd
+    }
+    'powershell':{
+      $http_get           = "powershell.exe -Command \"\$wc = New-Object System.Net.WebClient;\$wc.DownloadFile('${source}','${target_file}')\""
+      $ftp_get            = $http_get
+      $http_get_password  = "powershell.exe -Command \"\$wc = (New-Object System.Net.WebClient);\$wc.Credentials = New-Object System.Net.NetworkCredential('${username}','${password}');\$wc.DownloadFile(${source},${target_file})\""
+      $ftp_get_password   = $http_get_password
+    }
+  }
+
+  case $source {
+    /^\//: {
+      file { $target_file:
+        source  => $source,
+        replace => false,
+      }
+    }
+    /^puppet:\/\//: {
+      file { $target_file:
+        source  => $source,
+        replace => false,
+      }
+    }
+    /^http:\/\//: {
+      if $username { $command = $http_get_passwd }
+      else         { $command = $http_get        }
+      exec { $target_file:
+        command   => $command,
+      }
+    }
+    /^https:\/\//: {
+      if $username       { $command = $http_get_passwd }
+      elsif $certificate { $command = $http_get_cert   }
+      else               { $command = $http_get        }
+      exec { $target_file:
+        command => $command,
+      }
+    }
+    /^ftp:\/\//: {
+      if $username       { $command = $ftp_get_passwd }
+      else               { $command = $ftp_get        }
+      exec { $target_file:
+        command     => $command,
+      }
+    }
+    /^s3:\/\//: {
+      $command = "aws s3 cp ${source} ${target_file}"
+      exec { $target_file:
+        command   => $command,
+      }
+    }
+    default: {
+      fail("staging::file: do not recognize source ${source}.")
+    }
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/manifests/init.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,31 @@
+#   This module manages staging and extraction of files from various sources.
+#
+# #### Actions:
+#
+#   Creates the root staging directory. By default files will be created in a subdirectory matching the caller_module_name.
+#
+#      /opt/staging/
+#                 |-- puppet
+#                 |   `-- puppet.enterprise.2.0.tar.gz
+#                 `-- tomcat
+#                     `-- tomcat.5.0.tar.gz
+#
+class staging (
+  $path      = $staging::params::path,     #: staging directory filepath
+  $owner     = $staging::params::owner,    #: staging directory owner
+  $group     = $staging::params::group,    #: staging directory group
+  $mode      = $staging::params::mode,     #: staging directory permission
+  $exec_path = $staging::params::exec_path #: executable default path
+) inherits staging::params {
+
+  # Resolve conflict with pe_staging
+  if !defined(File[$path]) {
+    file { $path:
+      ensure => directory,
+      owner  => $owner,
+      group  => $group,
+      mode   => $mode,
+    }
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/manifests/params.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,33 @@
+# OS specific parameters
+class staging::params {
+  case $::osfamily {
+    default: {
+      $path      = '/opt/staging'
+      $owner     = '0'
+      $group     = '0'
+      $mode      = '0755'
+      $exec_path = '/usr/local/bin:/usr/bin:/bin'
+    }
+    'Solaris': {
+      $path      = '/opt/staging'
+      $owner     = '0'
+      $group     = '0'
+      $mode      = '0755'
+      $exec_path = '/usr/local/bin:/usr/bin:/bin:/usr/sfw/bin'
+    }
+    'windows': {
+      $path      = $::staging_windir
+      $owner     = 'S-1-5-32-544' # Adminstrators
+      $group     = 'S-1-5-18'     # SYSTEM
+      $mode      = '0660'
+      $exec_path = $::path
+    }
+    'FreeBSD': {
+      $path      = '/var/tmp/staging'
+      $owner     = '0'
+      $group     = '0'
+      $mode      = '0755'
+      $exec_path = '/usr/local/bin:/usr/bin:/bin'
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/metadata.json	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,106 @@
+{
+  "name": "nanliu-staging",
+  "version": "1.0.3",
+  "author": "Nan Liu",
+  "summary": "Compressed file staging and deployment",
+  "license": "Apache-2.0",
+  "source": "git://github.com/nanliu/puppet-staging",
+  "project_page": "https://github.com/nanliu/puppet-staging",
+  "issues_url": "https://github.com/nanliu/puppet-staging/issues",
+  "operatingsystem_support": [
+    {
+      "operatingsystem": "RedHat",
+      "operatingsystemrelease": [
+        "4",
+        "5",
+        "6"
+      ]
+    },
+    {
+      "operatingsystem": "CentOS",
+      "operatingsystemrelease": [
+        "4",
+        "5",
+        "6"
+      ]
+    },
+    {
+      "operatingsystem": "OracleLinux",
+      "operatingsystemrelease": [
+        "4",
+        "5",
+        "6"
+      ]
+    },
+    {
+      "operatingsystem": "Scientific",
+      "operatingsystemrelease": [
+        "4",
+        "5",
+        "6"
+      ]
+    },
+    {
+      "operatingsystem": "SLES",
+      "operatingsystemrelease": [
+        "11 SP1"
+      ]
+    },
+    {
+      "operatingsystem": "Debian",
+      "operatingsystemrelease": [
+        "6",
+        "7"
+      ]
+    },
+    {
+      "operatingsystem": "Ubuntu",
+      "operatingsystemrelease": [
+        "10.04",
+        "12.04"
+      ]
+    },
+    {
+      "operatingsystem": "Solaris",
+      "operatingsystemrelease": [
+        "10",
+        "11"
+      ]
+    },
+    {
+      "operatingsystem": "Windows",
+      "operatingsystemrelease": [
+        "Server 2003",
+        "Server 2003 R2",
+        "Server 2008",
+        "Server 2008 R2",
+        "Server 2012",
+        "Server 2012 R2",
+        "7",
+        "8"
+      ]
+    },
+    {
+      "operatingsystem": "AIX",
+      "operatingsystemrelease": [
+        "5.3",
+        "6.1",
+        "7.1"
+      ]
+    }
+  ],
+  "requirements": [
+    {
+      "name": "pe",
+      "version_requirement": "3.x"
+    },
+    {
+      "name": "puppet",
+      "version_requirement": ">=2.7.0 <4.0.0"
+    }
+  ],
+  "description": "Manages compressed file staging and deployment.",
+  "dependencies": [
+  
+  ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/defines/staging_deploy_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,27 @@
+require 'spec_helper'
+describe 'staging::deploy', :type => :define do
+
+  # forcing a more sane caller_module_name to match real usage.
+  let(:facts) { { :caller_module_name => 'spec',
+                  :osfamily           => 'RedHat',
+                  :staging_http_get   => 'curl',
+                  :path               => '/usr/local/bin:/usr/bin:/bin', } }
+
+  describe 'when deploying tar.gz' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'puppet:///modules/staging/sample.tar.gz',
+      :target => '/usr/local' } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_file('/opt/staging/spec/sample.tar.gz')
+      should contain_exec('extract sample.tar.gz').with({
+        :command => 'tar xzf /opt/staging/spec/sample.tar.gz',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/usr/local',
+        :creates => '/usr/local/sample'
+      })
+    }
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/defines/staging_extract_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,104 @@
+require 'spec_helper'
+describe 'staging::extract', :type => :define do
+
+  # forcing a more sane caller_module_name to match real usage.
+  let(:facts) { { :caller_module_name => 'spec',
+                  :osfamily           => 'RedHat',
+                  :path               => '/usr/local/bin:/usr/bin:/bin' } }
+
+  describe 'when deploying tar.gz' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :target => '/opt' } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('extract sample.tar.gz').with({
+        :command => 'tar xzf /opt/staging/spec/sample.tar.gz',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/opt',
+        :creates => '/opt/sample'
+      })
+    }
+  end
+
+  describe 'when deploying tar.gz with strip' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :target => '/opt',
+                     :strip  => 1, } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('extract sample.tar.gz').with({
+        :command => 'tar xzf /opt/staging/spec/sample.tar.gz --strip=1',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/opt',
+        :creates => '/opt/sample'
+      })
+    }
+  end
+
+  describe 'when deploying zip' do
+    let(:title) { 'sample.zip' }
+    let(:params) { { :target => '/opt' } }
+
+    it { should contain_file('/opt/staging')
+      should contain_exec('extract sample.zip').with({
+        :command => 'unzip /opt/staging/spec/sample.zip',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/opt',
+        :creates => '/opt/sample'
+      })
+    }
+  end
+
+  describe 'when deploying zip with strip (noop)' do
+    let(:title) { 'sample.zip' }
+    let(:params) { { :target => '/opt',
+                     :strip  => 1, } }
+
+    it { should contain_file('/opt/staging')
+      should contain_exec('extract sample.zip').with({
+        :command => 'unzip /opt/staging/spec/sample.zip',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/opt',
+        :creates => '/opt/sample'
+      })
+    }
+  end
+
+  describe 'when deploying war' do
+    let(:title) { 'sample.war' }
+    let(:params) { { :target => '/opt' } }
+
+    it { should contain_file('/opt/staging')
+      should contain_exec('extract sample.war').with({
+        :command => 'jar xf /opt/staging/spec/sample.war',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/opt',
+        :creates => '/opt/sample'
+      })
+    }
+  end
+
+  describe 'when deploying war with strip (noop)' do
+    let(:title) { 'sample.war' }
+    let(:params) { { :target => '/opt',
+                     :strip  => 1, } }
+
+    it { should contain_file('/opt/staging')
+      should contain_exec('extract sample.war').with({
+        :command => 'jar xf /opt/staging/spec/sample.war',
+        :path    => '/usr/local/bin:/usr/bin:/bin',
+        :cwd     => '/opt',
+        :creates => '/opt/sample'
+      })
+    }
+  end
+
+  describe 'when deploying unknown' do
+     let(:title) { 'sample.zzz'}
+     let(:params) { { :target => '/opt' } }
+
+     it { expect { should contain_exec("exec sample.zzz") }.to raise_error(Puppet::Error) }
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/defines/staging_file_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,163 @@
+require 'spec_helper'
+describe 'staging::file', :type => :define do
+
+  # forcing a more sane caller_module_name to match real usage.
+  let(:facts) { { :caller_module_name => 'spec',
+                  :osfamily           => 'RedHat',
+                  :staging_http_get   => 'curl' } }
+
+  describe 'when deploying via puppet' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'puppet:///modules/staging/sample.tar.gz' } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_file('/opt/staging/spec/sample.tar.gz')
+      should_not contain_exec('/opt/staging/spec/sample.tar.gz')
+    }
+  end
+
+  describe 'when deploying via local' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => '/nfs/sample.tar.gz',
+      :target => '/usr/local/sample.tar.gz',
+    } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_file('/usr/local/sample.tar.gz')
+      should_not contain_exec('/opt/staging/spec/sample.tar.gz')
+    }
+  end
+
+  describe 'when deploying via http' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'http://webserver/sample.tar.gz' } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('/opt/staging/spec/sample.tar.gz').with( {
+        :command => 'curl  -f -L -o /opt/staging/spec/sample.tar.gz http://webserver/sample.tar.gz',
+        :path        => '/usr/local/bin:/usr/bin:/bin',
+        :environment => nil,
+        :cwd         => '/opt/staging/spec',
+        :creates     => '/opt/staging/spec/sample.tar.gz',
+        :logoutput   => 'on_failure',
+      })
+    }
+  end
+
+  describe 'when deploying via http with custom curl options' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { {
+      :source => 'http://webserver/sample.tar.gz',
+      :curl_option => '-b',
+    } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('/opt/staging/spec/sample.tar.gz').with( {
+        :command => 'curl -b -f -L -o /opt/staging/spec/sample.tar.gz http://webserver/sample.tar.gz',
+        :path        => '/usr/local/bin:/usr/bin:/bin',
+        :environment => nil,
+        :cwd         => '/opt/staging/spec',
+        :creates     => '/opt/staging/spec/sample.tar.gz',
+        :logoutput   => 'on_failure',
+      })
+    }
+  end
+
+  describe 'when deploying via http with parameters' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'http://webserver/sample.tar.gz',
+      :target    => '/usr/local/sample.tar.gz',
+      :tries     => '10',
+      :try_sleep => '6',
+    } }
+
+    it { should contain_file('/opt/staging')
+      should contain_exec('/usr/local/sample.tar.gz').with( {
+        :command => 'curl  -f -L -o /usr/local/sample.tar.gz http://webserver/sample.tar.gz',
+        :path        => '/usr/local/bin:/usr/bin:/bin',
+        :environment => nil,
+        :cwd         => '/usr/local',
+        :creates     => '/usr/local/sample.tar.gz',
+        :tries       => '10',
+        :try_sleep   => '6',
+      })
+    }
+  end
+
+  describe 'when deploying via https' do
+     let(:title) { 'sample.tar.gz' }
+     let(:params) { { :source => 'https://webserver/sample.tar.gz' } }
+
+     it { should contain_file('/opt/staging') }
+     it { should contain_exec('/opt/staging/spec/sample.tar.gz').with( {
+       :command => 'curl  -f -L -o /opt/staging/spec/sample.tar.gz https://webserver/sample.tar.gz',
+       :path        => '/usr/local/bin:/usr/bin:/bin',
+       :environment => nil,
+       :cwd         => '/opt/staging/spec',
+       :creates     => '/opt/staging/spec/sample.tar.gz',
+       :logoutput   => 'on_failure',
+     }) }
+  end
+
+  describe 'when deploying via https with parameters' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'https://webserver/sample.tar.gz',
+      :username => 'puppet',
+      :password => 'puppet',
+    } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('/opt/staging/spec/sample.tar.gz').with( {
+        :command => 'curl  -f -L -o /opt/staging/spec/sample.tar.gz -u puppet:puppet https://webserver/sample.tar.gz',
+        :path        => '/usr/local/bin:/usr/bin:/bin',
+        :environment => nil,
+        :cwd         => '/opt/staging/spec',
+        :creates     => '/opt/staging/spec/sample.tar.gz',
+        :logoutput   => 'on_failure',
+      })
+    }
+  end
+
+  describe 'when deploying via ftp' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'ftp://webserver/sample.tar.gz' } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('/opt/staging/spec/sample.tar.gz').with( {
+        :command => 'curl  -o /opt/staging/spec/sample.tar.gz ftp://webserver/sample.tar.gz',
+        :path        => '/usr/local/bin:/usr/bin:/bin',
+        :environment => nil,
+        :cwd         => '/opt/staging/spec',
+        :creates     => '/opt/staging/spec/sample.tar.gz',
+        :logoutput   => 'on_failure',
+      })
+    }
+  end
+
+  describe 'when deploying via ftp with parameters' do
+    let(:title) { 'sample.tar.gz' }
+    let(:params) { { :source => 'ftp://webserver/sample.tar.gz',
+      :username => 'puppet',
+      :password => 'puppet',
+    } }
+
+    it {
+      should contain_file('/opt/staging')
+      should contain_exec('/opt/staging/spec/sample.tar.gz').with( {
+        :command => 'curl  -o /opt/staging/spec/sample.tar.gz -u puppet:puppet ftp://webserver/sample.tar.gz',
+        :path        => '/usr/local/bin:/usr/bin:/bin',
+        :environment => nil,
+        :cwd         => '/opt/staging/spec',
+        :creates     => '/opt/staging/spec/sample.tar.gz',
+        :logoutput   => 'on_failure',
+      })
+    }
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/fixtures/hiera.yaml	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,7 @@
+---
+:backends: - puppet
+
+:hierarchy: - common
+
+:puppet:
+    :datasource: data
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/spec_helper.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,29 @@
+require 'rubygems'
+require 'puppetlabs_spec_helper/module_spec_helper'
+
+RSpec.configure do |c|
+  c.include PuppetlabsSpec::Files
+
+  c.before :each do
+    # Ensure that we don't accidentally cache facts and environment
+    # between test cases.
+    Facter::Util::Loader.any_instance.stubs(:load_all)
+    Facter.clear
+    Facter.clear_messages
+
+    # Store any environment variables away to be restored later
+    @old_env = {}
+    ENV.each_key {|k| @old_env[k] = ENV[k]}
+
+    if Gem::Version.new(`puppet --version`) >= Gem::Version.new('3.5')
+      Puppet.settings[:strict_variables]=true
+    end
+    if ENV['PARSER']
+      Puppet.settings[:parser]=ENV['PARSER']
+    end
+  end
+
+  c.after :each do
+    PuppetlabsSpec::Files.cleanup
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/unit/puppet/parser/functions/scope_defaults_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,45 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+describe "the scope_defaults function" do
+  let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
+
+  it "should exist" do
+    Puppet::Parser::Functions.function("scope_defaults").should == "function_scope_defaults"
+  end
+
+  it "should raise a ParseError if there is less than 2 arguments" do
+    expect{ scope.function_scope_defaults([]) }.
+      to raise_error(Puppet::ParseError)
+  end
+
+  it "should raise a ParseError if there is more than 2 arguments" do
+    expect{ scope.function_scope_defaults(['exec', 'path', 'error']) }.
+      to raise_error(Puppet::ParseError)
+  end
+
+  it "should return false for invalid resource" do
+    result = scope.function_scope_defaults(['foo', 'path'])
+    result.should(eq(false))
+  end
+
+  it "should return false for resource without default attributes" do
+    if scope.respond_to? :define_settings
+      scope.define_settings('Exec', Puppet::Parser::Resource::Param.new(:name => :path, :value => "/bin"))
+    else
+      scope.setdefaults('Exec', Puppet::Parser::Resource::Param.new(:name => :path, :value => "/bin"))
+    end
+    result = scope.function_scope_defaults(['Exec', 'foo'])
+    result.should(eq(false))
+  end
+
+  it "should return true for resource with default attributes" do
+    if scope.respond_to? :define_settings
+      scope.define_settings('Exec', Puppet::Parser::Resource::Param.new(:name => :path, :value => "/bin"))
+    else
+      scope.setdefaults('Exec', Puppet::Parser::Resource::Param.new(:name => :path, :value => "/bin"))
+    end
+    result = scope.function_scope_defaults(['Exec', 'path'])
+    result.should(eq(true))
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/spec/unit/puppet/parser/functions/staging_parse_spec.rb	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,51 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+describe "the staging parser function" do
+  let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
+
+  it "should exist" do
+    Puppet::Parser::Functions.function("staging_parse").should == "function_staging_parse"
+  end
+
+  it "should raise a ParseError if there is less than 1 arguments" do
+    lambda { scope.function_staging_parse([]) }.should( raise_error(Puppet::ParseError))
+  end
+
+  it "should raise a ParseError if there is more than 3 arguments" do
+    lambda { scope.function_staging_parse(['/etc', 'filename', '.zip', 'error']) }.should( raise_error(Puppet::ParseError))
+  end
+
+  it "should raise a ParseError if there is an invalid info request" do
+    lambda { scope.function_staging_parse(['/etc', 'sheep', '.zip']) }.should( raise_error(Puppet::ParseError))
+  end
+
+  it "should raise a ParseError if 'source' doesn't have a URI path component" do
+    lambda { scope.function_staging_parse(['uri:without-path']) }.should( raise_error(Puppet::ParseError, /has no URI 'path' component/))
+  end
+
+  it "should return the filename by default" do
+    result = scope.function_staging_parse(["/etc/puppet/sample.tar.gz"])
+    result.should(eq('sample.tar.gz'))
+  end
+
+  it "should return the file basename" do
+    result = scope.function_staging_parse(["/etc/puppet/sample.tar.gz", "basename"])
+    result.should(eq('sample.tar'))
+  end
+
+  it "should return the file basename with custom extensions" do
+    result = scope.function_staging_parse(["/etc/puppet/sample.tar.gz", "basename", ".tar.gz"])
+    result.should(eq('sample'))
+  end
+
+  it "should return the file extname" do
+    result = scope.function_staging_parse(["/etc/puppet/sample.tar.gz", "extname"])
+    result.should(eq('.gz'))
+  end
+
+  it "should return the file parent" do
+    result = scope.function_staging_parse(["/etc/puppet/sample.tar.gz", "parent"])
+    result.should(eq('/etc/puppet'))
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/tests/deploy.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,4 @@
+staging::deploy { 'sample.tar.gz':
+  source => 'puppet:///modules/staging/sample.tar.gz',
+  target => '/usr/local',
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/tests/extract.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,25 @@
+$caller_module_name = 'demo'
+
+class { 'staging':
+  path => '/tmp/staging',
+}
+
+staging::file { 'sample.tar.gz':
+  source => 'puppet:///modules/staging/sample.tar.gz'
+}
+
+staging::extract { 'sample.tar.gz':
+  target  => '/tmp/staging',
+  creates => '/tmp/staging/sample',
+  require => Staging::File['sample.tar.gz'],
+}
+
+staging::file { 'sample.tar.bz2':
+  source => 'puppet:///modules/staging/sample.tar.bz2'
+}
+
+staging::extract { 'sample.tar.bz2':
+  target  => '/tmp/staging',
+  creates => '/tmp/staging/sample-tar-bz2',
+  require => Staging::File['sample.tar.bz2'],
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/tests/file.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,17 @@
+$caller_module_name = 'demo'
+
+class { 'staging':
+  path => '/tmp/staging',
+}
+
+staging::file { 'sample':
+  source => 'puppet:///modules/staging/sample',
+}
+
+staging::file { 'passwd':
+  source => '/etc/passwd',
+}
+
+staging::file { 'manpage.html':
+  source => 'http://curl.haxx.se/docs/manpage.html',
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/tests/init.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,1 @@
+include staging
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/tests/scope_defaults.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,7 @@
+Exec {
+  path => '/bin',
+}
+
+if scope_defaults('Exec', 'path') {
+  notice('good')
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/staging/tests/staging_parse.pp	Mon Mar 09 01:34:59 2015 +0000
@@ -0,0 +1,12 @@
+$file      = '/etc/puppetlabs/foo.bar.tar.gz'
+$filename  = staging_parse($file)
+$basename  = staging_parse($file, 'basename')
+$extname   = staging_parse($file, 'extname')
+$parent    = staging_parse($file, 'parent')
+$rbasename = staging_parse($file, 'basename', '.tar.gz')
+
+notice($filename)
+notice($basename)
+notice($extname)
+notice($parent)
+notice($rbasename)