Upgrade php from 5.4.* to 5.5.*

This commit is contained in:
Benoît Burnichon
2015-03-17 10:09:07 +01:00
parent e32816a8e0
commit 7ad443a88b
2027 changed files with 72 additions and 88 deletions

View File

@@ -0,0 +1,5 @@
fixtures:
repositories:
"stdlib": "https://github.com/puppetlabs/puppetlabs-stdlib"
symlinks:
"mysql": "#{source_dir}"

View File

@@ -0,0 +1,31 @@
---
default_set: 'centos-64-x64'
sets:
'centos-59-x64':
nodes:
"main.foo.vm":
prefab: 'centos-59-x64'
'centos-64-x64':
nodes:
"main.foo.vm":
prefab: 'centos-64-x64'
'fedora-18-x64':
nodes:
"main.foo.vm":
prefab: 'fedora-18-x64'
'debian-607-x64':
nodes:
"main.foo.vm":
prefab: 'debian-607-x64'
'debian-70rc1-x64':
nodes:
"main.foo.vm":
prefab: 'debian-70rc1-x64'
'ubuntu-server-10044-x64':
nodes:
"main.foo.vm":
prefab: 'ubuntu-server-10044-x64'
'ubuntu-server-12042-x64':
nodes:
"main.foo.vm":
prefab: 'ubuntu-server-12042-x64'

View File

@@ -0,0 +1,34 @@
---
branches:
only:
- master
language: ruby
bundler_args: --without development
script: "bundle exec rake spec SPEC_OPTS='--format documentation'"
after_success:
- git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-release
- .forge-release/publish
rvm:
- 1.8.7
- 1.9.3
- 2.0.0
env:
matrix:
- PUPPET_GEM_VERSION="~> 2.7.0"
- PUPPET_GEM_VERSION="~> 3.3.0"
- PUPPET_GEM_VERSION="~> 3.4.0"
global:
- PUBLISHER_LOGIN=puppetlabs
- secure: |-
Hc9OPm/kRTmjXSP3TbLir/y6Yy1LqmZS8zrqxdTbpo3Z04EYv1uKhaFDpECl
0a6bJRUWpLWIuDco08fHMeCTWoFGzE97EDelhHKSYiTNllzYKWPHy7ki/al6
wjz0gLtiDfmktHQOHatBy6EKLFjoyjGoE4cUUta4Ixq4tMBNzEA=
matrix:
fast_finish: true
exclude:
- rvm: 1.9.3
env: PUPPET_GEM_VERSION="~> 2.7.0"
- rvm: 2.0.0
env: PUPPET_GEM_VERSION="~> 2.7.0"
notifications:
email: false

View File

@@ -0,0 +1,476 @@
##2014-03-04 - Supported Release 2.2.3
###Summary
This is a supported release. This release removes a testing symlink that can
cause trouble on systems where /var is on a seperate filesystem from the
modulepath.
####Features
####Bugfixes
####Known Bugs
* No known bugs
##2014-03-04 - Supported Release 2.2.2
###Summary
This is a supported release. Mostly comprised of enhanced testing, plus a
bugfix for Suse.
####Bugfixes
- PHP bindings on Suse
- Test fixes
####Known Bugs
* No known bugs
##2014-02-19 - Version 2.2.1
###Summary
Minor release that repairs mysql_database{} so that it sees the correct
collation settings (it was only checking the global mysql ones, not the
actual database and constantly setting it over and over since January 22nd).
Also fixes a bunch of tests on various platforms.
##2014-02-13 - Version 2.2.0
###Summary
####Features
- Add `backupdirmode`, `backupdirowner`, `backupdirgroup` to
mysql::server::backup to allow customizing the mysqlbackupdir.
- Support multiple options of the same name, allowing you to
do 'replicate-do-db' => ['base1', 'base2', 'base3'] in order to get three
lines of replicate-do-db = base1, replicate-do-db = base2 etc.
####Bugfixes
- Fix `restart` so it actually stops mysql restarting if set to false.
- DRY out the defaults_file functionality in the providers.
- mysql_grant fixed to work with root@localhost/@.
- mysql_grant fixed for WITH MAX_QUERIES_PER_HOUR
- mysql_grant fixed so revoking all privileges accounts for GRANT OPTION
- mysql_grant fixed to remove duplicate privileges.
- mysql_grant fixed to handle PROCEDURES when removing privileges.
- mysql_database won't try to create existing databases, breaking replication.
- bind_address renamed bind-address in 'mysqld' options.
- key_buffer renamed to key_buffer_size.
- log_error renamed to log-error.
- pid_file renamed to pid-file.
- Ensure mysql::server:root_password runs before mysql::server::backup
- Fix options_override -> override_options in the README.
- Extensively rewrite the README to be accurate and awesome.
- Move to requiring stdlib 3.2.0, shipped in PE3.0
- Add many new tests.
##2013-11-13 - Version 2.1.0
###Summary
The most important changes in 2.1.0 are improvements to the my.cnf creation,
as well as providers. Setting options to = true strips them to be just the
key name itself, which is required for some options.
The provider updates fix a number of bugs, from lowercase privileges to
deprecation warnings.
Last, the new hiera integration functionality should make it easier to
externalize all your grants, users, and, databases. Another great set of
community submissions helped to make this release.
####Features
- Some options can not take a argument. Gets rid of the '= true' when an
option is set to true.
- Easier hiera integration: Add hash parameters to mysql::server to allow
specifying grants, users, and databases.
####Bugfixes
- Fix an issue with lowercase privileges in mysql_grant{} causing them to be reapplied needlessly.
- Changed defaults-file to defaults-extra-file in providers.
- Ensure /root/.my.cnf is 0600 and root owned.
- database_user deprecation warning was incorrect.
- Add anchor pattern for client.pp
- Documentation improvements.
- Various test fixes.
##2013-10-21 - Version 2.0.1
###Summary
This is a bugfix release to handle an issue where unsorted mysql_grant{}
privileges could cause Puppet to incorrectly reapply the permissions on
each run.
####Bugfixes
- Mysql_grant now sorts privileges in the type and provider for comparison.
- Comment and test tweak for PE3.1.
##2013-10-14 - Version 2.0.0
###Summary
(Previously detailed in the changelog for 2.0.0-rc1)
This module has been completely refactored and works significantly different.
The changes are broad and touch almost every piece of the module.
See the README.md for full details of all changes and syntax.
Please remain on 1.0.0 if you don't have time to fully test this in dev.
* mysql::server, mysql::client, and mysql::bindings are the primary interface
classes.
* mysql::server takes an `override_options` parameter to set my.cnf options,
with the hash format: { 'section' => { 'thing' => 'value' }}
* mysql attempts backwards compatibility by forwarding all parameters to
mysql::server.
##2013-10-09 - Version 2.0.0-rc5
###Summary
Hopefully the final rc! Further fixes to mysql_grant (stripping out the
cleverness so we match a much wider range of input.)
####Bugfixes
- Make mysql_grant accept '.*'@'.*' in terms of input for user@host.
##2013-10-09 - Version 2.0.0-rc4
###Summary
Bugfixes to mysql_grant and mysql_user form the bulk of this rc, as well as
ensuring that values in the override_options hash that contain a value of ''
are created as just "key" in the conf rather than "key =" or "key = false".
####Bugfixes
- Improve mysql_grant to work with IPv6 addresses (both long and short).
- Ensure @host users work as well as user@host users.
- Updated my.cnf template to support items with no values.
##2013-10-07 - Version 2.0.0-rc3
###Summary
Fix mysql::server::monitor's use of mysql_user{}.
####Bugfixes
- Fix myql::server::monitor's use of mysql_user{} to grant the proper
permissions. Add specs as well. (Thanks to treydock!)
##2013-10-03 - Version 2.0.0-rc2
###Summary
Bugfixes
####Bugfixes
- Fix a duplicate parameter in mysql::server
##2013-10-03 - Version 2.0.0-rc1
###Summary
This module has been completely refactored and works significantly different.
The changes are broad and touch almost every piece of the module.
See the README.md for full details of all changes and syntax.
Please remain on 1.0.0 if you don't have time to fully test this in dev.
* mysql::server, mysql::client, and mysql::bindings are the primary interface
classes.
* mysql::server takes an `override_options` parameter to set my.cnf options,
with the hash format: { 'section' => { 'thing' => 'value' }}
* mysql attempts backwards compatibility by forwarding all parameters to
mysql::server.
---
##2013-09-23 - Version 1.0.0
###Summary
This release introduces a number of new type/providers, to eventually
replace the database_ ones. The module has been converted to call the
new providers rather than the previous ones as they have a number of
fixes, additional options, and work with puppet resource.
This 1.0.0 release precedes a large refactoring that will be released
almost immediately after as 2.0.0.
####Features
- Added mysql_grant, mysql_database, and mysql_user.
- Add `mysql::bindings` class and refactor all other bindings to be contained underneath mysql::bindings:: namespace.
- Added support to back up specified databases only with 'mysqlbackup' parameter.
- Add option to mysql::backup to set the backup script to perform a mysqldump on each database to its own file
####Bugfixes
- Update my.cnf.pass.erb to allow custom socket support
- Add environment variable for .my.cnf in mysql::db.
- Add HOME environment variable for .my.cnf to mysqladmin command when
(re)setting root password
---
##2013-07-15 - Version 0.9.0
####Features
- Add `mysql::backup::backuprotate` parameter
- Add `mysql::backup::delete_before_dump` parameter
- Add `max_user_connections` attribute to `database_user` type
####Bugfixes
- Add client package dependency for `mysql::db`
- Remove duplicate `expire_logs_days` and `max_binlog_size` settings
- Make root's `.my.cnf` file path dynamic
- Update pidfile path for Suse variants
- Fixes for lint
##2013-07-05 - Version 0.8.1
####Bugfixes
- Fix a typo in the Fedora 19 support.
##2013-07-01 - Version 0.8.0
####Features
- mysql::perl class to install perl-DBD-mysql.
- minor improvements to the providers to improve reliability
- Install the MariaDB packages on Fedora 19 instead of MySQL.
- Add new `mysql` class parameters:
- `max_connections`: The maximum number of allowed connections.
- `manage_config_file`: Opt out of puppetized control of my.cnf.
- `ft_min_word_len`: Fine tune the full text search.
- `ft_max_word_len`: Fine tune the full text search.
- Add new `mysql` class performance tuning parameters:
- `key_buffer`
- `thread_stack`
- `thread_cache_size`
- `myisam-recover`
- `query_cache_limit`
- `query_cache_size`
- `max_connections`
- `tmp_table_size`
- `table_open_cache`
- `long_query_time`
- Add new `mysql` class replication parameters:
- `server_id`
- `sql_log_bin`
- `log_bin`
- `max_binlog_size`
- `binlog_do_db`
- `expire_logs_days`
- `log_bin_trust_function_creators`
- `replicate_ignore_table`
- `replicate_wild_do_table`
- `replicate_wild_ignore_table`
- `expire_logs_days`
- `max_binlog_size`
####Bugfixes
- No longer restart MySQL when /root/.my.cnf changes.
- Ensure mysql::config runs before any mysql::db defines.
##2013-06-26 - Version 0.7.1
####Bugfixes
- Single-quote password for special characters
- Update travis testing for puppet 3.2.x and missing Bundler gems
##2013-06-25 - Version 0.7.0
This is a maintenance release for community bugfixes and exposing
configuration variables.
* Add new `mysql` class parameters:
- `basedir`: The base directory mysql uses
- `bind_address`: The IP mysql binds to
- `client_package_name`: The name of the mysql client package
- `config_file`: The location of the server config file
- `config_template`: The template to use to generate my.cnf
- `datadir`: The directory MySQL's datafiles are stored
- `default_engine`: The default engine to use for tables
- `etc_root_password`: Whether or not to add the mysql root password to
/etc/my.cnf
- `java_package_name`: The name of the java package containing the java
connector
- `log_error`: Where to log errors
- `manage_service`: Boolean dictating if mysql::server should manage the
service
- `max_allowed_packet`: Maximum network packet size mysqld will accept
- `old_root_password`: Previous root user password
- `php_package_name`: The name of the phpmysql package to install
- `pidfile`: The location mysql will expect the pidfile to be
- `port`: The port mysql listens on
- `purge_conf_dir`: Value fed to recurse and purge parameters of the
/etc/mysql/conf.d resource
- `python_package_name`: The name of the python mysql package to install
- `restart`: Whether to restart mysqld
- `root_group`: Use specified group for root-owned files
- `root_password`: The root MySQL password to use
- `ruby_package_name`: The name of the ruby mysql package to install
- `ruby_package_provider`: The installation suite to use when installing the
ruby package
- `server_package_name`: The name of the server package to install
- `service_name`: The name of the service to start
- `service_provider`: The name of the service provider
- `socket`: The location of the MySQL server socket file
- `ssl_ca`: The location of the SSL CA Cert
- `ssl_cert`: The location of the SSL Certificate to use
- `ssl_key`: The SSL key to use
- `ssl`: Whether or not to enable ssl
- `tmpdir`: The directory MySQL's tmpfiles are stored
* Deprecate `mysql::package_name` parameter in favor of
`mysql::client_package_name`
* Fix local variable template deprecation
* Fix dependency ordering in `mysql::db`
* Fix ANSI quoting in queries
* Fix travis support (but still messy)
* Fix typos
##2013-01-11 - Version 0.6.1
* Fix providers when /root/.my.cnf is absent
##2013-01-09 - Version 0.6.0
* Add `mysql::server::config` define for specific config directives
* Add `mysql::php` class for php support
* Add `backupcompress` parameter to `mysql::backup`
* Add `restart` parameter to `mysql::config`
* Add `purge_conf_dir` parameter to `mysql::config`
* Add `manage_service` parameter to `mysql::server`
* Add syslog logging support via the `log_error` parameter
* Add initial SuSE support
* Fix remove non-localhost root user when fqdn != hostname
* Fix dependency in `mysql::server::monitor`
* Fix .my.cnf path for root user and root password
* Fix ipv6 support for users
* Fix / update various spec tests
* Fix typos
* Fix lint warnings
##2012-08-23 - Version 0.5.0
* Add puppetlabs/stdlib as requirement
* Add validation for mysql privs in provider
* Add `pidfile` parameter to mysql::config
* Add `ensure` parameter to mysql::db
* Add Amazon linux support
* Change `bind_address` parameter to be optional in my.cnf template
* Fix quoting root passwords
##2012-07-24 - Version 0.4.0
* Fix various bugs regarding database names
* FreeBSD support
* Allow specifying the storage engine
* Add a backup class
* Add a security class to purge default accounts
##2012-05-03 - Version 0.3.0
* 14218 Query the database for available privileges
* Add mysql::java class for java connector installation
* Use correct error log location on different distros
* Fix set_mysql_rootpw to properly depend on my.cnf
##2012-04-11 - Version 0.2.0
##2012-03-19 - William Van Hevelingen <blkperl@cat.pdx.edu>
* (#13203) Add ssl support (f7e0ea5)
##2012-03-18 - Nan Liu <nan@puppetlabs.com>
* Travis ci before script needs success exit code. (0ea463b)
##2012-03-18 - Nan Liu <nan@puppetlabs.com>
* Fix Puppet 2.6 compilation issues. (9ebbbc4)
##2012-03-16 - Nan Liu <nan@puppetlabs.com>
* Add travis.ci for testing multiple puppet versions. (33c72ef)
##2012-03-15 - William Van Hevelingen <blkperl@cat.pdx.edu>
* (#13163) Datadir should be configurable (f353fc6)
##2012-03-16 - Nan Liu <nan@puppetlabs.com>
* Document create_resources dependency. (558a59c)
##2012-03-16 - Nan Liu <nan@puppetlabs.com>
* Fix spec test issues related to error message. (eff79b5)
##2012-03-16 - Nan Liu <nan@puppetlabs.com>
* Fix mysql service on Ubuntu. (72da2c5)
##2012-03-16 - Dan Bode <dan@puppetlabs.com>
* Add more spec test coverage (55e399d)
##2012-03-16 - Nan Liu <nan@puppetlabs.com>
* (#11963) Fix spec test due to path changes. (1700349)
##2012-03-07 - François Charlier <fcharlier@ploup.net>
* Add a test to check path for 'mysqld-restart' (b14c7d1)
##2012-03-07 - François Charlier <fcharlier@ploup.net>
* Fix path for 'mysqld-restart' (1a9ae6b)
##2012-03-15 - Dan Bode <dan@puppetlabs.com>
* Add rspec-puppet tests for mysql::config (907331a)
##2012-03-15 - Dan Bode <dan@puppetlabs.com>
* Moved class dependency between sever and config to server (da62ad6)
##2012-03-14 - Dan Bode <dan@puppetlabs.com>
* Notify mysql restart from set_mysql_rootpw exec (0832a2c)
##2012-03-15 - Nan Liu <nan@puppetlabs.com>
* Add documentation related to osfamily fact. (8265d28)
##2012-03-14 - Dan Bode <dan@puppetlabs.com>
* Mention osfamily value in failure message (e472d3b)
##2012-03-14 - Dan Bode <dan@puppetlabs.com>
* Fix bug when querying for all database users (015490c)
##2012-02-09 - Nan Liu <nan@puppetlabs.com>
* Major refactor of mysql module. (b1f90fd)
##2012-01-11 - Justin Ellison <justin.ellison@buckle.com>
* Ruby and Python's MySQL libraries are named differently on different distros. (1e926b4)
##2012-01-11 - Justin Ellison <justin.ellison@buckle.com>
* Per @ghoneycutt, we should fail explicitly and explain why. (09af083)
##2012-01-11 - Justin Ellison <justin.ellison@buckle.com>
* Removing duplicate declaration (7513d03)
##2012-01-10 - Justin Ellison <justin.ellison@buckle.com>
* Use socket value from params class instead of hardcoding. (663e97c)
##2012-01-10 - Justin Ellison <justin.ellison@buckle.com>
* Instead of hardcoding the config file target, pull it from mysql::params (031a47d)
##2012-01-10 - Justin Ellison <justin.ellison@buckle.com>
* Moved $socket to within the case to toggle between distros. Added a $config_file variable to allow per-distro config file destinations. (360eacd)
##2012-01-10 - Justin Ellison <justin.ellison@buckle.com>
* Pretty sure this is a bug, 99% of Linux distros out there won't ever hit the default. (3462e6b)
##2012-02-09 - William Van Hevelingen <blkperl@cat.pdx.edu>
* Changed the README to use markdown (3b7dfeb)
##2012-02-04 - Daniel Black <grooverdan@users.sourceforge.net>
* (#12412) mysqltuner.pl update (b809e6f)
##2011-11-17 - Matthias Pigulla <mp@webfactory.de>
* (#11363) Add two missing privileges to grant: event_priv, trigger_priv (d15c9d1)
##2011-12-20 - Jeff McCune <jeff@puppetlabs.com>
* (minor) Fixup typos in Modulefile metadata (a0ed6a1)
##2011-12-19 - Carl Caum <carl@carlcaum.com>
* Only notify Exec to import sql if sql is given (0783c74)
##2011-12-19 - Carl Caum <carl@carlcaum.com>
* (#11508) Only load sql_scripts on DB creation (e3b9fd9)
##2011-12-13 - Justin Ellison <justin.ellison@buckle.com>
* Require not needed due to implicit dependencies (3058feb)
##2011-12-13 - Justin Ellison <justin.ellison@buckle.com>
* Bug #11375: puppetlabs-mysql fails on CentOS/RHEL (a557b8d)
##2011-06-03 - Dan Bode <dan@puppetlabs.com> - 0.0.1
* initial commit

View File

@@ -0,0 +1,25 @@
source ENV['GEM_SOURCE'] || 'https://rubygems.org'
group :development, :test do
gem 'mime-types', '<2.0', :require => false
gem 'rake', :require => false
gem 'rspec-puppet', :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec-system', :require => false
gem 'rspec-system-puppet', :require => false
gem 'rspec-system-serverspec', :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', :require => false
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end
# vim:ft=ruby

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013 Puppet Labs
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.

View File

@@ -0,0 +1,9 @@
name 'puppetlabs-mysql'
version '2.2.3'
source 'git://github.com/puppetlabs/puppetlabs-mysql.git'
author 'Puppet Labs'
license 'Apache 2.0'
summary 'Mysql module'
description 'Mysql module'
project_page 'http://github.com/puppetlabs/puppetlabs-mysql'
dependency 'puppetlabs/stdlib', '>= 3.2.0'

View File

@@ -0,0 +1,523 @@
#MySQL
####Table of Contents
1. [Overview](#overview)
2. [Module Description - What the module does and why it is useful](#module-description)
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)
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)
##Overview
The mysql module installs, configures, and manages the MySQL service.
##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!
##Setup
###What MySQL affects
* MySQL package
* MySQL configuration files
* MySQL service
###Beginning with MySQL
If you just 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' } }
}
```
##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`.
###Overrides
The hash structure for overrides in `mysql::server` is as follows:
```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.
If an option needs multiple instances, you can pass an array. For example
```puppet
$override_options = {
'mysqld' => {
'replicate-do-db' => ['base1', 'base2'],
}
}
```
will produce
<pre>
[mysql]
replicate-do-db = base1
replicate-do-db = base2
</pre>
###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.
##Reference
###Classes
####Public classes
* `mysql::server`: Installs and configures MySQL.
* `mysql::server::account_security`: Deletes default MySQL accounts.
* `mysql::server::monitor`: Sets up a monitoring user.
* `mysql::server::mysqltuner`: Installs MySQL tuner script.
* `mysql::server::backup`: Sets up MySQL backups via cron.
* `mysql::bindings`: Installs various MySQL language bindings.
* `mysql::client`: Installs MySQL client (for non-servers).
####Private classes
* `mysql::server::install`: Installs packages.
* `mysql::server::config`: Configures MYSQL.
* `mysql::server::service`: Manages service.
* `mysql::server::root_password`: Sets MySQL root password.
* `mysql::server::providers`: Creates users, grants, and databases.
* `mysql::bindings::java`: Installs Java bindings.
* `mysql::bindings::perl`: Installs Perl bindings.
* `mysql::bindings::python`: Installs Python bindings.
* `mysql::bindings::ruby`: Installs Ruby bindings.
* `mysql::client::install`: Installs MySQL client.
###Parameters
####mysql::server
#####`root_password`
The MySQL root password. Puppet will attempt to set the root password and update `/root/.my.cnf` with it.
#####`old_root_password`
The previous root password (**REQUIRED** if you wish 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
```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.
#####`config_file`
The location of the MySQL configuration file.
#####`manage_config_file`
Whether the MySQL configuration file should be managed.
#####`purge_conf_dir`
Whether the conf.d directory should be purged.
#####`restart`
Whether the service should be restarted when things change.
#####`root_group`
What is the group used for root?
#####`package_ensure`
What to set the package to. Can be 'present', 'absent', or 'x.y.z'.
#####`package_name`
The name of the mysql server package to install.
#####`remove_default_accounts`
Boolean to decide if we should automatically include
`mysql::server::account_security`.
#####`service_enabled`
Boolean to decide if the service should be enabled.
#####`service_manage`
Boolean to decide if the service should be managed.
#####`service_name`
The name of the mysql server service.
#####`service_provider`
The provider to use to manage the service.
#####`users`
Optional hash of users to create, which are passed to [mysql_user](#mysql_user).
```puppet
$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`
Optional hash of grants, which are passed to [mysql_grant](#mysql_grant).
```puppet
$grants = {
'someuser@localhost/somedb.*' => {
ensure => 'present',
options => ['GRANT'],
privileges => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
table => 'somedb.*',
user => 'someuser@localhost',
},
}
```
#####`databases`
Optional hash of databases to create, which are passed to [mysql_database](#mysql_database).
```puppet
$databases = {
'somedb' => {
ensure => 'present',
charset => 'utf8',
},
}
```
####mysql::server::backup
#####`backupuser`
MySQL user to create for backups.
#####`backuppassword`
MySQL user password for backups.
#####`backupdir`
Directory to back up into.
#####`backupdirmode`
Permissions applied to the backup directory. This parameter is passed directly
to the `file` resource.
#####`backupdirowner`
Owner for the backup directory. This parameter is passed directly to the `file`
resource.
#####`backupdirgroup`
Group owner for the backup directory. This parameter is passed directly to the
`file` resource.
#####`backupcompress`
Boolean to determine if backups should be compressed.
#####`backuprotate`
How many days to keep backups for.
#####`delete_before_dump`
Boolean to determine if you should cleanup before backing up or after.
#####`backupdatabases`
Array of databases to specifically back up.
#####`file_per_database`
Whether a separate file be used per database.
#####`ensure`
Allows you to remove the backup scripts. Can be 'present' or 'absent'.
#####`time`
An array of two elements to set the backup time. Allows ['23', '5'] or ['3', '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.
####mysql::server::monitor
#####`mysql_monitor_username`
The username to create for MySQL monitoring.
#####`mysql_monitor_password`
The password to create for MySQL monitoring.
#####`mysql_monitor_hostname`
The hostname to allow to access the MySQL monitoring user.
####mysql::bindings
#####`java_enable`
Boolean to decide if the Java bindings should be installed.
#####`perl_enable`
Boolean to decide if the Perl bindings should be installed.
#####`php_enable`
Boolean to decide if the PHP bindings should be installed.
#####`python_enable`
Boolean to decide if the Python bindings should be installed.
#####`ruby_enable`
Boolean to decide if the Ruby bindings should be installed.
#####`java_package_ensure`
What to set the package to. Can be 'present', 'absent', or 'x.y.z'.
#####`java_package_name`
The name of the package to install.
#####`java_package_provider`
What provider should be used to install the package.
#####`perl_package_ensure`
What to set the package to. Can be 'present', 'absent', or 'x.y.z'.
#####`perl_package_name`
The name of the package to install.
#####`perl_package_provider`
What provider should be used to install the package.
#####`python_package_ensure`
What to set the package to. Can be 'present', 'absent', or 'x.y.z'.
#####`python_package_name`
The name of the package to install.
#####`python_package_provider`
What provider should be used to install the package.
#####`ruby_package_ensure`
What to set the package to. Can be 'present', 'absent', or 'x.y.z'.
#####`ruby_package_name`
The name of the package to install.
#####`ruby_package_provider`
What provider should be used to install the package.
####mysql::client
#####`bindings_enable`
Boolean to automatically install all bindings.
#####`package_ensure`
What to set the package to. Can be 'present', 'absent', or 'x.y.z'.
#####`package_name`
What is the name of the mysql client package to install.
###Defines
####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'],
}
```
###Providers
####mysql_database
`mysql_database` can be used to create and manage databases within MySQL.
```puppet
mysql_database { 'information_schema':
ensure => 'present',
charset => 'utf8',
collate => 'utf8_swedish_ci',
}
mysql_database { 'mysql':
ensure => 'present',
charset => 'latin1',
collate => 'latin1_swedish_ci',
}
```
####mysql_user
`mysql_user` can be used to create and manage user grants within MySQL.
```puppet
mysql_user { 'root@127.0.0.1':
ensure => 'present',
max_connections_per_hour => '0',
max_queries_per_hour => '0',
max_updates_per_hour => '0',
max_user_connections => '0',
}
```
####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,
following the pattern of `username@hostname/database.table`:
```puppet
mysql_grant { 'root@localhost/*.*':
ensure => 'present',
options => ['GRANT'],
privileges => ['ALL'],
table => '*.*',
user => 'root@localhost',
}
```
##Limitations
This module has been tested on:
* RedHat Enterprise Linux 5/6
* Debian 6/7
* CentOS 5/6
* Ubuntu 12.04
Testing on other platforms has been light and cannot be guaranteed.
#Development
Puppet Labs modules on the Puppet Forge are open projects, and community
contributions are essential for keeping them great. We cant access the
huge number of platforms and myriad of hardware, software, and deployment
configurations that Puppet is intended to serve.
We want to keep it as easy as possible to contribute changes so that our
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)
### Authors
This module is based on work by David Schmitt. The following contributors have contributed patches to this module (beyond Puppet Labs):
* Larry Ludwig
* Christian G. Warden
* Daniel Black
* Justin Ellison
* Lowe Schmidt
* Matthias Pigulla
* William Van Hevelingen
* Michael Arnold
* Chris Weyl

View File

@@ -0,0 +1,2 @@
require 'puppetlabs_spec_helper/rake_tasks'
require 'rspec-system/rake_task'

View File

@@ -0,0 +1,8 @@
The best that I can tell is that this code traces back to David Schmitt. It has been forked many times since then :)
1. you cannot add databases to an instance that has a root password
2. you have to specify username as USER@BLAH or it cannot be found
3. mysql_grant does not complain if user does not exist
4. Needs support for pre-seeding on debian
5. the types may need to take user/password
6. rather or not to configure /etc/.my.cnf should be configurable

View File

@@ -0,0 +1,966 @@
#!/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:

View File

@@ -0,0 +1,58 @@
module Puppet::Parser::Functions
newfunction(:mysql_deepmerge, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
Recursively merges two or more hashes together and returns the resulting hash.
For example:
$hash1 = {'one' => 1, 'two' => 2, 'three' => { 'four' => 4 } }
$hash2 = {'two' => 'dos', 'three' => { 'five' => 5 } }
$merged_hash = mysql_deepmerge($hash1, $hash2)
# The resulting hash is equivalent to:
# $merged_hash = { 'one' => 1, 'two' => 'dos', 'three' => { 'four' => 4, 'five' => 5 } }
When there is a duplicate key that is a hash, they are recursively merged.
When there is a duplicate key that is not a hash, the key in the rightmost hash will "win."
When there are conficting uses of dashes and underscores in two keys (which mysql would otherwise equate),
the rightmost style will win.
ENDHEREDOC
if args.length < 2
raise Puppet::ParseError, ("mysql_deepmerge(): wrong number of arguments (#{args.length}; must be at least 2)")
end
result = Hash.new
args.each do |arg|
next if arg.is_a? String and arg.empty? # empty string is synonym for puppet's undef
# If the argument was not a hash, skip it.
unless arg.is_a?(Hash)
raise Puppet::ParseError, "mysql_deepmerge: unexpected argument type #{arg.class}, only expects hash arguments"
end
# Now we have to traverse our hash assigning our non-hash values
# to the matching keys in our result while following our hash values
# and repeating the process.
overlay( result, arg )
end
return( result )
end
end
def has_normalized!(hash, key)
return true if hash.has_key?( key )
return false unless key.match(/-|_/)
other_key = key.include?('-') ? key.gsub( '-', '_' ) : key.gsub( '_', '-' )
return false unless hash.has_key?( other_key )
hash[key] = hash.delete( other_key )
return true;
end
def overlay( hash1, hash2 )
hash2.each do |key, value|
if(has_normalized!( hash1, key ) and value.is_a?(Hash) and hash1[key].is_a?(Hash))
overlay( hash1[key], value )
else
hash1[key] = value
end
end
end

View File

@@ -0,0 +1,15 @@
# hash a string as mysql's "PASSWORD()" function would do it
require 'digest/sha1'
module Puppet::Parser::Functions
newfunction(:mysql_password, :type => :rvalue, :doc => <<-EOS
Returns the mysql password hash from the clear text password.
EOS
) do |args|
raise(Puppet::ParseError, 'mysql_password(): Wrong number of arguments ' +
"given (#{args.size} for 1)") if args.size != 1
'*' + Digest::SHA1.hexdigest(Digest::SHA1.digest(args[0])).upcase
end
end

View File

@@ -0,0 +1,21 @@
module Puppet::Parser::Functions
newfunction(:mysql_strip_hash, :type => :rvalue, :arity => 1, :doc => <<-EOS
TEMPORARY FUNCTION: EXPIRES 2014-03-10
When given a hash this function strips out all blank entries.
EOS
) do |args|
hash = args[0]
unless hash.is_a?(Hash)
raise(Puppet::ParseError, 'mysql_strip_hash(): Requires hash to work with')
end
# Filter out all the top level blanks.
hash.reject{|k,v| v == ''}.each do |k,v|
if v.is_a?(Hash)
v.reject!{|ki,vi| vi == '' }
end
end
end
end

View File

@@ -0,0 +1,41 @@
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

View File

@@ -0,0 +1,199 @@
# 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

View File

@@ -0,0 +1,65 @@
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

View File

@@ -0,0 +1,70 @@
class Puppet::Provider::Mysql < Puppet::Provider
# Without initvars commands won't work.
initvars
commands :mysql => 'mysql'
commands :mysqladmin => 'mysqladmin'
# Optional defaults file
def self.defaults_file
if File.file?("#{Facter.value(:root_home)}/.my.cnf")
"--defaults-extra-file=#{Facter.value(:root_home)}/.my.cnf"
else
nil
end
end
def defaults_file
self.class.defaults_file
end
def self.users
mysql([defaults_file, '-NBe', "SELECT CONCAT(User, '@',Host) AS User FROM mysql.user"].compact).split("\n")
end
# Take root@localhost and munge it to 'root'@'localhost'
def self.cmd_user(user)
"'#{user.sub('@', "'@'")}'"
end
# Take root.* and return ON `root`.*
def self.cmd_table(table)
table_string = ''
# We can't escape *.* so special case this.
if table == '*.*'
table_string << '*.*'
# Special case also for PROCEDURES
elsif table.start_with?('PROCEDURE ')
table_string << table.sub(/^PROCEDURE (.*)(\..*)/, 'PROCEDURE `\1`\2')
else
table_string << table.sub(/^(.*)(\..*)/, '`\1`\2')
end
table_string
end
def self.cmd_privs(privileges)
if privileges.include?('ALL')
return 'ALL PRIVILEGES'
else
priv_string = ''
privileges.each do |priv|
priv_string << "#{priv}, "
end
end
# Remove trailing , from the last element.
priv_string.sub(/, $/, '')
end
# Take in potential options and build up a query string with them.
def self.cmd_options(options)
option_string = ''
options.each do |opt|
if opt == 'GRANT'
option_string << ' WITH GRANT OPTION'
end
end
option_string
end
end

View File

@@ -0,0 +1,68 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
Puppet::Type.type(:mysql_database).provide(:mysql, :parent => Puppet::Provider::Mysql) do
desc 'Manages MySQL databases.'
commands :mysql => 'mysql'
def self.instances
mysql([defaults_file, '-NBe', 'show databases'].compact).split("\n").collect do |name|
attributes = {}
mysql([defaults_file, '-NBe', "show variables like '%_database'", name].compact).split("\n").each do |line|
k,v = line.split(/\s/)
attributes[k] = v
end
new(:name => name,
:ensure => :present,
:charset => attributes['character_set_database'],
:collate => attributes['collation_database']
)
end
end
# We iterate over each mysql_database entry in the catalog and compare it against
# the contents of the property_hash generated by self.instances
def self.prefetch(resources)
databases = instances
resources.keys.each do |database|
if provider = databases.find { |db| db.name == database }
resources[database].provider = provider
end
end
end
def create
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]
@property_hash[:collate] = @resource[:collate]
exists? ? (return true) : (return false)
end
def destroy
mysql([defaults_file, '-NBe', "drop database `#{@resource[:name]}`"].compact)
@property_hash.clear
exists? ? (return false) : (return true)
end
def exists?
@property_hash[:ensure] == :present || false
end
mk_resource_methods
def charset=(value)
mysql([defaults_file, '-NBe', "alter database `#{resource[:name]}` CHARACTER SET #{value}"].compact)
@property_hash[:charset] = value
charset == value ? (return true) : (return false)
end
def collate=(value)
mysql([defaults_file, '-NBe', "alter database `#{resource[:name]}` COLLATE #{value}"].compact)
@property_hash[:collate] = value
collate == value ? (return true) : (return false)
end
end

View File

@@ -0,0 +1,120 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
Puppet::Type.type(:mysql_grant).provide(:mysql, :parent => Puppet::Provider::Mysql) do
desc 'Set grants for users in MySQL.'
def self.instances
instances = []
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)
# 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.*)$/)
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
end
# Same here, but to remove OPTION leaving just GRANT.
options = ['GRANT'] if rest.match(/WITH\sGRANT\sOPTION/)
# We need to return an array of instances so capture these
instances << new(
:name => "#{user}@#{host}/#{table}",
:ensure => :present,
:privileges => stripped_privileges.sort,
:table => table,
:user => "#{user}@#{host}",
:options => options
)
end
end
end
return instances
end
def self.prefetch(resources)
users = instances
resources.keys.each do |name|
if provider = users.find { |user| user.name == name }
resources[name].provider = provider
end
end
end
def grant(user, table, privileges, options)
user_string = self.class.cmd_user(user)
priv_string = self.class.cmd_privs(privileges)
table_string = self.class.cmd_table(table)
query = "GRANT #{priv_string}"
query << " ON #{table_string}"
query << " TO #{user_string}"
query << self.class.cmd_options(options) unless options.nil?
mysql([defaults_file, '-e', query].compact)
end
def create
grant(@resource[:user], @resource[:table], @resource[:privileges], @resource[:options])
@property_hash[:ensure] = :present
@property_hash[:table] = @resource[:table]
@property_hash[:user] = @resource[:user]
@property_hash[:options] = @resource[:options] if @resource[:options]
@property_hash[:privileges] = @resource[:privileges]
exists? ? (return true) : (return false)
end
def revoke(user, table)
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.
query = "REVOKE GRANT OPTION ON #{table_string} FROM #{user_string}"
mysql([defaults_file, '-e', query].compact)
end
def destroy
revoke(@property_hash[:user], @property_hash[:table])
@property_hash.clear
exists? ? (return false) : (return true)
end
def exists?
@property_hash[:ensure] == :present || false
end
def flush
@property_hash.clear
mysql([defaults_file, '-NBe', 'FLUSH PRIVILEGES'].compact)
end
mk_resource_methods
def privileges=(privileges)
revoke(@property_hash[:user], @property_hash[:table])
grant(@property_hash[:user], @property_hash[:table], privileges, @property_hash[:options])
@property_hash[:privileges] = privileges
self.privileges
end
def options=(options)
revoke(@property_hash[:user], @property_hash[:table])
grant(@property_hash[:user], @property_hash[:table], @property_hash[:privileges], options)
@property_hash[:options] = options
self.options
end
end

View File

@@ -0,0 +1,115 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql'))
Puppet::Type.type(:mysql_user).provide(:mysql, :parent => Puppet::Provider::Mysql) do
desc 'manage users for a mysql database.'
commands :mysql => 'mysql'
# Build a property_hash containing all the discovered information about MySQL
# users.
def self.instances
users = mysql([defaults_file, '-NBe',
"SELECT CONCAT(User, '@',Host) AS User FROM mysql.user"].compact).split("\n")
# 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}'"
@max_user_connections, @max_connections_per_hour, @max_queries_per_hour,
@max_updates_per_hour, @password = mysql([defaults_file, "-NBe", query].compact).split(/\s/)
new(:name => name,
:ensure => :present,
:password_hash => @password,
: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
)
end
end
# We iterate over each mysql_user entry in the catalog and compare it against
# the contents of the property_hash generated by self.instances
def self.prefetch(resources)
users = instances
resources.keys.each do |name|
if provider = users.find { |user| user.name == name }
resources[name].provider = provider
end
end
end
def create
merged_name = @resource[:name].sub('@', "'@'")
password_hash = @resource.value(:password_hash)
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
@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
@property_hash[:max_updates_per_hour] = max_updates_per_hour
exists? ? (return true) : (return false)
end
def destroy
merged_name = @resource[:name].sub('@', "'@'")
mysql([defaults_file, '-e', "DROP USER '#{merged_name}'"].compact)
@property_hash.clear
exists? ? (return false) : (return true)
end
def exists?
@property_hash[:ensure] == :present || false
end
##
## MySQL user properties
##
# Generates method for all properties of the property_hash
mk_resource_methods
def password_hash=(string)
merged_name = self.class.cmd_user(@resource[:name])
mysql([defaults_file, '-e', "SET PASSWORD FOR #{merged_name} = '#{string}'"].compact)
password_hash == string ? (return true) : (return false)
end
def max_user_connections=(int)
merged_name = self.class.cmd_user(@resource[:name])
mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO #{merged_name} WITH MAX_USER_CONNECTIONS #{int}"].compact).chomp
max_user_connections == int ? (return true) : (return false)
end
def max_connections_per_hour=(int)
merged_name = self.class.cmd_user(@resource[:name])
mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO #{merged_name} WITH MAX_CONNECTIONS_PER_HOUR #{int}"].compact).chomp
max_connections_per_hour == int ? (return true) : (return false)
end
def max_queries_per_hour=(int)
merged_name = self.class.cmd_user(@resource[:name])
mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO #{merged_name} WITH MAX_QUERIES_PER_HOUR #{int}"].compact).chomp
max_queries_per_hour == int ? (return true) : (return false)
end
def max_updates_per_hour=(int)
merged_name = self.class.cmd_user(@resource[:name])
mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO #{merged_name} WITH MAX_UPDATES_PER_HOUR #{int}"].compact).chomp
max_updates_per_hour == int ? (return true) : (return false)
end
end

View File

@@ -0,0 +1,21 @@
# 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

View File

@@ -0,0 +1,79 @@
# 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

View File

@@ -0,0 +1,31 @@
# 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

View File

@@ -0,0 +1,22 @@
Puppet::Type.newtype(:mysql_database) do
@doc = 'Manage MySQL databases.'
ensurable
newparam(:name, :namevar => true) do
desc 'The name of the MySQL database to manage.'
end
newproperty(:charset) do
desc 'The CHARACTER SET setting for the database'
defaultto :utf8
newvalue(/^\S+$/)
end
newproperty(:collate) do
desc 'The COLLATE setting for the database'
defaultto :utf8_general_ci
newvalue(/^\S+$/)
end
end

View File

@@ -0,0 +1,72 @@
# This has to be a separate type to enable collecting
Puppet::Type.newtype(:mysql_grant) do
@doc = "Manage a MySQL user's rights."
ensurable
autorequire(:file) { '/root/.my.cnf' }
def initialize(*args)
super
# Forcibly munge any privilege with 'ALL' in the array to exist of just
# 'ALL'. This can't be done in the munge in the property as that iterates
# over the array and there's no way to replace the entire array before it's
# returned to the provider.
if self[:ensure] == :present and Array(self[:privileges]).count > 1 and self[:privileges].to_s.include?('ALL')
self[:privileges] = 'ALL'
end
# 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!
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?
end
newparam(:name, :namevar => true) do
desc 'Name to describe the grant.'
munge do |value|
value.delete("'")
end
end
newproperty(:privileges, :array_matching => :all) do
desc 'Privileges for user'
munge do |value|
value.upcase
end
end
newproperty(:table) do
desc 'Table to apply privileges to.'
munge do |value|
value.delete("`")
end
newvalues(/.*\..*/,/@/)
end
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'
end
end
end
newproperty(:options, :array_matching => :all) do
desc 'Options to grant.'
end
end

View File

@@ -0,0 +1,45 @@
# This has to be a separate type to enable collecting
Puppet::Type.newtype(:mysql_user) do
@doc = 'Manage a MySQL 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|
# 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
newproperty(:max_connections_per_hour) do
desc "Max connections per hour for the user. 0 means no (or global) limit."
newvalue(/\d+/)
end
newproperty(:max_queries_per_hour) do
desc "Max queries per hour for the user. 0 means no (or global) limit."
newvalue(/\d+/)
end
newproperty(:max_updates_per_hour) do
desc "Max updates per hour for the user. 0 means no (or global) limit."
newvalue(/\d+/)
end
end

View File

@@ -0,0 +1,31 @@
# 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,
}
}

View File

@@ -0,0 +1,33 @@
# See README.md.
class mysql::bindings (
# Boolean to determine if we should include the classes.
$java_enable = false,
$perl_enable = false,
$php_enable = false,
$python_enable = false,
$ruby_enable = 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
) 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' }
}

View File

@@ -0,0 +1,10 @@
# Private class
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,
}
}

View File

@@ -0,0 +1,10 @@
# Private class
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,
}
}

View File

@@ -0,0 +1,10 @@
# Private class: See README.md
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,
}
}

View File

@@ -0,0 +1,10 @@
# Private class
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,
}
}

View File

@@ -0,0 +1,10 @@
# Private class
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,
}
}

View File

@@ -0,0 +1,27 @@
#
class mysql::client (
$bindings_enable = $mysql::params::bindings_enable,
$package_ensure = $mysql::params::client_package_ensure,
$package_name = $mysql::params::client_package_name,
) inherits mysql::params {
include '::mysql::client::install'
if $bindings_enable {
class { 'mysql::bindings':
java_enable => true,
perl_enable => true,
php_enable => true,
python_enable => true,
ruby_enable => true,
}
}
# Anchor pattern workaround to avoid resources of mysql::client::install to
# "float off" outside mysql::client
anchor { 'mysql::client::start': } ->
Class['mysql::client::install'] ->
anchor { 'mysql::client::end': }
}

View File

@@ -0,0 +1,8 @@
class mysql::client::install {
package { 'mysql_client':
ensure => $mysql::client::package_ensure,
name => $mysql::client::package_name,
}
}

View File

@@ -0,0 +1,59 @@
# See README.md for details.
define mysql::db (
$user,
$password,
$charset = 'utf8',
$collate = 'utf8_general_ci',
$host = 'localhost',
$grant = 'ALL',
$sql = '',
$enforce_sql = false,
$ensure = 'present'
) {
#input validation
validate_re($ensure, '^(present|absent)$',
"${ensure} is not supported for ensure. Allowed values are 'present' and 'absent'.")
$table = "${name}.*"
include '::mysql::client'
mysql_database { $name:
ensure => $ensure,
charset => $charset,
collate => $collate,
provider => 'mysql',
require => [ Class['mysql::server'], Class['mysql::client'] ],
before => Mysql_user["${user}@${host}"],
}
$user_resource = {
ensure => $ensure,
password_hash => mysql_password($password),
provider => 'mysql',
require => Class['mysql::server'],
}
ensure_resource('mysql_user', "${user}@${host}", $user_resource)
if $ensure == 'present' {
mysql_grant { "${user}@${host}/${table}":
privileges => $grant,
provider => 'mysql',
user => "${user}@${host}",
table => $table,
require => [ Mysql_user["${user}@${host}"], Class['mysql::server'] ],
}
$refresh = ! $enforce_sql
if $sql {
exec{ "${name}-import":
command => "/usr/bin/mysql ${name} < ${sql}",
logoutput => true,
environment => "HOME=${::root_home}",
refreshonly => $refresh,
require => Mysql_grant["${user}@${host}/${table}"],
subscribe => Mysql_database[$name],
}
}
}
}

View File

@@ -0,0 +1,100 @@
#
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")
}
}

View File

@@ -0,0 +1,230 @@
# Private class: See README.md.
class mysql::params {
$manage_config_file = true
$old_root_password = ''
$purge_conf_dir = false
$restart = false
$root_password = 'UNSET'
$server_package_ensure = 'present'
$server_service_manage = true
$server_service_enabled = 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
case $::osfamily {
'RedHat': {
if $::operatingsystem == 'Fedora' and (is_integer($::operatingsystemrelease) and $::operatingsystemrelease >= 19 or $::operatingsystemrelease == "Rawhide") {
$client_package_name = 'mariadb'
$server_package_name = 'mariadb-server'
} else {
$client_package_name = 'mysql'
$server_package_name = 'mysql-server'
}
$basedir = '/usr'
$config_file = '/etc/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 = 'php-mysql'
$python_package_name = 'MySQL-python'
$ruby_package_name = 'ruby-mysql'
}
'Suse': {
$client_package_name = $::operatingsystem ? {
/OpenSuSE/ => 'mysql-community-server-client',
/(SLES|SLED)/ => 'mysql-client',
}
$server_package_name = $::operatingsystem ? {
/OpenSuSE/ => 'mysql-community-server',
/(SLES|SLED)/ => 'mysql',
}
$basedir = '/usr'
$config_file = '/etc/my.cnf'
$datadir = '/var/lib/mysql'
$log_error = $::operatingsystem ? {
/OpenSuSE/ => '/var/log/mysql/mysqld.log',
/(SLES|SLED)/ => '/var/log/mysqld.log',
}
$pidfile = $::operatingsystem ? {
/OpenSuSE/ => '/var/run/mysql/mysqld.pid',
/(SLES|SLED)/ => '/var/lib/mysql/mysqld.pid',
}
$root_group = 'root'
$server_service_name = 'mysql'
$socket = $::operatingsystem ? {
/OpenSuSE/ => '/var/run/mysql/mysql.sock',
/(SLES|SLED)/ => '/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 = 'apache2-mod_php53'
$python_package_name = 'python-mysql'
$ruby_package_name = $::operatingsystem ? {
/OpenSuSE/ => 'rubygem-mysql',
/(SLES|SLED)/ => 'ruby-mysql',
}
}
'Debian': {
$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'
# mysql::bindings
$java_package_name = 'libmysql-java'
$perl_package_name = 'libdbd-mysql-perl'
$php_package_name = 'php5-mysql'
$python_package_name = 'python-mysqldb'
$ruby_package_name = 'libmysql-ruby'
}
'FreeBSD': {
$client_package_name = 'databases/mysql55-client'
$server_package_name = 'databases/mysql55-server'
$basedir = '/usr/local'
$config_file = '/var/db/mysql/my.cnf'
$datadir = '/var/db/mysql'
$log_error = "/var/db/mysql/${::hostname}.err"
$pidfile = '/var/db/mysql/mysql.pid'
$root_group = 'wheel'
$server_service_name = 'mysql-server'
$socket = '/tmp/mysql.sock'
$ssl_ca = undef
$ssl_cert = undef
$ssl_key = undef
$tmpdir = '/tmp'
# mysql::bindings
$java_package_name = 'databases/mysql-connector-java'
$perl_package_name = 'p5-DBD-mysql'
$php_package_name = 'php5-mysql'
$python_package_name = 'databases/py-MySQLdb'
$ruby_package_name = 'databases/ruby-mysql'
}
default: {
case $::operatingsystem {
'Amazon': {
$client_package_name = 'mysql'
$server_package_name = 'mysql-server'
$basedir = '/usr'
$config_file = '/etc/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 = 'php-mysql'
$python_package_name = 'MySQL-python'
$ruby_package_name = 'ruby-mysql'
}
default: {
fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat, Debian, and FreeBSD, or operatingsystem Amazon")
}
}
}
}
case $::operatingsystem {
'Ubuntu': {
$server_service_provider = upstart
}
default: {
$server_service_provider = undef
}
}
$default_options = {
'client' => {
'port' => '3306',
'socket' => $mysql::params::socket,
},
'mysqld_safe' => {
'nice' => '0',
'log-error' => $mysql::params::log_error,
'socket' => $mysql::params::socket,
},
'mysqld' => {
'basedir' => $mysql::params::basedir,
'bind-address' => '127.0.0.1',
'datadir' => $mysql::params::datadir,
'expire_logs_days' => '10',
'key_buffer_size' => '16M',
'log-error' => $mysql::params::log_error,
'max_allowed_packet' => '16M',
'max_binlog_size' => '100M',
'max_connections' => '151',
'myisam_recover' => 'BACKUP',
'pid-file' => $mysql::params::pidfile,
'port' => '3306',
'query_cache_limit' => '1M',
'query_cache_size' => '16M',
'skip-external-locking' => true,
'socket' => $mysql::params::socket,
'ssl' => false,
'ssl-ca' => $mysql::params::ssl_ca,
'ssl-cert' => $mysql::params::ssl_cert,
'ssl-key' => $mysql::params::ssl_key,
'thread_cache_size' => '8',
'thread_stack' => '256K',
'tmpdir' => $mysql::params::tmpdir,
'user' => 'mysql',
},
'mysqldump' => {
'max_allowed_packet' => '16M',
'quick' => true,
'quote-names' => true,
},
'isamchk' => {
'key_buffer_size' => '16M',
},
}
}

View File

@@ -0,0 +1,82 @@
# Class: mysql::server: See README.md for documentation.
class mysql::server (
$config_file = $mysql::params::config_file,
$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_name = $mysql::params::server_package_name,
$purge_conf_dir = $mysql::params::purge_conf_dir,
$remove_default_accounts = false,
$restart = $mysql::params::restart,
$root_group = $mysql::params::root_group,
$root_password = $mysql::params::root_password,
$service_enabled = $mysql::params::server_service_enabled,
$service_manage = $mysql::params::server_service_manage,
$service_name = $mysql::params::server_service_name,
$service_provider = $mysql::params::server_service_provider,
$users = {},
$grants = {},
$databases = {},
# Deprecated parameters
$enabled = undef,
$manage_service = undef
) inherits mysql::params {
# Deprecated parameters.
if $enabled {
crit('This parameter has been renamed to service_enabled.')
$real_service_enabled = $enabled
} else {
$real_service_enabled = $service_enabled
}
if $manage_service {
crit('This parameter has been renamed to service_manage.')
$real_service_manage = $manage_service
} else {
$real_service_manage = $service_manage
}
# Create a merged together set of options. Rightmost hashes win over left.
$options = mysql_deepmerge($mysql::params::default_options, $override_options)
Class['mysql::server::root_password'] -> Mysql::Db <| |>
include '::mysql::server::install'
include '::mysql::server::config'
include '::mysql::server::service'
include '::mysql::server::root_password'
include '::mysql::server::providers'
if $remove_default_accounts {
class { '::mysql::server::account_security':
require => Anchor['mysql::server::end'],
}
}
anchor { 'mysql::server::start': }
anchor { 'mysql::server::end': }
if $restart {
Anchor['mysql::server::start'] ->
Class['mysql::server::install'] ->
# Only difference between the blocks is that we use ~> to restart if
# restart is set to true.
Class['mysql::server::config'] ~>
Class['mysql::server::service'] ->
Class['mysql::server::root_password'] ->
Class['mysql::server::providers'] ->
Anchor['mysql::server::end']
} else {
Anchor['mysql::server::start'] ->
Class['mysql::server::install'] ->
Class['mysql::server::config'] ->
Class['mysql::server::service'] ->
Class['mysql::server::root_password'] ->
Class['mysql::server::providers'] ->
Anchor['mysql::server::end']
}
}

View File

@@ -0,0 +1,22 @@
class mysql::server::account_security {
mysql_user {
[ "root@${::fqdn}",
'root@127.0.0.1',
'root@::1',
"@${::fqdn}",
'@localhost',
'@%']:
ensure => 'absent',
require => Anchor['mysql::server::end'],
}
if ($::fqdn != $::hostname) {
mysql_user { ["root@${::hostname}", "@${::hostname}"]:
ensure => 'absent',
require => Anchor['mysql::server::end'],
}
}
mysql_database { 'test':
ensure => 'absent',
require => Anchor['mysql::server::end'],
}
}

View File

@@ -0,0 +1,61 @@
# 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,
$delete_before_dump = false,
$backupdatabases = [],
$file_per_database = false,
$ensure = 'present',
$time = ['23', '5'],
$postscript = false,
) {
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' ],
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,
}
}

View File

@@ -0,0 +1,30 @@
# See README.me for options.
class mysql::server::config {
$options = $mysql::server::options
File {
owner => 'root',
group => $mysql::server::root_group,
mode => '0400',
}
file { '/etc/mysql':
ensure => directory,
mode => '0755',
}
file { '/etc/mysql/conf.d':
ensure => directory,
mode => '0755',
recurse => $mysql::server::purge_conf_dir,
purge => $mysql::server::purge_conf_dir,
}
if $mysql::server::manage_config_file {
file { $mysql::server::config_file:
content => template('mysql/my.cnf.erb'),
mode => '0644',
}
}
}

View File

@@ -0,0 +1,9 @@
#
class mysql::server::install {
package { 'mysql-server':
ensure => $mysql::server::package_ensure,
name => $mysql::server::package_name,
}
}

View File

@@ -0,0 +1,24 @@
#This is a helper class to add a monitoring user to the database
class mysql::server::monitor (
$mysql_monitor_username,
$mysql_monitor_password,
$mysql_monitor_hostname
) {
Anchor['mysql::server::end'] -> Class['mysql::server::monitor']
mysql_user { "${mysql_monitor_username}@${mysql_monitor_hostname}":
ensure => present,
password_hash => mysql_password($mysql_monitor_password),
require => Class['mysql::server::service'],
}
mysql_grant { "${mysql_monitor_username}@${mysql_monitor_hostname}/*.*":
ensure => present,
user => "${mysql_monitor_username}@${mysql_monitor_hostname}",
table => '*.*',
privileges => [ 'PROCESS', 'SUPER' ],
require => Mysql_user["${mysql_monitor_username}@${mysql_monitor_hostname}"],
}
}

View File

@@ -0,0 +1,9 @@
#
class mysql::server::mysqltuner($ensure='present') {
# mysql performance tester
file { '/usr/local/bin/mysqltuner':
ensure => $ensure,
mode => '0550',
source => 'puppet:///modules/mysql/mysqltuner.pl',
}
}

View File

@@ -0,0 +1,8 @@
# Convenience class to call each of the three providers with the corresponding
# hashes provided in mysql::server.
# See README.md for details.
class mysql::server::providers {
create_resources('mysql_user', $mysql::server::users)
create_resources('mysql_grant', $mysql::server::grants)
create_resources('mysql_database', $mysql::server::databases)
}

View File

@@ -0,0 +1,21 @@
#
class mysql::server::root_password {
$options = $mysql::server::options
# manage root password if it is set
if $mysql::server::root_password != 'UNSET' {
mysql_user { 'root@localhost':
ensure => present,
password_hash => mysql_password($mysql::server::root_password),
}
file { "${::root_home}/.my.cnf":
content => template('mysql/my.cnf.pass.erb'),
owner => 'root',
mode => '0600',
require => Mysql_user['root@localhost'],
}
}
}

View File

@@ -0,0 +1,19 @@
#
class mysql::server::service {
if $mysql::server::real_service_enabled {
$service_ensure = 'running'
} else {
$service_ensure = 'stopped'
}
if $mysql::server::real_service_manage {
service { 'mysqld':
ensure => $service_ensure,
name => $mysql::server::service_name,
enable => $mysql::server::real_service_enabled,
provider => $mysql::server::service_provider,
}
}
}

View File

@@ -0,0 +1,76 @@
{
"operatingsystem_support": [
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"5",
"6"
]
},
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"5",
"6"
]
},
{
"operatingsystem": "OracleLinux",
"operatingsystemrelease": [
"5",
"6"
]
},
{
"operatingsystem": "Scientific",
"operatingsystemrelease": [
"5",
"6"
]
},
{
"operatingsystem": "SLES",
"operatingsystemrelease": [
"11 SP1"
]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"6",
"7"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"10.04",
"12.04"
]
}
],
"requirements": [
{
"name": "pe",
"version_requirement": "3.2.x"
},
{
"name": "puppet",
"version_requirement": "3.x"
}
],
"name": "puppetlabs-mysql",
"version": "2.2.3",
"source": "git://github.com/puppetlabs/puppetlabs-mysql.git",
"author": "Puppet Labs",
"license": "Apache 2.0",
"summary": "Mysql module",
"description": "Mysql module",
"project_page": "http://github.com/puppetlabs/puppetlabs-mysql",
"dependencies": [
{
"name": "puppetlabs/stdlib",
"version_requirement": ">= 3.2.0"
}
]
}

View File

@@ -0,0 +1,25 @@
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

View File

@@ -0,0 +1,64 @@
require 'spec_helper_acceptance'
describe 'mysql::server::backup class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) 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':
user => 'backup',
password => 'secret',
}
class { 'mysql::server::backup':
backupuser => 'myuser',
backuppassword => 'mypassword',
backupdir => '/tmp/backups',
backupcompress => 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',
],
}
EOS
apply_manifest(pp, :catch_failures => true) do |r|
expect(r.stderr).to eq("")
end
apply_manifest(pp, :catch_failures => true) do |r|
expect(r.stderr).to eq("")
end
end
end
describe 'mysqlbackup.sh' do
it 'should run mysqlbackup.sh with no errors' do
shell("/usr/local/sbin/mysqlbackup.sh") do |r|
expect(r.stderr).to eq("")
end
end
it 'should dump all databases to single file' do
shell('ls -l /tmp/backups/mysql_backup_*-*.sql.bz2 | wc -l') do |r|
expect(r.stdout).to match(/1/)
expect(r.exit_code).to be_zero
end
end
context 'should create one file per database per run' do
it 'executes mysqlbackup.sh a second time' do
shell('sleep 1')
shell('/usr/local/sbin/mysqlbackup.sh')
end
it 'creates at least one backup tarball' do
shell('ls -l /tmp/backups/mysql_backup_*-*.sql.bz2 | wc -l') do |r|
expect(r.stdout).to match(/2/)
expect(r.exit_code).to be_zero
end
end
end
end
end

View File

@@ -0,0 +1,117 @@
require 'spec_helper_acceptance'
osfamily = fact('osfamily')
operatingsystem = fact('operatingsystem')
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'
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'
ruby_package = 'libmysql-ruby'
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
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 => undef,
}
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
describe package(ruby_package) do
it { should be_installed }
end
end
end

View File

@@ -0,0 +1,49 @@
require 'spec_helper_acceptance'
describe 'mysql::db define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
describe 'creating a database' do
# Using puppet_apply as a helper
it 'should work with no errors' do
pp = <<-EOS
class { 'mysql::server': root_password => 'password' }
mysql::db { 'spec1':
user => 'root1',
password => 'password',
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
expect(shell("mysql -e 'show databases;'|grep spec1").exit_code).to be_zero
end
end
describe 'creating a database with post-sql' do
# Using puppet_apply as a helper
it 'should work with no errors' do
pp = <<-EOS
class { 'mysql::server': override_options => { 'root_password' => 'password' } }
file { '/tmp/spec.sql':
ensure => file,
content => 'CREATE TABLE table1 (id int);',
before => Mysql::Db['spec2'],
}
mysql::db { 'spec2':
user => 'root1',
password => 'password',
sql => '/tmp/spec.sql',
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should have the table' do
expect(shell("mysql -e 'show tables;' spec2|grep table1").exit_code).to be_zero
end
end
end

View File

@@ -0,0 +1,47 @@
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

View File

@@ -0,0 +1,22 @@
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

View File

@@ -0,0 +1,71 @@
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('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

View File

@@ -0,0 +1,281 @@
require 'spec_helper_acceptance'
describe 'mysql class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
case fact('osfamily')
when 'RedHat'
package_name = 'mysql-server'
service_name = 'mysqld'
service_provider = 'undef'
mycnf = '/etc/my.cnf'
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 '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
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' } }
}
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)
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

View File

@@ -0,0 +1,12 @@
HOSTS:
centos-64-x64:
roles:
- master
- database
- dashboard
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
hypervisor : vagrant
CONFIG:
type: pe

View File

@@ -0,0 +1,10 @@
HOSTS:
centos-64-x64:
roles:
- master
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
hypervisor : vagrant
CONFIG:
type: foss

View File

@@ -0,0 +1,10 @@
HOSTS:
fedora-18-x64:
roles:
- master
platform: fedora-18-x86_64
box : fedora-18-x64-vbox4210-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: foss

View File

@@ -0,0 +1,10 @@
HOSTS:
sles-11-x64.local:
roles:
- master
platform: sles-11-x64
box : sles-11sp1-x64-vbox4210-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: foss

View File

@@ -0,0 +1,10 @@
HOSTS:
ubuntu-server-10044-x64:
roles:
- master
platform: ubuntu-10.04-amd64
box : ubuntu-server-10044-x64-vbox4210-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: foss

View File

@@ -0,0 +1,10 @@
HOSTS:
ubuntu-server-12042-x64:
roles:
- master
platform: ubuntu-12.04-amd64
box : ubuntu-server-12042-x64-vbox4210-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: foss

View File

@@ -0,0 +1,64 @@
require 'spec_helper_acceptance'
describe 'mysql_database', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
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 'creating database' do
it 'should work without errors' do
pp = <<-EOS
mysql_database { 'spec_db':
ensure => present,
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should find the database' do
shell("mysql -NBe \"SHOW DATABASES LIKE 'spec_db'\"") do |r|
expect(r.stdout).to match(/^spec_db$/)
expect(r.stderr).to be_empty
end
end
end
describe 'charset and collate' do
it 'should create two db of different types idempotently' do
pp = <<-EOS
mysql_database { 'spec_latin1':
charset => 'latin1',
collate => 'latin1_swedish_ci',
}
mysql_database { 'spec_utf8':
charset => 'utf8',
collate => 'utf8_general_ci',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should find latin1 db' do
shell("mysql -NBe \"SHOW VARIABLES LIKE '%_database'\" spec_latin1") do |r|
expect(r.stdout).to match(/^character_set_database\tlatin1\ncollation_database\tlatin1_swedish_ci$/)
expect(r.stderr).to be_empty
end
end
it 'should find utf8 db' do
shell("mysql -NBe \"SHOW VARIABLES LIKE '%_database'\" spec_utf8") do |r|
expect(r.stdout).to match(/^character_set_database\tutf8\ncollation_database\tutf8_general_ci$/)
expect(r.stderr).to be_empty
end
end
end
end

View File

@@ -0,0 +1,308 @@
require 'spec_helper_acceptance'
describe 'mysql_grant', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
describe 'setup' do
it 'setup mysql::server' do
pp = <<-EOS
class { 'mysql::server': }
EOS
apply_manifest(pp, :catch_failures => true)
end
end
describe 'missing privileges for user' do
it 'should fail' do
pp = <<-EOS
mysql_grant { 'test1@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test1@tester',
}
EOS
expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/privileges parameter is required/)
end
it 'should not find the user' do
expect(shell("mysql -NBe \"SHOW GRANTS FOR test1@tester\"", { :acceptable_exit_codes => 1}).stderr).to match(/There is no such grant defined for user 'test1' on host 'tester'/)
end
end
describe 'missing table for user' do
it 'should fail' do
pp = <<-EOS
mysql_grant { 'atest@tester/test.*':
ensure => 'present',
user => 'atest@tester',
privileges => ['ALL'],
}
EOS
apply_manifest(pp, :expect_failures => true)
end
it 'should not find the user' do
expect(shell("mysql -NBe \"SHOW GRANTS FOR atest@tester\"", {:acceptable_exit_codes => 1}).stderr).to match(/There is no such grant defined for user 'atest' on host 'tester'/)
end
end
describe 'adding privileges' do
it 'should work without errors' do
pp = <<-EOS
mysql_grant { 'test2@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test2@tester',
privileges => ['SELECT', 'UPDATE'],
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test2@tester\"") do |r|
expect(r.stdout).to match(/GRANT SELECT, UPDATE.*TO 'test2'@'tester'/)
expect(r.stderr).to be_empty
end
end
end
describe 'adding option' do
it 'should work without errors' do
pp = <<-EOS
mysql_grant { 'test3@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test3@tester',
options => ['GRANT'],
privileges => ['SELECT', 'UPDATE'],
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test3@tester\"") do |r|
expect(r.stdout).to match(/GRANT SELECT, UPDATE ON `test`.* TO 'test3'@'tester' WITH GRANT OPTION$/)
expect(r.stderr).to be_empty
end
end
end
describe 'adding all privileges without table' do
it 'should fail' do
pp = <<-EOS
mysql_grant { 'test4@tester/test.*':
ensure => 'present',
user => 'test4@tester',
options => ['GRANT'],
privileges => ['SELECT', 'UPDATE', 'ALL'],
}
EOS
expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/table parameter is required./)
end
end
describe 'adding all privileges' do
it 'should only try to apply ALL' do
pp = <<-EOS
mysql_grant { 'test4@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test4@tester',
options => ['GRANT'],
privileges => ['SELECT', 'UPDATE', 'ALL'],
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test4@tester\"") do |r|
expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test4'@'tester' WITH GRANT OPTION/)
expect(r.stderr).to be_empty
end
end
end
# Test combinations of user@host to ensure all cases work.
describe 'short hostname' do
it 'should apply' do
pp = <<-EOS
mysql_grant { 'test@short/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@short',
privileges => 'ALL',
}
mysql_grant { 'test@long.hostname.com/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@long.hostname.com',
privileges => 'ALL',
}
mysql_grant { 'test@192.168.5.6/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@192.168.5.6',
privileges => 'ALL',
}
mysql_grant { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@2607:f0d0:1002:0051:0000:0000:0000:0004',
privileges => 'ALL',
}
mysql_grant { 'test@::1/128/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@::1/128',
privileges => 'ALL',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'finds short hostname' do
shell("mysql -NBe \"SHOW GRANTS FOR test@short\"") do |r|
expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'short'/)
expect(r.stderr).to be_empty
end
end
it 'finds long hostname' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'long.hostname.com'\"") do |r|
expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'long.hostname.com'/)
expect(r.stderr).to be_empty
end
end
it 'finds ipv4' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.6'\"") do |r|
expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'192.168.5.6'/)
expect(r.stderr).to be_empty
end
end
it 'finds ipv6' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'\"") do |r|
expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'/)
expect(r.stderr).to be_empty
end
end
it 'finds short ipv6' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'::1/128'\"") do |r|
expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'::1\/128'/)
expect(r.stderr).to be_empty
end
end
end
describe 'complex test' do
it 'setup mysql::server' do
pp = <<-EOS
$dbSubnet = '10.10.10.%'
mysql_database { 'foo':
ensure => present,
}
exec { 'mysql-create-table':
command => '/usr/bin/mysql -NBe "CREATE TABLE foo.bar (name VARCHAR(20))"',
environment => "HOME=${::root_home}",
unless => '/usr/bin/mysql -NBe "SELECT 1 FROM foo.bar LIMIT 1;"',
require => Mysql_database['foo'],
}
Mysql_grant {
ensure => present,
options => ['GRANT'],
privileges => ['ALL'],
table => '*.*',
require => [ Mysql_database['foo'], Exec['mysql-create-table'] ],
}
mysql_grant { "user1@${dbSubnet}/*.*":
user => "user1@${dbSubnet}",
}
mysql_grant { "user2@${dbSubnet}/foo.bar":
privileges => ['SELECT', 'INSERT', 'UPDATE'],
user => "user2@${dbSubnet}",
table => 'foo.bar',
}
mysql_grant { "user3@${dbSubnet}/foo.*":
privileges => ['SELECT', 'INSERT', 'UPDATE'],
user => "user3@${dbSubnet}",
table => 'foo.*',
}
mysql_grant { 'web@%/*.*':
user => 'web@%',
}
mysql_grant { "web@${dbSubnet}/*.*":
user => "web@${dbSubnet}",
}
mysql_grant { "web@${fqdn}/*.*":
user => "web@${fqdn}",
}
mysql_grant { 'web@localhost/*.*':
user => 'web@localhost',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
end
describe 'lower case privileges' do
it 'create ALL privs' do
pp = <<-EOS
mysql_grant { 'lowercase@localhost/*.*':
user => 'lowercase@localhost',
privileges => 'ALL',
table => '*.*',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'create lowercase all privs' do
pp = <<-EOS
mysql_grant { 'lowercase@localhost/*.*':
user => 'lowercase@localhost',
privileges => 'all',
table => '*.*',
}
EOS
expect(apply_manifest(pp, :catch_failures => true).exit_code).to eq(0)
end
end
describe 'adding procedure privileges' do
it 'should work without errors' do
pp = <<-EOS
mysql_grant { 'test2@tester/PROCEDURE test.simpleproc':
ensure => 'present',
table => 'PROCEDURE test.simpleproc',
user => 'test2@tester',
privileges => ['EXECUTE'],
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test2@tester\"") do |r|
expect(r.stdout).to match(/GRANT EXECUTE ON PROCEDURE `test`.`simpleproc` TO 'test2'@'tester'/)
expect(r.stderr).to be_empty
end
end
end
end

View File

@@ -0,0 +1,32 @@
require 'spec_helper_acceptance'
describe 'mysql_user', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do
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 '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

View File

@@ -0,0 +1,10 @@
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

View File

@@ -0,0 +1,58 @@
require 'spec_helper'
describe 'mysql::bindings' do
let(:params) {{
'java_enable' => true,
'perl_enable' => true,
'php_enable' => true,
'python_enable' => true,
'ruby_enable' => true,
}}
shared_examples 'bindings' do |osfamily, operatingsystem, java_name, perl_name, php_name, python_name, ruby_name|
let :facts do
{ :osfamily => osfamily, :operatingsystem => operatingsystem, :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
context 'Debian' do
it_behaves_like 'bindings', 'Debian', 'Debian', 'libmysql-java', 'libdbd-mysql-perl', 'php5-mysql', 'python-mysqldb', 'libmysql-ruby'
it_behaves_like 'bindings', 'Debian', 'Ubuntu', 'libmysql-java', 'libdbd-mysql-perl', 'php5-mysql', 'python-mysqldb', 'libmysql-ruby'
end
context 'freebsd' do
it_behaves_like 'bindings', 'FreeBSD', 'FreeBSD', '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', 'mysql-connector-java', 'perl-DBD-MySQL', 'php-mysql', 'MySQL-python', 'ruby-mysql'
it_behaves_like 'bindings', 'RedHat', 'OpenSuSE', '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/)
end
end
end

View File

@@ -0,0 +1,16 @@
describe 'mysql::client' do
let(:facts) {{ :osfamily => 'RedHat' }}
context 'with defaults' do
it { should_not contain_class('mysql::bindings') }
it { should contain_package('mysql_client') }
end
context 'with bindings enabled' do
let(:params) {{ :bindings_enable => true }}
it { should contain_class('mysql::bindings') }
it { should contain_package('mysql_client') }
end
end

View File

@@ -0,0 +1,41 @@
require 'spec_helper'
describe 'mysql::server::account_security' do
let :facts do {
:fqdn => 'myhost.mydomain',
:hostname => 'myhost',
:root_home => '/root'
}
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')
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

View File

@@ -0,0 +1,183 @@
require 'spec_helper'
describe 'mysql::server::backup' do
let(:default_params) {
{ 'backupuser' => 'testuser',
'backuppassword' => 'testpass',
'backupdir' => '/tmp',
'backuprotate' => '25',
'delete_before_dump' => true,
}
}
context 'standard conditions' do
let(:params) { default_params }
it { should 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"]
)}
it { should contain_cron('mysql-backup').with(
:command => '/usr/local/sbin/mysqlbackup.sh',
:ensure => 'present'
)}
it { should contain_file('mysqlbackup.sh').with(
:path => '/usr/local/sbin/mysqlbackup.sh',
:ensure => 'present'
) }
it { should 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"',
])
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
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(
: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
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',
])
end
end
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
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
end

View File

@@ -0,0 +1,31 @@
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
let :default_params do
{
:mysql_monitor_username => 'monitoruser',
:mysql_monitor_password => 'monitorpass',
:mysql_monitor_hostname => 'monitorhost',
}
end
let :params do
default_params
end
it { should 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]'
)}
end

View File

@@ -0,0 +1,5 @@
describe 'mysql::server::mysqltuner' do
it { should contain_file('/usr/local/bin/mysqltuner') }
end

View File

@@ -0,0 +1,193 @@
require 'spec_helper'
describe 'mysql::server' do
let(:facts) {{:osfamily => 'RedHat', :root_home => '/root'}}
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
# 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
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
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
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
context 'with remove_default_accounts set' do
let (:params) {{ :remove_default_accounts => true }}
it { should contain_class('mysql::server::account_security') }
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::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
it do
should contain_file('/etc/my.cnf').with({
:mode => '0644',
})
end
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
})
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

View File

@@ -0,0 +1,51 @@
require 'spec_helper'
describe 'mysql::db', :type => :define do
let(:facts) {{ :osfamily => 'RedHat' }}
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 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'})
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})
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})
should contain_exec('test_db-import').with_refreshonly(false)
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 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
end

View File

@@ -0,0 +1,6 @@
--format
s
--colour
--loadby
mtime
--backtrace

View File

@@ -0,0 +1,5 @@
require 'simplecov'
SimpleCov.start do
add_filter "/spec/"
end
require 'puppetlabs_spec_helper/module_spec_helper'

View File

@@ -0,0 +1,48 @@
require 'beaker-rspec'
UNSUPPORTED_PLATFORMS = [ 'Windows', 'Solaris', 'AIX' ]
unless ENV['RS_PROVISION'] == 'no'
hosts.each do |host|
# Install Puppet
if host.is_pe?
install_pe
else
install_package host, 'rubygems'
on host, 'gem install puppet --no-ri --no-rdoc'
on host, "mkdir -p #{host['distmoduledir']}"
end
end
end
RSpec.configure do |c|
# Project root
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
# Readable test descriptions
c.formatter = :documentation
# Configure all nodes in nodeset
c.before :suite do
# Install module and dependencies
puppet_module_install(:source => proj_root, :module_name => 'mysql')
hosts.each do |host|
# Required for binding tests.
if fact('osfamily') == 'RedHat'
version = fact("operatingsystemmajrelease")
shell("yum localinstall -y http://yum.puppetlabs.com/puppetlabs-release-el-#{version}.noarch.rpm")
if version == '6'
shell("yum localinstall -y http://mirror.pnl.gov/epel/6/i386/epel-release-6-8.noarch.rpm")
elsif version == '5'
shell("yum localinstall -y http://mirrors.servercentral.net/fedora/epel/5/i386/epel-release-5-4.noarch.rpm")
else
puts "Sorry, this version is not supported."
exit
end
end
shell("/bin/touch #{default['distmoduledir']}/hiera.yaml")
shell('puppet module install puppetlabs-stdlib --version 3.2.0', { :acceptable_exit_codes => [0,1] })
end
end
end

View File

@@ -0,0 +1,25 @@
require 'rspec-system/spec_helper'
require 'rspec-system-puppet/helpers'
require 'rspec-system-serverspec/helpers'
include RSpecSystemPuppet::Helpers
RSpec.configure do |c|
# Project root
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
# Enable colour
c.tty = true
c.include RSpecSystemPuppet::Helpers
# This is where we 'setup' the nodes before running our tests
c.before :suite do
# Install puppet
puppet_install
# Install modules and dependencies
puppet_module_install(:source => proj_root, :module_name => 'mysql')
shell('puppet module install puppetlabs-stdlib')
end
end

View File

@@ -0,0 +1,35 @@
require 'spec_helper_system'
describe 'mysql::server::account_security 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': remove_default_accounts => true }
EOS
# Run it twice and test for idempotency
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
r.refresh
r.exit_code.should be_zero
end
end
describe 'accounts' do
it 'should delete accounts' do
shell("mysql -e 'show grants for root@127.0.01;'") do |s|
s.exit_code.should == 1
end
end
it 'should delete databases' do
shell("mysql -e 'show databases;' |grep test") do |s|
s.exit_code.should == 1
end
end
end
end
end

View File

@@ -0,0 +1,62 @@
require 'spec_helper_system'
describe 'mysql::server::backup class' do
context 'should work with no errors' do
pp = <<-EOS
class { 'mysql::server': override_options => { 'root_password' => 'password' } }
mysql::db { 'backup1':
user => 'backup',
password => 'secret',
}
class { 'mysql::server::backup':
backupuser => 'myuser',
backuppassword => 'mypassword',
backupdir => '/tmp/backups',
backupcompress => 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',
],
}
EOS
context puppet_apply(pp) do
its(:stderr) { should be_empty }
its(:exit_code) { should_not == 1 }
its(:refresh) { should be_nil }
its(:stderr) { should be_empty }
its(:exit_code) { should be_zero }
end
end
describe 'mysqlbackup.sh' do
context 'should run mysqlbackup.sh with no errors' do
context shell("/usr/local/sbin/mysqlbackup.sh") do
its(:exit_code) { should be_zero }
its(:stderr) { should be_empty }
end
end
context 'should dump all databases to single file' do
describe command('ls /tmp/backups/ | grep -c "mysql_backup_[0-9][0-9]*-[0-9][0-9]*.sql.bz2"') do
it { should return_stdout /1/ }
it { should return_exit_status 0 }
end
end
context 'should create one file per database per run' do
context shell("/usr/local/sbin/mysqlbackup.sh") do
its(:exit_code) { should be_zero }
its(:stderr) { should be_empty }
end
describe command('ls /tmp/backups/ | grep -c "mysql_backup_backup1_[0-9][0-9]*-[0-9][0-9]*.sql.bz2"') do
it { should return_stdout /2/ }
it { should return_exit_status 0 }
end
end
end
end

View File

@@ -0,0 +1,90 @@
require 'spec_helper_system'
describe 'mysql::bindings class' do
let(:os) {
node.facts['osfamily']
}
case node.facts['osfamily']
when 'RedHat'
java_package = 'mysql-connector-java'
perl_package = 'perl-DBD-MySQL'
python_package = 'MySQL-python'
ruby_package = 'ruby-mysql'
when 'Suse'
java_package = 'mysql-connector-java'
perl_package = 'perl-DBD-MySQL'
python_package = 'python-mysql'
case node.facts['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'
python_package = 'python-mysqldb'
ruby_package = 'libmysql-ruby'
when 'FreeBSD'
java_package = 'databases/mysql-connector-java'
perl_package = 'p5-DBD-mysql'
python_package = 'databases/py-MySQLdb'
ruby_package = 'ruby-mysql'
else
case node.facts['operatingsystem']
when 'Amazon'
java_package = 'mysql-connector-java'
perl_package = 'perl-DBD-MySQL'
python_package = 'MySQL-python'
ruby_package = 'ruby-mysql'
end
end
describe 'running puppet code' do
# Using puppet_apply as a helper
it 'should work with no errors' do
pp = <<-EOS
class { 'mysql::bindings': }
EOS
# Run it twice and test for idempotency
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
r.refresh
r.exit_code.should be_zero
end
end
end
describe 'enabling bindings' do
it 'should work with no errors' do
puppet_apply(%{
class { 'mysql::bindings':
java_enable => true,
perl_enable => true,
python_enable => true,
ruby_enable => true,
}
})
end
describe package(java_package) do
it { should be_installed }
end
describe package(perl_package) do
it { should be_installed }
end
describe package(python_package) do
it { should be_installed }
end
describe package(ruby_package) do
it { should be_installed }
end
end
end

View File

@@ -0,0 +1,61 @@
require 'spec_helper_system'
describe 'mysql::db define' do
describe 'creating a database' do
# Using puppet_apply as a helper
it 'should work with no errors' do
pp = <<-EOS
class { 'mysql::server': override_options => { 'root_password' => 'password' } }
mysql::db { 'spec1':
user => 'root1',
password => 'password',
}
EOS
# Run it twice and test for idempotency
puppet_apply(pp) do |r|
[0,2].should include r.exit_code
r.refresh
r.exit_code.should be_zero
end
end
it 'should have the database' do
shell("mysql -e 'show databases;'|grep spec1") do |s|
s.exit_code.should be_zero
end
end
end
describe 'creating a database with post-sql' do
# Using puppet_apply as a helper
it 'should work with no errors' do
pp = <<-EOS
class { 'mysql::server': override_options => { 'root_password' => 'password' } }
file { '/tmp/spec.sql':
ensure => file,
content => 'CREATE TABLE table1 (id int);',
before => Mysql::Db['spec2'],
}
mysql::db { 'spec2':
user => 'root1',
password => 'password',
sql => '/tmp/spec.sql',
}
EOS
# Run it twice and test for idempotency
puppet_apply(pp) do |r|
[0,2].should include r.exit_code
r.refresh
r.exit_code.should be_zero
end
end
it 'should have the table' do
shell("mysql -e 'show tables;' spec2|grep table1") do |s|
s.exit_code.should == 0
end
end
end
end

View File

@@ -0,0 +1,30 @@
require 'spec_helper_system'
describe 'mysql::server::monitor class' do
context '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
context puppet_apply(pp) do
its(:stderr) { should be_empty }
its(:exit_code) { should_not == 1 }
its(:refresh) { should be_nil }
its(:stderr) { should be_empty }
its(:exit_code) { should be_zero }
end
context 'should run mysqladmin ping with no errors' do
describe command("mysqladmin -u monitoruser -pmonitorpass -h localhost ping") do
it { should return_stdout /mysqld is alive/ }
it { should return_exit_status 0 }
end
end
end
end

View File

@@ -0,0 +1,71 @@
require 'spec_helper_system'
describe 'mysql::server::root_password class' do
describe 'reset' do
it 'shuts down mysql' do
pp = <<-EOS
class { 'mysql::server': service_enabled => false }
EOS
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
end
end
it 'deletes the /root/.my.cnf password' do
shell('rm -rf /root/.my.cnf')
end
it 'deletes all databases' do
case node.facts['osfamily']
when 'RedHat'
shell('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) do |r|
r.exit_code.should_not == 1
end
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
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
r.refresh
r.exit_code.should be_zero
end
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
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
r.refresh
r.exit_code.should be_zero
end
end
end
end

View File

@@ -0,0 +1,106 @@
require 'spec_helper_system'
describe 'mysql class' do
case node.facts['osfamily']
when 'RedHat'
package_name = 'mysql-server'
service_name = 'mysqld'
mycnf = '/etc/my.cnf'
when 'Suse'
package_name = 'mysql-community-server'
service_name = 'mysql'
mycnf = '/etc/my.cnf'
when 'Debian'
package_name = 'mysql-server'
service_name = 'mysql'
mycnf = '/etc/mysql/my.cnf'
end
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
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
r.refresh
r.exit_code.should be_zero
end
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
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
end
end
describe file(mycnf) do
it { should contain 'key_buffer = 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
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
end
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
pp = <<-EOS
class { 'mysql::server':
override_options => { 'mysqld' =>
{ 'replicate-do-db' => ['base1', 'base2', 'base3'], }
}
}
EOS
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
end
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
end

View File

@@ -0,0 +1,314 @@
require 'spec_helper_system'
describe 'mysql_grant' do
describe 'setup' do
it 'setup mysql::server' do
pp = <<-EOS
class { 'mysql::server': }
EOS
puppet_apply(pp)
end
end
describe 'missing privileges for user' do
it 'should fail' do
pp = <<-EOS
mysql_grant { 'test1@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test1@tester',
}
EOS
puppet_apply(pp) do |r|
r.stderr.should =~ /privileges parameter is required/
end
end
it 'should not find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test1@tester\"") do |r|
r.stderr.should =~ /There is no such grant defined for user 'test1' on host 'tester'/
r.exit_code.should eq 1
end
end
end
describe 'missing table for user' do
it 'should fail' do
pp = <<-EOS
mysql_grant { 'atest@tester/test.*':
ensure => 'present',
user => 'atest@tester',
privileges => ['ALL'],
}
EOS
puppet_apply(pp) do |r|
r.exit_code.should eq 1
end
end
it 'should not find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR atest@tester\"") do |r|
r.stderr.should =~ /There is no such grant defined for user 'atest' on host 'tester'/
r.exit_code.should eq 1
end
end
end
describe 'adding privileges' do
it 'should work without errors' do
pp = <<-EOS
mysql_grant { 'test2@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test2@tester',
privileges => ['SELECT', 'UPDATE'],
}
EOS
puppet_apply(pp)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test2@tester\"") do |r|
r.stdout.should =~ /GRANT SELECT, UPDATE.*TO 'test2'@'tester'/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
end
describe 'adding option' do
it 'should work without errors' do
pp = <<-EOS
mysql_grant { 'test3@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test3@tester',
options => ['GRANT'],
privileges => ['SELECT', 'UPDATE'],
}
EOS
puppet_apply(pp)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test3@tester\"") do |r|
r.stdout.should =~ /GRANT SELECT, UPDATE ON `test`.* TO 'test3'@'tester' WITH GRANT OPTION$/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
end
describe 'adding all privileges without table' do
it 'should fail' do
pp = <<-EOS
mysql_grant { 'test4@tester/test.*':
ensure => 'present',
user => 'test4@tester',
options => ['GRANT'],
privileges => ['SELECT', 'UPDATE', 'ALL'],
}
EOS
puppet_apply(pp) do |r|
r.stderr.should =~ /table parameter is required./
end
end
end
describe 'adding all privileges' do
it 'should only try to apply ALL' do
pp = <<-EOS
mysql_grant { 'test4@tester/test.*':
ensure => 'present',
table => 'test.*',
user => 'test4@tester',
options => ['GRANT'],
privileges => ['SELECT', 'UPDATE', 'ALL'],
}
EOS
puppet_apply(pp)
end
it 'should find the user' do
shell("mysql -NBe \"SHOW GRANTS FOR test4@tester\"") do |r|
r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test4'@'tester' WITH GRANT OPTION/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
end
# Test combinations of user@host to ensure all cases work.
describe 'short hostname' do
it 'should apply' do
pp = <<-EOS
mysql_grant { 'test@short/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@short',
privileges => 'ALL',
}
mysql_grant { 'test@long.hostname.com/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@long.hostname.com',
privileges => 'ALL',
}
mysql_grant { 'test@192.168.5.6/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@192.168.5.6',
privileges => 'ALL',
}
mysql_grant { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@2607:f0d0:1002:0051:0000:0000:0000:0004',
privileges => 'ALL',
}
mysql_grant { 'test@::1/128/test.*':
ensure => 'present',
table => 'test.*',
user => 'test@::1/128',
privileges => 'ALL',
}
EOS
puppet_apply(pp)
end
it 'finds short hostname' do
shell("mysql -NBe \"SHOW GRANTS FOR test@short\"") do |r|
r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'short'/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
it 'finds long hostname' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'long.hostname.com'\"") do |r|
r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'long.hostname.com'/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
it 'finds ipv4' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.6'\"") do |r|
r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'192.168.5.6'/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
it 'finds ipv6' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'\"") do |r|
r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
it 'finds short ipv6' do
shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'::1/128'\"") do |r|
r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'::1\/128'/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
end
describe 'complex test' do
it 'setup mysql::server' do
pp = <<-EOS
$dbSubnet = '10.10.10.%'
mysql_database { 'foo':
ensure => present,
}
exec { 'mysql-create-table':
command => '/usr/bin/mysql -NBe "CREATE TABLE foo.bar (name VARCHAR(20))"',
environment => "HOME=${::root_home}",
unless => '/usr/bin/mysql -NBe "SELECT 1 FROM foo.bar LIMIT 1;"',
require => Mysql_database['foo'],
}
Mysql_grant {
ensure => present,
options => ['GRANT'],
privileges => ['ALL'],
table => '*.*',
require => [ Mysql_database['foo'], Exec['mysql-create-table'] ],
}
mysql_grant { "user1@${dbSubnet}/*.*":
user => "user1@${dbSubnet}",
}
mysql_grant { "user2@${dbSubnet}/foo.bar":
privileges => ['SELECT', 'INSERT', 'UPDATE'],
user => "user2@${dbSubnet}",
table => 'foo.bar',
}
mysql_grant { "user3@${dbSubnet}/foo.*":
privileges => ['SELECT', 'INSERT', 'UPDATE'],
user => "user3@${dbSubnet}",
table => 'foo.*',
}
mysql_grant { 'web@%/*.*':
user => 'web@%',
}
mysql_grant { "web@${dbSubnet}/*.*":
user => "web@${dbSubnet}",
}
mysql_grant { "web@${fqdn}/*.*":
user => "web@${fqdn}",
}
mysql_grant { 'web@localhost/*.*':
user => 'web@localhost',
}
EOS
puppet_apply(pp) do |r|
r.exit_code.should_not == 1
r.refresh
r.exit_code.should be_zero
end
end
end
describe 'lower case privileges' do
it 'create ALL privs' do
pp = <<-EOS
mysql_grant { 'lowercase@localhost/*.*':
user => 'lowercase@localhost',
privileges => 'ALL',
table => '*.*',
}
EOS
puppet_apply(pp)
end
it 'create lowercase all privs' do
pp = <<-EOS
mysql_grant { 'lowercase@localhost/*.*':
user => 'lowercase@localhost',
privileges => 'all',
table => '*.*',
}
EOS
puppet_apply(pp) do |r|
r.exit_code.should be_zero
end
end
end
end

View File

@@ -0,0 +1,35 @@
require 'spec_helper_system'
describe 'mysql_user' do
describe 'setup' do
it 'should work with no errors' do
pp = <<-EOS
class { 'mysql::server': }
EOS
puppet_apply(pp)
end
end
describe 'adding user' do
it 'should work without errors' do
pp = <<-EOS
mysql_user { 'ashp@localhost':
password_hash => '6f8c114b58f2ce9e',
}
EOS
puppet_apply(pp)
end
it 'should find the user' do
shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r|
r.stdout.should =~ /^1$/
r.stderr.should be_empty
r.exit_code.should be_zero
end
end
end
end

View File

@@ -0,0 +1,27 @@
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

View File

@@ -0,0 +1,91 @@
#! /usr/bin/env ruby -S rspec
require 'spec_helper'
describe Puppet::Parser::Functions.function(:mysql_deepmerge) do
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
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\./
Puppet[:code] = '$x = mysql_deepmerge()'
expect {
scope.compiler.compile
}.to raise_error(Puppet::ParseError, /wrong number of arguments/)
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\./
Puppet[:code] = "$my_hash={'one' => 1}\n$x = mysql_deepmerge($my_hash)"
expect {
scope.compiler.compile
}.to raise_error(Puppet::ParseError, /wrong number of arguments/)
end
end
describe 'when calling mysql_deepmerge on the scope instance' do
it 'should require all parameters are hashes' do
expect { new_hash = scope.function_mysql_deepmerge([{}, '2'])}.to raise_error(Puppet::ParseError, /unexpected argument type String/)
expect { new_hash = scope.function_mysql_deepmerge([{}, 2])}.to raise_error(Puppet::ParseError, /unexpected argument type Fixnum/)
end
it 'should accept empty strings as puppet undef' do
expect { new_hash = scope.function_mysql_deepmerge([{}, ''])}.not_to raise_error
end
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'
end
it 'should mysql_deepmerge multiple hashes' do
hash = scope.function_mysql_deepmerge([{'one' => 1}, {'one' => '2'}, {'one' => '3'}])
hash['one'].should == '3'
end
it 'should accept empty hashes' do
scope.function_mysql_deepmerge([{},{},{}]).should == {}
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 }
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 }
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 }
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 }
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')
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')
end
end
end

View File

@@ -0,0 +1,86 @@
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

View File

@@ -0,0 +1,95 @@
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

View File

@@ -0,0 +1,119 @@
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

View File

@@ -0,0 +1,118 @@
require 'spec_helper'
describe Puppet::Type.type(:mysql_database).provider(:mysql) do
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) }
let(:resource) { Puppet::Type.type(:mysql_database).new(
{ :ensure => :present,
:charset => 'latin1',
:collate => 'latin1_swedish_ci',
:name => 'new_database',
: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 databases']).returns('new_database')
provider.class.stubs(:mysql).with([defaults_file, '-NBe', "show variables like '%_database'", 'new_database']).returns("character_set_database latin1\ncollation_database latin1_swedish_ci\nskip_show_database OFF")
end
let(:instance) { provider.class.instances.first }
describe 'self.instances' do
it 'returns an array of databases' do
provider.class.stubs(:mysql).with([defaults_file, '-NBe', 'show databases']).returns(raw_databases)
raw_databases.each_line do |db|
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)
end
end
describe 'self.prefetch' do
it 'exists' do
provider.class.instances
provider.class.prefetch({})
end
end
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(:exists?).returns(true)
provider.create.should be_true
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(:exists?).returns(false)
provider.destroy.should be_true
end
end
describe 'exists?' do
it 'checks if database exists' do
instance.exists?.should be_true
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'
end
it 'fails if file missing' do
File.stubs(:file?).with('/root/.my.cnf').returns(false)
provider.defaults_file.should be_nil
end
end
describe 'charset' do
it 'returns a charset' do
instance.charset.should == 'latin1'
end
end
describe 'charset=' do
it 'changes the charset' do
provider.expects(:mysql).with([defaults_file, '-NBe', "alter database `#{resource[:name]}` CHARACTER SET blah"]).returns('0')
provider.charset=('blah')
end
end
describe 'collate' do
it 'returns a collate' do
instance.collate.should == 'latin1_swedish_ci'
end
end
describe 'collate=' do
it 'changes the collate' do
provider.expects(:mysql).with([defaults_file, '-NBe', "alter database `#{resource[:name]}` COLLATE blah"]).returns('0')
provider.collate=('blah')
end
end
end

View File

@@ -0,0 +1,130 @@
require 'spec_helper'
describe Puppet::Type.type(:mysql_user).provider(:mysql) do
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 @localhost debian-sys-maint@localhost root@localhost usvn_user@localhost @vagrant-ubuntu-raring-64) }
let(:resource) { Puppet::Type.type(:mysql_user).new(
{ :ensure => :present,
:password_hash => '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4',
:name => 'joe@localhost',
:max_user_connections => '10',
:max_connections_per_hour => '10',
:max_queries_per_hour => '10',
:max_updates_per_hour => '10',
:provider => described_class.name
}
)}
let(:provider) { resource.provider }
before :each do
# Set up the stubs for an instances call.
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', "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')
end
let(:instance) { provider.class.instances.first }
describe 'self.instances' do
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 ')
end
usernames = provider.class.instances.collect {|x| x.name }
parsed_users.should match_array(usernames)
end
end
describe 'self.prefetch' do
it 'exists' do
provider.class.instances
provider.class.prefetch({})
end
end
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(:exists?).returns(true)
provider.create.should be_true
end
end
describe 'destroy' do
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
end
end
describe 'exists?' do
it 'checks if user exists' do
instance.exists?.should be_true
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'
end
it 'fails if file missing' do
File.expects(:file?).with('/root/.my.cnf').returns(false)
provider.defaults_file.should be_nil
end
end
describe 'password_hash' do
it 'returns a hash' do
instance.password_hash.should == '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'
end
end
describe 'password_hash=' do
it 'changes the hash' do
provider.expects(:mysql).with([defaults_file, '-e', "SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'"]).returns('0')
provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5')
provider.password_hash=('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5')
end
end
['max_user_connections', 'max_connections_per_hour', 'max_queries_per_hour',
'max_updates_per_hour'].each do |property|
describe property do
it "returns #{property}" do
instance.send("#{property}".to_sym).should == '10'
end
end
describe "#{property}=" do
it "changes #{property}" do
provider.expects(:mysql).with([defaults_file, '-e', "GRANT USAGE ON *.* TO 'joe'@'localhost' WITH #{property.upcase} 42"]).returns('0')
provider.expects(property.to_sym).returns('42')
provider.send("#{property}=".to_sym, '42')
end
end
end
end

View File

@@ -0,0 +1,29 @@
require 'puppet'
require 'puppet/type/mysql_database'
describe Puppet::Type.type(:mysql_database) do
before :each do
@user = Puppet::Type.type(:mysql_database).new(:name => 'test', :charset => 'utf8', :collate => 'utf8_blah_ci')
end
it 'should accept a database name' do
@user[:name].should == 'test'
end
it 'should accept a charset' do
@user[:charset] = 'latin1'
@user[:charset].should == 'latin1'
end
it 'should accept a collate' do
@user[:collate] = 'latin1_swedish_ci'
@user[:collate].should == 'latin1_swedish_ci'
end
it 'should require a name' do
expect {
Puppet::Type.type(:mysql_database).new({})
}.to raise_error(Puppet::Error, 'Title or name must be provided')
end
end

View File

@@ -0,0 +1,44 @@
require 'puppet'
require 'puppet/type/mysql_grant'
describe Puppet::Type.type(:mysql_grant) do
before :each do
@user = Puppet::Type.type(:mysql_grant).new(:name => 'foo@localhost/*.*', :privileges => ['ALL', 'PROXY'], :table => ['*.*','@'], :user => 'foo@localhost')
end
it 'should accept a grant name' do
@user[:name].should == 'foo@localhost/*.*'
end
it 'should accept ALL privileges' do
@user[:privileges] = 'ALL'
@user[:privileges].should == ['ALL']
end
it 'should accept PROXY privilege' do
@user[:privileges] = 'PROXY'
@user[:privileges].should == ['PROXY']
end
it 'should accept a table' do
@user[:table] = '*.*'
@user[:table].should == '*.*'
end
it 'should accept @ for table' do
@user[:table] = '@'
@user[:table].should == '@'
end
it 'should accept a user' do
@user[:user] = 'foo@localhost'
@user[:user].should == 'foo@localhost'
end
it 'should require a name' do
expect {
Puppet::Type.type(:mysql_grant).new({})
}.to raise_error(Puppet::Error, 'Title or name must be provided')
end
end

View File

@@ -0,0 +1,30 @@
require 'puppet'
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'
end
it 'should require a name' do
expect {
Puppet::Type.type(:mysql_user).new({})
}.to raise_error(Puppet::Error, 'Title or name must be provided')
end
end

View File

@@ -0,0 +1,18 @@
<% @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 vi != :undef -%>
<%= ki %> = <%= vi %>
<% end -%>
<% end -%>
<% end %>
<% end -%>
!includedir /etc/mysql/conf.d/

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