diff --git a/manifests/backup/mysqlbackup.pp b/manifests/backup/mysqlbackup.pp index 549279b..b671396 100644 --- a/manifests/backup/mysqlbackup.pp +++ b/manifests/backup/mysqlbackup.pp @@ -1,122 +1,124 @@ # @summary # Manage the mysqlbackup client. # # @api private # class mysql::backup::mysqlbackup ( $backupuser = '', $backuppassword = '', $maxallowedpacket = '1M', $backupdir = '', $backupdirmode = '0700', $backupdirowner = 'root', $backupdirgroup = $mysql::params::root_group, $backupcompress = true, $backuprotate = 30, $backupmethod = '', $backup_success_file_path = undef, $ignore_events = true, $delete_before_dump = false, $backupdatabases = [], $file_per_database = false, $include_triggers = true, $include_routines = false, $ensure = 'present', $time = ['23', '5'], $prescript = false, $postscript = false, $execpath = '/usr/bin:/usr/sbin:/bin:/sbin', $optional_args = [], $incremental_backups = false, $install_cron = true, + $compression_command = undef, + $compression_extension = undef, ) inherits mysql::params { mysql_user { "${backupuser}@localhost": ensure => $ensure, password_hash => mysql::password($backuppassword), require => Class['mysql::server::root_password'], } package { 'meb': ensure => $ensure, } # http://dev.mysql.com/doc/mysql-enterprise-backup/3.11/en/mysqlbackup.privileges.html mysql_grant { "${backupuser}@localhost/*.*": ensure => $ensure, user => "${backupuser}@localhost", table => '*.*', privileges => ['RELOAD', 'SUPER', 'REPLICATION CLIENT'], require => Mysql_user["${backupuser}@localhost"], } mysql_grant { "${backupuser}@localhost/mysql.backup_progress": ensure => $ensure, user => "${backupuser}@localhost", table => 'mysql.backup_progress', privileges => ['CREATE', 'INSERT', 'DROP', 'UPDATE'], require => Mysql_user["${backupuser}@localhost"], } mysql_grant { "${backupuser}@localhost/mysql.backup_history": ensure => $ensure, user => "${backupuser}@localhost", table => 'mysql.backup_history', privileges => ['CREATE', 'INSERT', 'SELECT', 'DROP', 'UPDATE'], require => Mysql_user["${backupuser}@localhost"], } if $install_cron { if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '5' { ensure_packages('crontabs') } elsif $::osfamily == 'RedHat' { ensure_packages('cronie') } elsif $::osfamily != 'FreeBSD' { ensure_packages('cron') } } cron { 'mysqlbackup-weekly': ensure => $ensure, command => 'mysqlbackup backup', user => 'root', hour => $time[0], minute => $time[1], weekday => '0', require => Package['meb'], } cron { 'mysqlbackup-daily': ensure => $ensure, command => 'mysqlbackup --incremental backup', user => 'root', hour => $time[0], minute => $time[1], weekday => '1-6', require => Package['meb'], } $default_options = { 'mysqlbackup' => { 'backup-dir' => $backupdir, 'with-timestamp' => true, 'incremental_base' => 'history:last_backup', 'incremental_backup_dir' => $backupdir, 'user' => $backupuser, 'password' => $backuppassword, }, } $options = mysql::normalise_and_deepmerge($default_options, $mysql::server::override_options) file { 'mysqlbackup-config-file': path => '/etc/mysql/conf.d/meb.cnf', content => template('mysql/meb.cnf.erb'), mode => '0600', } file { $backupdir: ensure => 'directory', mode => $backupdirmode, owner => $backupdirowner, group => $backupdirgroup, } } diff --git a/manifests/backup/mysqldump.pp b/manifests/backup/mysqldump.pp index 5c57a50..29c68d3 100644 --- a/manifests/backup/mysqldump.pp +++ b/manifests/backup/mysqldump.pp @@ -1,108 +1,110 @@ # @summary # "Provider" for mysqldump # @api private # class mysql::backup::mysqldump ( $backupuser = '', $backuppassword = '', $backupdir = '', $maxallowedpacket = '1M', $backupdirmode = '0700', $backupdirowner = 'root', $backupdirgroup = $mysql::params::root_group, $backupcompress = true, $backuprotate = 30, $backupmethod = 'mysqldump', $backup_success_file_path = undef, $ignore_events = true, $delete_before_dump = false, $backupdatabases = [], $file_per_database = false, $include_triggers = false, $include_routines = false, $ensure = 'present', $time = ['23', '5'], $prescript = false, $postscript = false, $execpath = '/usr/bin:/usr/sbin:/bin:/sbin', $optional_args = [], $mysqlbackupdir_ensure = 'directory', $mysqlbackupdir_target = undef, $incremental_backups = false, $install_cron = true, + $compression_command = 'bzcat -zc', + $compression_extension = '.bz2' ) inherits mysql::params { unless $::osfamily == 'FreeBSD' { - if $backupcompress { + if $backupcompress and $compression_command == 'bzcat -zc' { ensure_packages(['bzip2']) Package['bzip2'] -> File['mysqlbackup.sh'] } } mysql_user { "${backupuser}@localhost": ensure => $ensure, password_hash => mysql::password($backuppassword), require => Class['mysql::server::root_password'], } if $include_triggers { $privs = ['SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS', 'TRIGGER'] } else { $privs = ['SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS'] } mysql_grant { "${backupuser}@localhost/*.*": ensure => $ensure, user => "${backupuser}@localhost", table => '*.*', privileges => $privs, require => Mysql_user["${backupuser}@localhost"], } if $install_cron { if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '5' { ensure_packages('crontabs') } elsif $::osfamily == 'RedHat' { ensure_packages('cronie') } elsif $::osfamily != 'FreeBSD' { ensure_packages('cron') } } cron { 'mysql-backup': ensure => $ensure, command => '/usr/local/sbin/mysqlbackup.sh', user => 'root', hour => $time[0], minute => $time[1], monthday => $time[2], month => $time[3], weekday => $time[4], require => File['mysqlbackup.sh'], } file { 'mysqlbackup.sh': ensure => $ensure, path => '/usr/local/sbin/mysqlbackup.sh', mode => '0700', owner => 'root', group => $mysql::params::root_group, content => template('mysql/mysqlbackup.sh.erb'), } if $mysqlbackupdir_target { file { $backupdir: ensure => $mysqlbackupdir_ensure, target => $mysqlbackupdir_target, mode => $backupdirmode, owner => $backupdirowner, group => $backupdirgroup, } } else { file { $backupdir: ensure => $mysqlbackupdir_ensure, mode => $backupdirmode, owner => $backupdirowner, group => $backupdirgroup, } } } diff --git a/manifests/backup/xtrabackup.pp b/manifests/backup/xtrabackup.pp index 82c3d42..8d705a8 100644 --- a/manifests/backup/xtrabackup.pp +++ b/manifests/backup/xtrabackup.pp @@ -1,130 +1,132 @@ # @summary # "Provider" for Percona XtraBackup/MariaBackup # @api private # class mysql::backup::xtrabackup ( $xtrabackup_package_name = $mysql::params::xtrabackup_package_name, $backupuser = undef, $backuppassword = undef, $backupdir = '', $maxallowedpacket = '1M', $backupmethod = 'xtrabackup', $backupdirmode = '0700', $backupdirowner = 'root', $backupdirgroup = $mysql::params::root_group, $backupcompress = true, $backuprotate = 30, $backupscript_template = 'mysql/xtrabackup.sh.erb', $backup_success_file_path = undef, $ignore_events = true, $delete_before_dump = false, $backupdatabases = [], $file_per_database = false, $include_triggers = true, $include_routines = false, $ensure = 'present', $time = ['23', '5'], $prescript = false, $postscript = false, $execpath = '/usr/bin:/usr/sbin:/bin:/sbin', $optional_args = [], $additional_cron_args = '--backup', $incremental_backups = true, $install_cron = true, + $compression_command = undef, + $compression_extension = undef, ) inherits mysql::params { ensure_packages($xtrabackup_package_name) if $backupuser and $backuppassword { mysql_user { "${backupuser}@localhost": ensure => $ensure, password_hash => mysql::password($backuppassword), require => Class['mysql::server::root_password'], } mysql_grant { "${backupuser}@localhost/*.*": ensure => $ensure, user => "${backupuser}@localhost", table => '*.*', privileges => ['RELOAD', 'PROCESS', 'LOCK TABLES', 'REPLICATION CLIENT'], require => Mysql_user["${backupuser}@localhost"], } } if $install_cron { if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '5' { ensure_packages('crontabs') } elsif $::osfamily == 'RedHat' { ensure_packages('cronie') } elsif $::osfamily != 'FreeBSD' { ensure_packages('cron') } } if $incremental_backups { # Warn if old backups are removed too soon. Incremental backups will fail # if the full backup is no longer available. if ($backuprotate.convert_to(Integer) < 7) { warning(translate('The value for `backuprotate` is too low, it must be set to at least 7 days when using incremental backups.')) } # The --target-dir uses a more predictable value for the full backup so # that it can easily be calculated and used in incremental backup jobs. # Besides that it allows to have multiple full backups. cron { 'xtrabackup-weekly': ensure => $ensure, command => "/usr/local/sbin/xtrabackup.sh --target-dir=${backupdir}/$(date +\\%F)_full ${additional_cron_args}", user => 'root', hour => $time[0], minute => $time[1], weekday => '0', require => Package[$xtrabackup_package_name], } } # Wether to use GNU or BSD date format. case $::osfamily { 'FreeBSD','OpenBSD': { $dateformat = '$(date -v-sun +\\%F)_full' } default: { $dateformat = '$(date -d "last sunday" +\\%F)_full' } } $daily_cron_data = ($incremental_backups) ? { true => { 'directories' => "--incremental-basedir=${backupdir}/${dateformat} --target-dir=${backupdir}/$(date +\\%F_\\%H-\\%M-\\%S)", 'weekday' => '1-6', }, false => { 'directories' => "--target-dir=${backupdir}/$(date +\\%F_\\%H-\\%M-\\%S)", 'weekday' => '*', }, } cron { 'xtrabackup-daily': ensure => $ensure, command => "/usr/local/sbin/xtrabackup.sh ${daily_cron_data['directories']} ${additional_cron_args}", user => 'root', hour => $time[0], minute => $time[1], weekday => $daily_cron_data['weekday'], require => Package[$xtrabackup_package_name], } file { $backupdir: ensure => 'directory', mode => $backupdirmode, owner => $backupdirowner, group => $backupdirgroup, } file { 'xtrabackup.sh': ensure => $ensure, path => '/usr/local/sbin/xtrabackup.sh', mode => '0700', owner => 'root', group => $mysql::params::root_group, content => template($backupscript_template), } } diff --git a/manifests/server/backup.pp b/manifests/server/backup.pp index f89063a..0088590 100644 --- a/manifests/server/backup.pp +++ b/manifests/server/backup.pp @@ -1,132 +1,141 @@ # @summary # Create and manage a MySQL backup. # # @example Create a basic MySQL backup: # class { 'mysql::server': # root_password => 'password' # } # class { 'mysql::server::backup': # backupuser => 'myuser', # backuppassword => 'mypassword', # backupdir => '/tmp/backups', # } # class { 'mysql::server::backup': # backupmethod => 'mariabackup', # provider => 'xtrabackup', # backupdir => '/tmp/backups', # } # # @param backupuser # MySQL user to create with backup administrator privileges. # @param backuppassword # Password to create for `backupuser`. # @param backupdir # Directory to store backup. # @param backupdirmode # Permissions applied to the backup directory. This parameter is passed directly to the file resource. # @param backupdirowner # Owner for the backup directory. This parameter is passed directly to the file resource. # @param backupdirgroup # Group owner for the backup directory. This parameter is passed directly to the file resource. # @param backupcompress # Whether or not to compress the backup (when using the mysqldump or xtrabackup provider) # @param backupmethod # The execution binary for backing up. ex. mysqldump, xtrabackup, mariabackup # @param backup_success_file_path # Specify a path where upon successfull backup a file should be created for checking purposes. # @param backuprotate # Backup rotation interval in 24 hour periods. # @param ignore_events # Ignore the mysql.event table. # @param delete_before_dump # Whether to delete old .sql files before backing up. Setting to true deletes old files before backing up, while setting to false deletes them after backup. # @param backupdatabases # Databases to backup (required if using xtrabackup provider). By default `[]` will back up all databases. # @param file_per_database # Use file per database mode creating one file per database backup. # @param include_routines # Dump stored routines (procedures and functions) from dumped databases when doing a `file_per_database` backup. # @param include_triggers # Dump triggers for each dumped table when doing a `file_per_database` backup. # @param incremental_backups # A flag to activate/deactivate incremental backups. Currently only supported by the xtrabackup provider. # @param ensure # @param time # An array of two elements to set the backup time. Allows ['23', '5'] (i.e., 23:05) or ['3', '45'] (i.e., 03:45) for HH:MM times. # @param prescript # A script that is executed before the backup begins. # @param postscript # A script that is executed when the backup is finished. This could be used to sync the backup to a central store. This script can be either a single line that is directly executed or a number of lines supplied as an array. It could also be one or more externally managed (executable) files. # @param execpath # Allows you to set a custom PATH should your MySQL installation be non-standard places. Defaults to `/usr/bin:/usr/sbin:/bin:/sbin`. # @param provider # Sets the server backup implementation. Valid values are: # @param maxallowedpacket # Defines the maximum SQL statement size for the backup dump script. The default value is 1MB, as this is the default MySQL Server value. # @param optional_args # Specifies an array of optional arguments which should be passed through to the backup tool. (Supported by the xtrabackup and mysqldump providers.) # @param install_cron # Manage installation of cron package +# @param compression_command +# Configure the command used to compress the backup (when using the mysqldump provider). Make sure the command exists +# on the target system. Packages for it are NOT automatically installed. +# @param compression_extension +# Configure the file extension for the compressed backup (when using the mysqldump provider) class mysql::server::backup ( $backupuser = undef, $backuppassword = undef, $backupdir = undef, $backupdirmode = '0700', $backupdirowner = 'root', $backupdirgroup = $mysql::params::root_group, $backupcompress = true, $backuprotate = 30, $backupmethod = undef, $backup_success_file_path = '/tmp/mysqlbackup_success', $ignore_events = true, $delete_before_dump = false, $backupdatabases = [], $file_per_database = false, $include_routines = false, $include_triggers = false, $ensure = 'present', $time = ['23', '5'], $prescript = false, $postscript = false, $execpath = '/usr/bin:/usr/sbin:/bin:/sbin', $provider = 'mysqldump', $maxallowedpacket = '1M', $optional_args = [], $incremental_backups = true, $install_cron = true, + $compression_command = undef, + $compression_extension = undef ) inherits mysql::params { if $prescript and $provider =~ /(mysqldump|mysqlbackup)/ { warning(translate("The 'prescript' option is not currently implemented for the %{provider} backup provider.", { 'provider' => $provider })) } create_resources('class', { "mysql::backup::${provider}" => { 'backupuser' => $backupuser, 'backuppassword' => $backuppassword, 'backupdir' => $backupdir, 'backupdirmode' => $backupdirmode, 'backupdirowner' => $backupdirowner, 'backupdirgroup' => $backupdirgroup, 'backupcompress' => $backupcompress, 'backuprotate' => $backuprotate, 'backupmethod' => $backupmethod, 'backup_success_file_path' => $backup_success_file_path, 'ignore_events' => $ignore_events, 'delete_before_dump' => $delete_before_dump, 'backupdatabases' => $backupdatabases, 'file_per_database' => $file_per_database, 'include_routines' => $include_routines, 'include_triggers' => $include_triggers, 'ensure' => $ensure, 'time' => $time, 'prescript' => $prescript, 'postscript' => $postscript, 'execpath' => $execpath, 'maxallowedpacket' => $maxallowedpacket, 'optional_args' => $optional_args, 'incremental_backups' => $incremental_backups, 'install_cron' => $install_cron, + 'compression_command' => $compression_command, + 'compression_extension' => $compression_extension, } }) } diff --git a/spec/classes/mysql_backup_mysqldump_spec.rb b/spec/classes/mysql_backup_mysqldump_spec.rb index b9dfbcf..4afe8b3 100644 --- a/spec/classes/mysql_backup_mysqldump_spec.rb +++ b/spec/classes/mysql_backup_mysqldump_spec.rb @@ -1,58 +1,77 @@ # frozen_string_literal: true require 'spec_helper' describe 'mysql::backup::mysqldump' do on_supported_os.each do |os, facts| context "on #{os}" do let(:pre_condition) do <<-EOF class { 'mysql::server': } EOF end let(:facts) do facts.merge(root_home: '/root') end let(:default_params) do { 'backupuser' => 'testuser', 'backuppassword' => 'testpass', 'backupdir' => '/tmp/mysql-backup', 'backuprotate' => '25', 'delete_before_dump' => true, 'execpath' => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', 'maxallowedpacket' => '1M' } end context 'with time included' do let(:params) do { time: [23, 59, 30, 12, 6] }.merge(default_params) end it { is_expected.to contain_cron('mysql-backup').with( hour: 23, minute: 59, monthday: 30, month: 12, weekday: 6, ) } end context 'with defaults' do let(:params) { default_params } it { is_expected.to contain_cron('mysql-backup').with( command: '/usr/local/sbin/mysqlbackup.sh', ensure: 'present', hour: 23, minute: 5, ) } end + + context 'with compression_command' do + let(:params) do + { + compression_command: 'TEST -TEST', + compression_extension: '.TEST' + }.merge(default_params) + end + + it { + is_expected.to contain_file('mysqlbackup.sh').with_content( + %r{(\| TEST -TEST)}, + ) + is_expected.to contain_file('mysqlbackup.sh').with_content( + %r{(\.TEST)}, + ) + is_expected.not_to contain_package('bzip2') + } + end end end # rubocop:enable RSpec/NestedGroups end diff --git a/templates/mysqlbackup.sh.erb b/templates/mysqlbackup.sh.erb index 19706fb..aea8cae 100755 --- a/templates/mysqlbackup.sh.erb +++ b/templates/mysqlbackup.sh.erb @@ -1,125 +1,125 @@ <%- if @kernel == 'Linux' -%> #!/bin/bash <%- else -%> #!/bin/sh <%- end -%> # # 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 %>' MAX_ALLOWED_PACKET=<%= @maxallowedpacket %> DIR=<%= @backupdir %> ROTATE=<%= [ Integer(@backuprotate) - 1, 0 ].max %> # Create temporary mysql cnf file. TMPFILE=`mktemp /tmp/backup.XXXXXX` || exit 1 <%- if @kernel == 'SunOS' -%> echo "[client]\npassword=$PASS\nuser=$USER\nmax_allowed_packet=$MAX_ALLOWED_PACKET" > $TMPFILE <%- else -%> echo -e "[client]\npassword=$PASS\nuser=$USER\nmax_allowed_packet=$MAX_ALLOWED_PACKET" > $TMPFILE <%- end -%> <% if @prescript -%> <%- [@prescript].flatten.compact.each do |script|%> <%= script %> <%- end -%> <% end -%> # Ensure backup directory exist. mkdir -p $DIR PREFIX=mysql_backup_ <% if @ignore_events %> ADDITIONAL_OPTIONS="--ignore-table=mysql.event" <% else %> ADDITIONAL_OPTIONS="--events" <% end %> <%# Only include routines or triggers if we're doing a file per database -%> <%# backup. This happens if we named databases, or if we explicitly set -%> <%# file per database mode -%> <% if !@backupdatabases.empty? || @file_per_database -%> <% if @include_triggers -%> ADDITIONAL_OPTIONS="$ADDITIONAL_OPTIONS --triggers" <% else -%> ADDITIONAL_OPTIONS="$ADDITIONAL_OPTIONS --skip-triggers" <% end -%> <% if @include_routines -%> ADDITIONAL_OPTIONS="$ADDITIONAL_OPTIONS --routines" <% else -%> ADDITIONAL_OPTIONS="$ADDITIONAL_OPTIONS --skip-routines" <% end -%> <% end -%> <%- if @optional_args and @optional_args.is_a?(Array) -%> <%- @optional_args.each do |arg| -%> ADDITIONAL_OPTIONS="$ADDITIONAL_OPTIONS <%= arg %>" <%- end -%> <%- end -%> ##### STOP CONFIG #################################################### PATH=<%= @execpath %> <%- if @kernel == 'Linux' -%> set -o pipefail <%- end -%> cleanup() { <%- if @kernel == 'SunOS' -%> gfind "${DIR}/" -maxdepth 1 -type f -name "${PREFIX}*.sql*" -mtime +${ROTATE} -print0 | gxargs -0 -r rm -f <%- else -%> find "${DIR}/" -maxdepth 1 -type f -name "${PREFIX}*.sql*" -mtime +${ROTATE} -print0 | xargs -0 -r rm -f <%- end -%> } <% if @delete_before_dump -%> cleanup <% end -%> <% if @backupdatabases.empty? -%> <% if @file_per_database -%> mysql --defaults-extra-file=$TMPFILE -s -r -N -e 'SHOW DATABASES' | while read dbname do <%= @backupmethod -%> --defaults-extra-file=$TMPFILE --opt --flush-logs --single-transaction \ ${ADDITIONAL_OPTIONS} \ - ${dbname} <% if @backupcompress %>| bzcat -zc <% end %>> ${DIR}/${PREFIX}${dbname}_`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %>.bz2<% end %> + ${dbname} <% if @backupcompress %>| <%= @compression_command %> <% end %>> ${DIR}/${PREFIX}${dbname}_`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %><%= @compression_extension %><% end %> done <% else -%> <%= @backupmethod -%> --defaults-extra-file=$TMPFILE --opt --flush-logs --single-transaction \ ${ADDITIONAL_OPTIONS} \ - --all-databases <% if @backupcompress %>| bzcat -zc <% end %>> ${DIR}/${PREFIX}`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %>.bz2<% end %> + --all-databases <% if @backupcompress %>| <%= @compression_command %> <% end %>> ${DIR}/${PREFIX}`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %><%= @compression_extension %><% end %> <% end -%> <% else -%> <% @backupdatabases.each do |db| -%> <%= @backupmethod -%> --defaults-extra-file=$TMPFILE --opt --flush-logs --single-transaction \ ${ADDITIONAL_OPTIONS} \ - <%= db %><% if @backupcompress %>| bzcat -zc <% end %>> ${DIR}/${PREFIX}<%= db %>_`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %>.bz2<% end %> + <%= db %><% if @backupcompress %>| <%= @compression_command %> <% end %>> ${DIR}/${PREFIX}<%= db %>_`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %><%= @compression_extension %><% end %> <% end -%> <% end -%> <% unless @delete_before_dump -%> if [ $? -eq 0 ] ; then cleanup touch <%= @backup_success_file_path %> fi <% end -%> <% if @postscript -%> <%- [@postscript].flatten.compact.each do |script|%> <%= script %> <%- end -%> <% end -%> # Remove temporary file rm -f $TMPFILE