diff --git a/lib/puppet/provider/mysql_grant/mysql.rb b/lib/puppet/provider/mysql_grant/mysql.rb index d26b0fb..157f8ed 100644 --- a/lib/puppet/provider/mysql_grant/mysql.rb +++ b/lib/puppet/provider/mysql_grant/mysql.rb @@ -1,178 +1,182 @@ # frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql')) Puppet::Type.type(:mysql_grant).provide(:mysql, parent: Puppet::Provider::Mysql) do desc 'Set grants for users in MySQL.' commands mysql_raw: 'mysql' def self.instances instances = [] users.map do |user| user_string = cmd_user(user) query = "SHOW GRANTS FOR #{user_string};" begin grants = mysql_caller(query, 'regular') rescue Puppet::ExecutionFailure => e # Silently ignore users with no grants. Can happen e.g. if user is # defined with fqdn and server is run with skip-name-resolve. Example: # Default root user created by mysql_install_db on a host with fqdn # of myhost.mydomain.my: root@myhost.mydomain.my, when MySQL is started # with --skip-name-resolve. next if %r{There is no such grant defined for user}.match?(e.inspect) raise Puppet::Error, _('#mysql had an error -> %{inspect}') % { inspect: e.inspect } end # Once we have the list of grants generate entries for each. grants.each_line do |grant| # Match the munges we do in the type. munged_grant = grant.delete("'").delete('`').delete('"') # Matching: GRANT (SELECT, UPDATE) PRIVILEGES ON (*.*) TO ('root')@('127.0.0.1') (WITH GRANT OPTION) next unless match = munged_grant.match(%r{^GRANT\s(.+)\sON\s(.+)\sTO\s(.*)@(.*?)(\s.*)?$}) # rubocop:disable Lint/AssignmentInCondition privileges, table, user, host, rest = match.captures table.gsub!('\\\\', '\\') # split on ',' if it is not a non-'('-containing string followed by a # closing parenthesis ')'-char - e.g. only split comma separated elements not in # parentheses stripped_privileges = privileges.strip.split(%r{\s*,\s*(?![^(]*\))}).map do |priv| # split and sort the column_privileges in the parentheses and rejoin if priv.include?('(') type, col = priv.strip.split(%r{\s+|\b}, 2) type.upcase + ' (' + col.slice(1...-1).strip.split(%r{\s*,\s*}).sort.join(', ') + ')' else # Once we split privileges up on the , we need to make sure we # shortern ALL PRIVILEGES to just all. (priv == 'ALL PRIVILEGES') ? 'ALL' : priv.strip end end + sorted_privileges = stripped_privileges.sort + if self.newer_than('mysql' => '8.0.0') + sorted_privileges = (sorted_privileges == ['ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROLE', 'CREATE ROUTINE', 'CREATE TABLESPACE', 'CREATE TEMPORARY TABLES', 'CREATE USER', 'CREATE VIEW', 'DELETE', 'DROP', 'DROP ROLE', 'EVENT', 'EXECUTE', 'FILE', 'INDEX', 'INSERT', 'LOCK TABLES', 'PROCESS', 'REFERENCES', 'RELOAD', 'REPLICATION CLIENT', 'REPLICATION SLAVE', 'SELECT', 'SHOW DATABASES', 'SHOW VIEW', 'SHUTDOWN', 'SUPER', 'TRIGGER', 'UPDATE']) ? ['ALL'] : sorted_privileges + end # Same here, but to remove OPTION leaving just GRANT. options = if %r{WITH\sGRANT\sOPTION}.match?(rest) ['GRANT'] else ['NONE'] end # fix double backslash that MySQL prints, so resources match table.gsub!('\\\\', '\\') # We need to return an array of instances so capture these instances << new( name: "#{user}@#{host}/#{table}", ensure: :present, - privileges: stripped_privileges.sort, + privileges: sorted_privileges, table: table, user: "#{user}@#{host}", options: options, ) end end instances end def self.prefetch(resources) users = instances resources.each_key do |name| if provider = users.find { |user| user.name == name } # rubocop:disable Lint/AssignmentInCondition resources[name].provider = provider end end end def grant(user, table, privileges, options) user_string = self.class.cmd_user(user) priv_string = self.class.cmd_privs(privileges) table_string = privileges.include?('PROXY') ? self.class.cmd_user(table) : self.class.cmd_table(table) query = "GRANT #{priv_string}" query += " ON #{table_string}" query += " TO #{user_string}" query += self.class.cmd_options(options) unless options.nil? self.class.mysql_caller(query, 'system') end def create grant(@resource[:user], @resource[:table], @resource[:privileges], @resource[:options]) @property_hash[:ensure] = :present @property_hash[:table] = @resource[:table] @property_hash[:user] = @resource[:user] @property_hash[:options] = @resource[:options] if @resource[:options] @property_hash[:privileges] = @resource[:privileges] exists? ? (return true) : (return false) end def revoke(user, table, revoke_privileges = ['ALL']) user_string = self.class.cmd_user(user) table_string = revoke_privileges.include?('PROXY') ? self.class.cmd_user(table) : self.class.cmd_table(table) priv_string = self.class.cmd_privs(revoke_privileges) # revoke grant option needs to be a extra query, because # "REVOKE ALL PRIVILEGES, GRANT OPTION [..]" is only valid mysql syntax # if no ON clause is used. # It hast to be executed before "REVOKE ALL [..]" since a GRANT has to # exist to be executed successfully if revoke_privileges.include?('ALL') && !revoke_privileges.include?('PROXY') query = "REVOKE GRANT OPTION ON #{table_string} FROM #{user_string}" self.class.mysql_caller(query, 'system') end query = "REVOKE #{priv_string} ON #{table_string} FROM #{user_string}" self.class.mysql_caller(query, 'system') end def destroy # if the user was dropped, it'll have been removed from the user hash # as the grants are already removed by the DROP statement if self.class.users.include? @property_hash[:user] if @property_hash[:privileges].include?('PROXY') revoke(@property_hash[:user], @property_hash[:table], @property_hash[:privileges]) else revoke(@property_hash[:user], @property_hash[:table]) end end @property_hash.clear exists? ? (return false) : (return true) end def exists? @property_hash[:ensure] == :present || false end def flush @property_hash.clear self.class.mysql_caller('FLUSH PRIVILEGES', 'regular') end mk_resource_methods def diff_privileges(privileges_old, privileges_new) diff = { revoke: [], grant: [] } if privileges_old.include? 'ALL' diff[:revoke] = privileges_old diff[:grant] = privileges_new elsif privileges_new.include? 'ALL' diff[:grant] = privileges_new else diff[:revoke] = privileges_old - privileges_new diff[:grant] = privileges_new - privileges_old end diff end def privileges=(privileges) diff = diff_privileges(@property_hash[:privileges], privileges) unless diff[:revoke].empty? revoke(@property_hash[:user], @property_hash[:table], diff[:revoke]) end unless diff[:grant].empty? grant(@property_hash[:user], @property_hash[:table], diff[:grant], @property_hash[:options]) end @property_hash[:privileges] = privileges self.privileges end def options=(options) revoke(@property_hash[:user], @property_hash[:table]) grant(@property_hash[:user], @property_hash[:table], @property_hash[:privileges], options) @property_hash[:options] = options self.options end end diff --git a/spec/acceptance/00_mysql_server_spec.rb b/spec/acceptance/00_mysql_server_spec.rb index e1538bd..b4c63c9 100644 --- a/spec/acceptance/00_mysql_server_spec.rb +++ b/spec/acceptance/00_mysql_server_spec.rb @@ -1,86 +1,86 @@ # frozen_string_literal: true require 'spec_helper_acceptance' export_locales describe 'mysql class' do describe 'advanced config' do let(:pp) do <<-MANIFEST class { 'mysql::server': manage_config_file => 'true', override_options => { 'mysqld' => { 'key_buffer_size' => '32M' }}, package_ensure => 'present', purge_conf_dir => 'true', remove_default_accounts => 'true', restart => 'true', root_group => 'root', root_password => 'test', service_enabled => 'true', service_manage => 'true', users => { 'someuser@localhost' => { ensure => 'present', max_connections_per_hour => '0', max_queries_per_hour => '0', max_updates_per_hour => '0', max_user_connections => '0', password_hash => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF', }}, grants => { 'someuser@localhost/somedb.*' => { ensure => 'present', options => ['GRANT'], privileges => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'], table => 'somedb.*', user => 'someuser@localhost', }, }, databases => { 'somedb' => { ensure => 'present', - charset => 'utf8', + charset => #{$charset}, }, } } MANIFEST end it 'behaves idempotently' do idempotent_apply(pp) end describe 'override_options' do let(:pp) do <<-MANIFEST class { '::mysql::server': override_options => { 'mysqld' => { 'log-bin' => '/var/log/mariadb/mariadb-bin.log',} } } MANIFEST end it 'can be set' do apply_manifest(pp, catch_failures: true) do |r| expect(r.stderr).to be_empty end end end end describe 'syslog configuration' do let(:pp) do <<-MANIFEST class { 'mysql::server': override_options => { 'mysqld' => { 'log-error' => undef }, 'mysqld_safe' => { 'log-error' => false, 'syslog' => true }}, } MANIFEST end it 'behaves idempotently' do idempotent_apply(pp) end end end diff --git a/spec/acceptance/01_mysql_db_spec.rb b/spec/acceptance/01_mysql_db_spec.rb index 15930a6..25af5fc 100644 --- a/spec/acceptance/01_mysql_db_spec.rb +++ b/spec/acceptance/01_mysql_db_spec.rb @@ -1,83 +1,87 @@ # frozen_string_literal: true require 'spec_helper_acceptance' describe 'mysql::db define' do describe 'creating a database' do + let(:pp) do <<-MANIFEST class { 'mysql::server': root_password => 'password', service_enabled => 'true', service_manage => 'true', } mysql::db { 'spec1': user => 'root1', password => 'password', + charset => #{$charset}, } MANIFEST end it 'behaves idempotently' do idempotent_apply(pp) end it 'Checking exit code and stdout' do result = run_shell("mysql -e 'show databases;'") expect(result.exit_code).to eq 0 expect(result.stdout).to match %r{^spec1$} end end describe 'creating a database with post-sql' do let(:pp) do <<-MANIFEST class { 'mysql::server': override_options => { 'root_password' => 'password' } } file { '/tmp/spec.sql': ensure => file, content => 'CREATE TABLE table1 (id int);', before => Mysql::Db['spec2'], } mysql::db { 'spec2': user => 'root1', password => 'password', sql => '/tmp/spec.sql', + charset => #{$charset}, } MANIFEST end it 'behaves idempotently' do idempotent_apply(pp) end it 'Checking exit code and stdout' do result = run_shell("mysql -e 'show tables;' spec2") expect(result.exit_code).to eq 0 expect(result.stdout).to match %r{^table1$} end end describe 'creating a database with dbname parameter' do let(:check_command) { ' | grep realdb' } let(:pp) do <<-MANIFEST class { 'mysql::server': override_options => { 'root_password' => 'password' } } mysql::db { 'spec1': user => 'root1', password => 'password', dbname => 'realdb', + charset => #{$charset}, } MANIFEST end it 'behaves idempotently' do idempotent_apply(pp) end it 'Checking exit code and stdout' do result = run_shell("mysql -e 'show databases;'") expect(result.exit_code).to eq 0 expect(result.stdout).to match %r{^realdb$} end end end diff --git a/spec/acceptance/04_mysql_backup_spec.rb b/spec/acceptance/04_mysql_backup_spec.rb index 34b4e45..7a00264 100644 --- a/spec/acceptance/04_mysql_backup_spec.rb +++ b/spec/acceptance/04_mysql_backup_spec.rb @@ -1,337 +1,341 @@ # frozen_string_literal: true require 'spec_helper_acceptance' describe 'mysql::server::backup class' do context 'should work with no errors' do pp = <<-MANIFEST class { 'mysql::server': root_password => 'password' } mysql::db { [ 'backup1', 'backup2' ]: user => 'backup', password => 'secret', + charset => #{$charset}, } class { 'mysql::server::backup': backupuser => 'myuser', backuppassword => 'mypassword', backupdir => '/tmp/backups', backupcompress => true, postscript => [ 'rm -rf /var/tmp/mysqlbackups', 'rm -f /var/tmp/mysqlbackups.done', 'cp -r /tmp/backups /var/tmp/mysqlbackups', 'touch /var/tmp/mysqlbackups.done', ], execpath => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', } MANIFEST it 'when configuring mysql backups' do idempotent_apply(pp) end end describe 'mysqlbackup.sh', if: Gem::Version.new(mysql_version) < Gem::Version.new('5.7.0') do it 'runs mysqlbackup.sh with no errors' do run_shell('/usr/local/sbin/mysqlbackup.sh') do |r| expect(r.stderr).to eq('') end end it 'dumps all databases to single file' do run_shell('ls -l /tmp/backups/mysql_backup_*-*.sql.bz2 | wc -l') do |r| expect(r.stdout).to match(%r{1}) expect(r.exit_code).to be_zero end end context 'should create one file per database per run' do it 'executes mysqlbackup.sh a second time' do run_shell('sleep 1') run_shell('/usr/local/sbin/mysqlbackup.sh') end it 'creates at least one backup tarball' do run_shell('ls -l /tmp/backups/mysql_backup_*-*.sql.bz2 | wc -l') do |r| expect(r.stdout).to match(%r{2}) expect(r.exit_code).to be_zero end end end end context 'with one file per database' do context 'should work with no errors' do pp = <<-MANIFEST class { 'mysql::server': root_password => 'password' } mysql::db { [ 'backup1', 'backup2' ]: user => 'backup', password => 'secret', + charset => #{$charset}, } class { 'mysql::server::backup': backupuser => 'myuser', backuppassword => 'mypassword', backupdir => '/tmp/backups', backupcompress => true, file_per_database => true, postscript => [ 'rm -rf /var/tmp/mysqlbackups', 'rm -f /var/tmp/mysqlbackups.done', 'cp -r /tmp/backups /var/tmp/mysqlbackups', 'touch /var/tmp/mysqlbackups.done', ], execpath => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', } MANIFEST it 'when configuring mysql backups' do idempotent_apply(pp) end end describe 'mysqlbackup.sh', if: Gem::Version.new(mysql_version) < Gem::Version.new('5.7.0') do it 'runs mysqlbackup.sh with no errors without root credentials' do run_shell('HOME=/tmp/dontreadrootcredentials /usr/local/sbin/mysqlbackup.sh') do |r| expect(r.stderr).to eq('') end end it 'creates one file per database' do ['backup1', 'backup2'].each do |database| run_shell("ls -l /tmp/backups/mysql_backup_#{database}_*-*.sql.bz2 | wc -l") do |r| expect(r.stdout).to match(%r{1}) expect(r.exit_code).to be_zero end end end it 'executes mysqlbackup.sh a second time' do run_shell('sleep 1') run_shell('HOME=/tmp/dontreadrootcredentials /usr/local/sbin/mysqlbackup.sh') end it 'has one file per database per run' do ['backup1', 'backup2'].each do |database| run_shell("ls -l /tmp/backups/mysql_backup_#{database}_*-*.sql.bz2 | wc -l") do |r| expect(r.stdout).to match(%r{2}) expect(r.exit_code).to be_zero end end end end end context 'with xtrabackup enabled' do context 'should work with no errors', if: ((os[:family] == 'debian') || (os[:family] == 'ubuntu' && os[:release] =~ %r{^16\.04|^18\.04}) || (os[:family] == 'redhat' && os[:release].to_i > 6)) do pp = <<-MANIFEST class { 'mysql::server': root_password => 'password' } mysql::db { [ 'backup1', 'backup2' ]: user => 'backup', password => 'secret', + charset => #{$charset}, } case $facts['os']['family'] { /Debian/: { if versioncmp($::operatingsystemmajrelease, '8') >= 0 { $source_url = "http://repo.percona.com/apt/percona-release_1.0-22.generic_all.deb" } else { $source_url = "http://repo.percona.com/apt/percona-release_latest.${facts['os']['distro']['codename']}_all.deb" } file { '/tmp/percona-release_latest.deb': ensure => present, source => $source_url, } ensure_packages('gnupg') ensure_packages('gnupg2') ensure_packages('curl') ensure_packages('percona-release',{ ensure => present, provider => 'dpkg', source => '/tmp/percona-release_latest.deb', notify => Exec['apt-get update'], }) exec { 'apt-get update': path => '/usr/bin:/usr/sbin:/bin:/sbin', refreshonly => true, } } /RedHat/: { # RHEL/CentOS 5 is no longer supported by Percona, but older versions # of the repository are still available. if versioncmp($::operatingsystemmajrelease, '6') >= 0 { $percona_url = 'http://repo.percona.com/yum/percona-release-latest.noarch.rpm' $epel_url = "https://download.fedoraproject.org/pub/epel/epel-release-latest-${facts['os']['release']['major']}.noarch.rpm" } else { $percona_url = 'http://repo.percona.com/yum/release/5/os/noarch/percona-release-0.1-3.noarch.rpm' $epel_url = 'https://archives.fedoraproject.org/pub/archive/epel/epel-release-latest-5.noarch.rpm' } ensure_packages('percona-release',{ ensure => present, provider => 'rpm', source => $percona_url, }) ensure_packages('epel-release',{ ensure => present, provider => 'rpm', source => $epel_url, }) if ($facts['os']['name'] == 'Scientific') { # $releasever resolves to '6.10' instead of '6' which breaks Percona repos file { '/etc/yum/vars/releasever': ensure => present, content => '6', } } } default: { } } class { 'mysql::server::backup': backupuser => 'myuser', backuppassword => 'mypassword', backupdir => '/tmp/xtrabackups', provider => 'xtrabackup', execpath => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', } MANIFEST it 'when configuring mysql backup' do idempotent_apply(pp) end end describe 'xtrabackup.sh', if: Gem::Version.new(mysql_version) < Gem::Version.new('5.7.0') && ((os[:family] == 'debian' && os[:release].to_i >= 9) || (os[:family] == 'ubuntu' && os[:release] =~ %r{^16\.04|^18\.04}) || (os[:family] == 'redhat' && os[:release].to_i > 6)) do # rubocop:disable Layout/LineLength it 'runs xtrabackup.sh full backup with no errors' do run_shell('/usr/local/sbin/xtrabackup.sh --target-dir=/tmp/xtrabackups/$(date +%F)_full --backup 2>&1 | tee /tmp/xtrabackup_full.log') do |r| expect(r.exit_code).to be_zero end end it 'xtrabackup reports success for the full backup' do # NOTE: Once support for CentOS 6 is dropped, we should check for "completed OK" instead. run_shell('grep "xtrabackup: Transaction log of lsn" /tmp/xtrabackup_full.log') do |r| expect(r.exit_code).to be_zero end end it 'creates a subdirectory for the full backup' do run_shell('find /tmp/xtrabackups -mindepth 1 -maxdepth 1 -type d -name $(date +%Y)\*full | wc -l') do |r| expect(r.stdout).to match(%r{1}) expect(r.exit_code).to be_zero end end it 'runs xtrabackup.sh incremental backup with no errors' do run_shell('sleep 1') run_shell('/usr/local/sbin/xtrabackup.sh --incremental-basedir=/tmp/xtrabackups/$(date +%F)_full --target-dir=/tmp/xtrabackups/$(date +%F_%H-%M-%S) --backup 2>&1 | tee /tmp/xtrabackup_inc.log') do |r| # rubocop:disable Layout/LineLength expect(r.exit_code).to be_zero end end it 'xtrabackup reports success for the incremental backup' do # NOTE: Once support for CentOS 6 is dropped, we should check for "completed OK" instead. run_shell('grep "xtrabackup: Transaction log of lsn" /tmp/xtrabackup_inc.log') do |r| expect(r.exit_code).to be_zero end end it 'creates a new subdirectory for each backup' do run_shell('find /tmp/xtrabackups -mindepth 1 -maxdepth 1 -type d -name $(date +%Y)\* | wc -l') do |r| expect(r.stdout).to match(%r{2}) expect(r.exit_code).to be_zero end end end end context 'with xtrabackup enabled and incremental backups disabled' do context 'should work with no errors', if: ((os[:family] == 'debian' && os[:release].to_i >= 9) || (os[:family] == 'ubuntu' && os[:release] =~ %r{^16\.04|^18\.04}) || (os[:family] == 'redhat' && os[:release].to_i > 6)) do # rubocop:disable Layout/LineLength pp = <<-MANIFEST class { 'mysql::server': root_password => 'password' } mysql::db { [ 'backup1', 'backup2' ]: user => 'backup', password => 'secret', + charset => #{$charset}, } case $facts['os']['family'] { /Debian/: { $source_url = "http://repo.percona.com/apt/percona-release_1.0-22.generic_all.deb" file { '/tmp/percona-release_latest.deb': ensure => present, source => $source_url, } ensure_packages('gnupg') ensure_packages('gnupg2') ensure_packages('percona-release',{ ensure => present, provider => 'dpkg', source => '/tmp/percona-release_latest.deb', notify => Exec['apt-get update'], }) exec { 'apt-get update': path => '/usr/bin:/usr/sbin:/bin:/sbin', refreshonly => true, } } /RedHat/: { $percona_url = 'http://repo.percona.com/yum/percona-release-latest.noarch.rpm' $epel_url = "https://download.fedoraproject.org/pub/epel/epel-release-latest-${facts['os']['release']['major']}.noarch.rpm" ensure_packages('percona-release',{ ensure => present, provider => 'rpm', source => $percona_url, }) ensure_packages('epel-release',{ ensure => present, provider => 'rpm', source => $epel_url, }) if ($facts['os']['name'] == 'Scientific') { # $releasever resolves to '6.10' instead of '6' which breaks Percona repos file { '/etc/yum/vars/releasever': ensure => present, content => '6', } } } default: { } } class { 'mysql::server::backup': backupuser => 'myuser', backuppassword => 'mypassword', backupdir => '/tmp/xtrabackups', provider => 'xtrabackup', incremental_backups => false, execpath => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', } MANIFEST it 'when configuring mysql backup' do idempotent_apply(pp) end end describe 'xtrabackup.sh', if: Gem::Version.new(mysql_version) < Gem::Version.new('5.7.0') && ((os[:family] == 'debian' && os[:release].to_i >= 9) || (os[:family] == 'ubuntu' && os[:release] =~ %r{^16\.04|^18\.04}) || (os[:family] == 'redhat' && os[:release].to_i > 6)) do # rubocop:disable Layout/LineLength it 'runs xtrabackup.sh with no errors' do run_shell('/usr/local/sbin/xtrabackup.sh --target-dir=/tmp/xtrabackups/$(date +%F_%H-%M-%S) --backup 2>&1 | tee /tmp/xtrabackup.log') do |r| expect(r.exit_code).to be_zero end end it 'xtrabackup reports success for the backup' do # NOTE: Once support for CentOS 6 is dropped, we should check for "completed OK" instead. run_shell('grep "xtrabackup: Transaction log of lsn" /tmp/xtrabackup.log') do |r| expect(r.exit_code).to be_zero end end end end end diff --git a/spec/acceptance/types/mysql_database_spec.rb b/spec/acceptance/types/mysql_database_spec.rb index 03579fe..47cfb94 100644 --- a/spec/acceptance/types/mysql_database_spec.rb +++ b/spec/acceptance/types/mysql_database_spec.rb @@ -1,62 +1,63 @@ # frozen_string_literal: true require 'spec_helper_acceptance' describe 'mysql_database' do describe 'setup' do pp = <<-MANIFEST class { 'mysql::server': } MANIFEST it 'works with no errors' do apply_manifest(pp, catch_failures: true) end end describe 'creating database' do pp = <<-MANIFEST mysql_database { 'spec_db': - ensure => present, + ensure => present, + charset => #{$charset}, } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the database #stdout' do run_shell("mysql -NBe \"SHOW DATABASES LIKE 'spec_db'\"") do |r| expect(r.stdout).to match(%r{^spec_db$}) expect(r.stderr).to be_empty end end end describe 'charset and collate' do pp = <<-MANIFEST mysql_database { 'spec_latin1': charset => 'latin1', collate => 'latin1_swedish_ci', } mysql_database { 'spec_utf8': - charset => 'utf8', + charset => #{$charset}, collate => 'utf8_general_ci', } MANIFEST it 'creates two db of different types idempotently' do idempotent_apply(pp) end it 'finds latin1 db #stdout' do run_shell("mysql -NBe \"SHOW VARIABLES LIKE '%_database'\" spec_latin1") do |r| expect(r.stdout).to match(%r{^character_set_database\tlatin1\ncollation_database\tlatin1_swedish_ci$}) expect(r.stderr).to be_empty end end it 'finds utf8 db #stdout' do run_shell("mysql -NBe \"SHOW VARIABLES LIKE '%_database'\" spec_utf8") do |r| - expect(r.stdout).to match(%r{^character_set_database\tutf8\ncollation_database\tutf8_general_ci$}) + expect(r.stdout).to match(%r{^character_set_database\tutf8(mb3)?\ncollation_database\tutf8_general_ci$}) expect(r.stderr).to be_empty end end end end diff --git a/spec/acceptance/types/mysql_grant_spec.rb b/spec/acceptance/types/mysql_grant_spec.rb index 0318a55..00c7167 100644 --- a/spec/acceptance/types/mysql_grant_spec.rb +++ b/spec/acceptance/types/mysql_grant_spec.rb @@ -1,738 +1,701 @@ # frozen_string_literal: true require 'spec_helper_acceptance' describe 'mysql_grant' do before(:all) do pp = <<-MANIFEST class { 'mysql::server': root_password => 'password', } MANIFEST apply_manifest(pp, catch_failures: true) end describe 'missing privileges for user' do pp = <<-MANIFEST mysql_user { 'test1@tester': ensure => present, } mysql_grant { 'test1@tester/test.*': ensure => 'present', table => 'test.*', user => 'test1@tester', require => Mysql_user['test1@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to contain(%r{`privileges` `parameter` is required}) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test1@tester"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'test1' on host 'tester'}) end end describe 'missing table for user' do pp = <<-MANIFEST mysql_user { 'atest@tester': ensure => present, } mysql_grant { 'atest@tester/test.*': ensure => 'present', user => 'atest@tester', privileges => ['ALL'], require => Mysql_user['atest@tester'], } MANIFEST it 'fails' do apply_manifest(pp, expect_failures: true) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR atest@tester"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'atest' on host 'tester'}) end end describe 'adding privileges' do pp = <<-MANIFEST mysql_user { 'test2@tester': ensure => present, } mysql_grant { 'test2@tester/test.*': ensure => 'present', table => 'test.*', user => 'test2@tester', privileges => ['SELECT', 'UPDATE'], require => Mysql_user['test2@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test2@tester"') expect(result.stdout).to contain(%r{GRANT SELECT, UPDATE.*TO ['|`]test2['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end end describe 'adding privileges with special character in name' do pp = <<-MANIFEST mysql_user { 'test-2@tester': ensure => present, } mysql_grant { 'test-2@tester/test.*': ensure => 'present', table => 'test.*', user => 'test-2@tester', privileges => ['SELECT', 'UPDATE'], require => Mysql_user['test-2@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell("mysql -NBe \"SHOW GRANTS FOR 'test-2'@tester\"") expect(result.stdout).to contain(%r{GRANT SELECT, UPDATE.*TO ['|`]test-2['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end end describe 'adding option' do pp = <<-MANIFEST mysql_user { 'test3@tester': ensure => present, } mysql_grant { 'test3@tester/test.*': ensure => 'present', table => 'test.*', user => 'test3@tester', options => ['GRANT'], privileges => ['SELECT', 'UPDATE'], require => Mysql_user['test3@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test3@tester"') expect(result.stdout).to contain(%r{GRANT SELECT, UPDATE ON `test`.* TO ['|`]test3['|`]@['|`]tester['|`] WITH GRANT OPTION$}) expect(result.stderr).to be_empty end end describe 'adding all privileges without table' do pp = <<-MANIFEST mysql_user { 'test4@tester': ensure => present, } mysql_grant { 'test4@tester/test.*': ensure => 'present', user => 'test4@tester', options => ['GRANT'], privileges => ['SELECT', 'UPDATE', 'ALL'], require => Mysql_user['test4@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to contain(%r{`table` `parameter` is required.}) end end describe 'adding all privileges' do pp = <<-MANIFEST mysql_user { 'test4@tester': ensure => present, } mysql_grant { 'test4@tester/test.*': ensure => 'present', table => 'test.*', user => 'test4@tester', options => ['GRANT'], privileges => ['SELECT', 'UPDATE', 'ALL'], require => Mysql_user['test4@tester'], } MANIFEST it 'onlies try to apply ALL' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test4@tester"') expect(result.stdout).to contain(%r{GRANT ALL PRIVILEGES ON `test`.* TO ['|`]test4['|`]@['|`]tester['|`] WITH GRANT OPTION}) expect(result.stderr).to be_empty end end # Test combinations of user@host to ensure all cases work. describe 'short hostname' do pp = <<-MANIFEST mysql_user { 'test@short': ensure => present, } mysql_grant { 'test@short/test.*': ensure => 'present', table => 'test.*', user => 'test@short', privileges => 'ALL', require => Mysql_user['test@short'], } mysql_user { 'test@long.hostname.com': ensure => present, } mysql_grant { 'test@long.hostname.com/test.*': ensure => 'present', table => 'test.*', user => 'test@long.hostname.com', privileges => 'ALL', require => Mysql_user['test@long.hostname.com'], } mysql_user { 'test@192.168.5.6': ensure => present, } mysql_grant { 'test@192.168.5.6/test.*': ensure => 'present', table => 'test.*', user => 'test@192.168.5.6', privileges => 'ALL', require => Mysql_user['test@192.168.5.6'], } mysql_user { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004': ensure => present, } mysql_grant { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004/test.*': ensure => 'present', table => 'test.*', user => 'test@2607:f0d0:1002:0051:0000:0000:0000:0004', privileges => 'ALL', require => Mysql_user['test@2607:f0d0:1002:0051:0000:0000:0000:0004'], } mysql_user { 'test@::1/128': ensure => present, } mysql_grant { 'test@::1/128/test.*': ensure => 'present', table => 'test.*', user => 'test@::1/128', privileges => 'ALL', require => Mysql_user['test@::1/128'], } MANIFEST it 'applies' do apply_manifest(pp, catch_failures: true) end it 'finds short hostname #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test@short"') expect(result.stdout).to contain(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]short['|`]}) expect(result.stderr).to be_empty end it 'finds long hostname #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'long.hostname.com'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]long.hostname.com['|`]}) expect(r.stderr).to be_empty end end it 'finds ipv4 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.6'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]192.168.5.6['|`]}) expect(r.stderr).to be_empty end end it 'finds ipv6 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]2607:f0d0:1002:0051:0000:0000:0000:0004['|`]}) expect(r.stderr).to be_empty end end it 'finds short ipv6 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'::1/128'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]::1\/128['|`]}) expect(r.stderr).to be_empty end end end - # On Ubuntu 20.04 'ALL' now returns as the sum of it's constitute parts and so require a specific test - describe 'ALL privilege on newer MySQL versions', if: os[:family] == 'ubuntu' && os[:release] =~ %r{^20\.04} do - pp_one = <<-MANIFEST - mysql_user { 'all@localhost': - ensure => present, - } - mysql_grant { 'all@localhost/*.*': - user => 'all@localhost', - privileges => ['ALL'], - table => '*.*', - require => Mysql_user['all@localhost'], - } - MANIFEST - it "create ['ALL'] privs" do - apply_manifest(pp_one, catch_failures: true) - end - - pp_two = <<-MANIFEST - mysql_user { 'all@localhost': - ensure => present, - } - mysql_grant { 'all@localhost/*.*': - user => 'all@localhost', - privileges => ['ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROLE', 'CREATE ROUTINE', 'CREATE TABLESPACE', 'CREATE TEMPORARY TABLES', 'CREATE USER', 'CREATE VIEW', 'DELETE', 'DROP', 'DROP ROLE', 'EVENT', 'EXECUTE', 'FILE', 'INDEX', 'INSERT', 'LOCK TABLES', 'PROCESS', 'REFERENCES', 'RELOAD', 'REPLICATION CLIENT', 'REPLICATION SLAVE', 'SELECT', 'SHOW DATABASES', 'SHOW VIEW', 'SHUTDOWN', 'SUPER', 'TRIGGER', 'UPDATE'], - table => '*.*', - require => Mysql_user['all@localhost'], - } - MANIFEST - it "create ['ALL'] constitute parts privs" do - apply_manifest(pp_two, catch_changes: true) - end - end - describe 'complex test' do - # On Ubuntu 20.04 'ALL' now returns as the sum of it's constitute parts and so is no longer idempotent when set - privileges = if os[:family] == 'ubuntu' && os[:release] =~ %r{^20\.04} - "['SELECT', 'INSERT', 'UPDATE']" - else - "['ALL']" - end pp = <<-MANIFEST $dbSubnet = '10.10.10.%' mysql_database { 'foo': - ensure => present, + ensure => present, + charset => '#{$charset}', } exec { 'mysql-create-table': command => '/usr/bin/mysql -NBe "CREATE TABLE foo.bar (name VARCHAR(20))"', environment => "HOME=${::root_home}", unless => '/usr/bin/mysql -NBe "SELECT 1 FROM foo.bar LIMIT 1;"', require => Mysql_database['foo'], } Mysql_grant { ensure => present, options => ['GRANT'], - privileges => #{privileges}, + privileges => ['ALL'], table => '*.*', require => [ Mysql_database['foo'], Exec['mysql-create-table'] ], } mysql_user { "user1@${dbSubnet}": ensure => present, } mysql_grant { "user1@${dbSubnet}/*.*": user => "user1@${dbSubnet}", require => Mysql_user["user1@${dbSubnet}"], } mysql_user { "user2@${dbSubnet}": ensure => present, } mysql_grant { "user2@${dbSubnet}/foo.bar": privileges => ['SELECT', 'INSERT', 'UPDATE'], user => "user2@${dbSubnet}", table => 'foo.bar', require => Mysql_user["user2@${dbSubnet}"], } mysql_user { "user3@${dbSubnet}": ensure => present, } mysql_grant { "user3@${dbSubnet}/foo.*": privileges => ['SELECT', 'INSERT', 'UPDATE'], user => "user3@${dbSubnet}", table => 'foo.*', require => Mysql_user["user3@${dbSubnet}"], } mysql_user { 'web@%': ensure => present, } mysql_grant { 'web@%/*.*': user => 'web@%', require => Mysql_user['web@%'], } mysql_user { "web@${dbSubnet}": ensure => present, } mysql_grant { "web@${dbSubnet}/*.*": user => "web@${dbSubnet}", require => Mysql_user["web@${dbSubnet}"], } mysql_user { "web@${::networking['ip']}": ensure => present, } mysql_grant { "web@${::networking['ip']}/*.*": user => "web@${::networking['ip']}", require => Mysql_user["web@${::networking['ip']}"], } mysql_user { 'web@localhost': ensure => present, } mysql_grant { 'web@localhost/*.*': user => 'web@localhost', require => Mysql_user['web@localhost'], } MANIFEST it 'setup mysql::server' do idempotent_apply(pp) end end describe 'lower case privileges' do pp_one = <<-MANIFEST mysql_user { 'lowercase@localhost': ensure => present, } mysql_grant { 'lowercase@localhost/*.*': user => 'lowercase@localhost', privileges => ['SELECT', 'INSERT', 'UPDATE'], table => '*.*', require => Mysql_user['lowercase@localhost'], } MANIFEST it "create ['SELECT', 'INSERT', 'UPDATE'] privs" do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'lowercase@localhost': ensure => present, } mysql_grant { 'lowercase@localhost/*.*': user => 'lowercase@localhost', privileges => ['select', 'insert', 'update'], table => '*.*', require => Mysql_user['lowercase@localhost'], } MANIFEST it "create lowercase ['select', 'insert', 'update'] privs" do apply_manifest(pp_two, catch_changes: true) end end describe 'adding procedure privileges' do pp = <<-MANIFEST exec { 'simpleproc-create': command => 'mysql --user="root" --password="password" --database=mysql --delimiter="//" -NBe "CREATE PROCEDURE simpleproc (OUT param1 INT) BEGIN SELECT COUNT(*) INTO param1 FROM t; end//"', path => '/usr/bin/', before => Mysql_user['test2@tester'], } mysql_user { 'test2@tester': ensure => present, } mysql_grant { 'test2@tester/PROCEDURE mysql.simpleproc': ensure => 'present', table => 'PROCEDURE mysql.simpleproc', user => 'test2@tester', privileges => ['EXECUTE'], require => Mysql_user['test2@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test2@tester"') expect(result.stdout).to match(%r{GRANT EXECUTE ON PROCEDURE `mysql`.`simpleproc` TO ['|`]test2['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end end describe 'adding function privileges' do it 'works without errors' do pp = <<-EOS exec { 'simplefunc-create': command => '/usr/bin/mysql --user="root" --password="password" --database=mysql -NBe "CREATE FUNCTION simplefunc (s CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT(\\'Hello, \\', s, \\'!\\')"', before => Mysql_user['test3@tester'], } mysql_user { 'test3@tester': ensure => 'present', } mysql_grant { 'test3@tester/FUNCTION mysql.simplefunc': ensure => 'present', table => 'FUNCTION mysql.simplefunc', user => 'test3@tester', privileges => ['EXECUTE'], require => Mysql_user['test3@tester'], } EOS apply_manifest(pp, catch_failures: true) end # rubocop:enable RSpec/ExampleLength it 'finds the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test3@tester"') expect(result.stdout).to match(%r{GRANT EXECUTE ON FUNCTION `mysql`.`simplefunc` TO ['|`]test3['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end # rubocop:enable RSpec/MultipleExpectations end describe 'proxy privilieges' do describe 'adding proxy privileges', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy1@tester': ensure => present, } mysql_grant { 'proxy1@tester/proxy_user@proxy_host': ensure => 'present', table => 'proxy_user@proxy_host', user => 'proxy1@tester', privileges => ['PROXY'], require => Mysql_user['proxy1@tester'], } MANIFEST it 'works without errors when version greater than 5.5.0' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do run_shell('mysql -NBe "SHOW GRANTS FOR proxy1@tester"') do |r| expect(r.stdout).to match(%r{GRANT PROXY ON 'proxy_user'@'proxy_host' TO ['|`]proxy1['|`]@['|`]tester['|`]}) expect(r.stderr).to be_empty end end end describe 'removing proxy privileges', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy1@tester': ensure => present, } mysql_grant { 'proxy1@tester/proxy_user@proxy_host': ensure => 'absent', table => 'proxy_user@proxy_host', user => 'proxy1@tester', privileges => ['PROXY'], require => Mysql_user['proxy1@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do run_shell('mysql -NBe "SHOW GRANTS FOR proxy1@tester"') do |r| expect(r.stdout).not_to match(%r{GRANT PROXY ON 'proxy_user'@'proxy_host' TO ['|`]proxy1['|`]@['|`]tester['|`]}) expect(r.stderr).to be_empty end end end describe 'adding proxy privileges with other privileges', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy2@tester': ensure => present, } mysql_grant { 'proxy2@tester/proxy_user@proxy_host': ensure => 'present', table => 'proxy_user@proxy_host', user => 'proxy2@tester', privileges => ['PROXY', 'SELECT'], require => Mysql_user['proxy2@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to match(%r{`privileges` `parameter`: PROXY can only be specified by itself}) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR proxy2@tester"', expect_failures: true) expect(result.stderr).to match(%r{There is no such grant defined for user 'proxy2' on host 'tester'}) end end describe 'adding proxy privileges with mysql version less than 5.5.0', unless: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy3@tester': ensure => present, } mysql_grant { 'proxy3@tester/proxy_user@proxy_host': ensure => 'present', table => 'proxy_user@proxy_host', user => 'proxy3@tester', privileges => ['PROXY', 'SELECT'], require => Mysql_user['proxy3@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to match(%r{PROXY user not supported on mysql versions < 5\.5\.0}i) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR proxy2@tester"', expect_failures: true) expect(result.stderr).to match(%r{There is no such grant defined for user 'proxy2' on host 'tester'}) end end describe 'adding proxy privileges with invalid proxy user', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy3@tester': ensure => present, } mysql_grant { 'proxy3@tester/invalid_proxy_user': ensure => 'present', table => 'invalid_proxy_user', user => 'proxy3@tester', privileges => ['PROXY'], require => Mysql_user['proxy3@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to match(%r{`table` `property` for PROXY should be specified as proxy_user@proxy_host.}) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR proxy3@tester"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'proxy3' on host 'tester'}) end end end describe 'grants with skip-name-resolve specified' do pp_one = <<-MANIFEST class { 'mysql::server': override_options => { 'mysqld' => {'skip-name-resolve' => true} }, restart => true, } MANIFEST it 'setup mysql::server' do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'test@fqdn.com': ensure => present, } mysql_grant { 'test@fqdn.com/test.*': ensure => 'present', table => 'test.*', user => 'test@fqdn.com', privileges => 'ALL', require => Mysql_user['test@fqdn.com'], } mysql_user { 'test@192.168.5.7': ensure => present, } mysql_grant { 'test@192.168.5.7/test.*': ensure => 'present', table => 'test.*', user => 'test@192.168.5.7', privileges => 'ALL', require => Mysql_user['test@192.168.5.7'], } MANIFEST it 'applies' do apply_manifest(pp_two, catch_failures: true) end it 'fails with fqdn' do unless Gem::Version.new(mysql_version) > Gem::Version.new('5.7.0') result = run_shell('mysql -NBe "SHOW GRANTS FOR test@fqdn.com"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'test' on host 'fqdn.com'}) end end it 'finds ipv4 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.7'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON `test`.* TO ['|`]test['|`]@['|`]192.168.5.7['|`]}) expect(r.stderr).to be_empty end end pp_three = <<-MANIFEST mysql_user { 'test@fqdn.com': ensure => present, } mysql_grant { 'test@fqdn.com/test.*': ensure => 'present', table => 'test.*', user => 'test@fqdn.com', privileges => 'ALL', require => Mysql_user['test@fqdn.com'], } MANIFEST it 'fails to execute while applying' do mysql_cmd = run_shell('which mysql').stdout.chomp run_shell("mv #{mysql_cmd} #{mysql_cmd}.bak") result = apply_manifest(pp_three, expect_failures: true) expect(result.stderr).to match(%r{Could not find a suitable provider for mysql_grant}) run_shell("mv #{mysql_cmd}.bak #{mysql_cmd}") end pp_four = <<-MANIFEST class { 'mysql::server': restart => true, } MANIFEST it 'reset mysql::server config' do apply_manifest(pp_four, catch_failures: true) end end describe 'adding privileges to specific table' do # Using puppet_apply as a helper pp_one = <<-MANIFEST class { 'mysql::server': override_options => { 'root_password' => 'password' } } MANIFEST it 'setup mysql server' do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'test@localhost': ensure => present, } mysql_grant { 'test@localhost/grant_spec_db.grant_spec_table_doesnt_exist': user => 'test@localhost', privileges => ['SELECT'], table => 'grant_spec_db.grant_spec_table_doesnt_exist', require => Mysql_user['test@localhost'], } MANIFEST it 'creates grant on missing table will fail' do result = apply_manifest(pp_two, expect_failures: true) expect(result.stderr).to match(%r{Table 'grant_spec_db\.grant_spec_table_doesnt_exist' doesn't exist}) end pp_three = <<-MANIFEST file { '/tmp/grant_spec_table.sql': ensure => file, content => 'CREATE TABLE grant_spec_table (id int);', before => Mysql::Db['grant_spec_db'], } mysql::db { 'grant_spec_db': user => 'root1', password => 'password', sql => '/tmp/grant_spec_table.sql', + charset => #{$charset}, } MANIFEST it 'creates table' do apply_manifest(pp_three, catch_failures: true) end it 'has the table' do result = run_shell("mysql -e 'show tables;' grant_spec_db|grep grant_spec_table") expect(result.exit_code).to be_zero end end end diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb index 4ac8d7e..fe32e28 100644 --- a/spec/spec_helper_acceptance.rb +++ b/spec/spec_helper_acceptance.rb @@ -1,6 +1,13 @@ # frozen_string_literal: true require 'puppet_litmus' require 'spec_helper_acceptance_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_acceptance_local.rb')) PuppetLitmus.configure! + +# On Ubuntu 20.04 'utf8' charset now sets 'utf8mb3' internally and breaks idempotence +$charset = if os[:family] == 'ubuntu' && os[:release] =~ %r{^20\.04} + "utf8mb3" + else + "utf8" + end