Skip to content

Commit

Permalink
[plugin/clone] PS-8188 : Make the clone_plugin to not force plugins t…
Browse files Browse the repository at this point in the history
…o match on recipient/donor (percona#5032)

PS-8188 : Make the clone_plugin to not force plugins to match on recipient/donor

https://jira.percona.com/browse/PS-8188

Problem:
--------
Clone enforces donor and recipient have same plugins installed.
For example, if a donor has an extra authentication plugin or a data masking plugin
installed but not installed on recipient, it is not possible to clone.
It will fail with the below error.

'Clone Donor plugin <plugin_name> is not active in Recipient.'

Fix:
----
Introduce new clone option 'clone_exclude_plugins_match' to take list of plugins that
are not installed on recipient.

This option is used on recipient server. The following plugins are disallowed. They must
be installed on recipient.

"daemon_keyring_proxy_plugin",
"binlog",
"performance_schema",
"memory",
"innodb",
"keyring_file",
"keyring_vault"

Co-authored-by: Satya Bodapati <satya.bodapati@percona.com>

--------------------------------------------------------------------------

PS-8844: Fix the failing clone.plugin_mismatch

https://jira.percona.com/browse/PS-8844

The test clone.plugin_mismatch fails when run from installed
directories due to the different path of the plugin directory used by
the test.

Failure:
        CURRENT_TEST: clone.plugin_mismatch
        mysqltest: At line 28: Command "remove_file" failed with error 1. my_errno=2.

The test involves simulating the secnario of missing plugins on
recipient server. To do the same, the test earlier used to copy all
plugins to recipient server's plugin_dir and delete some plugins to test
the clone plugin's behavior when recipeint had some plugins missing.

During the test investigation, it was found that it is just sufficient
to have only the clone plugin copied to the recipient server so that it
simulates the missing plugins.

This patch makes the the test independent of plugin paths so it works on
both build and installed directories.
  • Loading branch information
dutow authored and dlenev committed Jul 25, 2024
1 parent e00840e commit 6abc9c2
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 0 deletions.
39 changes: 39 additions & 0 deletions mysql-test/suite/clone/r/plugin_mismatch.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# PS-8188 : Make the clone_plugin to not force plugins to match on recipient/donor
#
# Install the plugins that will be active and present on donor but
# not installed on recipient
INSTALL PLUGIN example SONAME 'ha_example.so';
INSTALL PLUGIN version_tokens SONAME 'version_token.so';;

# create a different plugin_dir for recipient without the example and
# audit_log plugin. Now the recipient server do not have these plugins
# and we do a clone now from donor which has these plugins installed.
# restart: --plugin_dir=RECIPIENT_NEW_PLUGIN_DIR
#connection donor
CREATE TABLE t1(col1 INT PRIMARY KEY, col2 char(64), FULLTEXT KEY fts_index(col2));
INSERT INTO t1 VALUES(10, 'clone row 1');
INSERT INTO t1 VALUES(20, 'clone row 2');
INSERT INTO t1 VALUES(30, 'clone row 3');
INSTALL PLUGIN clone SONAME 'CLONE_PLUGIN';
#connection recipient
INSTALL PLUGIN clone SONAME 'CLONE_PLUGIN';
SET GLOBAL clone_valid_donor_list = 'HOST:PORT';
CLONE INSTANCE FROM USER@HOST:PORT IDENTIFIED BY '' DATA DIRECTORY='CLONED_DATADIR';
ERROR HY000: Clone Donor plugin EXAMPLE is not active in Recipient.
SET GLOBAL clone_exclude_plugins_list='example';
CLONE INSTANCE FROM USER@HOST:PORT IDENTIFIED BY '' DATA DIRECTORY='CLONED_DATADIR';
ERROR HY000: Clone Donor plugin version_tokens is not active in Recipient.
SET GLOBAL clone_exclude_plugins_list='example,version_tokens';
CLONE INSTANCE FROM USER@HOST:PORT IDENTIFIED BY '' DATA DIRECTORY='CLONED_DATADIR';
call mtr.add_suppression("\\[Warning\\] .*MY-\\d+.* Couldn't load plugin named '.*' with soname '.*'.");
call mtr.add_suppression("\\[ERROR\\] .*MY-\\d+.* Can't open shared library.*");
# restart: --datadir=CLONED_DATADIR --plugin_dir=RECIPIENT_NEW_PLUGIN_DIR
# restart with default datadir
# restart:
UNINSTALL PLUGIN clone;
UNINSTALL PLUGIN version_tokens;
UNINSTALL PLUGIN clone;
UNINSTALL PLUGIN example;
DROP TABLE t1;
# restart:
52 changes: 52 additions & 0 deletions mysql-test/suite/clone/r/plugin_mismatch_variable.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
INSTALL PLUGIN clone SONAME 'CLONE_PLUGIN';
# test with empty value
SET GLOBAL clone_exclude_plugins_list='';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list
# test without GLOBAL
SET clone_exclude_plugins_list='';
ERROR HY000: Variable 'clone_exclude_plugins_list' is a GLOBAL variable and should be set with SET GLOBAL
# test with allowed single value
SET GLOBAL clone_exclude_plugins_list='example';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list example
# test allowed plugin names(any names except the disallowed list)
SET GLOBAL clone_exclude_plugins_list='blah1,blah2';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list blah1,blah2
# test allowed plugin names(any names except the disallowed list)
SET GLOBAL clone_exclude_plugins_list='example,audit_log';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list example,audit_log
# test disallowed plugin names
SET GLOBAL clone_exclude_plugins_list='InnoDB';
ERROR HY000: Incorrect arguments to Clone: The following plugins cannot be excluded: innodb
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list example,audit_log
SET GLOBAL clone_exclude_plugins_list='example,audit_log,binlog';
ERROR HY000: Incorrect arguments to Clone: The following plugins cannot be excluded: binlog
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list example,audit_log
# test disallowed plugin names
SET GLOBAL clone_exclude_plugins_list='InnoDB,Memory,peformance_schema,keyring_file';
ERROR HY000: Incorrect arguments to Clone: The following plugins cannot be excluded: innodb,memory,keyring_file
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list example,audit_log
# test with SET PERSIST
SET PERSIST clone_exclude_plugins_list='example,audit_log';
# restart
# After restart, it should show example, audit_log
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
Variable_name Value
clone_exclude_plugins_list example,audit_log
RESET PERSIST;
UNINSTALL PLUGIN clone;
14 changes: 14 additions & 0 deletions mysql-test/suite/clone/t/plugin_mismatch.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Test remote clone command to replace data directory

[mysqld.1]
server_id=1

[mysqld.2]
server_id=2

[ENV]
SERVER_PORT_1 = @mysqld.1.port
SERVER_SOCK_1 = @mysqld.1.socket

SERVER_PORT_2 = @mysqld.2.port
SERVER_SOCK_2 = @mysqld.2.socket
105 changes: 105 additions & 0 deletions mysql-test/suite/clone/t/plugin_mismatch.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
--source include/have_example_plugin.inc

--echo #
--echo # PS-8188 : Make the clone_plugin to not force plugins to match on recipient/donor
--echo #

--let $HOST = 127.0.0.1
--let $PORT =`select @@port`
--let $USER = root

--echo # Install the plugins that will be active and present on donor but
--echo # not installed on recipient

--eval INSTALL PLUGIN example SONAME '$EXAMPLE_PLUGIN'
--eval INSTALL PLUGIN version_tokens SONAME '$VERSION_TOKEN';

--let $recipient_port= \$SERVER_PORT_2
--connect (conn_recipient, 127.0.0.1, root,,test,$recipient_port)
--connection conn_recipient

--echo
--echo # create a different plugin_dir for recipient without the example and
--echo # audit_log plugin. Now the recipient server do not have these plugins
--echo # and we do a clone now from donor which has these plugins installed.

--mkdir $MYSQL_TMP_DIR/plugin_dir
--let $plugin_dir=`SELECT @@plugin_dir`
--let $RECIPIENT_NEW_PLUGIN_DIR=$MYSQL_TMP_DIR/plugin_dir

# Copy only mysql_clone plugin that is required for the test.
--copy_file $plugin_dir/mysql_clone.so $RECIPIENT_NEW_PLUGIN_DIR/mysql_clone.so

--replace_result $RECIPIENT_NEW_PLUGIN_DIR RECIPIENT_NEW_PLUGIN_DIR
--let $restart_parameters="restart: --plugin_dir=$RECIPIENT_NEW_PLUGIN_DIR"
--source include/restart_mysqld.inc
--let $restart_parameters=

--echo #connection donor
--connection default
CREATE TABLE t1(col1 INT PRIMARY KEY, col2 char(64), FULLTEXT KEY fts_index(col2));

INSERT INTO t1 VALUES(10, 'clone row 1');
INSERT INTO t1 VALUES(20, 'clone row 2');
INSERT INTO t1 VALUES(30, 'clone row 3');

--let $checksum_table_donor=query_get_value(CHECKSUM TABLE t1, Checksum, 1)

--replace_result $CLONE_PLUGIN CLONE_PLUGIN
--eval INSTALL PLUGIN clone SONAME '$CLONE_PLUGIN'

--echo #connection recipient
--connection conn_recipient
--replace_result $CLONE_PLUGIN CLONE_PLUGIN
--eval INSTALL PLUGIN clone SONAME '$CLONE_PLUGIN'
--replace_result $HOST HOST $PORT PORT
--eval SET GLOBAL clone_valid_donor_list = '$HOST:$PORT'

--let $CLONED_DATADIR=$MYSQL_TMP_DIR/cloned_data_dir
--replace_result $CLONED_DATADIR CLONED_DATADIR $USER USER $HOST HOST $PORT PORT
--error ER_CLONE_PLUGIN_MATCH
--eval CLONE INSTANCE FROM $USER@$HOST:$PORT IDENTIFIED BY '' DATA DIRECTORY='$CLONED_DATADIR'

SET GLOBAL clone_exclude_plugins_list='example';
--replace_result $CLONED_DATADIR CLONED_DATADIR $USER USER $HOST HOST $PORT PORT
--error ER_CLONE_PLUGIN_MATCH
--eval CLONE INSTANCE FROM $USER@$HOST:$PORT IDENTIFIED BY '' DATA DIRECTORY='$CLONED_DATADIR'

SET GLOBAL clone_exclude_plugins_list='example,version_tokens';
--replace_result $CLONED_DATADIR CLONED_DATADIR $USER USER $HOST HOST $PORT PORT
--eval CLONE INSTANCE FROM $USER@$HOST:$PORT IDENTIFIED BY '' DATA DIRECTORY='$CLONED_DATADIR'

--connection conn_recipient
# Restart server on cloned data directory

call mtr.add_suppression("\\[Warning\\] .*MY-\\d+.* Couldn't load plugin named '.*' with soname '.*'.");
call mtr.add_suppression("\\[ERROR\\] .*MY-\\d+.* Can't open shared library.*");

--replace_result $CLONED_DATADIR CLONED_DATADIR $RECIPIENT_NEW_PLUGIN_DIR RECIPIENT_NEW_PLUGIN_DIR
--let $restart_parameters="restart: --datadir=$CLONED_DATADIR --plugin_dir=$RECIPIENT_NEW_PLUGIN_DIR"
--source include/restart_mysqld.inc
--connection conn_recipient

--let $checksum_table_recipient=query_get_value(CHECKSUM TABLE t1, Checksum, 1)
--assert($checksum_table_donor == $checksum_table_recipient)

--echo # restart with default datadir
--let $restart_parameters="restart:"
--source include/restart_mysqld.inc
--connection conn_recipient

--source include/count_sessions.inc
UNINSTALL PLUGIN clone;

--connection default
--disable_warnings
UNINSTALL PLUGIN version_tokens;
UNINSTALL PLUGIN clone;
UNINSTALL PLUGIN example;
--enable_warnings
DROP TABLE t1;
--disconnect conn_recipient
--source include/wait_until_count_sessions.inc
--force-rmdir $CLONED_DATADIR
--force-rmdir $MYSQL_TMP_DIR/plugin_dir
--source include/restart_mysqld.inc
47 changes: 47 additions & 0 deletions mysql-test/suite/clone/t/plugin_mismatch_variable.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--replace_result $CLONE_PLUGIN CLONE_PLUGIN
--eval INSTALL PLUGIN clone SONAME '$CLONE_PLUGIN'

--echo # test with empty value
SET GLOBAL clone_exclude_plugins_list='';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--echo # test without GLOBAL
--error ER_GLOBAL_VARIABLE
SET clone_exclude_plugins_list='';

--echo # test with allowed single value
SET GLOBAL clone_exclude_plugins_list='example';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--echo # test allowed plugin names(any names except the disallowed list)
SET GLOBAL clone_exclude_plugins_list='blah1,blah2';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--echo # test allowed plugin names(any names except the disallowed list)
SET GLOBAL clone_exclude_plugins_list='example,audit_log';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--echo # test disallowed plugin names
--error ER_WRONG_ARGUMENTS
SET GLOBAL clone_exclude_plugins_list='InnoDB';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--error ER_WRONG_ARGUMENTS
SET GLOBAL clone_exclude_plugins_list='example,audit_log,binlog';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--echo # test disallowed plugin names
--error ER_WRONG_ARGUMENTS
SET GLOBAL clone_exclude_plugins_list='InnoDB,Memory,peformance_schema,keyring_file';
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';

--echo # test with SET PERSIST
SET PERSIST clone_exclude_plugins_list='example,audit_log';
--source include/restart_mysqld.inc
--echo # After restart, it should show example, audit_log
SHOW VARIABLES LIKE 'clone_exclude_plugins_list';
RESET PERSIST;

UNINSTALL PLUGIN clone;
2 changes: 2 additions & 0 deletions plugin/clone/include/clone.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ const uint CLONE_MIN_BLOCK = 1024 * 1024;
/** Minimum network packet. Safe margin for meta information */
const uint CLONE_MIN_NET_BLOCK = 2 * CLONE_MIN_BLOCK;

extern char *clone_exclude_plugins_list;

/** Clone supported compression libs */
#define CLONE_COMPRESSION_ALGORITHM_COUNT_MAX 3
extern const char
Expand Down
41 changes: 41 additions & 0 deletions plugin/clone/src/clone_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Clone Plugin: Client implementation
*/
#include <inttypes.h>

#include "plugin/clone/include/clone.h"
#include "plugin/clone/include/clone_client.h"
#include "plugin/clone/include/clone_os.h"

Expand Down Expand Up @@ -1051,6 +1052,26 @@ int Client::validate_remote_params() {
last_error = ER_CLONE_PLUGIN_MATCH;
}

/* Build list of plugins from comma separated value of the variable
clone_exclude_plugins_list */
std::vector<std::string> excl_plugins_vec;
if (clone_exclude_plugins_list != nullptr) {
std::string excl_plugins_str(clone_exclude_plugins_list);
std::transform(excl_plugins_str.begin(), excl_plugins_str.end(),
excl_plugins_str.begin(), ::tolower);

std::stringstream exclude_stream(excl_plugins_str);

while (exclude_stream.good()) {
std::string substr;
getline(exclude_stream, substr, ',');
substr.erase(remove(substr.begin(), substr.end(), ' '), substr.end());
if (!substr.empty()) {
excl_plugins_vec.push_back(substr);
}
}
}

/* Validate plugins and check if shared objects can be loaded. */
for (auto &plugin : m_parameters.m_plugins_with_so) {
assert(m_share->m_protocol_version > CLONE_PROTOCOL_VERSION_V1);
Expand Down Expand Up @@ -1080,6 +1101,26 @@ int Client::validate_remote_params() {
continue;
}

/* Plugin is not installed in recipient but active on donor. Check if this
plugin can be exempted from matching with donor */
if (clone_exclude_plugins_list != nullptr) {
std::string plugin_str(plugin_name);
std::transform(plugin_str.begin(), plugin_str.end(), plugin_str.begin(),
::tolower);

if (std::find(excl_plugins_vec.begin(), excl_plugins_vec.end(),
plugin_str) != excl_plugins_vec.end()) {
char info_mesg[256];
snprintf(info_mesg, 256,
"Ignoring plugin %s installed on the source server but not on "
"the recepient. The mismatch is ignored due to "
"'clone_exclude_plugins_list'.",
plugin_name.c_str());
LogPluginErr(INFORMATION_LEVEL, ER_CLONE_CLIENT_TRACE, info_mesg);
continue;
}
}

/* Donor plugin is not there in recipient. */
my_error(ER_CLONE_PLUGIN_MATCH, MYF(0), plugin_name.c_str());
last_error = ER_CLONE_PLUGIN_MATCH;
Expand Down
Loading

0 comments on commit 6abc9c2

Please sign in to comment.