diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index 1f2ca11..0000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
index caf1445..83bf0dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,18 +4,77 @@
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
-# Ignore bundler config.
-/.bundle
+# Created by http://www.gitignore.io
+
+### Vagrant ###
+.vagrant/
+
+
+### SublimeText ###
+# workspace files are user-specific
+*.sublime-workspace
+
+# project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using SublimeText
+# *.sublime-project
+
+#sftp configuration file
+sftp-config.json
+
+
+### OSX ###
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
-# Ignore the default SQLite database.
-/db/*.sqlite3
-/db/*.sqlite3-journal
-# Ignore all logfiles and tempfiles.
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+
+### Rails ###
+*.rbc
+capybara-*.html
+.rspec
+/log
/log/*.log
/tmp
+/db/*.sqlite3
+/db/*.sqlite3-journal
+/public/system
+/coverage/
+/spec/tmp
+**.orig
+rerun.txt
+pickle-email-*.html
# Ignore secret assets
/lib/assets/*
-.DS_Store
+# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
+config/initializers/secret_token.rb
+config/secrets.yml
+
+## Environment normalisation:
+/.bundle
+/vendor/bundle
+
+# these should all be checked in to normalise the environment:
+# Gemfile.lock, .ruby-version, .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
diff --git a/NOTES.txt b/NOTES.txt
new file mode 100644
index 0000000..7dbc002
--- /dev/null
+++ b/NOTES.txt
@@ -0,0 +1,59 @@
+
+* Road blocks and fixes:
+
+##- When trying to do 'vagrant plugin install vagrant-vbguest' got the follownig nokogiri error. Vagrant v1.6.3 on OSX 10.8.5.
+
+ Installing the 'vagrant-vbguest' plugin. This can take a few minutes...
+ Building nokogiri using packaged libraries.
+ Building libxml2-2.8.0 for nokogiri with the following patches applied:
+ - 0001-Fix-parser-local-buffers-size-problems.patch
+ ...
+ ...
+ ...
+ Bundler, the underlying system Vagrant uses to install plugins,
+ reported an error. The error is shown below. These errors are usually
+ caused by misconfigured plugin installations or transient network
+ issues. The error from Bundler is:
+
+ An error occurred while installing nokogiri (1.6.3.1), and Bundler cannot continue.
+ Make sure that `gem install nokogiri -v '1.6.3.1'` succeeds before bundling.
+ ...
+ ...
+
+- Workaround
+$ export NOKOGIRI_USE_SYSTEM_LIBRARIES=true
+$ vagrant plugin install vagrant vagrant-vbguest
+$ vagrant plugin list (validate install)
+
+
+##- Executing "rake db:create" you get the following errors
+ PG::Error: ERROR: new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII)
+ HINT: Use the same encoding as in the template database, or use template0 as template.
+ : CREATE DATABASE "db/bikedb_development" ENCODING = 'utf8'
+
+- Solution: follow the instructions here
+http://journal.tianhao.info/2010/12/postgresql-change-default-encoding-of-new-databases-to-utf-8-optional/
+
+
+## UPDATE - 2014.09.03
+Implemented a simple psql script that does the exactly as the url above. Do the following inside guest-OS
+
+$ vagrant ssh
+
+$ psql -d postgres -f /vagrant/etc/postgres/enable_utf8_template.sql
+$ sudo -u postgres psql postgres
+
+postgres=# \l
+
+(The ENCODING should be UTF8 for 'template1' database)
+
+
+Original resources for the Vagrantfile.
+http://gorails.com/guides/using-vagrant-for-rails-development
+
+=========================================
+
+## TODOs ##
+- Add the etc/enable_utf8_template.sql into Vagrant provision routine. One less task to for developer to execute.
+- Create developement instructions (README.rdoc?), outlining the commands get the Vagrant/Bike-DB up and running.
+
diff --git a/README.md b/README.md
index ffc63fe..36409fc 100644
--- a/README.md
+++ b/README.md
@@ -8,4 +8,4 @@ An instance can be seen running at http://secret-earth-4230.herokuapp.com/
The storyboard / feature list is at https://trello.com/b/ZCZ9sThH/bike-db-app
-Feel free to request feature additions!
+Feel free to request feature additions!
\ No newline at end of file
diff --git a/README.rdoc b/README.rdoc
index dd4e97e..acf26f9 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -25,4 +25,4 @@ Things you may want to cover:
Please feel free to use a different markup language if you do not plan to run
-rake doc:app.
+rake doc:app.
\ No newline at end of file
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 0000000..9888691
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,36 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+
+Vagrant.configure('2') do |config|
+ config.vm.box = 'precise32'
+ config.vm.box_url = 'http://files.vagrantup.com/precise32.box'
+ config.vm.hostname = 'rails-dev-box'
+
+ config.vm.provider 'vmware_fusion' do |v, override|
+ override.vm.box = 'precise64'
+ override.vm.box_url = 'http://files.vagrantup.com/precise64_vmware.box'
+ end
+
+ config.vm.provider 'parallels' do |v, override|
+ override.vm.box = 'parallels/ubuntu-12.04'
+ override.vm.box_url = 'https://vagrantcloud.com/parallels/ubuntu-12.04'
+
+ # Can be running at background, see https://github.com/Parallels/vagrant-parallels/issues/39
+ v.customize ['set', :id, '--on-window-close', 'keep-running']
+ end
+
+ # Enable, if you are NOT OSX.
+ # config.vm.synced_folder '.', '/vagrant', type: 'rsync'
+
+ # Enable ,if you are on OSX for increase performance. NOTE: May require Admin password
+ config.vm.synced_folder '.', '/vagrant', type: 'nfs'
+
+ config.vm.network :private_network, ip: '33.33.33.33'
+ config.vm.network :forwarded_port, guest: 3000, host: 3000
+
+ config.vm.provision :puppet do |puppet|
+ puppet.manifests_path = 'puppet/manifests'
+ puppet.module_path = 'puppet/modules'
+ end
+end
\ No newline at end of file
diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb
deleted file mode 100644
index cfdc1e5..0000000
--- a/config/initializers/secret_token.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Your secret key is used for verifying the integrity of signed cookies.
-# If you change this key, all old signed cookies will become invalid!
-
-# Make sure the secret is at least 30 characters and all random,
-# no regular words or you'll be exposed to dictionary attacks.
-# You can use `rake secret` to generate a secure secret key.
-
-# Make sure your secret_key_base is kept private
-# if you're sharing your code publicly.
-Bikedb::Application.config.secret_key_base = '55bed17e71286b1810e75f047637fd1acd64351fcdd5591ab83af67bdc628cb65958d98992a653268015aec9274a2e573b66fb5895d2bf86acd8a5c0e9d35813'
diff --git a/etc/postgres/enable_utf8_template.sql b/etc/postgres/enable_utf8_template.sql
new file mode 100644
index 0000000..cf3ab24
--- /dev/null
+++ b/etc/postgres/enable_utf8_template.sql
@@ -0,0 +1,16 @@
+/*
+* Updates the default 'template1' database into UTF-8 instead of SQL_ASCII.
+*/
+
+UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1';
+
+DROP DATABASE template1;
+
+CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = 'UNICODE';
+
+UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1';
+
+\c template1
+VACUUM FREEZE;
+
+UPDATE pg_database SET datallowconn = FALSE WHERE datname = 'template1';
diff --git a/lib/.DS_Store b/lib/.DS_Store
deleted file mode 100644
index b88ae09..0000000
Binary files a/lib/.DS_Store and /dev/null differ
diff --git a/log/.keep b/log/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/puppet/manifests/default.pp b/puppet/manifests/default.pp
new file mode 100755
index 0000000..27a65dc
--- /dev/null
+++ b/puppet/manifests/default.pp
@@ -0,0 +1,158 @@
+$ar_databases = ['activerecord_unittest', 'activerecord_unittest2']
+$as_vagrant = 'sudo -u vagrant -H bash -l -c'
+$home = '/home/vagrant'
+
+# Pick a Ruby version modern enough, that works in the currently supported Rails
+# versions, and for which RVM provides binaries.
+$ruby_version = '2.1.1'
+
+Exec {
+ path => ['/usr/sbin', '/usr/bin', '/sbin', '/bin']
+}
+
+# --- Preinstall Stage ---------------------------------------------------------
+
+stage { 'preinstall':
+ before => Stage['main']
+}
+
+class apt_get_update {
+ exec { 'apt-get -y update':
+ unless => "test -e ${home}/.rvm"
+ }
+}
+class { 'apt_get_update':
+ stage => preinstall
+}
+
+# --- SQLite -------------------------------------------------------------------
+
+package { ['sqlite3', 'libsqlite3-dev']:
+ ensure => installed;
+}
+
+# --- MySQL --------------------------------------------------------------------
+
+class install_mysql {
+ class { 'mysql': }
+
+ class { 'mysql::server':
+ config_hash => { 'root_password' => '' }
+ }
+
+ database { $ar_databases:
+ ensure => present,
+ charset => 'utf8',
+ require => Class['mysql::server']
+ }
+
+ database_user { 'rails@localhost':
+ ensure => present,
+ require => Class['mysql::server']
+ }
+
+ database_grant { ['rails@localhost/activerecord_unittest', 'rails@localhost/activerecord_unittest2', 'rails@localhost/inexistent_activerecord_unittest']:
+ privileges => ['all'],
+ require => Database_user['rails@localhost']
+ }
+
+ package { 'libmysqlclient15-dev':
+ ensure => installed
+ }
+}
+class { 'install_mysql': }
+
+# --- PostgreSQL ---------------------------------------------------------------
+
+class install_postgres {
+ class { 'postgresql': }
+
+ class { 'postgresql::server': }
+
+ pg_database { $ar_databases:
+ ensure => present,
+ encoding => 'UTF8',
+ require => Class['postgresql::server']
+ }
+
+ pg_user { 'rails':
+ ensure => present,
+ require => Class['postgresql::server']
+ }
+
+ pg_user { 'vagrant':
+ ensure => present,
+ superuser => true,
+ require => Class['postgresql::server']
+ }
+
+ package { 'libpq-dev':
+ ensure => installed
+ }
+
+ package { 'postgresql-contrib':
+ ensure => installed,
+ require => Class['postgresql::server'],
+ }
+}
+class { 'install_postgres': }
+
+# --- Memcached ----------------------------------------------------------------
+
+class { 'memcached': }
+
+# --- Packages -----------------------------------------------------------------
+
+package { 'curl':
+ ensure => installed
+}
+
+package { 'build-essential':
+ ensure => installed
+}
+
+package { 'git-core':
+ ensure => installed
+}
+
+# Nokogiri dependencies.
+package { ['libxml2', 'libxml2-dev', 'libxslt1-dev']:
+ ensure => installed
+}
+
+# ExecJS runtime.
+package { 'nodejs':
+ ensure => installed
+}
+
+# --- Ruby ---------------------------------------------------------------------
+
+exec { 'install_rvm':
+ command => "${as_vagrant} 'curl -L https://get.rvm.io | bash -s stable'",
+ creates => "${home}/.rvm/bin/rvm",
+ require => Package['curl']
+}
+
+exec { 'install_ruby':
+ # We run the rvm executable directly because the shell function assumes an
+ # interactive environment, in particular to display messages or ask questions.
+ # The rvm executable is more suitable for automated installs.
+ #
+ # use a ruby patch level known to have a binary
+ command => "${as_vagrant} '${home}/.rvm/bin/rvm install ruby-${ruby_version} --binary --autolibs=enabled && rvm alias create default ${ruby_version}'",
+ creates => "${home}/.rvm/bin/ruby",
+ require => Exec['install_rvm']
+}
+
+# RVM installs a version of bundler, but for edge Rails we want the most recent one.
+exec { "${as_vagrant} 'gem install bundler --no-rdoc --no-ri'":
+ creates => "${home}/.rvm/bin/bundle",
+ require => Exec['install_ruby']
+}
+
+# --- Locale -------------------------------------------------------------------
+
+# Needed for docs generation.
+exec { 'update-locale':
+ command => 'update-locale LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8'
+}
diff --git a/puppet/modules/memcached/.gitignore b/puppet/modules/memcached/.gitignore
new file mode 100755
index 0000000..6ee20c2
--- /dev/null
+++ b/puppet/modules/memcached/.gitignore
@@ -0,0 +1,2 @@
+pkg/
+*.swp
diff --git a/puppet/modules/memcached/Modulefile b/puppet/modules/memcached/Modulefile
new file mode 100755
index 0000000..45bb6d6
--- /dev/null
+++ b/puppet/modules/memcached/Modulefile
@@ -0,0 +1,8 @@
+name 'saz-memcached'
+version '2.0.2'
+source 'git://github.com/saz/puppet-memcached.git'
+author 'saz'
+license 'Apache License, Version 2.0'
+summary 'UNKNOWN'
+description 'Manage memcached via Puppet'
+project_page 'https://github.com/saz/puppet-memcached'
diff --git a/puppet/modules/memcached/README.md b/puppet/modules/memcached/README.md
new file mode 100755
index 0000000..4d06d7e
--- /dev/null
+++ b/puppet/modules/memcached/README.md
@@ -0,0 +1,29 @@
+
+# puppet-memcached
+
+Manage memcached via Puppet
+
+## How to use
+
+### Use roughly 90% of memory
+
+```
+ class { 'memcached': }
+```
+
+### Set a fixed memory limit in MB
+
+```
+ class { 'memcached':
+ max_memory => 2048
+ }
+```
+
+### Other class parameters
+
+* $logfile = '/var/log/memcached.log'
+* $listen_ip = '0.0.0.0'
+* $tcp_port = 11211
+* $udp_port = 11211
+* $user = '' (OS specific setting, see params.pp)
+* $max_connections = 8192
diff --git a/puppet/modules/memcached/manifests/init.pp b/puppet/modules/memcached/manifests/init.pp
new file mode 100755
index 0000000..f11ed5c
--- /dev/null
+++ b/puppet/modules/memcached/manifests/init.pp
@@ -0,0 +1,33 @@
+class memcached(
+ $package_ensure = 'present',
+ $logfile = '/var/log/memcached.log',
+ $max_memory = false,
+ $listen_ip = '0.0.0.0',
+ $tcp_port = 11211,
+ $udp_port = 11211,
+ $user = $::memcached::params::user,
+ $max_connections = '8192',
+ $verbosity = undef,
+ $unix_socket = undef
+) inherits memcached::params {
+
+ package { $memcached::params::package_name:
+ ensure => $package_ensure,
+ }
+
+ file { $memcached::params::config_file:
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ content => template($memcached::params::config_tmpl),
+ require => Package[$memcached::params::package_name],
+ }
+
+ service { $memcached::params::service_name:
+ ensure => running,
+ enable => true,
+ hasrestart => true,
+ hasstatus => false,
+ subscribe => File[$memcached::params::config_file],
+ }
+}
diff --git a/puppet/modules/memcached/manifests/params.pp b/puppet/modules/memcached/manifests/params.pp
new file mode 100755
index 0000000..316d831
--- /dev/null
+++ b/puppet/modules/memcached/manifests/params.pp
@@ -0,0 +1,21 @@
+class memcached::params {
+ case $::osfamily {
+ 'Debian': {
+ $package_name = 'memcached'
+ $service_name = 'memcached'
+ $config_file = '/etc/memcached.conf'
+ $config_tmpl = "${module_name}/memcached.conf.erb"
+ $user = 'nobody'
+ }
+ 'RedHat': {
+ $package_name = 'memcached'
+ $service_name = 'memcached'
+ $config_file = '/etc/sysconfig/memcached'
+ $config_tmpl = "${module_name}/memcached_sysconfig.erb"
+ $user = 'memcached'
+ }
+ default: {
+ fail("Unsupported platform: ${::osfamily}")
+ }
+ }
+}
diff --git a/puppet/modules/memcached/templates/memcached.conf.erb b/puppet/modules/memcached/templates/memcached.conf.erb
new file mode 100755
index 0000000..df0e01f
--- /dev/null
+++ b/puppet/modules/memcached/templates/memcached.conf.erb
@@ -0,0 +1,46 @@
+# File managed by puppet
+
+# Run memcached as a daemon.
+-d
+
+# pidfile
+-P /var/run/memcached.pid
+
+# Log memcached's output
+logfile <%= logfile %>
+
+<% if @verbosity -%>
+# Verbosity
+-<%= verbosity %>
+<% end -%>
+
+# Use MB memory max to use for object storage.
+<% if max_memory -%>
+-m <%= max_memory %>
+<% else -%>
+-m <%= ((memorysize.to_f*1024)*0.95).floor %>
+<% end -%>
+
+<% if @unix_socket -%>
+# UNIX socket path to listen on
+-s <%= unix_socket %>
+<% else -%>
+
+# IP to listen on
+-l <%= listen_ip %>
+
+# TCP port to listen on
+-p <%= tcp_port %>
+
+# UDP port to listen on
+-U <%= udp_port %>
+<% end -%>
+
+# Run daemon as user
+-u <%= user %>
+
+# Limit the number of simultaneous incoming connections.
+-c <%= max_connections %>
+
+# Number of threads to use to process incoming requests.
+-t <%= processorcount %>
diff --git a/puppet/modules/memcached/templates/memcached_sysconfig.erb b/puppet/modules/memcached/templates/memcached_sysconfig.erb
new file mode 100755
index 0000000..5e02958
--- /dev/null
+++ b/puppet/modules/memcached/templates/memcached_sysconfig.erb
@@ -0,0 +1,5 @@
+PORT="<%= udp_port %>"
+USER="<%= user %>"
+MAXCONN="<%= max_connections %>"
+CACHESIZE="<%= max_memory %>"
+OPTIONS=""
diff --git a/puppet/modules/mysql/.fixtures.yml b/puppet/modules/mysql/.fixtures.yml
new file mode 100755
index 0000000..c395f0c
--- /dev/null
+++ b/puppet/modules/mysql/.fixtures.yml
@@ -0,0 +1,3 @@
+fixtures:
+ symlinks:
+ "mysql": "#{source_dir}"
diff --git a/puppet/modules/mysql/.gemfile b/puppet/modules/mysql/.gemfile
new file mode 100755
index 0000000..9aad840
--- /dev/null
+++ b/puppet/modules/mysql/.gemfile
@@ -0,0 +1,5 @@
+source :rubygems
+
+puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7']
+gem 'puppet', puppetversion
+gem 'puppetlabs_spec_helper', '>= 0.1.0'
diff --git a/puppet/modules/mysql/LICENSE b/puppet/modules/mysql/LICENSE
new file mode 100755
index 0000000..8d968b6
--- /dev/null
+++ b/puppet/modules/mysql/LICENSE
@@ -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 [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/puppet/modules/mysql/Modulefile b/puppet/modules/mysql/Modulefile
new file mode 100755
index 0000000..cd15dfb
--- /dev/null
+++ b/puppet/modules/mysql/Modulefile
@@ -0,0 +1,8 @@
+name 'puppetlabs-mysql'
+version '0.4.0'
+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'
diff --git a/puppet/modules/mysql/README.md b/puppet/modules/mysql/README.md
new file mode 100755
index 0000000..fd73898
--- /dev/null
+++ b/puppet/modules/mysql/README.md
@@ -0,0 +1,124 @@
+# Mysql module for Puppet
+
+This module manages mysql on Linux (RedHat/Debian) distros. A native mysql provider implements database resource type to handle database, database user, and database permission.
+
+## Description
+
+This module uses the fact osfamily which is supported by Facter 1.6.1+. If you do not have facter 1.6.1 in your environment, the following manifests will provide the same functionality in site.pp (before declaring any node):
+
+ if ! $::osfamily {
+ case $::operatingsystem {
+ 'RedHat', 'Fedora', 'CentOS', 'Scientific', 'SLC', 'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL': {
+ $osfamily = 'RedHat'
+ }
+ 'ubuntu', 'debian': {
+ $osfamily = 'Debian'
+ }
+ 'SLES', 'SLED', 'OpenSuSE', 'SuSE': {
+ $osfamily = 'Suse'
+ }
+ 'Solaris', 'Nexenta': {
+ $osfamily = 'Solaris'
+ }
+ default: {
+ $osfamily = $::operatingsystem
+ }
+ }
+ }
+
+This module depends on creates_resources function which is introduced in Puppet 2.7. Users on puppet 2.6 can use the following module which provides this functionality:
+
+[http://github.com/puppetlabs/puppetlabs-create_resources](http://github.com/puppetlabs/puppetlabs-create_resources)
+
+This module is based on work by David Schmitt. The following contributor have contributed patches to this module (beyond Puppet Labs):
+
+* Christian G. Warden
+* Daniel Black
+* Justin Ellison
+* Lowe Schmidt
+* Matthias Pigulla
+* William Van Hevelingen
+* Michael Arnold
+
+## Usage
+
+### mysql
+Installs the mysql-client package.
+
+ class { 'mysql': }
+
+### mysql::java
+Installs mysql bindings for java.
+
+ class { 'mysql::java': }
+
+### mysql::python
+Installs mysql bindings for python.
+
+ class { 'mysql::python': }
+
+### mysql::ruby
+Installs mysql bindings for ruby.
+
+ class { 'mysql::ruby': }
+
+### mysql::server
+Installs mysql-server packages, configures my.cnf and starts mysqld service:
+
+ class { 'mysql::server':
+ config_hash => { 'root_password' => 'foo' }
+ }
+
+Database login information stored in `/root/.my.cnf`.
+
+### mysql::db
+Creates a database with a user and assign some privileges.
+
+ mysql::db { 'mydb':
+ user => 'myuser',
+ password => 'mypass',
+ host => 'localhost',
+ grant => ['all'],
+ }
+
+### mysql::backup
+Installs a mysql backup script, cronjob, and priviledged backup user.
+
+ class { 'mysql::backup':
+ backupuser => 'myuser',
+ backuppassword => 'mypassword',
+ backupdir => '/tmp/backups',
+ }
+
+### Providers for database types:
+MySQL provider supports puppet resources command:
+
+ $ puppet resource database
+ database { 'information_schema':
+ ensure => 'present',
+ charset => 'utf8',
+ }
+ database { 'mysql':
+ ensure => 'present',
+ charset => 'latin1',
+ }
+
+The custom resources can be used in any other manifests:
+
+ database { 'mydb':
+ charset => 'latin1',
+ }
+
+ database_user { 'bob@localhost':
+ password_hash => mysql_password('foo')
+ }
+
+ database_grant { 'user@localhost/database':
+ privileges => ['all'] ,
+ }
+
+A resource default can be specified to handle dependency:
+
+ Database {
+ require => Class['mysql::server'],
+ }
diff --git a/puppet/modules/mysql/files/mysqltuner.pl b/puppet/modules/mysql/files/mysqltuner.pl
new file mode 100755
index 0000000..f61881c
--- /dev/null
+++ b/puppet/modules/mysql/files/mysqltuner.pl
@@ -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 .
+#
+# 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 Connect to a remote host to perform tests (default: localhost)\n".
+ " --socket Use a different socket for a local connection\n".
+ " --port Port to use for connection (default: 3306)\n".
+ " --user Username to use for authentication\n".
+ " --pass 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 Amount of RAM installed in megabytes\n".
+ " --forceswap 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-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 \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:
diff --git a/puppet/modules/mysql/lib/puppet/parser/functions/mysql_password.rb b/puppet/modules/mysql/lib/puppet/parser/functions/mysql_password.rb
new file mode 100755
index 0000000..74281b8
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/parser/functions/mysql_password.rb
@@ -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
diff --git a/puppet/modules/mysql/lib/puppet/provider/database/mysql.rb b/puppet/modules/mysql/lib/puppet/provider/database/mysql.rb
new file mode 100755
index 0000000..7964a0c
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/provider/database/mysql.rb
@@ -0,0 +1,42 @@
+Puppet::Type.type(:database).provide(:mysql) do
+
+ desc "Manages MySQL database."
+
+ defaultfor :kernel => 'Linux'
+
+ optional_commands :mysql => 'mysql'
+ optional_commands :mysqladmin => 'mysqladmin'
+
+ def self.instances
+ mysql('-NBe', "show databases").split("\n").collect do |name|
+ new(:name => name)
+ end
+ end
+
+ def create
+ mysql('-NBe', "create database `#{@resource[:name]}` character set #{resource[:charset]}")
+ end
+
+ def destroy
+ mysqladmin('-f', 'drop', @resource[:name])
+ end
+
+ def charset
+ mysql('-NBe', "show create database `#{resource[:name]}`").match(/.*?(\S+)\s\*\//)[1]
+ end
+
+ def charset=(value)
+ mysql('-NBe', "alter database `#{resource[:name]}` CHARACTER SET #{value}")
+ end
+
+ def exists?
+ begin
+ mysql('-NBe', "show databases").match(/^#{@resource[:name]}$/)
+ rescue => e
+ debug(e.message)
+ return nil
+ end
+ end
+
+end
+
diff --git a/puppet/modules/mysql/lib/puppet/provider/database_grant/mysql.rb b/puppet/modules/mysql/lib/puppet/provider/database_grant/mysql.rb
new file mode 100755
index 0000000..e5bfb15
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/provider/database_grant/mysql.rb
@@ -0,0 +1,177 @@
+# 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
+
+Puppet::Type.type(:database_grant).provide(: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("mysql", "-Be", "describe user")
+ 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("mysql", "-Be", "describe db")
+ 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 "flush-privileges"
+ 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 "mysql", "-e", "INSERT INTO user (host, user) VALUES ('%s', '%s')" % [
+ name[:host], name[:user],
+ ]
+ when :db
+ mysql "mysql", "-e", "INSERT INTO db (host, user, db) VALUES ('%s', '%s', '%s')" % [
+ name[:host], name[:user], name[:db],
+ ]
+ end
+ mysql_flush
+ end
+ end
+
+ def destroy
+ mysql "mysql", "-e", "REVOKE ALL ON '%s'.* FROM '%s@%s'" % [ @resource[:privileges], @resource[:database], @resource[:name], @resource[:host] ]
+ end
+
+ def row_exists?
+ name = split_name(@resource[:name])
+ fields = [:user, :host]
+ if name[:type] == :db
+ fields << :db
+ end
+ not mysql( "mysql", "-NBe", 'SELECT "1" FROM %s WHERE %s' % [ name[:type], fields.map do |f| "%s = '%s'" % [f, name[f]] end.join(' AND ')]).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 "mysql", "-Be", 'select * from user where user="%s" and host="%s"' % [ name[:user], name[:host] ]
+ when :db
+ privs = mysql "mysql", "-Be", 'select * from db where user="%s" and host="%s" and db="%s"' % [ name[:user], name[:host], name[:db] ]
+ 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
+
+ mysql "mysql", "-Be", stmt
+ mysql_flush
+ end
+end
diff --git a/puppet/modules/mysql/lib/puppet/provider/database_user/mysql.rb b/puppet/modules/mysql/lib/puppet/provider/database_user/mysql.rb
new file mode 100755
index 0000000..fb647bd
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/provider/database_user/mysql.rb
@@ -0,0 +1,42 @@
+Puppet::Type.type(:database_user).provide(:mysql) do
+
+ desc "manage users for a mysql database."
+
+ defaultfor :kernel => 'Linux'
+
+ optional_commands :mysql => 'mysql'
+ optional_commands :mysqladmin => 'mysqladmin'
+
+ def self.instances
+ users = mysql("mysql", '-BNe' "select concat(User, '@',Host) as User from mysql.user").split("\n")
+ users.select{ |user| user =~ /.+@/ }.collect do |name|
+ new(:name => name)
+ end
+ end
+
+ def create
+ mysql("mysql", "-e", "create user '%s' identified by PASSWORD '%s'" % [ @resource[:name].sub("@", "'@'"), @resource.value(:password_hash) ])
+ end
+
+ def destroy
+ mysql("mysql", "-e", "drop user '%s'" % @resource.value(:name).sub("@", "'@'") )
+ end
+
+ def password_hash
+ mysql("mysql", "-NBe", "select password from user where CONCAT(user, '@', host) = '%s'" % @resource.value(:name)).chomp
+ end
+
+ def password_hash=(string)
+ mysql("mysql", "-e", "SET PASSWORD FOR '%s' = '%s'" % [ @resource[:name].sub("@", "'@'"), string ] )
+ end
+
+ def exists?
+ not mysql("mysql", "-NBe", "select '1' from user where CONCAT(user, '@', host) = '%s'" % @resource.value(:name)).empty?
+ end
+
+ def flush
+ @property_hash.clear
+ mysqladmin "flush-privileges"
+ end
+
+end
diff --git a/puppet/modules/mysql/lib/puppet/type/database.rb b/puppet/modules/mysql/lib/puppet/type/database.rb
new file mode 100755
index 0000000..a27fc00
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/type/database.rb
@@ -0,0 +1,17 @@
+# 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."
+ end
+
+ newproperty(:charset) do
+ desc "The characterset to use for a database"
+ defaultto :utf8
+ newvalue(/^\S+$/)
+ end
+
+end
diff --git a/puppet/modules/mysql/lib/puppet/type/database_grant.rb b/puppet/modules/mysql/lib/puppet/type/database_grant.rb
new file mode 100755
index 0000000..965695b
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/type/database_grant.rb
@@ -0,0 +1,75 @@
+# 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"
+ 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
+
diff --git a/puppet/modules/mysql/lib/puppet/type/database_user.rb b/puppet/modules/mysql/lib/puppet/type/database_user.rb
new file mode 100755
index 0000000..ef98547
--- /dev/null
+++ b/puppet/modules/mysql/lib/puppet/type/database_user.rb
@@ -0,0 +1,25 @@
+# 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 priveleges"
+
+ 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
+
+end
diff --git a/puppet/modules/mysql/manifests/backup.pp b/puppet/modules/mysql/manifests/backup.pp
new file mode 100755
index 0000000..741d497
--- /dev/null
+++ b/puppet/modules/mysql/manifests/backup.pp
@@ -0,0 +1,68 @@
+# Class: mysql::backup
+#
+# This module handles ...
+#
+# Parameters:
+# [*backupuser*] - The name of the mysql backup user.
+# [*backuppassword*] - The password of the mysql backup user.
+# [*backupdir*] - The target directory of the mysqldump.
+#
+# Actions:
+# GRANT SELECT, RELOAD, LOCK TABLES ON *.* TO 'user'@'localhost'
+# IDENTIFIED BY 'password';
+#
+# Requires:
+# Class['mysql::config']
+#
+# Sample Usage:
+# class { 'mysql::backup':
+# backupuser => 'myuser',
+# backuppassword => 'mypassword',
+# backupdir => '/tmp/backups',
+# }
+#
+class mysql::backup (
+ $backupuser,
+ $backuppassword,
+ $backupdir,
+ $ensure = 'present'
+) {
+
+ database_user { "${backupuser}@localhost":
+ ensure => $ensure,
+ password_hash => mysql_password($backuppassword),
+ provider => 'mysql',
+ require => Class['mysql::config'],
+ }
+
+ database_grant { "${backupuser}@localhost":
+ privileges => [ 'Select_priv', 'Reload_priv', 'Lock_tables_priv' ],
+ require => Database_user["${backupuser}@localhost"],
+ }
+
+ cron { 'mysql-backup':
+ ensure => $ensure,
+ command => '/usr/local/sbin/mysqlbackup.sh',
+ user => 'root',
+ hour => 23,
+ minute => 5,
+ 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 => '0700',
+ owner => 'root',
+ group => 'root',
+ }
+}
diff --git a/puppet/modules/mysql/manifests/config.pp b/puppet/modules/mysql/manifests/config.pp
new file mode 100755
index 0000000..c3b446c
--- /dev/null
+++ b/puppet/modules/mysql/manifests/config.pp
@@ -0,0 +1,122 @@
+# Class: mysql::config
+#
+# Parameters:
+#
+# [*root_password*] - root user password.
+# [*old_root_password*] - previous root user password,
+# [*bind_address*] - address to bind service.
+# [*port*] - port to bind service.
+# [*etc_root_password*] - whether to save /etc/.my.cnf.
+# [*service_name*] - mysql service name.
+# [*config_file*] - my.cnf configuration file path.
+# [*socket*] - mysql socket.
+# [*datadir*] - path to datadir.
+# [*ssl] - enable ssl
+# [*ssl_ca] - path to ssl-ca
+# [*ssl_cert] - path to ssl-cert
+# [*ssl_key] - path to ssl-key
+#
+# Actions:
+#
+# Requires:
+#
+# class mysql::server
+#
+# Usage:
+#
+# class { 'mysql::config':
+# root_password => 'changeme',
+# bind_address => $::ipaddress,
+# }
+#
+class mysql::config(
+ $root_password = 'UNSET',
+ $old_root_password = '',
+ $bind_address = $mysql::params::bind_address,
+ $port = $mysql::params::port,
+ $etc_root_password = $mysql::params::etc_root_password,
+ $service_name = $mysql::params::service_name,
+ $config_file = $mysql::params::config_file,
+ $socket = $mysql::params::socket,
+ $datadir = $mysql::params::datadir,
+ $ssl = $mysql::params::ssl,
+ $ssl_ca = $mysql::params::ssl_ca,
+ $ssl_cert = $mysql::params::ssl_cert,
+ $ssl_key = $mysql::params::ssl_key,
+ $log_error = $mysql::params::log_error,
+ $default_engine = 'UNSET',
+ $root_group = $mysql::params::root_group
+) inherits mysql::params {
+
+ File {
+ owner => 'root',
+ group => $root_group,
+ mode => '0400',
+ notify => Exec['mysqld-restart'],
+ }
+
+ if $ssl and $ssl_ca == undef {
+ fail('The ssl_ca parameter is required when ssl is true')
+ }
+
+ if $ssl and $ssl_cert == undef {
+ fail('The ssl_cert parameter is required when ssl is true')
+ }
+
+ if $ssl and $ssl_key == undef {
+ fail('The ssl_key parameter is required when ssl is true')
+ }
+
+ # This kind of sucks, that I have to specify a difference resource for
+ # restart. the reason is that I need the service to be started before mods
+ # to the config file which can cause a refresh
+ exec { 'mysqld-restart':
+ command => "service ${service_name} restart",
+ logoutput => on_failure,
+ refreshonly => true,
+ path => '/sbin/:/usr/sbin/:/usr/bin/:/bin/',
+ }
+
+ # manage root password if it is set
+ if $root_password != 'UNSET' {
+ case $old_root_password {
+ '': { $old_pw='' }
+ default: { $old_pw="-p'${old_root_password}'" }
+ }
+
+ exec { 'set_mysql_rootpw':
+ command => "mysqladmin -u root ${old_pw} password '${root_password}'",
+ logoutput => true,
+ unless => "mysqladmin -u root -p'${root_password}' status > /dev/null",
+ path => '/usr/local/sbin:/usr/bin:/usr/local/bin',
+ notify => Exec['mysqld-restart'],
+ require => File['/etc/mysql/conf.d'],
+ }
+
+ file { '/root/.my.cnf':
+ content => template('mysql/my.cnf.pass.erb'),
+ require => Exec['set_mysql_rootpw'],
+ }
+
+ if $etc_root_password {
+ file{ '/etc/my.cnf':
+ content => template('mysql/my.cnf.pass.erb'),
+ require => Exec['set_mysql_rootpw'],
+ }
+ }
+ }
+
+ file { '/etc/mysql':
+ ensure => directory,
+ mode => '0755',
+ }
+ file { '/etc/mysql/conf.d':
+ ensure => directory,
+ mode => '0755',
+ }
+ file { $config_file:
+ content => template('mysql/my.cnf.erb'),
+ mode => '0644',
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/db.pp b/puppet/modules/mysql/manifests/db.pp
new file mode 100755
index 0000000..0663edc
--- /dev/null
+++ b/puppet/modules/mysql/manifests/db.pp
@@ -0,0 +1,77 @@
+# Define: mysql::db
+#
+# This module creates database instances, a user, and grants that user
+# privileges to the database. It can also import SQL from a file in order to,
+# for example, initialize a database schema.
+#
+# Since it requires class mysql::server, we assume to run all commands as the
+# root mysql user against the local mysql server.
+#
+# Parameters:
+# [*title*] - mysql database name.
+# [*user*] - username to create and grant access.
+# [*password*] - user's password.
+# [*charset*] - database charset.
+# [*host*] - host for assigning privileges to user.
+# [*grant*] - array of privileges to grant user.
+# [*enforce_sql*] - whether to enforce or conditionally run sql on creation.
+# [*sql*] - sql statement to run.
+#
+# Actions:
+#
+# Requires:
+#
+# class mysql::server
+#
+# Sample Usage:
+#
+# mysql::db { 'mydb':
+# user => 'my_user',
+# password => 'password',
+# host => $::hostname,
+# grant => ['all']
+# }
+#
+define mysql::db (
+ $user,
+ $password,
+ $charset = 'utf8',
+ $host = 'localhost',
+ $grant = 'all',
+ $sql = '',
+ $enforce_sql = false
+) {
+
+ database { $name:
+ ensure => present,
+ charset => $charset,
+ provider => 'mysql',
+ require => Class['mysql::server'],
+ }
+
+ database_user { "${user}@${host}":
+ ensure => present,
+ password_hash => mysql_password($password),
+ provider => 'mysql',
+ require => Database[$name],
+ }
+
+ database_grant { "${user}@${host}/${name}":
+ privileges => $grant,
+ provider => 'mysql',
+ require => Database_user["${user}@${host}"],
+ }
+
+ $refresh = ! $enforce_sql
+
+ if $sql {
+ exec{ "${name}-import":
+ command => "/usr/bin/mysql ${name} < ${sql}",
+ logoutput => true,
+ refreshonly => $refresh,
+ require => Database_grant["${user}@${host}/${name}"],
+ subscribe => Database[$name],
+ }
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/init.pp b/puppet/modules/mysql/manifests/init.pp
new file mode 100755
index 0000000..dff2ddd
--- /dev/null
+++ b/puppet/modules/mysql/manifests/init.pp
@@ -0,0 +1,24 @@
+# Class: mysql
+#
+# This class installs mysql client software.
+#
+# Parameters:
+# [*client_package_name*] - The name of the mysql client package.
+#
+# Actions:
+#
+# Requires:
+#
+# Sample Usage:
+#
+class mysql (
+ $package_name = $mysql::params::client_package_name,
+ $package_ensure = 'present'
+) inherits mysql::params {
+
+ package { 'mysql_client':
+ name => $package_name,
+ ensure => $package_ensure,
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/java.pp b/puppet/modules/mysql/manifests/java.pp
new file mode 100755
index 0000000..2713c05
--- /dev/null
+++ b/puppet/modules/mysql/manifests/java.pp
@@ -0,0 +1,24 @@
+# Class: mysql::java
+#
+# This class installs the mysql-java-connector.
+#
+# Parameters:
+# [*java_package_name*] - The name of the mysql java package.
+#
+# Actions:
+#
+# Requires:
+#
+# Sample Usage:
+#
+class mysql::java (
+ $package_name = $mysql::params::java_package_name,
+ $package_ensure = 'present'
+) inherits mysql::params {
+
+ package { 'mysql-connector-java':
+ ensure => $package_ensure,
+ name => $package_name,
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/params.pp b/puppet/modules/mysql/manifests/params.pp
new file mode 100755
index 0000000..1cec72f
--- /dev/null
+++ b/puppet/modules/mysql/manifests/params.pp
@@ -0,0 +1,91 @@
+# Class: mysql::params
+#
+# The mysql configuration settings.
+#
+# Parameters:
+#
+# Actions:
+#
+# Requires:
+#
+# Sample Usage:
+#
+class mysql::params {
+
+ $bind_address = '127.0.0.1'
+ $port = 3306
+ $etc_root_password = false
+ $ssl = false
+
+ case $::operatingsystem {
+ "Ubuntu": {
+ $service_provider = upstart
+ }
+ default: {
+ $service_provider = undef
+ }
+ }
+
+ case $::osfamily {
+ 'RedHat': {
+ $basedir = '/usr'
+ $datadir = '/var/lib/mysql'
+ $service_name = 'mysqld'
+ $client_package_name = 'mysql'
+ $server_package_name = 'mysql-server'
+ $socket = '/var/lib/mysql/mysql.sock'
+ $config_file = '/etc/my.cnf'
+ $log_error = '/var/log/mysqld.log'
+ $ruby_package_name = 'ruby-mysql'
+ $ruby_package_provider = 'gem'
+ $python_package_name = 'MySQL-python'
+ $java_package_name = 'mysql-connector-java'
+ $root_group = 'root'
+ $ssl_ca = '/etc/mysql/cacert.pem'
+ $ssl_cert = '/etc/mysql/server-cert.pem'
+ $ssl_key = '/etc/mysql/server-key.pem'
+ }
+
+ 'Debian': {
+ $basedir = '/usr'
+ $datadir = '/var/lib/mysql'
+ $service_name = 'mysql'
+ $client_package_name = 'mysql-client'
+ $server_package_name = 'mysql-server'
+ $socket = '/var/run/mysqld/mysqld.sock'
+ $config_file = '/etc/mysql/my.cnf'
+ $log_error = '/var/log/mysql/error.log'
+ $ruby_package_name = 'libmysql-ruby'
+ $python_package_name = 'python-mysqldb'
+ $java_package_name = 'libmysql-java'
+ $root_group = 'root'
+ $ssl_ca = '/etc/mysql/cacert.pem'
+ $ssl_cert = '/etc/mysql/server-cert.pem'
+ $ssl_key = '/etc/mysql/server-key.pem'
+ }
+
+ 'FreeBSD': {
+ $basedir = '/usr/local'
+ $datadir = '/var/db/mysql'
+ $service_name = 'mysql-server'
+ $client_package_name = 'databases/mysql55-client'
+ $server_package_name = 'databases/mysql55-server'
+ $socket = '/tmp/mysql.sock'
+ $config_file = '/var/db/mysql/my.cnf'
+ $log_error = "/var/db/mysql/${::hostname}.err"
+ $ruby_package_name = 'ruby-mysql'
+ $ruby_package_provider = 'gem'
+ $python_package_name = 'databases/py-MySQLdb'
+ $java_package_name = 'databases/mysql-connector-java'
+ $root_group = 'wheel'
+ $ssl_ca = undef
+ $ssl_cert = undef
+ $ssl_key = undef
+ }
+
+ default: {
+ fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat Debian and FreeBSD")
+ }
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/python.pp b/puppet/modules/mysql/manifests/python.pp
new file mode 100755
index 0000000..fb7f643
--- /dev/null
+++ b/puppet/modules/mysql/manifests/python.pp
@@ -0,0 +1,26 @@
+# Class: mysql::python
+#
+# This class installs the python libs for mysql.
+#
+# Parameters:
+# [*ensure*] - ensure state for package.
+# can be specified as version.
+# [*package_name*] - name of package
+#
+# Actions:
+#
+# Requires:
+#
+# Sample Usage:
+#
+class mysql::python(
+ $package_name = $mysql::params::python_package_name,
+ $package_ensure = 'present'
+) inherits mysql::params {
+
+ package { 'python-mysqldb':
+ name => $package_name,
+ ensure => $package_ensure,
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/ruby.pp b/puppet/modules/mysql/manifests/ruby.pp
new file mode 100755
index 0000000..9c630f8
--- /dev/null
+++ b/puppet/modules/mysql/manifests/ruby.pp
@@ -0,0 +1,28 @@
+# Class: mysql::ruby
+#
+# installs the ruby bindings for mysql
+#
+# Parameters:
+# [*ensure*] - ensure state for package.
+# can be specified as version.
+# [*package_name*] - name of package
+#
+# Actions:
+#
+# Requires:
+#
+# Sample Usage:
+#
+class mysql::ruby (
+ $package_name = $mysql::params::ruby_package_name,
+ $package_provider = $mysql::params::ruby_package_provider,
+ $package_ensure = 'present'
+) inherits mysql::params {
+
+ package{ 'ruby_mysql':
+ name => $package_name,
+ ensure => $package_ensure,
+ provider => $package_provider,
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/server.pp b/puppet/modules/mysql/manifests/server.pp
new file mode 100755
index 0000000..1f711d4
--- /dev/null
+++ b/puppet/modules/mysql/manifests/server.pp
@@ -0,0 +1,52 @@
+# Class: mysql::server
+#
+# manages the installation of the mysql server. manages the package, service,
+# my.cnf
+#
+# Parameters:
+# [*package_name*] - name of package
+# [*service_name*] - name of service
+# [*config_hash*] - hash of config parameters that need to be set.
+#
+# Actions:
+#
+# Requires:
+#
+# Sample Usage:
+#
+class mysql::server (
+ $package_name = $mysql::params::server_package_name,
+ $package_ensure = 'present',
+ $service_name = $mysql::params::service_name,
+ $service_provider = $mysql::params::service_provider,
+ $config_hash = {},
+ $enabled = true
+) inherits mysql::params {
+
+ Class['mysql::server'] -> Class['mysql::config']
+
+ $config_class = {}
+ $config_class['mysql::config'] = $config_hash
+
+ create_resources( 'class', $config_class )
+
+ package { 'mysql-server':
+ name => $package_name,
+ ensure => $package_ensure,
+ }
+
+ if $enabled {
+ $service_ensure = 'running'
+ } else {
+ $service_ensure = 'stopped'
+ }
+
+ service { 'mysqld':
+ name => $service_name,
+ ensure => $service_ensure,
+ enable => $enabled,
+ require => Package['mysql-server'],
+ provider => $service_provider,
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/server/account_security.pp b/puppet/modules/mysql/manifests/server/account_security.pp
new file mode 100755
index 0000000..8d81958
--- /dev/null
+++ b/puppet/modules/mysql/manifests/server/account_security.pp
@@ -0,0 +1,13 @@
+class mysql::server::account_security {
+ # Some installations have some default users which are not required.
+ # We remove them here. You can subclass this class to overwrite this behavior.
+ database_user { [ "root@${::fqdn}", "root@${::hostname}", 'root@127.0.0.1',
+ "@${::fqdn}", "@${::hostname}", '@localhost', '@%' ]:
+ ensure => 'absent',
+ require => Class['mysql::config'],
+ }
+ database { 'test':
+ ensure => 'absent',
+ require => Class['mysql::config'],
+ }
+}
diff --git a/puppet/modules/mysql/manifests/server/monitor.pp b/puppet/modules/mysql/manifests/server/monitor.pp
new file mode 100755
index 0000000..9692ff1
--- /dev/null
+++ b/puppet/modules/mysql/manifests/server/monitor.pp
@@ -0,0 +1,19 @@
+class mysql::server::monitor (
+ $mysql_monitor_username,
+ $mysql_monitor_password,
+ $mysql_monitor_hostname
+) {
+
+ Class['mysql::server'] -> Class['mysql::server::monitor']
+
+ database_user{ "${mysql_monitor_username}@${mysql_monitor_hostname}":
+ password_hash => mysql_password($mysql_monitor_password),
+ ensure => present,
+ }
+
+ database_grant { "${mysql_monitor_username}@${mysql_monitor_hostname}":
+ privileges => [ 'process_priv', 'super_priv' ],
+ require => Mysql_user["${mysql_monitor_username}@${mysql_monitor_hostname}"],
+ }
+
+}
diff --git a/puppet/modules/mysql/manifests/server/mysqltuner.pp b/puppet/modules/mysql/manifests/server/mysqltuner.pp
new file mode 100755
index 0000000..416a3da
--- /dev/null
+++ b/puppet/modules/mysql/manifests/server/mysqltuner.pp
@@ -0,0 +1,22 @@
+# Copyright 2009 Larry Ludwig (larrylud@gmail.com)
+#
+# 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.
+#
+class mysql::server::mysqltuner {
+ # mysql performance tester
+ file { '/usr/bin/mysqltuner':
+ ensure => present,
+ mode => '0550',
+ source => 'puppet:///modules/mysql/mysqltuner.pl',
+ }
+}
diff --git a/puppet/modules/mysql/templates/my.cnf.erb b/puppet/modules/mysql/templates/my.cnf.erb
new file mode 100755
index 0000000..e09c7c9
--- /dev/null
+++ b/puppet/modules/mysql/templates/my.cnf.erb
@@ -0,0 +1,42 @@
+[client]
+port = <%= port %>
+socket = <%= socket %>
+[mysqld_safe]
+socket = <%= socket %>
+nice = 0
+[mysqld]
+user = mysql
+socket = <%= socket %>
+port = <%= port %>
+basedir = <%= basedir %>
+datadir = <%= datadir %>
+tmpdir = /tmp
+skip-external-locking
+bind-address = <%= bind_address %>
+key_buffer = 16M
+max_allowed_packet = 16M
+thread_stack = 192K
+thread_cache_size = 8
+myisam-recover = BACKUP
+query_cache_limit = 1M
+query_cache_size = 16M
+log_error = <%= log_error %>
+expire_logs_days = 10
+max_binlog_size = 100M
+<% if default_engine != 'UNSET' %>
+default-storage-engine = <%= default_engine %>
+<% end %>
+<% if ssl == true %>
+ssl-ca = <%= ssl_ca %>
+ssl-cert = <%= ssl_cert %>
+ssl-key = <%= ssl_key %>
+<% end %>
+
+[mysqldump]
+quick
+quote-names
+max_allowed_packet = 16M
+[mysql]
+[isamchk]
+key_buffer = 16M
+!includedir /etc/mysql/conf.d/
diff --git a/puppet/modules/mysql/templates/my.cnf.pass.erb b/puppet/modules/mysql/templates/my.cnf.pass.erb
new file mode 100755
index 0000000..38a3a4a
--- /dev/null
+++ b/puppet/modules/mysql/templates/my.cnf.pass.erb
@@ -0,0 +1,6 @@
+[client]
+user=root
+host=localhost
+<% unless root_password == 'UNSET' -%>
+password=<%= root_password %>
+<% end -%>
diff --git a/puppet/modules/mysql/templates/mysqlbackup.sh.erb b/puppet/modules/mysql/templates/mysqlbackup.sh.erb
new file mode 100755
index 0000000..a2dba33
--- /dev/null
+++ b/puppet/modules/mysql/templates/mysqlbackup.sh.erb
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# MySQL Backup Script
+# Dumps mysql databases to a file for another backup tool to pick up.
+#
+# MySQL code:
+# GRANT SELECT, RELOAD, LOCK TABLES ON *.* TO 'user'@'localhost'
+# IDENTIFIED BY 'password';
+# FLUSH PRIVILEGES;
+#
+##### START CONFIG ###################################################
+
+USER=<%= backupuser %>
+PASS=<%= backuppassword %>
+DIR=<%= backupdir %>
+
+##### STOP CONFIG ####################################################
+PATH=/usr/bin:/usr/sbin:/bin:/sbin
+
+find $DIR -mtime +30 -exec rm -f {} \;
+mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \
+ --all-databases | bzcat -zc > ${DIR}/mysql_backup_`date +%Y%m%d-%H%M%S`.sql.bz2
+
diff --git a/puppet/modules/postgresql/GPL-3 b/puppet/modules/postgresql/GPL-3
new file mode 100755
index 0000000..94a9ed0
--- /dev/null
+++ b/puppet/modules/postgresql/GPL-3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/puppet/modules/postgresql/Modulefile b/puppet/modules/postgresql/Modulefile
new file mode 100755
index 0000000..0b027bf
--- /dev/null
+++ b/puppet/modules/postgresql/Modulefile
@@ -0,0 +1,13 @@
+name 'akumria-postgresql'
+version '1.0.0'
+source 'UNKNOWN'
+author 'akumria'
+license 'GNU General Public License, Version 3+'
+summary 'Install and configure postgresql database system'
+description "Postgresql is a database system which is broadly compatible with SQL
+ standards. You can setup users and configure databases using this module.
+"
+project_page 'https://github.com/akumria/puppet-postgresql'
+
+## Add dependencies, if any:
+dependency 'puppetlabs/stdlib', '>= 2.3.1'
diff --git a/puppet/modules/postgresql/README.md b/puppet/modules/postgresql/README.md
new file mode 100755
index 0000000..f49b663
--- /dev/null
+++ b/puppet/modules/postgresql/README.md
@@ -0,0 +1,156 @@
+Puppet module for postgresql
+============================
+
+Basic usage
+-----------
+
+To install the client software
+
+ class {'postgresql': }
+
+To specify a particular version
+
+ class {'postgresql':
+ version => '9.1',
+ }
+
+To install the server
+
+ class {'postgresql::server': }
+
+By default, the system-wide locale is assumed to be en_US.UTF-8. If the
+locale is not installed or available, you can specify an alternative:
+
+ class { 'postgresql::server':
+ locale => 'es_ES.UTF-8',
+ }
+
+Again, a particular version
+
+ class {'postgresql::server':
+ version => '9.1',
+ }
+
+Listen on a specific post / IP address
+
+ class {'postgresql::server':
+ listen => ['192.168.0.1', ],
+ port => 5432,
+ }
+
+To allow a remote host to connect to the server, now that you are listening
+on the Internet.
+
+ class {'postgresql::server':
+ listen => ['192.168.0.1', ],
+ port => 5432,
+ acl => ['host all all 192.168.0.2/32 md5', ],
+ }
+
+Refer to the [pg_hba.conf docs](http://www.postgresql.org/docs/devel/static/auth-pg-hba-conf.html) for
+the specifics of what each possible ACL field can be set to.
+
+To create a database owned by a user
+
+ postgresql::db { 'myuser':
+ password => 'mypassword',
+ }
+
+This will create `myuser` and then create a database called `myuser`
+which will owned by `myuser`. You can override the default locale and
+encoding and, if required, specify a different owner. For example:
+
+ postgresql::db { 'mydatabase':
+ owner => 'myuser',
+ password => 'mypassword',
+ locale => 'en_AU.UTF-8',
+ encoding => 'C',
+ }
+
+
+Read on, if your specific setup does not fall within this
+ (admittedly simple) framework.
+
+Create a user
+-------------
+
+This creates a role in the database cluster, by default the user
+is able to login and will inherit the permissions of any groups it
+is a member of.
+
+ pg_user {'pguser':
+ ensure => present,
+ password => 'pgpassword',
+ }
+
+You can also modify other attributes like whether the user can create
+databases (`createdb`), create other roles (`createrole`) or is the
+superuser (`superuser`).
+
+For example:
+
+ pg_user {'mighty_pguser':
+ ensure => present,
+ password => 'themightyone',
+ createdb => true,
+ createrole => true,
+ }
+
+
+Create a database
+-----------------
+
+This creates a database and adds a dependancy relationship to the user
+
+ pg_database {'pgdb':
+ ensure => present,
+ owner => 'pguser',
+ require => Pg_user['pguser'],
+ }
+
+The default is UTF-8 and en_US.UTF-8 , for English. If required,
+you can also specify both the locale and encoding of a database.
+
+ pg_database {'pgdb':
+ ensure => present,
+ owner => 'pguser',
+ encoding => 'UTF8',
+ locale => 'de_DE.UTF-8',
+ require => Pg_user['pguser'],
+ }
+
+
+Notes
+-----
+
+This module will not (yet) update either the user or database once they have
+been initially created. i.e. changing the `login` permission of a user does not work.
+Nor does changing the locale of an existing database.
+
+
+Contributors
+------------
+
+ * [Anand Kumria](https://github.com/akumria) ([@akumria](https://twitter.com/akumria))
+ * [Federico Maggi](https://github.com/phretor)
+ * [Joe Topjian](https://github.com/jtopjian)
+ * [Stephan Hochdörfer](https://github.com/shochdoerfer)
+ * [Marcello Barnaba](https://github.com/vjt)
+
+Copyright and License
+---------------------
+
+Copyright 2012 [Linuxpeak](https://www.linuxpeak.com/) Pty Ltd.
+
+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 .
diff --git a/puppet/modules/postgresql/lib/puppet/provider/pg_database/debian_postgresql.rb b/puppet/modules/postgresql/lib/puppet/provider/pg_database/debian_postgresql.rb
new file mode 100755
index 0000000..39c15c7
--- /dev/null
+++ b/puppet/modules/postgresql/lib/puppet/provider/pg_database/debian_postgresql.rb
@@ -0,0 +1,30 @@
+Puppet::Type.type(:pg_database).provide(:debian_postgresql) do
+
+ desc "Manage databases for a postgres database cluster"
+
+ defaultfor :operatingsystem => [:debian, :ubuntu]
+
+ optional_commands :psql => 'psql'
+ optional_commands :su => 'su'
+
+ def create
+ su("-", "postgres", "-c", "createdb -T template0 -E %s -l %s -O %s %s" % [ @resource.value(:encoding), @resource.value(:locale), @resource.value(:owner), @resource.value(:name) ])
+ end
+
+ def destroy
+ su("-", "postgres", "-c", "dropdb %s" % [ @resource.value(:name) ])
+ end
+
+ def exists?
+ su_output = su("-", "postgres", "-c", "psql --quiet -A -t -c \"select 1 from pg_database where datname = '%s';\"" % @resource.value(:name))
+ return false if su_output.length == 0
+ su_output.each do |line|
+ if line == "1\n"
+ return true
+ else
+ return false
+ end
+ end
+ end
+
+end
diff --git a/puppet/modules/postgresql/lib/puppet/provider/pg_database/default.rb b/puppet/modules/postgresql/lib/puppet/provider/pg_database/default.rb
new file mode 100755
index 0000000..48cbbc0
--- /dev/null
+++ b/puppet/modules/postgresql/lib/puppet/provider/pg_database/default.rb
@@ -0,0 +1,17 @@
+Puppet::Type.type(:pg_database).provide(:default) do
+
+ desc "A default pg_database provider which just fails."
+
+ def create
+ return false
+ end
+
+ def destroy
+ return false
+ end
+
+ def exists?
+ fail('This is just the default provider for pg_database, all it does is fail')
+ end
+
+end
diff --git a/puppet/modules/postgresql/lib/puppet/provider/pg_user/debian_postgresql.rb b/puppet/modules/postgresql/lib/puppet/provider/pg_user/debian_postgresql.rb
new file mode 100755
index 0000000..c6cb613
--- /dev/null
+++ b/puppet/modules/postgresql/lib/puppet/provider/pg_user/debian_postgresql.rb
@@ -0,0 +1,63 @@
+Puppet::Type.type(:pg_user).provide(:debian_postgresql) do
+
+ desc "Manage users for a postgres database cluster"
+
+ defaultfor :operatingsystem => [:debian, :ubuntu]
+
+ optional_commands :psql => 'psql'
+ optional_commands :su => 'su'
+
+ def create
+ stm = "create role %s encrypted password '%s'" % [\
+ @resource.value(:name), @resource.value(:password) ]
+
+ if @resource.value(:createdb) == true
+ stm = stm + " createdb"
+ else
+ stm = stm + " nocreatedb"
+ end
+
+ if @resource.value(:inherit) == false
+ stm = stm + " noinherit"
+ else
+ stm = stm + " inherit"
+ end
+
+ if @resource.value(:login) == false
+ stm = stm + " nologin"
+ else
+ stm = stm + " login"
+ end
+
+ if @resource.value(:createrole) == true
+ stm = stm + " createrole"
+ else
+ stm = stm + " nocreaterole"
+ end
+
+ if @resource.value(:superuser) == true
+ stm = stm + " superuser"
+ else
+ stm = stm + " nosuperuser"
+ end
+
+ su("-", "postgres", "-c", "psql -c \"%s\"" % stm)
+ end
+
+ def destroy
+ su("-", "postgres", "-c", "dropuser %s" % [ @resource.value(:name) ])
+ end
+
+ def exists?
+ su_output = su("-", "postgres", "-c", "psql --quiet -A -t -c \"select 1 from pg_roles where rolname = '%s';\"" % @resource.value(:name))
+ return false if su_output.length == 0
+ su_output.each do |line|
+ if line == "1\n"
+ return true
+ else
+ return false
+ end
+ end
+ end
+
+end
diff --git a/puppet/modules/postgresql/lib/puppet/provider/pg_user/default.rb b/puppet/modules/postgresql/lib/puppet/provider/pg_user/default.rb
new file mode 100755
index 0000000..38457c6
--- /dev/null
+++ b/puppet/modules/postgresql/lib/puppet/provider/pg_user/default.rb
@@ -0,0 +1,17 @@
+Puppet::Type.type(:pg_user).provide(:default) do
+
+ desc "A default pg_user provider which just fails."
+
+ def create
+ return false
+ end
+
+ def destroy
+ return false
+ end
+
+ def exists?
+ fail('This is just the default provider for pg_user, all it does is fail')
+ end
+
+end
diff --git a/puppet/modules/postgresql/lib/puppet/type/pg_database.rb b/puppet/modules/postgresql/lib/puppet/type/pg_database.rb
new file mode 100755
index 0000000..5e58312
--- /dev/null
+++ b/puppet/modules/postgresql/lib/puppet/type/pg_database.rb
@@ -0,0 +1,29 @@
+# This has to be a separate type to enable collecting
+Puppet::Type.newtype(:pg_database) do
+ @doc = "Manage Postgresql databases."
+
+ ensurable
+
+ newparam(:name, :namevar=>true) do
+ desc "The name of the database."
+ end
+
+ newparam(:owner) do
+ desc "The owner of the database"
+
+ defaultto :postgres
+ end
+
+ newparam(:encoding) do
+ desc "The character set encoding to use for the database"
+
+ defaultto :UTF8
+ end
+
+ newparam(:locale) do
+ desc "The locale to use for collation. Typical values include 'C' or 'en_US.UTF-8' or other specifiers"
+
+ defaultto :'en_US.UTF-8'
+ end
+
+end
diff --git a/puppet/modules/postgresql/lib/puppet/type/pg_user.rb b/puppet/modules/postgresql/lib/puppet/type/pg_user.rb
new file mode 100755
index 0000000..b1cc134
--- /dev/null
+++ b/puppet/modules/postgresql/lib/puppet/type/pg_user.rb
@@ -0,0 +1,45 @@
+# This has to be a separate type to enable collecting
+Puppet::Type.newtype(:pg_user) do
+ @doc = "Manage a Postgresql database user/role."
+
+ ensurable
+
+ newparam(:name, :namevar=>true) do
+ desc "The name of the user/role"
+ end
+
+ newparam(:password) do
+ desc "The password for the user/role"
+ end
+
+ newparam(:createdb) do
+ desc "Is the user allowed to create databases."
+
+ defaultto :false
+ end
+
+ newparam(:inherit) do
+ desc "Inherit privileges of roles this user/role is a member of."
+
+ defaultto :true
+ end
+
+ newparam(:login) do
+ desc "Can the user/role/ login?"
+
+ defaultto :true
+ end
+
+ newparam(:createrole) do
+ desc "Can the user/role create other users/roles?"
+
+ defaultto :false
+ end
+
+ newparam(:superuser) do
+ desc "Is the user/role a superuser?"
+
+ defaultto :false
+ end
+
+end
diff --git a/puppet/modules/postgresql/manifests/db.pp b/puppet/modules/postgresql/manifests/db.pp
new file mode 100755
index 0000000..3bf461c
--- /dev/null
+++ b/puppet/modules/postgresql/manifests/db.pp
@@ -0,0 +1,20 @@
+define postgresql::db (
+ $password,
+ $owner = $name,
+ $encoding = 'UTF8',
+ $locale = 'en_US.UTF-8',
+) {
+
+ pg_user {$owner:
+ ensure => present,
+ password => $password,
+ }
+
+ pg_database {$name:
+ ensure => present,
+ owner => $owner,
+ require => Pg_user[$owner],
+ encoding => $encoding,
+ locale => $locale,
+ }
+}
diff --git a/puppet/modules/postgresql/manifests/init.pp b/puppet/modules/postgresql/manifests/init.pp
new file mode 100755
index 0000000..852f361
--- /dev/null
+++ b/puppet/modules/postgresql/manifests/init.pp
@@ -0,0 +1,12 @@
+class postgresql (
+ $client_package = $postgresql::params::client_package,
+ $version = $postgresql::params::version
+
+) inherits postgresql::params {
+
+ package { "postgresql-client-$version":
+ name => sprintf("%s-%s", $client_package, $version),
+ ensure => present,
+ }
+
+}
diff --git a/puppet/modules/postgresql/manifests/params.pp b/puppet/modules/postgresql/manifests/params.pp
new file mode 100755
index 0000000..25b4130
--- /dev/null
+++ b/puppet/modules/postgresql/manifests/params.pp
@@ -0,0 +1,15 @@
+class postgresql::params {
+ $locale = 'en_US.UTF-8'
+ case $::operatingsystem {
+ /(Ubuntu|Debian)/: {
+ $version = '9.1'
+ $client_package = 'postgresql-client'
+ $server_package = 'postgresql'
+ $listen_address = 'localhost'
+ $port = 5432
+ }
+ default: {
+ fail("Unsupported platform: ${::operatingsystem}")
+ }
+ }
+}
diff --git a/puppet/modules/postgresql/manifests/server.pp b/puppet/modules/postgresql/manifests/server.pp
new file mode 100755
index 0000000..47f118d
--- /dev/null
+++ b/puppet/modules/postgresql/manifests/server.pp
@@ -0,0 +1,47 @@
+class postgresql::server (
+ $server_package = $postgresql::params::server_package,
+ $locale = $postgresql::params::locale,
+ $version = $postgresql::params::version,
+ $listen = $postgresql::params::listen_address,
+ $port = $postgresql::params::port,
+ $acl = []
+) inherits postgresql::params {
+
+ package { "postgresql-server-$version":
+ name => sprintf("%s-%s", $server_package, $version),
+ ensure => present,
+ }
+
+ service { "postgresql-system-$version":
+ name => 'postgresql',
+ enable => true,
+ ensure => running,
+ hasstatus => false,
+ hasrestart => true,
+ provider => 'debian',
+ subscribe => Package["postgresql-server-$version"],
+ }
+
+ file { "postgresql-server-config-$version":
+ name => "/etc/postgresql/$version/main/postgresql.conf",
+ ensure => present,
+ content => template('postgresql/postgresql.conf.erb'),
+ owner => 'postgres',
+ group => 'postgres',
+ mode => '0644',
+ require => Package["postgresql-server-$version"],
+ notify => Service["postgresql-system-$version"],
+ }
+
+ file { "postgresql-server-hba-config-$version":
+ name => "/etc/postgresql/$version/main/pg_hba.conf",
+ ensure => present,
+ content => template('postgresql/pg_hba.conf.erb'),
+ owner => 'postgres',
+ group => 'postgres',
+ mode => '0640',
+ require => Package["postgresql-server-$version"],
+ notify => Service["postgresql-system-$version"],
+ }
+
+}
diff --git a/puppet/modules/postgresql/templates/pg_hba.conf.erb b/puppet/modules/postgresql/templates/pg_hba.conf.erb
new file mode 100755
index 0000000..70e2601
--- /dev/null
+++ b/puppet/modules/postgresql/templates/pg_hba.conf.erb
@@ -0,0 +1,105 @@
+# PostgreSQL Client Authentication Configuration File - managed by puppet - DO NOT EDIT
+# =====================================================================================
+#
+# Refer to the "Client Authentication" section in the PostgreSQL
+# documentation for a complete description of this file. A short
+# synopsis follows.
+#
+# This file controls: which hosts are allowed to connect, how clients
+# are authenticated, which PostgreSQL user names they can use, which
+# databases they can access. Records take one of these forms:
+#
+# local DATABASE USER METHOD [OPTIONS]
+# host DATABASE USER ADDRESS METHOD [OPTIONS]
+# hostssl DATABASE USER ADDRESS METHOD [OPTIONS]
+# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS]
+#
+# (The uppercase items must be replaced by actual values.)
+#
+# The first field is the connection type: "local" is a Unix-domain
+# socket, "host" is either a plain or SSL-encrypted TCP/IP socket,
+# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a
+# plain TCP/IP socket.
+#
+# DATABASE can be "all", "sameuser", "samerole", "replication", a
+# database name, or a comma-separated list thereof. The "all"
+# keyword does not match "replication". Access to replication
+# must be enabled in a separate record (see example below).
+#
+# USER can be "all", a user name, a group name prefixed with "+", or a
+# comma-separated list thereof. In both the DATABASE and USER fields
+# you can also write a file name prefixed with "@" to include names
+# from a separate file.
+#
+# ADDRESS specifies the set of hosts the record matches. It can be a
+# host name, or it is made up of an IP address and a CIDR mask that is
+# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
+# specifies the number of significant bits in the mask. A host name
+# that starts with a dot (.) matches a suffix of the actual host name.
+# Alternatively, you can write an IP address and netmask in separate
+# columns to specify the set of hosts. Instead of a CIDR-address, you
+# can write "samehost" to match any of the server's own IP addresses,
+# or "samenet" to match any address in any subnet that the server is
+# directly connected to.
+#
+# METHOD can be "trust", "reject", "md5", "password", "gss", "sspi",
+# "krb5", "ident", "peer", "pam", "ldap", "radius" or "cert". Note that
+# "password" sends passwords in clear text; "md5" is preferred since
+# it sends encrypted passwords.
+#
+# OPTIONS are a set of options for the authentication in the format
+# NAME=VALUE. The available options depend on the different
+# authentication methods -- refer to the "Client Authentication"
+# section in the documentation for a list of which options are
+# available for which authentication methods.
+#
+# Database and user names containing spaces, commas, quotes and other
+# special characters must be quoted. Quoting one of the keywords
+# "all", "sameuser", "samerole" or "replication" makes the name lose
+# its special character, and just match a database or username with
+# that name.
+#
+# This file is read on server startup and when the postmaster receives
+# a SIGHUP signal. If you edit the file on a running system, you have
+# to SIGHUP the postmaster for the changes to take effect. You can
+# use "pg_ctl reload" to do that.
+
+# Put your actual configuration here
+# ----------------------------------
+#
+# If you want to allow non-local connections, you need to add more
+# "host" records. In that case you will also need to make PostgreSQL
+# listen on a non-local interface via the listen_addresses
+# configuration parameter, or via the -i or -h command line switches.
+
+
+
+
+# DO NOT DISABLE!
+# If you change this first entry you will need to make sure that the
+# database superuser can access the database using some other method.
+# Noninteractive access to all databases is required during automatic
+# maintenance (custom daily cronjobs, replication, and similar tasks).
+#
+# Database administrative login by Unix domain socket
+local all postgres peer
+
+# TYPE DATABASE USER ADDRESS METHOD
+
+# "local" is for Unix domain socket connections only
+local all all peer
+# IPv4 local connections:
+host all all 127.0.0.1/32 md5
+# IPv6 local connections:
+host all all ::1/128 md5
+
+# site-specific access control list
+<% acl.each do |entry| -%>
+<%= entry %>
+<% end -%>
+
+# Allow replication connections from localhost, by a user with the
+# replication privilege.
+#local replication postgres peer
+#host replication postgres 127.0.0.1/32 md5
+#host replication postgres ::1/128 md5
diff --git a/puppet/modules/postgresql/templates/postgresql.conf.erb b/puppet/modules/postgresql/templates/postgresql.conf.erb
new file mode 100755
index 0000000..98426af
--- /dev/null
+++ b/puppet/modules/postgresql/templates/postgresql.conf.erb
@@ -0,0 +1,559 @@
+# ---------------------------------------------------------------
+# PostgreSQL configuration file - managed by puppet - DO NOT EDIT
+# ---------------------------------------------------------------
+#
+# This file consists of lines of the form:
+#
+# name = value
+#
+# (The "=" is optional.) Whitespace may be used. Comments are introduced with
+# "#" anywhere on a line. The complete list of parameter names and allowed
+# values can be found in the PostgreSQL documentation.
+#
+# The commented-out settings shown in this file represent the default values.
+# Re-commenting a setting is NOT sufficient to revert it to the default value;
+# you need to reload the server.
+#
+# This file is read on server startup and when the server receives a SIGHUP
+# signal. If you edit the file on a running system, you have to SIGHUP the
+# server for the changes to take effect, or use "pg_ctl reload". Some
+# parameters, which are marked below, require a server shutdown and restart to
+# take effect.
+#
+# Any parameter can also be given as a command-line option to the server, e.g.,
+# "postgres -c log_connections=on". Some parameters can be changed at run time
+# with the "SET" SQL command.
+#
+# Memory units: kB = kilobytes Time units: ms = milliseconds
+# MB = megabytes s = seconds
+# GB = gigabytes min = minutes
+# h = hours
+# d = days
+
+
+#------------------------------------------------------------------------------
+# FILE LOCATIONS
+#------------------------------------------------------------------------------
+
+# The default values of these variables are driven from the -D command-line
+# option or PGDATA environment variable, represented here as ConfigDir.
+
+data_directory = '/var/lib/postgresql/<%= @version %>/main' # use data in another directory
+ # (change requires restart)
+hba_file = '/etc/postgresql/<%= @version %>/main/pg_hba.conf' # host-based authentication file
+ # (change requires restart)
+ident_file = '/etc/postgresql/<%= @version %>/main/pg_ident.conf' # ident configuration file
+ # (change requires restart)
+
+# If external_pid_file is not explicitly set, no extra PID file is written.
+external_pid_file = '/var/run/postgresql/<%= @version %>-main.pid' # write an extra PID file
+ # (change requires restart)
+
+
+#------------------------------------------------------------------------------
+# CONNECTIONS AND AUTHENTICATION
+#------------------------------------------------------------------------------
+
+# - Connection Settings -
+
+<% if listen.is_a? Array -%>
+listen_addresses = '<%= @listen.join ',' %>'
+<% else %>
+listen_addresses = '<%= @listen %>'
+<% end %>
+#listen_addresses = 'localhost' # what IP address(es) to listen on;
+ # comma-separated list of addresses;
+ # defaults to 'localhost', '*' = all
+ # (change requires restart)
+port = <%= @port %> # (change requires restart)
+max_connections = 100 # (change requires restart)
+# Note: Increasing max_connections costs ~400 bytes of shared memory per
+# connection slot, plus lock space (see max_locks_per_transaction).
+#superuser_reserved_connections = 3 # (change requires restart)
+unix_socket_directory = '/var/run/postgresql' # (change requires restart)
+#unix_socket_group = '' # (change requires restart)
+#unix_socket_permissions = 0777 # begin with 0 to use octal notation
+ # (change requires restart)
+#bonjour = off # advertise server via Bonjour
+ # (change requires restart)
+#bonjour_name = '' # defaults to the computer name
+ # (change requires restart)
+
+# - Security and Authentication -
+
+#authentication_timeout = 1min # 1s-600s
+ssl = true # (change requires restart)
+#ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers
+ # (change requires restart)
+#ssl_renegotiation_limit = 512MB # amount of data between renegotiations
+#password_encryption = on
+#db_user_namespace = off
+
+# Kerberos and GSSAPI
+#krb_server_keyfile = ''
+#krb_srvname = 'postgres' # (Kerberos only)
+#krb_caseins_users = off
+
+# - TCP Keepalives -
+# see "man 7 tcp" for details
+
+#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
+ # 0 selects the system default
+#tcp_keepalives_count = 0 # TCP_KEEPCNT;
+ # 0 selects the system default
+
+
+#------------------------------------------------------------------------------
+# RESOURCE USAGE (except WAL)
+#------------------------------------------------------------------------------
+
+# - Memory -
+
+shared_buffers = 24MB # min 128kB
+ # (change requires restart)
+#temp_buffers = 8MB # min 800kB
+#max_prepared_transactions = 0 # zero disables the feature
+ # (change requires restart)
+# Note: Increasing max_prepared_transactions costs ~600 bytes of shared memory
+# per transaction slot, plus lock space (see max_locks_per_transaction).
+# It is not advisable to set max_prepared_transactions nonzero unless you
+# actively intend to use prepared transactions.
+#work_mem = 1MB # min 64kB
+#maintenance_work_mem = 16MB # min 1MB
+#max_stack_depth = 2MB # min 100kB
+
+# - Kernel Resource Usage -
+
+#max_files_per_process = 1000 # min 25
+ # (change requires restart)
+#shared_preload_libraries = '' # (change requires restart)
+
+# - Cost-Based Vacuum Delay -
+
+#vacuum_cost_delay = 0ms # 0-100 milliseconds
+#vacuum_cost_page_hit = 1 # 0-10000 credits
+#vacuum_cost_page_miss = 10 # 0-10000 credits
+#vacuum_cost_page_dirty = 20 # 0-10000 credits
+#vacuum_cost_limit = 200 # 1-10000 credits
+
+# - Background Writer -
+
+#bgwriter_delay = 200ms # 10-10000ms between rounds
+#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round
+#bgwriter_lru_multiplier = 2.0 # 0-10.0 multipler on buffers scanned/round
+
+# - Asynchronous Behavior -
+
+#effective_io_concurrency = 1 # 1-1000. 0 disables prefetching
+
+
+#------------------------------------------------------------------------------
+# WRITE AHEAD LOG
+#------------------------------------------------------------------------------
+
+# - Settings -
+
+#wal_level = minimal # minimal, archive, or hot_standby
+ # (change requires restart)
+#fsync = on # turns forced synchronization on or off
+#synchronous_commit = on # synchronization level; on, off, or local
+#wal_sync_method = fsync # the default is the first option
+ # supported by the operating system:
+ # open_datasync
+ # fdatasync (default on Linux)
+ # fsync
+ # fsync_writethrough
+ # open_sync
+#full_page_writes = on # recover from partial page writes
+#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
+ # (change requires restart)
+#wal_writer_delay = 200ms # 1-10000 milliseconds
+
+#commit_delay = 0 # range 0-100000, in microseconds
+#commit_siblings = 5 # range 1-1000
+
+# - Checkpoints -
+
+#checkpoint_segments = 3 # in logfile segments, min 1, 16MB each
+#checkpoint_timeout = 5min # range 30s-1h
+#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
+#checkpoint_warning = 30s # 0 disables
+
+# - Archiving -
+
+#archive_mode = off # allows archiving to be done
+ # (change requires restart)
+#archive_command = '' # command to use to archive a logfile segment
+#archive_timeout = 0 # force a logfile segment switch after this
+ # number of seconds; 0 disables
+
+
+#------------------------------------------------------------------------------
+# REPLICATION
+#------------------------------------------------------------------------------
+
+# - Master Server -
+
+# These settings are ignored on a standby server
+
+#max_wal_senders = 0 # max number of walsender processes
+ # (change requires restart)
+#wal_sender_delay = 1s # walsender cycle time, 1-10000 milliseconds
+#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
+#replication_timeout = 60s # in milliseconds; 0 disables
+#synchronous_standby_names = '' # standby servers that provide sync rep
+ # comma-separated list of application_name
+ # from standby(s); '*' = all
+
+# - Standby Servers -
+
+# These settings are ignored on a master server
+
+#hot_standby = off # "on" allows queries during recovery
+ # (change requires restart)
+#max_standby_archive_delay = 30s # max delay before canceling queries
+ # when reading WAL from archive;
+ # -1 allows indefinite delay
+#max_standby_streaming_delay = 30s # max delay before canceling queries
+ # when reading streaming WAL;
+ # -1 allows indefinite delay
+#wal_receiver_status_interval = 10s # send replies at least this often
+ # 0 disables
+#hot_standby_feedback = off # send info from standby to prevent
+ # query conflicts
+
+
+#------------------------------------------------------------------------------
+# QUERY TUNING
+#------------------------------------------------------------------------------
+
+# - Planner Method Configuration -
+
+#enable_bitmapscan = on
+#enable_hashagg = on
+#enable_hashjoin = on
+#enable_indexscan = on
+#enable_material = on
+#enable_mergejoin = on
+#enable_nestloop = on
+#enable_seqscan = on
+#enable_sort = on
+#enable_tidscan = on
+
+# - Planner Cost Constants -
+
+#seq_page_cost = 1.0 # measured on an arbitrary scale
+#random_page_cost = 4.0 # same scale as above
+#cpu_tuple_cost = 0.01 # same scale as above
+#cpu_index_tuple_cost = 0.005 # same scale as above
+#cpu_operator_cost = 0.0025 # same scale as above
+#effective_cache_size = 128MB
+
+# - Genetic Query Optimizer -
+
+#geqo = on
+#geqo_threshold = 12
+#geqo_effort = 5 # range 1-10
+#geqo_pool_size = 0 # selects default based on effort
+#geqo_generations = 0 # selects default based on effort
+#geqo_selection_bias = 2.0 # range 1.5-2.0
+#geqo_seed = 0.0 # range 0.0-1.0
+
+# - Other Planner Options -
+
+#default_statistics_target = 100 # range 1-10000
+#constraint_exclusion = partition # on, off, or partition
+#cursor_tuple_fraction = 0.1 # range 0.0-1.0
+#from_collapse_limit = 8
+#join_collapse_limit = 8 # 1 disables collapsing of explicit
+ # JOIN clauses
+
+
+#------------------------------------------------------------------------------
+# ERROR REPORTING AND LOGGING
+#------------------------------------------------------------------------------
+
+# - Where to Log -
+
+#log_destination = 'stderr' # Valid values are combinations of
+ # stderr, csvlog, syslog, and eventlog,
+ # depending on platform. csvlog
+ # requires logging_collector to be on.
+
+# This is used when logging to stderr:
+#logging_collector = off # Enable capturing of stderr and csvlog
+ # into log files. Required to be on for
+ # csvlogs.
+ # (change requires restart)
+
+# These are only used if logging_collector is on:
+#log_directory = 'pg_log' # directory where log files are written,
+ # can be absolute or relative to PGDATA
+#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
+ # can include strftime() escapes
+#log_file_mode = 0600 # creation mode for log files,
+ # begin with 0 to use octal notation
+#log_truncate_on_rotation = off # If on, an existing log file with the
+ # same name as the new log file will be
+ # truncated rather than appended to.
+ # But such truncation only occurs on
+ # time-driven rotation, not on restarts
+ # or size-driven rotation. Default is
+ # off, meaning append to existing files
+ # in all cases.
+#log_rotation_age = 1d # Automatic rotation of logfiles will
+ # happen after that time. 0 disables.
+#log_rotation_size = 10MB # Automatic rotation of logfiles will
+ # happen after that much log output.
+ # 0 disables.
+
+# These are relevant when logging to syslog:
+#syslog_facility = 'LOCAL0'
+#syslog_ident = 'postgres'
+
+#silent_mode = off # Run server silently.
+ # DO NOT USE without syslog or
+ # logging_collector
+ # (change requires restart)
+
+
+# - When to Log -
+
+#client_min_messages = notice # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # log
+ # notice
+ # warning
+ # error
+
+#log_min_messages = warning # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic
+
+#log_min_error_statement = error # values in order of decreasing detail:
+ # debug5
+ # debug4
+ # debug3
+ # debug2
+ # debug1
+ # info
+ # notice
+ # warning
+ # error
+ # log
+ # fatal
+ # panic (effectively off)
+
+#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
+ # and their durations, > 0 logs only
+ # statements running at least this number
+ # of milliseconds
+
+
+# - What to Log -
+
+#debug_print_parse = off
+#debug_print_rewritten = off
+#debug_print_plan = off
+#debug_pretty_print = on
+#log_checkpoints = off
+#log_connections = off
+#log_disconnections = off
+#log_duration = off
+#log_error_verbosity = default # terse, default, or verbose messages
+#log_hostname = off
+log_line_prefix = '%t ' # special values:
+ # %a = application name
+ # %u = user name
+ # %d = database name
+ # %r = remote host and port
+ # %h = remote host
+ # %p = process ID
+ # %t = timestamp without milliseconds
+ # %m = timestamp with milliseconds
+ # %i = command tag
+ # %e = SQL state
+ # %c = session ID
+ # %l = session line number
+ # %s = session start timestamp
+ # %v = virtual transaction ID
+ # %x = transaction ID (0 if none)
+ # %q = stop here in non-session
+ # processes
+ # %% = '%'
+#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_statement = 'none' # none, ddl, mod, all
+#log_temp_files = -1 # log temporary files equal or larger
+ # than the specified size in kilobytes;
+ # -1 disables, 0 logs all temp files
+#log_timezone = '(defaults to server environment setting)'
+
+
+#------------------------------------------------------------------------------
+# RUNTIME STATISTICS
+#------------------------------------------------------------------------------
+
+# - Query/Index Statistics Collector -
+
+#track_activities = on
+#track_counts = on
+#track_functions = none # none, pl, all
+#track_activity_query_size = 1024 # (change requires restart)
+#update_process_title = on
+#stats_temp_directory = 'pg_stat_tmp'
+
+
+# - Statistics Monitoring -
+
+#log_parser_stats = off
+#log_planner_stats = off
+#log_executor_stats = off
+#log_statement_stats = off
+
+
+#------------------------------------------------------------------------------
+# AUTOVACUUM PARAMETERS
+#------------------------------------------------------------------------------
+
+#autovacuum = on # Enable autovacuum subprocess? 'on'
+ # requires track_counts to also be on.
+#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
+ # their durations, > 0 logs only
+ # actions running at least this number
+ # of milliseconds.
+#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
+ # (change requires restart)
+#autovacuum_naptime = 1min # time between autovacuum runs
+#autovacuum_vacuum_threshold = 50 # min number of row updates before
+ # vacuum
+#autovacuum_analyze_threshold = 50 # min number of row updates before
+ # analyze
+#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
+#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
+#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
+ # (change requires restart)
+#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
+ # autovacuum, in milliseconds;
+ # -1 means use vacuum_cost_delay
+#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
+ # autovacuum, -1 means use
+ # vacuum_cost_limit
+
+
+#------------------------------------------------------------------------------
+# CLIENT CONNECTION DEFAULTS
+#------------------------------------------------------------------------------
+
+# - Statement Behavior -
+
+#search_path = '"$user",public' # schema names
+#default_tablespace = '' # a tablespace name, '' uses the default
+#temp_tablespaces = '' # a list of tablespace names, '' uses
+ # only default tablespace
+#check_function_bodies = on
+#default_transaction_isolation = 'read committed'
+#default_transaction_read_only = off
+#default_transaction_deferrable = off
+#session_replication_role = 'origin'
+#statement_timeout = 0 # in milliseconds, 0 is disabled
+#vacuum_freeze_min_age = 50000000
+#vacuum_freeze_table_age = 150000000
+#bytea_output = 'hex' # hex, escape
+#xmlbinary = 'base64'
+#xmloption = 'content'
+
+# - Locale and Formatting -
+
+datestyle = 'iso, mdy'
+#intervalstyle = 'postgres'
+#timezone = '(defaults to server environment setting)'
+#timezone_abbreviations = 'Default' # Select the set of available time zone
+ # abbreviations. Currently, there are
+ # Default
+ # Australia
+ # India
+ # You can create your own file in
+ # share/timezonesets/.
+#extra_float_digits = 0 # min -15, max 3
+#client_encoding = sql_ascii # actually, defaults to database
+ # encoding
+
+# These settings are initialized by initdb, but they can be changed.
+lc_messages = '<%= @locale %>' # locale for system error message strings
+lc_monetary = '<%= @locale %>' # locale for monetary formatting
+lc_numeric = '<%= @locale %>' # locale for number formatting
+lc_time = '<%= @locale %>' # locale for time formatting
+
+# default configuration for text search
+default_text_search_config = 'pg_catalog.english'
+
+# - Other Defaults -
+
+#dynamic_library_path = '$libdir'
+#local_preload_libraries = ''
+
+
+#------------------------------------------------------------------------------
+# LOCK MANAGEMENT
+#------------------------------------------------------------------------------
+
+#deadlock_timeout = 1s
+#max_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+# Note: Each lock table slot uses ~270 bytes of shared memory, and there are
+# max_locks_per_transaction * (max_connections + max_prepared_transactions)
+# lock table slots.
+#max_pred_locks_per_transaction = 64 # min 10
+ # (change requires restart)
+
+#------------------------------------------------------------------------------
+# VERSION/PLATFORM COMPATIBILITY
+#------------------------------------------------------------------------------
+
+# - Previous PostgreSQL Versions -
+
+#array_nulls = on
+#backslash_quote = safe_encoding # on, off, or safe_encoding
+#default_with_oids = off
+#escape_string_warning = on
+#lo_compat_privileges = off
+#quote_all_identifiers = off
+#sql_inheritance = on
+#standard_conforming_strings = on
+#synchronize_seqscans = on
+
+# - Other Platforms and Clients -
+
+#transform_null_equals = off
+
+
+#------------------------------------------------------------------------------
+# ERROR HANDLING
+#------------------------------------------------------------------------------
+
+#exit_on_error = off # terminate session on any error?
+#restart_after_crash = on # reinitialize after backend crash?
+
+
+#------------------------------------------------------------------------------
+# CUSTOMIZED OPTIONS
+#------------------------------------------------------------------------------
+
+#custom_variable_classes = '' # list of custom variable class names
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..4310a20
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,35 @@
+# Created by http://www.gitignore.io
+
+
+### Rails ###
+*.rbc
+capybara-*.html
+.rspec
+/log
+/log/*.log
+/tmp
+/db/*.sqlite3
+/db/*.sqlite3-journal
+/public/system
+/coverage/
+/spec/tmp
+**.orig
+rerun.txt
+pickle-email-*.html
+
+# Ignore secret assets
+/lib/assets/*
+
+# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
+config/initializers/secret_token.rb
+config/secrets.yml
+
+## Environment normalisation:
+/.bundle
+/vendor/bundle
+
+# these should all be checked in to normalise the environment:
+# Gemfile.lock, .ruby-version, .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
\ No newline at end of file
diff --git a/Gemfile b/src/Gemfile
similarity index 100%
rename from Gemfile
rename to src/Gemfile
diff --git a/Gemfile.lock b/src/Gemfile.lock
similarity index 100%
rename from Gemfile.lock
rename to src/Gemfile.lock
diff --git a/Rakefile b/src/Rakefile
similarity index 100%
rename from Rakefile
rename to src/Rakefile
diff --git a/app/assets/images/.keep b/src/app/assets/images/.keep
similarity index 100%
rename from app/assets/images/.keep
rename to src/app/assets/images/.keep
diff --git a/app/assets/javascripts/application.js b/src/app/assets/javascripts/application.js
similarity index 100%
rename from app/assets/javascripts/application.js
rename to src/app/assets/javascripts/application.js
diff --git a/app/assets/javascripts/bikes.js b/src/app/assets/javascripts/bikes.js
similarity index 100%
rename from app/assets/javascripts/bikes.js
rename to src/app/assets/javascripts/bikes.js
diff --git a/app/assets/javascripts/datepicker.js b/src/app/assets/javascripts/datepicker.js
similarity index 100%
rename from app/assets/javascripts/datepicker.js
rename to src/app/assets/javascripts/datepicker.js
diff --git a/app/assets/javascripts/selectpicker.js b/src/app/assets/javascripts/selectpicker.js
similarity index 100%
rename from app/assets/javascripts/selectpicker.js
rename to src/app/assets/javascripts/selectpicker.js
diff --git a/app/assets/stylesheets/application.css b/src/app/assets/stylesheets/application.css
similarity index 100%
rename from app/assets/stylesheets/application.css
rename to src/app/assets/stylesheets/application.css
diff --git a/app/assets/stylesheets/bikes.css.scss b/src/app/assets/stylesheets/bikes.css.scss
similarity index 100%
rename from app/assets/stylesheets/bikes.css.scss
rename to src/app/assets/stylesheets/bikes.css.scss
diff --git a/app/assets/stylesheets/scaffolds.css.scss b/src/app/assets/stylesheets/scaffolds.css.scss
similarity index 100%
rename from app/assets/stylesheets/scaffolds.css.scss
rename to src/app/assets/stylesheets/scaffolds.css.scss
diff --git a/app/controllers/application_controller.rb b/src/app/controllers/application_controller.rb
similarity index 100%
rename from app/controllers/application_controller.rb
rename to src/app/controllers/application_controller.rb
diff --git a/app/controllers/bikes_controller.rb b/src/app/controllers/bikes_controller.rb
similarity index 100%
rename from app/controllers/bikes_controller.rb
rename to src/app/controllers/bikes_controller.rb
diff --git a/app/controllers/concerns/.keep b/src/app/controllers/concerns/.keep
similarity index 100%
rename from app/controllers/concerns/.keep
rename to src/app/controllers/concerns/.keep
diff --git a/app/controllers/static_pages_controller.rb b/src/app/controllers/static_pages_controller.rb
similarity index 100%
rename from app/controllers/static_pages_controller.rb
rename to src/app/controllers/static_pages_controller.rb
diff --git a/app/controllers/volunteers_controller.rb b/src/app/controllers/volunteers_controller.rb
similarity index 100%
rename from app/controllers/volunteers_controller.rb
rename to src/app/controllers/volunteers_controller.rb
diff --git a/app/helpers/application_helper.rb b/src/app/helpers/application_helper.rb
similarity index 100%
rename from app/helpers/application_helper.rb
rename to src/app/helpers/application_helper.rb
diff --git a/app/helpers/bikes_helper.rb b/src/app/helpers/bikes_helper.rb
similarity index 100%
rename from app/helpers/bikes_helper.rb
rename to src/app/helpers/bikes_helper.rb
diff --git a/app/mailers/.keep b/src/app/mailers/.keep
similarity index 100%
rename from app/mailers/.keep
rename to src/app/mailers/.keep
diff --git a/app/models/.keep b/src/app/models/.keep
similarity index 100%
rename from app/models/.keep
rename to src/app/models/.keep
diff --git a/app/models/bike.rb b/src/app/models/bike.rb
similarity index 100%
rename from app/models/bike.rb
rename to src/app/models/bike.rb
diff --git a/app/models/concerns/.keep b/src/app/models/concerns/.keep
similarity index 100%
rename from app/models/concerns/.keep
rename to src/app/models/concerns/.keep
diff --git a/app/models/user.rb b/src/app/models/user.rb
similarity index 100%
rename from app/models/user.rb
rename to src/app/models/user.rb
diff --git a/app/models/volunteer.rb b/src/app/models/volunteer.rb
similarity index 100%
rename from app/models/volunteer.rb
rename to src/app/models/volunteer.rb
diff --git a/app/views/bikes/_fields.html.haml b/src/app/views/bikes/_fields.html.haml
similarity index 100%
rename from app/views/bikes/_fields.html.haml
rename to src/app/views/bikes/_fields.html.haml
diff --git a/app/views/bikes/_form.html.haml b/src/app/views/bikes/_form.html.haml
similarity index 100%
rename from app/views/bikes/_form.html.haml
rename to src/app/views/bikes/_form.html.haml
diff --git a/app/views/bikes/edit.html.haml b/src/app/views/bikes/edit.html.haml
similarity index 100%
rename from app/views/bikes/edit.html.haml
rename to src/app/views/bikes/edit.html.haml
diff --git a/app/views/bikes/index.html.haml b/src/app/views/bikes/index.html.haml
similarity index 100%
rename from app/views/bikes/index.html.haml
rename to src/app/views/bikes/index.html.haml
diff --git a/app/views/bikes/new.html.haml b/src/app/views/bikes/new.html.haml
similarity index 100%
rename from app/views/bikes/new.html.haml
rename to src/app/views/bikes/new.html.haml
diff --git a/app/views/bikes/show.html.haml b/src/app/views/bikes/show.html.haml
similarity index 100%
rename from app/views/bikes/show.html.haml
rename to src/app/views/bikes/show.html.haml
diff --git a/app/views/devise/confirmations/new.html.erb b/src/app/views/devise/confirmations/new.html.erb
similarity index 100%
rename from app/views/devise/confirmations/new.html.erb
rename to src/app/views/devise/confirmations/new.html.erb
diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/src/app/views/devise/mailer/confirmation_instructions.html.erb
similarity index 100%
rename from app/views/devise/mailer/confirmation_instructions.html.erb
rename to src/app/views/devise/mailer/confirmation_instructions.html.erb
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/src/app/views/devise/mailer/reset_password_instructions.html.erb
similarity index 100%
rename from app/views/devise/mailer/reset_password_instructions.html.erb
rename to src/app/views/devise/mailer/reset_password_instructions.html.erb
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/src/app/views/devise/mailer/unlock_instructions.html.erb
similarity index 100%
rename from app/views/devise/mailer/unlock_instructions.html.erb
rename to src/app/views/devise/mailer/unlock_instructions.html.erb
diff --git a/app/views/devise/passwords/edit.html.erb b/src/app/views/devise/passwords/edit.html.erb
similarity index 100%
rename from app/views/devise/passwords/edit.html.erb
rename to src/app/views/devise/passwords/edit.html.erb
diff --git a/app/views/devise/passwords/new.html.erb b/src/app/views/devise/passwords/new.html.erb
similarity index 100%
rename from app/views/devise/passwords/new.html.erb
rename to src/app/views/devise/passwords/new.html.erb
diff --git a/app/views/devise/registrations/edit.html.erb b/src/app/views/devise/registrations/edit.html.erb
similarity index 100%
rename from app/views/devise/registrations/edit.html.erb
rename to src/app/views/devise/registrations/edit.html.erb
diff --git a/app/views/devise/registrations/new.html.haml b/src/app/views/devise/registrations/new.html.haml
similarity index 100%
rename from app/views/devise/registrations/new.html.haml
rename to src/app/views/devise/registrations/new.html.haml
diff --git a/app/views/devise/sessions/new.html.erb b/src/app/views/devise/sessions/new.html.erb
similarity index 100%
rename from app/views/devise/sessions/new.html.erb
rename to src/app/views/devise/sessions/new.html.erb
diff --git a/app/views/devise/shared/_links.erb b/src/app/views/devise/shared/_links.erb
similarity index 100%
rename from app/views/devise/shared/_links.erb
rename to src/app/views/devise/shared/_links.erb
diff --git a/app/views/devise/unlocks/new.html.erb b/src/app/views/devise/unlocks/new.html.erb
similarity index 100%
rename from app/views/devise/unlocks/new.html.erb
rename to src/app/views/devise/unlocks/new.html.erb
diff --git a/app/views/layouts/_head.html.erb b/src/app/views/layouts/_head.html.erb
similarity index 100%
rename from app/views/layouts/_head.html.erb
rename to src/app/views/layouts/_head.html.erb
diff --git a/app/views/layouts/_navbar.html.erb b/src/app/views/layouts/_navbar.html.erb
similarity index 100%
rename from app/views/layouts/_navbar.html.erb
rename to src/app/views/layouts/_navbar.html.erb
diff --git a/app/views/layouts/application.html.erb b/src/app/views/layouts/application.html.erb
similarity index 100%
rename from app/views/layouts/application.html.erb
rename to src/app/views/layouts/application.html.erb
diff --git a/app/views/static_pages/home.html.haml b/src/app/views/static_pages/home.html.haml
similarity index 100%
rename from app/views/static_pages/home.html.haml
rename to src/app/views/static_pages/home.html.haml
diff --git a/app/views/volunteers/_fields.html.haml b/src/app/views/volunteers/_fields.html.haml
similarity index 100%
rename from app/views/volunteers/_fields.html.haml
rename to src/app/views/volunteers/_fields.html.haml
diff --git a/app/views/volunteers/_form.html.haml b/src/app/views/volunteers/_form.html.haml
similarity index 100%
rename from app/views/volunteers/_form.html.haml
rename to src/app/views/volunteers/_form.html.haml
diff --git a/app/views/volunteers/edit.html.haml b/src/app/views/volunteers/edit.html.haml
similarity index 100%
rename from app/views/volunteers/edit.html.haml
rename to src/app/views/volunteers/edit.html.haml
diff --git a/app/views/volunteers/index.html.haml b/src/app/views/volunteers/index.html.haml
similarity index 100%
rename from app/views/volunteers/index.html.haml
rename to src/app/views/volunteers/index.html.haml
diff --git a/app/views/volunteers/new.html.haml b/src/app/views/volunteers/new.html.haml
similarity index 100%
rename from app/views/volunteers/new.html.haml
rename to src/app/views/volunteers/new.html.haml
diff --git a/app/views/volunteers/show.html.haml b/src/app/views/volunteers/show.html.haml
similarity index 100%
rename from app/views/volunteers/show.html.haml
rename to src/app/views/volunteers/show.html.haml
diff --git a/bin/bundle b/src/bin/bundle
similarity index 100%
rename from bin/bundle
rename to src/bin/bundle
diff --git a/bin/rails b/src/bin/rails
similarity index 100%
rename from bin/rails
rename to src/bin/rails
diff --git a/bin/rake b/src/bin/rake
similarity index 100%
rename from bin/rake
rename to src/bin/rake
diff --git a/config.ru b/src/config.ru
similarity index 100%
rename from config.ru
rename to src/config.ru
diff --git a/config/application.rb b/src/config/application.rb
similarity index 100%
rename from config/application.rb
rename to src/config/application.rb
diff --git a/config/boot.rb b/src/config/boot.rb
similarity index 100%
rename from config/boot.rb
rename to src/config/boot.rb
diff --git a/config/database.yml b/src/config/database.yml
similarity index 100%
rename from config/database.yml
rename to src/config/database.yml
diff --git a/config/environment.rb b/src/config/environment.rb
similarity index 100%
rename from config/environment.rb
rename to src/config/environment.rb
diff --git a/config/environments/development.rb b/src/config/environments/development.rb
similarity index 100%
rename from config/environments/development.rb
rename to src/config/environments/development.rb
diff --git a/config/environments/production.rb b/src/config/environments/production.rb
similarity index 100%
rename from config/environments/production.rb
rename to src/config/environments/production.rb
diff --git a/config/environments/test.rb b/src/config/environments/test.rb
similarity index 100%
rename from config/environments/test.rb
rename to src/config/environments/test.rb
diff --git a/config/initializers/backtrace_silencers.rb b/src/config/initializers/backtrace_silencers.rb
similarity index 100%
rename from config/initializers/backtrace_silencers.rb
rename to src/config/initializers/backtrace_silencers.rb
diff --git a/config/initializers/devise.rb b/src/config/initializers/devise.rb
similarity index 100%
rename from config/initializers/devise.rb
rename to src/config/initializers/devise.rb
diff --git a/config/initializers/filter_parameter_logging.rb b/src/config/initializers/filter_parameter_logging.rb
similarity index 100%
rename from config/initializers/filter_parameter_logging.rb
rename to src/config/initializers/filter_parameter_logging.rb
diff --git a/config/initializers/inflections.rb b/src/config/initializers/inflections.rb
similarity index 100%
rename from config/initializers/inflections.rb
rename to src/config/initializers/inflections.rb
diff --git a/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb
similarity index 100%
rename from config/initializers/mime_types.rb
rename to src/config/initializers/mime_types.rb
diff --git a/config/initializers/session_store.rb b/src/config/initializers/session_store.rb
similarity index 100%
rename from config/initializers/session_store.rb
rename to src/config/initializers/session_store.rb
diff --git a/config/initializers/wrap_parameters.rb b/src/config/initializers/wrap_parameters.rb
similarity index 100%
rename from config/initializers/wrap_parameters.rb
rename to src/config/initializers/wrap_parameters.rb
diff --git a/config/locales/devise.en.yml b/src/config/locales/devise.en.yml
similarity index 100%
rename from config/locales/devise.en.yml
rename to src/config/locales/devise.en.yml
diff --git a/config/locales/en.yml b/src/config/locales/en.yml
similarity index 100%
rename from config/locales/en.yml
rename to src/config/locales/en.yml
diff --git a/config/routes.rb b/src/config/routes.rb
similarity index 100%
rename from config/routes.rb
rename to src/config/routes.rb
diff --git a/db/migrate/20140118202104_create_bike_table.rb b/src/db/migrate/20140118202104_create_bike_table.rb
similarity index 100%
rename from db/migrate/20140118202104_create_bike_table.rb
rename to src/db/migrate/20140118202104_create_bike_table.rb
diff --git a/db/migrate/20140118205930_create_bikes.rb b/src/db/migrate/20140118205930_create_bikes.rb
similarity index 100%
rename from db/migrate/20140118205930_create_bikes.rb
rename to src/db/migrate/20140118205930_create_bikes.rb
diff --git a/db/migrate/20140214152631_change_type_to_bicycle_type.rb b/src/db/migrate/20140214152631_change_type_to_bicycle_type.rb
similarity index 100%
rename from db/migrate/20140214152631_change_type_to_bicycle_type.rb
rename to src/db/migrate/20140214152631_change_type_to_bicycle_type.rb
diff --git a/db/migrate/20140312232550_add_fields_to_bike.rb b/src/db/migrate/20140312232550_add_fields_to_bike.rb
similarity index 100%
rename from db/migrate/20140312232550_add_fields_to_bike.rb
rename to src/db/migrate/20140312232550_add_fields_to_bike.rb
diff --git a/db/migrate/20140313000619_create_volunteers.rb b/src/db/migrate/20140313000619_create_volunteers.rb
similarity index 100%
rename from db/migrate/20140313000619_create_volunteers.rb
rename to src/db/migrate/20140313000619_create_volunteers.rb
diff --git a/db/migrate/20140814020954_devise_create_users.rb b/src/db/migrate/20140814020954_devise_create_users.rb
similarity index 100%
rename from db/migrate/20140814020954_devise_create_users.rb
rename to src/db/migrate/20140814020954_devise_create_users.rb
diff --git a/db/schema.rb b/src/db/schema.rb
similarity index 100%
rename from db/schema.rb
rename to src/db/schema.rb
diff --git a/db/seeds.rb b/src/db/seeds.rb
similarity index 100%
rename from db/seeds.rb
rename to src/db/seeds.rb
diff --git a/lib/tasks/.keep b/src/lib/tasks/.keep
similarity index 100%
rename from lib/tasks/.keep
rename to src/lib/tasks/.keep
diff --git a/lib/tasks/bikeloader.rake b/src/lib/tasks/bikeloader.rake
similarity index 100%
rename from lib/tasks/bikeloader.rake
rename to src/lib/tasks/bikeloader.rake
diff --git a/public/404.html b/src/public/404.html
similarity index 100%
rename from public/404.html
rename to src/public/404.html
diff --git a/public/422.html b/src/public/422.html
similarity index 100%
rename from public/422.html
rename to src/public/422.html
diff --git a/public/500.html b/src/public/500.html
similarity index 100%
rename from public/500.html
rename to src/public/500.html
diff --git a/public/favicon.ico b/src/public/favicon.ico
similarity index 100%
rename from public/favicon.ico
rename to src/public/favicon.ico
diff --git a/public/robots.txt b/src/public/robots.txt
similarity index 100%
rename from public/robots.txt
rename to src/public/robots.txt
diff --git a/spec/controllers/bikes_controller_spec.rb b/src/spec/controllers/bikes_controller_spec.rb
similarity index 100%
rename from spec/controllers/bikes_controller_spec.rb
rename to src/spec/controllers/bikes_controller_spec.rb
diff --git a/spec/factories/bikes.rb b/src/spec/factories/bikes.rb
similarity index 100%
rename from spec/factories/bikes.rb
rename to src/spec/factories/bikes.rb
diff --git a/spec/factories/users.rb b/src/spec/factories/users.rb
similarity index 100%
rename from spec/factories/users.rb
rename to src/spec/factories/users.rb
diff --git a/spec/helpers/bikes_helper_spec.rb b/src/spec/helpers/bikes_helper_spec.rb
similarity index 100%
rename from spec/helpers/bikes_helper_spec.rb
rename to src/spec/helpers/bikes_helper_spec.rb
diff --git a/spec/models/bike_spec.rb b/src/spec/models/bike_spec.rb
similarity index 100%
rename from spec/models/bike_spec.rb
rename to src/spec/models/bike_spec.rb
diff --git a/spec/models/user_spec.rb b/src/spec/models/user_spec.rb
similarity index 100%
rename from spec/models/user_spec.rb
rename to src/spec/models/user_spec.rb
diff --git a/spec/requests/bikes_spec.rb b/src/spec/requests/bikes_spec.rb
similarity index 100%
rename from spec/requests/bikes_spec.rb
rename to src/spec/requests/bikes_spec.rb
diff --git a/spec/routing/bikes_routing_spec.rb b/src/spec/routing/bikes_routing_spec.rb
similarity index 100%
rename from spec/routing/bikes_routing_spec.rb
rename to src/spec/routing/bikes_routing_spec.rb
diff --git a/spec/spec_helper.rb b/src/spec/spec_helper.rb
similarity index 100%
rename from spec/spec_helper.rb
rename to src/spec/spec_helper.rb
diff --git a/spec/views/bikes/edit.html.erb_spec.rb b/src/spec/views/bikes/edit.html.erb_spec.rb
similarity index 100%
rename from spec/views/bikes/edit.html.erb_spec.rb
rename to src/spec/views/bikes/edit.html.erb_spec.rb
diff --git a/spec/views/bikes/index.html.erb_spec.rb b/src/spec/views/bikes/index.html.erb_spec.rb
similarity index 100%
rename from spec/views/bikes/index.html.erb_spec.rb
rename to src/spec/views/bikes/index.html.erb_spec.rb
diff --git a/spec/views/bikes/new.html.erb_spec.rb b/src/spec/views/bikes/new.html.erb_spec.rb
similarity index 100%
rename from spec/views/bikes/new.html.erb_spec.rb
rename to src/spec/views/bikes/new.html.erb_spec.rb
diff --git a/spec/views/bikes/show.html.erb_spec.rb b/src/spec/views/bikes/show.html.erb_spec.rb
similarity index 100%
rename from spec/views/bikes/show.html.erb_spec.rb
rename to src/spec/views/bikes/show.html.erb_spec.rb
diff --git a/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.eot b/src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.eot
similarity index 100%
rename from vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.eot
rename to src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.eot
diff --git a/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.svg b/src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.svg
similarity index 100%
rename from vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.svg
rename to src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.svg
diff --git a/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf b/src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf
similarity index 100%
rename from vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf
rename to src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf
diff --git a/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff b/src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff
similarity index 100%
rename from vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff
rename to src/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff
diff --git a/vendor/assets/javascripts/bootstrap-datepicker.js b/src/vendor/assets/javascripts/bootstrap-datepicker.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap-datepicker.js
rename to src/vendor/assets/javascripts/bootstrap-datepicker.js
diff --git a/vendor/assets/javascripts/bootstrap-select.js b/src/vendor/assets/javascripts/bootstrap-select.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap-select.js
rename to src/vendor/assets/javascripts/bootstrap-select.js
diff --git a/vendor/assets/javascripts/bootstrap.js b/src/vendor/assets/javascripts/bootstrap.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap.js
rename to src/vendor/assets/javascripts/bootstrap.js
diff --git a/vendor/assets/javascripts/bootstrap.js alias b/src/vendor/assets/javascripts/bootstrap.js alias
similarity index 100%
rename from vendor/assets/javascripts/bootstrap.js alias
rename to src/vendor/assets/javascripts/bootstrap.js alias
diff --git a/vendor/assets/javascripts/bootstrap/affix.js b/src/vendor/assets/javascripts/bootstrap/affix.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/affix.js
rename to src/vendor/assets/javascripts/bootstrap/affix.js
diff --git a/vendor/assets/javascripts/bootstrap/alert.js b/src/vendor/assets/javascripts/bootstrap/alert.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/alert.js
rename to src/vendor/assets/javascripts/bootstrap/alert.js
diff --git a/vendor/assets/javascripts/bootstrap/button.js b/src/vendor/assets/javascripts/bootstrap/button.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/button.js
rename to src/vendor/assets/javascripts/bootstrap/button.js
diff --git a/vendor/assets/javascripts/bootstrap/carousel.js b/src/vendor/assets/javascripts/bootstrap/carousel.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/carousel.js
rename to src/vendor/assets/javascripts/bootstrap/carousel.js
diff --git a/vendor/assets/javascripts/bootstrap/collapse.js b/src/vendor/assets/javascripts/bootstrap/collapse.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/collapse.js
rename to src/vendor/assets/javascripts/bootstrap/collapse.js
diff --git a/vendor/assets/javascripts/bootstrap/dropdown.js b/src/vendor/assets/javascripts/bootstrap/dropdown.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/dropdown.js
rename to src/vendor/assets/javascripts/bootstrap/dropdown.js
diff --git a/vendor/assets/javascripts/bootstrap/modal.js b/src/vendor/assets/javascripts/bootstrap/modal.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/modal.js
rename to src/vendor/assets/javascripts/bootstrap/modal.js
diff --git a/vendor/assets/javascripts/bootstrap/popover.js b/src/vendor/assets/javascripts/bootstrap/popover.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/popover.js
rename to src/vendor/assets/javascripts/bootstrap/popover.js
diff --git a/vendor/assets/javascripts/bootstrap/scrollspy.js b/src/vendor/assets/javascripts/bootstrap/scrollspy.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/scrollspy.js
rename to src/vendor/assets/javascripts/bootstrap/scrollspy.js
diff --git a/vendor/assets/javascripts/bootstrap/tab.js b/src/vendor/assets/javascripts/bootstrap/tab.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/tab.js
rename to src/vendor/assets/javascripts/bootstrap/tab.js
diff --git a/vendor/assets/javascripts/bootstrap/tooltip.js b/src/vendor/assets/javascripts/bootstrap/tooltip.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/tooltip.js
rename to src/vendor/assets/javascripts/bootstrap/tooltip.js
diff --git a/vendor/assets/javascripts/bootstrap/transition.js b/src/vendor/assets/javascripts/bootstrap/transition.js
similarity index 100%
rename from vendor/assets/javascripts/bootstrap/transition.js
rename to src/vendor/assets/javascripts/bootstrap/transition.js
diff --git a/vendor/assets/stylesheets/bootstrap-select.css b/src/vendor/assets/stylesheets/bootstrap-select.css
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap-select.css
rename to src/vendor/assets/stylesheets/bootstrap-select.css
diff --git a/vendor/assets/stylesheets/bootstrap.scss b/src/vendor/assets/stylesheets/bootstrap.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap.scss
rename to src/vendor/assets/stylesheets/bootstrap.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_alerts.scss b/src/vendor/assets/stylesheets/bootstrap/_alerts.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_alerts.scss
rename to src/vendor/assets/stylesheets/bootstrap/_alerts.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_badges.scss b/src/vendor/assets/stylesheets/bootstrap/_badges.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_badges.scss
rename to src/vendor/assets/stylesheets/bootstrap/_badges.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss b/src/vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss
rename to src/vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_button-groups.scss b/src/vendor/assets/stylesheets/bootstrap/_button-groups.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_button-groups.scss
rename to src/vendor/assets/stylesheets/bootstrap/_button-groups.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_buttons.scss b/src/vendor/assets/stylesheets/bootstrap/_buttons.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_buttons.scss
rename to src/vendor/assets/stylesheets/bootstrap/_buttons.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_carousel.scss b/src/vendor/assets/stylesheets/bootstrap/_carousel.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_carousel.scss
rename to src/vendor/assets/stylesheets/bootstrap/_carousel.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_close.scss b/src/vendor/assets/stylesheets/bootstrap/_close.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_close.scss
rename to src/vendor/assets/stylesheets/bootstrap/_close.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_code.scss b/src/vendor/assets/stylesheets/bootstrap/_code.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_code.scss
rename to src/vendor/assets/stylesheets/bootstrap/_code.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_component-animations.scss b/src/vendor/assets/stylesheets/bootstrap/_component-animations.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_component-animations.scss
rename to src/vendor/assets/stylesheets/bootstrap/_component-animations.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_dropdowns.scss b/src/vendor/assets/stylesheets/bootstrap/_dropdowns.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_dropdowns.scss
rename to src/vendor/assets/stylesheets/bootstrap/_dropdowns.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_forms.scss b/src/vendor/assets/stylesheets/bootstrap/_forms.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_forms.scss
rename to src/vendor/assets/stylesheets/bootstrap/_forms.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_glyphicons.scss b/src/vendor/assets/stylesheets/bootstrap/_glyphicons.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_glyphicons.scss
rename to src/vendor/assets/stylesheets/bootstrap/_glyphicons.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_grid.scss b/src/vendor/assets/stylesheets/bootstrap/_grid.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_grid.scss
rename to src/vendor/assets/stylesheets/bootstrap/_grid.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_input-groups.scss b/src/vendor/assets/stylesheets/bootstrap/_input-groups.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_input-groups.scss
rename to src/vendor/assets/stylesheets/bootstrap/_input-groups.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_jumbotron.scss b/src/vendor/assets/stylesheets/bootstrap/_jumbotron.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_jumbotron.scss
rename to src/vendor/assets/stylesheets/bootstrap/_jumbotron.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_labels.scss b/src/vendor/assets/stylesheets/bootstrap/_labels.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_labels.scss
rename to src/vendor/assets/stylesheets/bootstrap/_labels.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_list-group.scss b/src/vendor/assets/stylesheets/bootstrap/_list-group.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_list-group.scss
rename to src/vendor/assets/stylesheets/bootstrap/_list-group.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_media.scss b/src/vendor/assets/stylesheets/bootstrap/_media.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_media.scss
rename to src/vendor/assets/stylesheets/bootstrap/_media.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_mixins.scss b/src/vendor/assets/stylesheets/bootstrap/_mixins.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_mixins.scss
rename to src/vendor/assets/stylesheets/bootstrap/_mixins.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_modals.scss b/src/vendor/assets/stylesheets/bootstrap/_modals.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_modals.scss
rename to src/vendor/assets/stylesheets/bootstrap/_modals.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_navbar.scss b/src/vendor/assets/stylesheets/bootstrap/_navbar.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_navbar.scss
rename to src/vendor/assets/stylesheets/bootstrap/_navbar.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_navs.scss b/src/vendor/assets/stylesheets/bootstrap/_navs.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_navs.scss
rename to src/vendor/assets/stylesheets/bootstrap/_navs.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_normalize.scss b/src/vendor/assets/stylesheets/bootstrap/_normalize.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_normalize.scss
rename to src/vendor/assets/stylesheets/bootstrap/_normalize.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_pager.scss b/src/vendor/assets/stylesheets/bootstrap/_pager.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_pager.scss
rename to src/vendor/assets/stylesheets/bootstrap/_pager.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_pagination.scss b/src/vendor/assets/stylesheets/bootstrap/_pagination.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_pagination.scss
rename to src/vendor/assets/stylesheets/bootstrap/_pagination.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_panels.scss b/src/vendor/assets/stylesheets/bootstrap/_panels.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_panels.scss
rename to src/vendor/assets/stylesheets/bootstrap/_panels.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_popovers.scss b/src/vendor/assets/stylesheets/bootstrap/_popovers.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_popovers.scss
rename to src/vendor/assets/stylesheets/bootstrap/_popovers.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_print.scss b/src/vendor/assets/stylesheets/bootstrap/_print.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_print.scss
rename to src/vendor/assets/stylesheets/bootstrap/_print.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_progress-bars.scss b/src/vendor/assets/stylesheets/bootstrap/_progress-bars.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_progress-bars.scss
rename to src/vendor/assets/stylesheets/bootstrap/_progress-bars.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_responsive-utilities.scss b/src/vendor/assets/stylesheets/bootstrap/_responsive-utilities.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_responsive-utilities.scss
rename to src/vendor/assets/stylesheets/bootstrap/_responsive-utilities.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_scaffolding.scss b/src/vendor/assets/stylesheets/bootstrap/_scaffolding.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_scaffolding.scss
rename to src/vendor/assets/stylesheets/bootstrap/_scaffolding.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_tables.scss b/src/vendor/assets/stylesheets/bootstrap/_tables.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_tables.scss
rename to src/vendor/assets/stylesheets/bootstrap/_tables.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_theme.scss b/src/vendor/assets/stylesheets/bootstrap/_theme.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_theme.scss
rename to src/vendor/assets/stylesheets/bootstrap/_theme.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_thumbnails.scss b/src/vendor/assets/stylesheets/bootstrap/_thumbnails.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_thumbnails.scss
rename to src/vendor/assets/stylesheets/bootstrap/_thumbnails.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_tooltip.scss b/src/vendor/assets/stylesheets/bootstrap/_tooltip.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_tooltip.scss
rename to src/vendor/assets/stylesheets/bootstrap/_tooltip.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_type.scss b/src/vendor/assets/stylesheets/bootstrap/_type.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_type.scss
rename to src/vendor/assets/stylesheets/bootstrap/_type.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_utilities.scss b/src/vendor/assets/stylesheets/bootstrap/_utilities.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_utilities.scss
rename to src/vendor/assets/stylesheets/bootstrap/_utilities.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_variables.scss b/src/vendor/assets/stylesheets/bootstrap/_variables.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_variables.scss
rename to src/vendor/assets/stylesheets/bootstrap/_variables.scss
diff --git a/vendor/assets/stylesheets/bootstrap/_wells.scss b/src/vendor/assets/stylesheets/bootstrap/_wells.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/_wells.scss
rename to src/vendor/assets/stylesheets/bootstrap/_wells.scss
diff --git a/vendor/assets/stylesheets/bootstrap/bootstrap.scss b/src/vendor/assets/stylesheets/bootstrap/bootstrap.scss
similarity index 100%
rename from vendor/assets/stylesheets/bootstrap/bootstrap.scss
rename to src/vendor/assets/stylesheets/bootstrap/bootstrap.scss
diff --git a/vendor/assets/stylesheets/datepicker.css b/src/vendor/assets/stylesheets/datepicker.css
similarity index 100%
rename from vendor/assets/stylesheets/datepicker.css
rename to src/vendor/assets/stylesheets/datepicker.css
diff --git a/vendor/.DS_Store b/vendor/.DS_Store
deleted file mode 100644
index c328838..0000000
Binary files a/vendor/.DS_Store and /dev/null differ
diff --git a/vendor/assets/.DS_Store b/vendor/assets/.DS_Store
deleted file mode 100644
index ee27f37..0000000
Binary files a/vendor/assets/.DS_Store and /dev/null differ
diff --git a/vendor/assets/javascripts/.DS_Store b/vendor/assets/javascripts/.DS_Store
deleted file mode 100644
index 5008ddf..0000000
Binary files a/vendor/assets/javascripts/.DS_Store and /dev/null differ
diff --git a/vendor/assets/stylesheets/.DS_Store b/vendor/assets/stylesheets/.DS_Store
deleted file mode 100644
index c96c04d..0000000
Binary files a/vendor/assets/stylesheets/.DS_Store and /dev/null differ