From 47076216e4e9e1beccd6033f2307971a2f71e88a Mon Sep 17 00:00:00 2001 From: Marco Tusa Date: Fri, 2 Aug 2024 15:26:19 +0200 Subject: [PATCH] PXC-4469: Implement CLONE method for SST https://perconadev.atlassian.net/browse/PXC-4469 Implemented SST method using clone plugin. --- mysql-test/r/grant_dynamic.result | 83 +- mysql-test/r/mysql_upgrade.result | 2 +- mysql-test/r/mysqld--help-notwin.result | 2 +- mysql-test/r/ps_sys_upgrade.result | 1 + mysql-test/r/transactional_acl_tables.result | 2 + .../funcs_1/r/is_schema_privileges.result | 2 + .../funcs_1/r/is_table_privileges.result | 2 + .../suite/galera/r/galera_defaults.result | 2 +- .../r/galera_sst_allowed_methods.result | 4 +- .../suite/galera/r/galera_sst_clone.result | 362 +++++ .../suite/galera/t/galera_sst_clone.cnf | 14 + .../suite/galera/t/galera_sst_clone.test | 20 + .../perfschema/r/privilege_table_io.result | 8 + scripts/CMakeLists.txt | 3 +- scripts/mysql_system_tables_fix.sql | 179 ++- scripts/mysql_system_users.sql | 37 +- scripts/wsrep_sst_clone.sh | 1197 +++++++++++++++++ scripts/wsrep_sst_common.sh | 82 +- scripts/wsrep_sst_xtrabackup-v2.sh | 15 +- sql/wsrep_sst.cc | 146 +- sql/wsrep_sst.h | 1 + 21 files changed, 2025 insertions(+), 139 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_sst_clone.result create mode 100644 mysql-test/suite/galera/t/galera_sst_clone.cnf create mode 100644 mysql-test/suite/galera/t/galera_sst_clone.test create mode 100755 scripts/wsrep_sst_clone.sh diff --git a/mysql-test/r/grant_dynamic.result b/mysql-test/r/grant_dynamic.result index 58dd2048414e..744cb856252f 100644 --- a/mysql-test/r/grant_dynamic.result +++ b/mysql-test/r/grant_dynamic.result @@ -18,6 +18,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -26,6 +27,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -51,7 +53,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -115,6 +122,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -123,6 +131,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -148,7 +157,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -211,6 +225,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -219,6 +234,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -244,7 +260,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -320,6 +341,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -328,6 +350,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -353,7 +376,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -421,6 +449,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -429,6 +458,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -454,7 +484,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -517,6 +552,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -525,6 +561,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -550,7 +587,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -636,6 +678,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -644,6 +687,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -669,7 +713,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -746,6 +795,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -754,6 +804,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -779,7 +830,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -864,6 +920,7 @@ mysql.infoschema localhost AUDIT_ABORT_EXEMPT N mysql.infoschema localhost FIREWALL_EXEMPT N mysql.infoschema localhost SYSTEM_USER N mysql.pxc.internal.session localhost APPLICATION_PASSWORD_ADMIN Y +mysql.pxc.internal.session localhost AUDIT_ABORT_EXEMPT Y mysql.pxc.internal.session localhost AUDIT_ADMIN Y mysql.pxc.internal.session localhost AUTHENTICATION_POLICY_ADMIN Y mysql.pxc.internal.session localhost BACKUP_ADMIN Y @@ -872,6 +929,7 @@ mysql.pxc.internal.session localhost BINLOG_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost CLONE_ADMIN Y mysql.pxc.internal.session localhost CONNECTION_ADMIN Y mysql.pxc.internal.session localhost ENCRYPTION_KEY_ADMIN Y +mysql.pxc.internal.session localhost FIREWALL_EXEMPT Y mysql.pxc.internal.session localhost FLUSH_OPTIMIZER_COSTS Y mysql.pxc.internal.session localhost FLUSH_STATUS Y mysql.pxc.internal.session localhost FLUSH_TABLES Y @@ -897,7 +955,12 @@ mysql.pxc.internal.session localhost SYSTEM_VARIABLES_ADMIN Y mysql.pxc.internal.session localhost TABLE_ENCRYPTION_ADMIN Y mysql.pxc.internal.session localhost TELEMETRY_LOG_ADMIN Y mysql.pxc.internal.session localhost XA_RECOVER_ADMIN Y -mysql.pxc.sst.role localhost BACKUP_ADMIN N +mysql.pxc.sst.role localhost BACKUP_ADMIN Y +mysql.pxc.sst.role localhost CLONE_ADMIN N +mysql.pxc.sst.role localhost CONNECTION_ADMIN N +mysql.pxc.sst.role localhost INNODB_REDO_LOG_ARCHIVE N +mysql.pxc.sst.role localhost SYSTEM_USER N +mysql.pxc.sst.role localhost SYSTEM_VARIABLES_ADMIN N mysql.session localhost AUDIT_ABORT_EXEMPT N mysql.session localhost AUTHENTICATION_POLICY_ADMIN N mysql.session localhost BACKUP_ADMIN N @@ -1486,7 +1549,7 @@ GRANT ALL ON *.* TO u1@localhost; SHOW GRANTS; Grants for u1@localhost GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `u1`@`localhost` -GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `u1`@`localhost` +GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ABORT_EXEMPT,AUDIT_ADMIN,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FIREWALL_EXEMPT,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `u1`@`localhost` REVOKE ALL ON *.* FROM CURRENT_USER(); SHOW GRANTS; Grants for u1@localhost diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result index e9d6cdc92cc1..aa2b702b2809 100644 --- a/mysql-test/r/mysql_upgrade.result +++ b/mysql-test/r/mysql_upgrade.result @@ -103,7 +103,7 @@ GRANT PROXY ON ``@`` TO `root`@`localhost` WITH GRANT OPTION SHOW GRANTS for 'mysql.pxc.internal.session'@localhost; Grants for mysql.pxc.internal.session@localhost GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `mysql.pxc.internal.session`@`localhost` WITH GRANT OPTION -GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `mysql.pxc.internal.session`@`localhost` WITH GRANT OPTION +GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ABORT_EXEMPT,AUDIT_ADMIN,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FIREWALL_EXEMPT,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `mysql.pxc.internal.session`@`localhost` WITH GRANT OPTION DROP USER u34068378; End of tests diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 1c0d5e143f72..945269a53fe7 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -2436,7 +2436,7 @@ wsrep-retry-autocommit 1 wsrep-slave-FK-checks TRUE wsrep-slave-UK-checks FALSE wsrep-slave-threads 1 -wsrep-sst-allowed-methods xtrabackup-v2 +wsrep-sst-allowed-methods xtrabackup-v2,clone wsrep-sst-donor wsrep-sst-donor-rejects-queries FALSE wsrep-sst-method xtrabackup-v2 diff --git a/mysql-test/r/ps_sys_upgrade.result b/mysql-test/r/ps_sys_upgrade.result index 2376d4f60a27..5f43905fb7fa 100644 --- a/mysql-test/r/ps_sys_upgrade.result +++ b/mysql-test/r/ps_sys_upgrade.result @@ -210,6 +210,7 @@ MODIFY User char(16) NOT NULL default '', MODIFY Grantor char(77) DEFAULT '' NOT NULL; Warnings: Warning 1265 Data truncated for column 'User' at row 1 +Warning 1265 Data truncated for column 'User' at row 2 ALTER TABLE mysql.columns_priv MODIFY User char(16) NOT NULL default ''; ALTER TABLE mysql.user diff --git a/mysql-test/r/transactional_acl_tables.result b/mysql-test/r/transactional_acl_tables.result index 8ce437129527..0a4ab4b21402 100644 --- a/mysql-test/r/transactional_acl_tables.result +++ b/mysql-test/r/transactional_acl_tables.result @@ -1347,6 +1347,7 @@ host db user table_name grantor table_priv column_priv h test u1 t1 root@localhost Select,Insert,Update,References h test u1 t2 root@localhost Insert localhost PERCONA_SCHEMA mysql.pxc.sst.role xtrabackup_history boot@ Select,Insert,Create,Alter +localhost mysql mysql.pxc.sst.role plugin boot@ Insert,Delete localhost mysql mysql.session user root@localhost Select localhost sys mysql.sys sys_config root@localhost Select DELETE FROM mysql.columns_priv WHERE host = 'h' AND user = 'u1' @@ -1358,6 +1359,7 @@ host db user table_name column_name column_priv SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv; host db user table_name grantor table_priv column_priv localhost PERCONA_SCHEMA mysql.pxc.sst.role xtrabackup_history boot@ Select,Insert,Create,Alter +localhost mysql mysql.pxc.sst.role plugin boot@ Insert,Delete localhost mysql mysql.session user root@localhost Select localhost sys mysql.sys sys_config root@localhost Select SHOW GRANTS FOR u1@h; diff --git a/mysql-test/suite/funcs_1/r/is_schema_privileges.result b/mysql-test/suite/funcs_1/r/is_schema_privileges.result index 74d2a466b038..9b83f227508c 100644 --- a/mysql-test/suite/funcs_1/r/is_schema_privileges.result +++ b/mysql-test/suite/funcs_1/r/is_schema_privileges.result @@ -54,6 +54,8 @@ FROM information_schema.schema_privileges WHERE table_catalog IS NOT NULL; GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE 'mysql.sys'@'localhost' def sys TRIGGER 'mysql.pxc.sst.role'@'localhost' def performance_schema SELECT +'mysql.pxc.sst.role'@'localhost' def performance_schema INSERT +'mysql.pxc.sst.role'@'localhost' def performance_schema UPDATE 'mysql.session'@'localhost' def performance_schema SELECT 'mysql.pxc.sst.role'@'localhost' def PERCONA_SCHEMA CREATE ''@'%' def test SELECT diff --git a/mysql-test/suite/funcs_1/r/is_table_privileges.result b/mysql-test/suite/funcs_1/r/is_table_privileges.result index d962155d4d83..f1dbe4a591fa 100644 --- a/mysql-test/suite/funcs_1/r/is_table_privileges.result +++ b/mysql-test/suite/funcs_1/r/is_table_privileges.result @@ -59,6 +59,8 @@ def PERCONA_SCHEMA xtrabackup_history ALTER def PERCONA_SCHEMA xtrabackup_history CREATE def PERCONA_SCHEMA xtrabackup_history INSERT def PERCONA_SCHEMA xtrabackup_history SELECT +def mysql plugin DELETE +def mysql plugin INSERT def mysql user SELECT def sys sys_config SELECT ###################################################################### diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result index 003d2b5494a6..5181e22325e5 100644 --- a/mysql-test/suite/galera/r/galera_defaults.result +++ b/mysql-test/suite/galera/r/galera_defaults.result @@ -49,7 +49,7 @@ WSREP_SLAVE_FK_CHECKS ON WSREP_SLAVE_THREADS 1 WSREP_SLAVE_UK_CHECKS OFF WSREP_SR_STORE table -WSREP_SST_ALLOWED_METHODS xtrabackup-v2 +WSREP_SST_ALLOWED_METHODS xtrabackup-v2,clone WSREP_SST_DONOR WSREP_SST_DONOR_REJECTS_QUERIES OFF WSREP_SST_METHOD xtrabackup-v2 diff --git a/mysql-test/suite/galera/r/galera_sst_allowed_methods.result b/mysql-test/suite/galera/r/galera_sst_allowed_methods.result index 0173bbe0446b..d594fa00ddb0 100644 --- a/mysql-test/suite/galera/r/galera_sst_allowed_methods.result +++ b/mysql-test/suite/galera/r/galera_sst_allowed_methods.result @@ -92,8 +92,8 @@ Restarting nodes to satisfy MTR's end-of-test checks # restart SHOW VARIABLES LIKE 'wsrep_sst_allowed_methods'; Variable_name Value -wsrep_sst_allowed_methods xtrabackup-v2 +wsrep_sst_allowed_methods xtrabackup-v2,clone # restart SHOW VARIABLES LIKE 'wsrep_sst_allowed_methods'; Variable_name Value -wsrep_sst_allowed_methods xtrabackup-v2 +wsrep_sst_allowed_methods xtrabackup-v2,clone diff --git a/mysql-test/suite/galera/r/galera_sst_clone.result b/mysql-test/suite/galera/r/galera_sst_clone.result new file mode 100644 index 000000000000..71f7d9a33669 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_sst_clone.result @@ -0,0 +1,362 @@ +Performing State Transfer on a server that has been shut down cleanly and restarted +CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +COMMIT; +Shutting down server ... +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +COMMIT; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +Starting server ... +# restart +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +COMMIT; +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +COMMIT; +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +ROLLBACK; +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +COMMIT; +SET AUTOCOMMIT=ON; +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +DROP TABLE t1; +COMMIT; +SET AUTOCOMMIT=ON; +Performing State Transfer on a server that starts from a clean var directory +This is accomplished by shutting down node #2 and removing its var directory before restarting it +CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +COMMIT; +Shutting down server ... +Cleaning var directory ... +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +COMMIT; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +Starting server ... +# restart +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +COMMIT; +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +COMMIT; +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +ROLLBACK; +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +COMMIT; +SET AUTOCOMMIT=ON; +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +DROP TABLE t1; +COMMIT; +SET AUTOCOMMIT=ON; +Performing State Transfer on a server that has been killed and restarted +CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +COMMIT; +Killing server ... +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +INSERT INTO t1 VALUES ('node1_committed_during'); +COMMIT; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +Performing --wsrep-recover ... +Starting server ... +Using --wsrep-start-position when starting mysqld ... +# restart: --wsrep-start-position= +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +INSERT INTO t1 VALUES ('node2_committed_after'); +COMMIT; +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 VALUES ('node1_to_be_committed_after'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +INSERT INTO t1 VALUES ('node1_committed_after'); +COMMIT; +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 VALUES ('node1_to_be_rollbacked_after'); +ROLLBACK; +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +COMMIT; +SET AUTOCOMMIT=ON; +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +DROP TABLE t1; +COMMIT; +SET AUTOCOMMIT=ON; +Performing State Transfer on a server that has been killed and restarted +while a DDL was in progress on it +CREATE TABLE t1 (f1 CHAR(255)) ENGINE=InnoDB; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +INSERT INTO t1 VALUES ('node1_committed_before'); +START TRANSACTION; +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +INSERT INTO t1 VALUES ('node2_committed_before'); +COMMIT; +SET GLOBAL debug = 'd,sync.alter_opened_table'; +ALTER TABLE t1 ADD COLUMN f2 INTEGER; +SET wsrep_sync_wait = 0; +Killing server ... +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 (f1) VALUES ('node1_committed_during'); +INSERT INTO t1 (f1) VALUES ('node1_committed_during'); +INSERT INTO t1 (f1) VALUES ('node1_committed_during'); +INSERT INTO t1 (f1) VALUES ('node1_committed_during'); +INSERT INTO t1 (f1) VALUES ('node1_committed_during'); +COMMIT; +START TRANSACTION; +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +Performing --wsrep-recover ... +Starting server ... +Using --wsrep-start-position when starting mysqld ... +# restart: --wsrep-start-position= +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 (f1) VALUES ('node2_committed_after'); +INSERT INTO t1 (f1) VALUES ('node2_committed_after'); +INSERT INTO t1 (f1) VALUES ('node2_committed_after'); +INSERT INTO t1 (f1) VALUES ('node2_committed_after'); +INSERT INTO t1 (f1) VALUES ('node2_committed_after'); +COMMIT; +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_committed_after'); +COMMIT; +SET AUTOCOMMIT=OFF; +START TRANSACTION; +INSERT INTO t1 (f1) VALUES ('node1_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_committed_after'); +INSERT INTO t1 (f1) VALUES ('node1_committed_after'); +COMMIT; +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +INSERT INTO t1 (f1) VALUES ('node1_to_be_rollbacked_after'); +ROLLBACK; +SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1'; +COUNT(*) = 2 +1 +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +COMMIT; +SET AUTOCOMMIT=ON; +SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1'; +COUNT(*) = 2 +1 +SELECT COUNT(*) = 35 FROM t1; +COUNT(*) = 35 +1 +SELECT COUNT(*) = 0 FROM (SELECT COUNT(*) AS c, f1 FROM t1 GROUP BY f1 HAVING c NOT IN (5, 10)) AS a1; +COUNT(*) = 0 +1 +DROP TABLE t1; +COMMIT; +SET AUTOCOMMIT=ON; diff --git a/mysql-test/suite/galera/t/galera_sst_clone.cnf b/mysql-test/suite/galera/t/galera_sst_clone.cnf new file mode 100644 index 000000000000..c49f40e8e937 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sst_clone.cnf @@ -0,0 +1,14 @@ +!include ../galera_2nodes.cnf + +[mysqld] +wsrep_sst_method=clone +wsrep_debug=1 + +[mysqld.1] +wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcache.size=1;pc.ignore_sb=true' + +[mysqld.2] +wsrep_provider_options='base_port=@mysqld.2.#galera_port;gcache.size=1;pc.ignore_sb=true' + +[sst] +wsrep-debug=true diff --git a/mysql-test/suite/galera/t/galera_sst_clone.test b/mysql-test/suite/galera/t/galera_sst_clone.test new file mode 100644 index 000000000000..87c2f9265767 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sst_clone.test @@ -0,0 +1,20 @@ +--source include/big_test.inc +--source include/galera_cluster.inc + +--source suite/galera/include/galera_st_shutdown_slave.inc +--source suite/galera/include/galera_st_clean_slave.inc + +--source suite/galera/include/galera_st_kill_slave.inc +--source suite/galera/include/galera_st_kill_slave_ddl.inc + +--remove_file $MYSQL_TMP_DIR/galera_wsrep_recover.log + +# Install suppressions at the end of the test. This is important especially for node_2 +# because clone SST transfers only InnoDB part. +--disable_query_log +--connection node_1 +CALL mtr.add_suppression("Failed to remove page file"); +--connection node_2 +CALL mtr.add_suppression("Failed to remove page file"); +--enable_query_log + diff --git a/mysql-test/suite/perfschema/r/privilege_table_io.result b/mysql-test/suite/perfschema/r/privilege_table_io.result index a7f0c7008a0a..563740fc6ccd 100644 --- a/mysql-test/suite/perfschema/r/privilege_table_io.result +++ b/mysql-test/suite/perfschema/r/privilege_table_io.result @@ -151,12 +151,20 @@ wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql global_grants fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql role_edges fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql default_roles fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql tables_priv fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql tables_priv fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql tables_priv fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql tables_priv fetch 1 +wait/io/table/sql/handler handler.cc: TABLE mysql tables_priv fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql procs_priv fetch 1 wait/io/table/sql/handler handler.cc: TABLE mysql servers fetch 1 wait/io/table/sql/handler handler.cc: TABLE test marker insert 1 diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index e589b303fe8c..050d0ad80d38 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -406,7 +406,7 @@ IF(WIN32) ELSE() IF(WITH_WSREP) - SET(WSREP_BINARIES wsrep_sst_common wsrep_sst_xtrabackup-v2 clustercheck pyclustercheck) + SET(WSREP_BINARIES wsrep_sst_common wsrep_sst_xtrabackup-v2 clustercheck pyclustercheck wsrep_sst_clone) ENDIF() SET(PKGCONFIG_FILE ${LIBMYSQL_OS_OUTPUT_NAME}.pc) @@ -459,6 +459,7 @@ ELSE() ${BIN_SCRIPTS} wsrep_sst_common wsrep_sst_xtrabackup-v2 + wsrep_sst_clone ) ENDIF() diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index aea8625d1c7c..47f34462d93a 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -619,142 +619,142 @@ DROP PREPARE stmt; -- Add the privilege XA_RECOVER_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige XA_RECOVER_ADMIN. -SET @hadXARecoverAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'XA_RECOVER_ADMIN'); +SET @hadXARecoverAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'XA_RECOVER_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'XA_RECOVER_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadXARecoverAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadXARecoverAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege CLONE_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilege CLONE_ADMIN. -SET @hadCloneAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'CLONE_ADMIN'); +SET @hadCloneAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'CLONE_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'CLONE_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadCloneAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadCloneAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege INNODB_REDO_LOG_ENABLE for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilege INNODB_REDO_LOG_ENABLE. -SET @hadRedoLogEnablePriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'INNODB_REDO_LOG_ENABLE'); +SET @hadRedoLogEnablePriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'INNODB_REDO_LOG_ENABLE' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'INNODB_REDO_LOG_ENABLE', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadRedoLogEnablePriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadRedoLogEnablePriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege BACKUP_ADMIN for every user who has the privilege RELOAD -- provided that there isn't a user who already has the privilege BACKUP_ADMIN. -SET @hadBackupAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'BACKUP_ADMIN'); +SET @hadBackupAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'BACKUP_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'BACKUP_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE Reload_priv = 'Y' AND @hadBackupAdminPriv = 0; +FROM mysql.user WHERE Reload_priv = 'Y' AND @hadBackupAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege INNODB_REDO_LOG_ARCHIVE for every user who has the privilege BACKUP_ADMIN -- provided that there isn't a user who already has the privilege INNODB_REDO_LOG_ARCHIVE. -SET @hadInnodbRedoLogArchivePriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'INNODB_REDO_LOG_ARCHIVE'); +SET @hadInnodbRedoLogArchivePriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'INNODB_REDO_LOG_ARCHIVE' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'INNODB_REDO_LOG_ARCHIVE', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE Reload_priv = 'Y' AND @hadInnodbRedoLogArchivePriv = 0; +FROM mysql.user WHERE Reload_priv = 'Y' AND @hadInnodbRedoLogArchivePriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege RESOURCE_GROUP_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilege RESOURCE_GROUP_ADMIN. -SET @hadResourceGroupAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'RESOURCE_GROUP_ADMIN'); +SET @hadResourceGroupAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'RESOURCE_GROUP_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'RESOURCE_GROUP_ADMIN', -IF(grant_priv = 'Y', 'Y', 'N') FROM mysql.user WHERE super_priv = 'Y' AND @hadResourceGroupAdminPriv = 0; +IF(grant_priv = 'Y', 'Y', 'N') FROM mysql.user WHERE super_priv = 'Y' AND @hadResourceGroupAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege SERVICE_CONNECTION_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilege SERVICE_CONNECTION_ADMIN. -SET @hadServiceConnectionAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SERVICE_CONNECTION_ADMIN'); +SET @hadServiceConnectionAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SERVICE_CONNECTION_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'SERVICE_CONNECTION_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadServiceConnectionAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadServiceConnectionAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); -- Add the privilege APPLICATION_PASSWORD_ADMIN for every user who has the -- privilege CREATE USER provided that there isn't a user who already has -- privilege APPLICATION_PASSWORD_ADMIN -SET @hadApplicationPasswordAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'APPLICATION_PASSWORD_ADMIN'); +SET @hadApplicationPasswordAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'APPLICATION_PASSWORD_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'APPLICATION_PASSWORD_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE Create_user_priv = 'Y' AND @hadApplicationPasswordAdminPriv = 0; +FROM mysql.user WHERE Create_user_priv = 'Y' AND @hadApplicationPasswordAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege AUDIT_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige AUDIT_ADMIN. -SET @hadAuditAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'AUDIT_ADMIN'); +SET @hadAuditAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'AUDIT_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'AUDIT_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadAuditAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadAuditAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege TELEMETRY_LOG_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige TELEMETRY_LOG_ADMIN. -SET @hadAuditAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'TELEMETRY_LOG_ADMIN'); +SET @hadAuditAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'TELEMETRY_LOG_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'TELEMETRY_LOG_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadAuditAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadAuditAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege BINLOG_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige BINLOG_ADMIN. -SET @hadBinLogAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'BINLOG_ADMIN'); +SET @hadBinLogAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'BINLOG_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'BINLOG_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadBinLogAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadBinLogAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege BINLOG_ENCRYPTION_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige BINLOG_ENCRYPTION_ADMIN. -SET @hadBinLogEncryptionAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'BINLOG_ENCRYPTION_ADMIN'); +SET @hadBinLogEncryptionAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'BINLOG_ENCRYPTION_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'BINLOG_ENCRYPTION_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadBinLogEncryptionAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadBinLogEncryptionAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege CONNECTION_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige CONNECTION_ADMIN. -SET @hadConnectionAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'CONNECTION_ADMIN'); +SET @hadConnectionAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'CONNECTION_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'CONNECTION_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadConnectionAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadConnectionAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege ENCRYPTION_KEY_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige ENCRYPTION_KEY_ADMIN. -SET @hadEncryptionKeyAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'ENCRYPTION_KEY_ADMIN'); +SET @hadEncryptionKeyAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'ENCRYPTION_KEY_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'ENCRYPTION_KEY_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadEncryptionKeyAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadEncryptionKeyAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege GROUP_REPLICATION_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige GROUP_REPLICATION_ADMIN. -SET @hadGroupReplicationAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'GROUP_REPLICATION_ADMIN'); +SET @hadGroupReplicationAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'GROUP_REPLICATION_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'GROUP_REPLICATION_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadGroupReplicationAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadGroupReplicationAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege PERSIST_RO_VARIABLES_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige PERSIST_RO_VARIABLES_ADMIN. -SET @hadPersistRoVariablesAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'PERSIST_RO_VARIABLES_ADMIN'); +SET @hadPersistRoVariablesAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'PERSIST_RO_VARIABLES_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'PERSIST_RO_VARIABLES_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadPersistRoVariablesAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadPersistRoVariablesAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege REPLICATION_SLAVE_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige REPLICATION_SLAVE_ADMIN. -SET @hadReplicationSlaveAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'REPLICATION_SLAVE_ADMIN'); +SET @hadReplicationSlaveAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'REPLICATION_SLAVE_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'REPLICATION_SLAVE_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadReplicationSlaveAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadReplicationSlaveAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege RESOURCE_GROUP_USER for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige RESOURCE_GROUP_USER. -SET @hadResourceGroupUserPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'RESOURCE_GROUP_USER'); +SET @hadResourceGroupUserPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'RESOURCE_GROUP_USER' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'RESOURCE_GROUP_USER', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadResourceGroupUserPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadResourceGroupUserPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege ROLE_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige ROLE_ADMIN. -SET @hadRoleAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'ROLE_ADMIN'); +SET @hadRoleAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'ROLE_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'ROLE_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadRoleAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadRoleAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege SESSION_VARIABLES_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige SESSION_VARIABLES_ADMIN. -SET @hadSessionVariablesAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SESSION_VARIABLES_ADMIN'); +SET @hadSessionVariablesAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SESSION_VARIABLES_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'SESSION_VARIABLES_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadSessionVariablesAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadSessionVariablesAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege SET_USER_ID for every user who has the privilege SUPER @@ -766,9 +766,9 @@ COMMIT; -- Add the privilege SYSTEM_VARIABLES_ADMIN for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige SYSTEM_VARIABLES_ADMIN. -SET @hadSystemVariablesAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SYSTEM_VARIABLES_ADMIN'); +SET @hadSystemVariablesAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SYSTEM_VARIABLES_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'SYSTEM_VARIABLES_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadSystemVariablesAdminPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadSystemVariablesAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege SYSTEM_USER for every user who has privilege SET_USER_ID privilege @@ -779,9 +779,9 @@ COMMIT; -- Add the privilege SYSTEM_USER for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilege SYSTEM_USER -SET @hadSystemUserPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SYSTEM_USER'); +SET @hadSystemUserPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SYSTEM_USER' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'SYSTEM_USER', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadSystemUserPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadSystemUserPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege TABLE_ENCRYPTION_ADMIN for every user who has the privilege SUPER @@ -797,9 +797,9 @@ COMMIT; -- Add the privilege REPLICATION_APPLIER for every user who has the privilege SUPER -- provided that there isn't a user who already has the privilige REPLICATION_APPLIER. -SET @hadReplicationApplierPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'REPLICATION_APPLIER'); +SET @hadReplicationApplierPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'REPLICATION_APPLIER' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'REPLICATION_APPLIER', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE super_priv = 'Y' AND @hadReplicationApplierPriv = 0; +FROM mysql.user WHERE super_priv = 'Y' AND @hadReplicationApplierPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege SHOW_ROUTINE for every user who has global SELECT privilege @@ -811,16 +811,16 @@ COMMIT; -- Add the privilege AUTHENTICATION_POLICY_ADMIN for every user who has the SYSTEM_VARIABLES_ADMIN privilege -- provided that there isn't a user who already has the privilege AUTHENTICATION_POLICY_ADMIN. -SET @hadAuthenticationPolicyAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'AUTHENTICATION_POLICY_ADMIN'); +SET @hadAuthenticationPolicyAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'AUTHENTICATION_POLICY_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT mu.user, mu.host, 'AUTHENTICATION_POLICY_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user mu, global_grants gg WHERE mu.user = gg.user AND gg.priv = 'SYSTEM_VARIABLES_ADMIN' AND @hadAuthenticationPolicyAdminPriv = 0; +FROM mysql.user mu, global_grants gg WHERE mu.user = gg.user AND gg.priv = 'SYSTEM_VARIABLES_ADMIN' AND @hadAuthenticationPolicyAdminPriv = 0 AND mu.user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- Add the privilege PASSWORDLESS_USER_ADMIN for every user who has the privilege CREATE USER -- provided that there isn't a user who already has the privilege PASSWORDLESS_USER_ADMIN. -SET @hadPasswordlessUserAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'PASSWORDLESS_USER_ADMIN'); +SET @hadPasswordlessUserAdminPriv = (SELECT COUNT(*) FROM global_grants WHERE priv = 'PASSWORDLESS_USER_ADMIN' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO global_grants SELECT user, host, 'PASSWORDLESS_USER_ADMIN', IF(grant_priv = 'Y', 'Y', 'N') -FROM mysql.user WHERE Create_user_priv = 'Y' AND @hadPasswordlessUserAdminPriv = 0; +FROM mysql.user WHERE Create_user_priv = 'Y' AND @hadPasswordlessUserAdminPriv = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; # Activate the new, possible modified privilege tables @@ -1331,52 +1331,103 @@ DROP PREPARE stmt; # #INSERT IGNORE INTO mysql.user VALUES ('localhost','mysql.pxc.internal.session','N','N','N','N','N','N','Y','N','N','N','Y','N','N','N','N','Y','N','N','N','N','N','N','N','N','N','Y','N','N','N','','','','',0,0,0,0,'caching_sha2_password','$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED','N',CURRENT_TIMESTAMP, NULL,'Y','N','N',NULL,NULL,NULL,NULL); +# The above commented INSERT IGNORE INTO should be enough, but check the comment about +# 'bugs with roles' in mysql_system_users.sql where we create mysql.pxc.internal.session. +# This is why below we do what 'GRANT ALL PRIVILEGES ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION;' +# does. +# +# ================ IMPORTANT: ================ +# +# Note that this file contains multiple updates to dynamic privileges in sections like +# -- Add the privilege SOME_PRIVILEGE for every user who has the privilege OTHER_PRIVILEGE +# and we don't want mysql.pxc.internal.session or mysql.pxc.internal.session cause them not to work. +# It means that every such privilege update should exclude the above two users from consideration +# adding AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role') clause. +# # (Use this since the CREATE DATABASE doesn't work when the grant is in a role) # So we assign the mysql.pxc.internal.session root privileges with grant option # These are the values for # GRANT ALL PRIVILEGES ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; -# GRANT BACKUP_ADMIN, LOCK TABLES, PROCESS, RELOAD, REPLICATION CLIENT, SUPER ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; # INSERT IGNORE INTO mysql.user VALUES ('localhost','mysql.pxc.internal.session','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'caching_sha2_password','$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED','N',CURRENT_TIMESTAMP,NULL,'Y','Y','Y',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'AUDIT_ABORT_EXEMPT', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'AUDIT_ADMIN', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'AUTHENTICATION_POLICY_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'BACKUP_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'BINLOG_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'BINLOG_ENCRYPTION_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'CLONE_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'CONNECTION_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'ENCRYPTION_KEY_ADMIN', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'FIREWALL_EXEMPT', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'FLUSH_OPTIMIZER_COSTS', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'FLUSH_STATUS', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'FLUSH_TABLES', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'FLUSH_USER_RESOURCES', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'GROUP_REPLICATION_ADMIN', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'GROUP_REPLICATION_STREAM', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'INNODB_REDO_LOG_ARCHIVE', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'INNODB_REDO_LOG_ENABLE', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'PASSWORDLESS_USER_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'PERSIST_RO_VARIABLES_ADMIN', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'REPLICATION_APPLIER', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'REPLICATION_SLAVE_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'RESOURCE_GROUP_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'RESOURCE_GROUP_USER', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'ROLE_ADMIN', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SENSITIVE_VARIABLES_OBSERVER', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SERVICE_CONNECTION_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SESSION_VARIABLES_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SET_USER_ID', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SHOW_ROUTINE', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SYSTEM_USER', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'SYSTEM_VARIABLES_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'TABLE_ENCRYPTION_ADMIN', 'Y'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'TELEMETRY_LOG_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.internal.session', 'localhost', 'XA_RECOVER_ADMIN', 'Y'); # # mysql.pxc.sst.role -# See the comments in mysql_system_tables.sql +# See the comments in mysql_system_users.sql +# Below we try to restore mysql.pxc.sst.role if someone deleted it -# These are the values for -# GRANT BACKUP_ADMIN, LOCK TABLES, PROCESS, RELOAD, REPLICATION CLIENT, SUPER, INNODB_REDO_LOG_ARCHIVE ON *.* TO 'mysql.pxc.sst.role'@localhost; +# These are the values for sst_xtrabackup +# (needed by sst_xtrabackup): +# GRANT BACKUP_ADMIN, INNODB_REDO_LOG_ARCHIVE, LOCK TABLES, PROCESS, RELOAD, REPLICATION CLIENT, SUPER ON *.* TO 'mysql.pxc.sst.role'@localhost; # GRANT ALTER, CREATE, SELECT, INSERT ON PERCONA_SCHEMA.xtrabackup_history TO 'mysql.pxc.sst.role'@localhost; # GRANT SELECT ON performance_schema.* TO 'mysql.pxc.sst.role'@localhost; # GRANT CREATE ON PERCONA_SCHEMA.* to 'mysql.pxc.sst.role'@localhost; -INSERT IGNORE INTO mysql.user VALUES ('localhost','mysql.pxc.sst.role','N','N','N','N','N','N','Y','N','Y','N','N','N','N','N','N','Y','N','Y','N','N','Y','N','N','N','N','N','N','N','N','','','','',0,0,0,0,'caching_sha2_password','','Y',CURRENT_TIMESTAMP,NULL,'Y','N','N',NULL,NULL,NULL,NULL); -INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'BACKUP_ADMIN', 'N'); +# +# (additionally needed by sst_clone): +# GRANT BACKUP_ADMIN, EXECUTE ON *.* TO 'mysql.pxc.sst.role'@localhost WITH GRANT OPTION; +# GRANT SELECT ON performance_schema.* TO 'mysql.pxc.sst.role'@localhost WITH GRANT OPTION; +# +# Note: CREATE USER enables CREATE_ROLE and DROP_ROLE +# GRANT CLONE_ADMIN, SYSTEM_USER, SHUTDOWN, CONNECTION_ADMIN, CREATE USER, CREATE ROLE, DROP ROLE, SYSTEM_VARIABLES_ADMIN ON *.* TO 'mysql.pxc.sst.role'@localhost; +# GRANT INSERT, DELETE ON mysql.plugin TO 'mysql.pxc.sst.role'@localhost; +# GRANT UPDATE, INSERT ON performance_schema.* TO 'mysql.pxc.sst.role'@localhost; + +# common +INSERT IGNORE INTO mysql.user VALUES ('localhost','mysql.pxc.sst.role','N','N','N','N','N','N','Y','Y','Y','N','Y','N','N','N','N','Y','N','Y','Y','N','Y','N','N','N','N','Y','N','N','N','','','','',0,0,0,0,'caching_sha2_password','','Y',CURRENT_TIMESTAMP,NULL,'Y','Y','Y',NULL,NULL,NULL,NULL); + +# pxb-sst +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'BACKUP_ADMIN', 'Y'); INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'INNODB_REDO_LOG_ARCHIVE', 'N'); INSERT IGNORE INTO mysql.tables_priv VALUES ('localhost', 'PERCONA_SCHEMA', 'mysql.pxc.sst.role', 'xtrabackup_history', 'root\@localhost', CURRENT_TIMESTAMP, 'Alter,Select,Insert,Create', ''); -INSERT IGNORE INTO mysql.db VALUES ('localhost', 'performance_schema', 'mysql.pxc.sst.role','Y','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N'); INSERT IGNORE INTO mysql.db VALUES ('localhost', 'PERCONA_SCHEMA', 'mysql.pxc.sst.role','N','N','N','N','Y','N','N','N','N','N','N','N','N','N','N','N','N','N','N'); +# common +INSERT IGNORE INTO mysql.db VALUES ('localhost', 'performance_schema', 'mysql.pxc.sst.role','Y','Y','Y','N','N','N','Y','N','N','N','N','N','N','N','N','N','N','N','N'); + +# clone-sst +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'CLONE_ADMIN', 'N'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'SYSTEM_USER', 'N'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'CONNECTION_ADMIN', 'N'); +INSERT IGNORE INTO mysql.global_grants VALUES ('mysql.pxc.sst.role', 'localhost', 'SYSTEM_VARIABLES_ADMIN', 'N'); +INSERT IGNORE INTO mysql.tables_priv VALUES ('localhost', 'mysql', 'mysql.pxc.sst.role', 'plugin', 'root\@localhost', CURRENT_TIMESTAMP, 'Insert,Delete', ''); + #! PXC_SECTION::END # flush privileges at this stage can cause problem with upgrade from 57 -> 80 # FLUSH PRIVILEGES; @@ -1735,23 +1786,23 @@ ALTER TABLE time_zone MODIFY Use_leap_seconds enum('Y','N') COLLATE utf8mb3_general_ci DEFAULT 'N' NOT NULL; -- grant AUDIT_ABORT_EXEMPT to all current holders of SYSTEM_USER -SET @hadAuditAbortExempt = (SELECT COUNT(*) FROM global_grants WHERE priv = 'AUDIT_ABORT_EXEMPT'); +SET @hadAuditAbortExempt = (SELECT COUNT(*) FROM global_grants WHERE priv = 'AUDIT_ABORT_EXEMPT' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO mysql.global_grants SELECT user, host, 'AUDIT_ABORT_EXEMPT', IF (WITH_GRANT_OPTION = 'Y', 'Y', 'N') - FROM mysql.global_grants WHERE priv = 'SYSTEM_USER' AND @hadAuditAbortExempt = 0; + FROM mysql.global_grants WHERE priv = 'SYSTEM_USER' AND @hadAuditAbortExempt = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); -- grant FIREWALL_EXEMPT to all current holders of SYSTEM_USER -SET @hadFirewallExempt = (SELECT COUNT(*) FROM global_grants WHERE priv = 'FIREWALL_EXEMPT'); +SET @hadFirewallExempt = (SELECT COUNT(*) FROM global_grants WHERE priv = 'FIREWALL_EXEMPT' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO mysql.global_grants SELECT user, host, 'FIREWALL_EXEMPT', IF (WITH_GRANT_OPTION = 'Y', 'Y', 'N') - FROM mysql.global_grants WHERE priv = 'SYSTEM_USER' AND @hadFirewallExempt = 0; + FROM mysql.global_grants WHERE priv = 'SYSTEM_USER' AND @hadFirewallExempt = 0 AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role'); -- Add the privilege SENSITIVE_VARIABLES_OBSERVER for every user who has the SYSTEM_VARIABLES_ADMIN privilege -- provided that there isn't a user who already has the privilege SENSITIVE_VARIABLES_OBSERVER. -SET @hadSensitiveVariablesAdmin = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SENSITIVE_VARIABLES_OBSERVER'); +SET @hadSensitiveVariablesAdmin = (SELECT COUNT(*) FROM global_grants WHERE priv = 'SENSITIVE_VARIABLES_OBSERVER' AND user NOT IN ('mysql.pxc.internal.session','mysql.pxc.sst.role')); INSERT INTO mysql.global_grants SELECT user, host, 'SENSITIVE_VARIABLES_OBSERVER', IF (WITH_GRANT_OPTION = 'Y', 'Y', 'N') - FROM mysql.global_grants WHERE priv = 'SYSTEM_VARIABLES_ADMIN' AND @hadSensitiveVariablesAdmin = 0 AND user NOT IN ('mysql.infoschema','mysql.session','mysql.sys'); + FROM mysql.global_grants WHERE priv = 'SYSTEM_VARIABLES_ADMIN' AND @hadSensitiveVariablesAdmin = 0 AND user NOT IN ('mysql.infoschema','mysql.session','mysql.sys','mysql.pxc.internal.session','mysql.pxc.sst.role'); COMMIT; -- add the PK for mysql.firewall_membership, if missing and if the table is present @@ -1783,4 +1834,4 @@ ALTER TABLE mysql.columns_priv DROP PRIMARY KEY, ALTER TABLE mysql.procs_priv DROP PRIMARY KEY, ADD PRIMARY KEY (`Host`,`User`,`Db`,`Routine_name`,`Routine_type`); -SET @@session.sql_mode = @old_sql_mode; +SET @@session.sql_mode = @old_sql_mode; \ No newline at end of file diff --git a/scripts/mysql_system_users.sql b/scripts/mysql_system_users.sql index f8ba96b9a691..75dd261af992 100644 --- a/scripts/mysql_system_users.sql +++ b/scripts/mysql_system_users.sql @@ -77,25 +77,46 @@ CREATE USER 'mysql.pxc.internal.session'@localhost IDENTIFIED WITH caching_sha2_ REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysql.pxc.internal.session'@localhost; -- Due to bugs with roles, we need to grant superuser access here +-- If the new dynamic privilege is introduced, remember to include it in the corresponding +-- part in mysql_syste_tables_fix.sql GRANT ALL PRIVILEGES ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; -GRANT BACKUP_ADMIN, LOCK TABLES, PROCESS, RELOAD, REPLICATION CLIENT, SUPER ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; + +-- TODO: Investigate why commit 7e467c54 claims that there is a "bug with roles" +-- and grants all privileges. It should be enough to use only the following grants +-- as per the comment above. +-- We need mysql.pxc.internal.session only for one purpose: create SST user +-- and grant mysql.pxc.sst.role role to it. -- GRANT CREATE USER ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; -- GRANT SUPER ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; -- GRANT RELOAD ON *.* TO 'mysql.pxc.internal.session'@localhost WITH GRANT OPTION; - -- Create the PXC SST role -- This role is used by the SST user during an SST (on the donor) -- These are the permissions needed to backup the database (using Percona XtraBackup) -- See https://www.percona.com/doc/percona-xtrabackup/8.0/using_xtrabackup/privileges.html CREATE ROLE 'mysql.pxc.sst.role'@localhost; REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysql.pxc.sst.role'@localhost; -GRANT BACKUP_ADMIN, LOCK TABLES, PROCESS, RELOAD, REPLICATION CLIENT, SUPER, INNODB_REDO_LOG_ARCHIVE ON *.* - TO 'mysql.pxc.sst.role'@localhost; -GRANT ALTER, CREATE, SELECT, INSERT ON PERCONA_SCHEMA.xtrabackup_history - TO 'mysql.pxc.sst.role'@localhost; --- For some reason this is also needed, although the docs say BACKUP_ADMIN is enough -GRANT SELECT ON performance_schema.* TO 'mysql.pxc.sst.role'@localhost; + +-- Needed by sst_xtrabackup-v2 +GRANT LOCK TABLES, PROCESS, RELOAD, REPLICATION CLIENT, SUPER, INNODB_REDO_LOG_ARCHIVE ON *.* TO 'mysql.pxc.sst.role'@localhost; +GRANT ALTER, CREATE, SELECT, INSERT ON PERCONA_SCHEMA.xtrabackup_history TO 'mysql.pxc.sst.role'@localhost; + +-- Common. WITH GRANT OPTION is needed by clone-sst script +GRANT BACKUP_ADMIN, EXECUTE ON *.* TO 'mysql.pxc.sst.role'@localhost WITH GRANT OPTION; + +-- The reason why we need SELECT on performance_schema.* is because both the SST script +-- and PXB need to query keyring status and log_status PFS tables respectively. +GRANT SELECT ON performance_schema.* TO 'mysql.pxc.sst.role'@localhost WITH GRANT OPTION; + +-- Additional grants needed by sst_clone +-- Together with CREATE USER, grant CREATE/DROP ROLE, because during the server upgrade +-- they will be set anyway (and it affects mtr_check) +-- See mysql_system_tables_fix.sql: +-- UPDATE user SET Create_role_priv= 'Y', Drop_role_priv= 'Y' WHERE Create_user_priv = 'Y'; +GRANT CLONE_ADMIN, SYSTEM_USER, SHUTDOWN, CONNECTION_ADMIN, CREATE USER, CREATE ROLE, DROP ROLE, SYSTEM_VARIABLES_ADMIN ON *.* TO 'mysql.pxc.sst.role'@localhost; +GRANT INSERT, DELETE ON mysql.plugin TO 'mysql.pxc.sst.role'@localhost; +GRANT UPDATE, INSERT ON performance_schema.* TO 'mysql.pxc.sst.role'@localhost; + -- Need this to create the PERCONA_SCHEMA database if needed GRANT CREATE ON PERCONA_SCHEMA.* to 'mysql.pxc.sst.role'@localhost; diff --git a/scripts/wsrep_sst_clone.sh b/scripts/wsrep_sst_clone.sh new file mode 100755 index 000000000000..f9de46bf6589 --- /dev/null +++ b/scripts/wsrep_sst_clone.sh @@ -0,0 +1,1197 @@ +#!/usr/bin/env bash +# Copyright (C) 2024 Percona Inc +# +# 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; version 2 of the License. +# +# 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; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston +# MA 02110-1301 USA. + +############################################################################# +# While this script is based on clone script from Codership, we have +# done many changes to adapt it to PXC +#=========================================================================== +# ┌──────────────────┐ +# │ Joiner starts │ +# └────────┬─────────┘ +# ┌────────▼─────────┐ +# │Open NetCat listnr│ +# └────────┬─────────┘ +# ┌────────▼─────────┐ +# │Send message to Dn│ +# └────────┬─────────┘ +# ┌────────▼─────────┐ +# │Donor decide IST │ +# │or SST. Send msg │ +# │through NC and │ +# │waits for Joiner │ +# │Clone instance │ +# └────────┬─────────┘ +# ┌────────▼─────────┐ +# │Joiner get IST │ +# │or SST.If IST │ +# │Bypass all and │ +# │Wait for IST. │ +# │SST Start clone │ +# │Instance and wait │ +# │for donor to clone│ +# └─────────┬────────┘ +# ┌─────────▼─────────────┐ +# │Clone process is │ +# │reported. │ +# │When done Dn waits │ +# │Joiner close instance │ +# │And performs 2 restarts│ +# └──────────┬────────────┘ +# ┌──────────▼──────────────┐ +# │1) To fix dictionary & │ +# │ Final cleanup │ +# │2) To recover position │ +# └──────────┬──────────────┘ +# ┌──────────▼──────────────┐ +# │Send final ready signal │ +# │Waits for IST │ +# └─────────────────────────┘ +#---------------------------------------------------------------------------- +# Config variables: +# [sst] +# wsrep-debug=true +# joiner-timeout-wait-donor-message=60 +# joiner-timeout-clone-instance=90 +# donor-timeout-wait-joiner=200 +# +# NOTE for SSL usage +# When using clone SSL certificates MUST be manually (or by automation) placed in a location on both servers DONOR/RECEIVER +# this location better not be the data directory given it is manipulated by the clone process. +# It is recommended to explicitly set the ssl certificates in the my.cnf as: +# [client] +# ssl-ca = //ca.pem +# ssl-cert = //client-cert.pem +# ssl-key = //client-key.pem +# [mysqld] +# ssl-ca = //ca.pem +# ssl-cert = //server-cert.pem +# ssl-key = //server-key.pem +# OR +# [mysqld] +# clone_ssl_ca=/path/to/ca.pem +# clone_ssl_cert=/path/to/client-cert.pem +# clone_ssl_key=/path/to/client-key.pem +# Where is not the data directory +############################################################################# + +CMDLINE="$0 $*" + +set -o nounset -o errexit + +readonly EINVAL=22 +readonly EPIPE=32 +readonly ETIMEDOUT=110 +readonly ENODATA=61 +CLEANUP_CLONE_PLUGIN="" +CLONE_USER="" +NC_PID="" +CLONE_INSTANCE_PID="" + +# The following variable will store the position (GTID:POS) coming from the donor in the message exchange. +# It will be used ONLY in emergency as last measure if for any reason the position recovery (at the end) will fail +RP_PURGED_EMERGENCY="" +WSREP_SST_OPT_LPORT="" +WSREP_SST_OPT_PARENT="" +#Default to 4444 as we do for XB +CLONE_INSTANCE_PORT=4444 + + +# wsrep_gen_secret() generates enough randomness, yet some MySQL password +# policy may insist on having upper and lower case letters, numbers and +# special symbols. Make sure that whatever we generate, it has it. +readonly PSWD_POLICY="Aa" + +OS=$(uname) +[ "$OS" = "Darwin" ] && export -n LD_LIBRARY_PATH + +. $(dirname "$0")/wsrep_sst_common #_clone + +JOINER_TIMEOUT_WAIT_DONOR_MESSAGE=$(parse_cnf sst joiner-timeout-wait-donor-message "60") +JOINER_TIMEOUT_WAIT_CLONE_INSTANCE=$(parse_cnf sst joiner-timeout-clone-instance "90") +DONOR_TIMEOUT_WAIT_JOINER_CLONE_INSTANCE=$(parse_cnf sst donor-timeout-wait-joiner "200") + + +wsrep_log_info "Running: $CMDLINE" + +# READ user/pw from stdin +read_variables_from_stdin +readonly WSREP_SST_OPT_ADDR_LOCAL=$WSREP_SST_OPT_ADDR +wsrep_log_debug "-> WSREP_SST_OPT_HOST: $WSREP_SST_OPT_HOST" +wsrep_log_debug "-> WSREP_SST_OPT_USER: $WSREP_SST_OPT_USER" +#wsrep_log_debug "-> WSREP_SST_OPT_PSWD: $WSREP_SST_OPT_PSWD" +wsrep_log_debug "-> WSREP_SST_OPT_ADDR: $WSREP_SST_OPT_ADDR" +wsrep_log_debug "-> WSREP_SST_OPT_ADDR_LOCAL: $WSREP_SST_OPT_ADDR_LOCAL" +wsrep_log_debug "-> WSREP_SST_OPT_LPORT: $WSREP_SST_OPT_LPORT" + +# To not use possible [client] section in my.cnf +MYSQL_CLIENT="$MYSQL_CLIENT --no-defaults" + +# Linux and FreeBSD have different mktemp syntax with respect to a parent +# directory. This wrapper takes parent dir as a first argument and passes +# the rest to mktemp directly. +wsrep_mktemp_in_dir() +{ + local OS=$(uname) + local tmpdir="$1" + shift + if [ "$OS" = "Linux" ] + then + # Linux mktemp does not respect TMPDIR if template is given + mktemp --tmpdir="$tmpdir" $* + else + echo $(export TMPDIR="$tmpdir"; mktemp $*) + fi +} + +# Generate a string equivalent to 16 random bytes +wsrep_gen_secret() +{ + if [ -x /usr/bin/openssl ] + then + echo `/usr/bin/openssl rand -hex 16` + else + printf "%04x%04x%04x%04x%04x%04x%04x%04x" \ + $RANDOM $RANDOM $RANDOM $RANDOM \ + $RANDOM $RANDOM $RANDOM $RANDOM + fi +} + +cleanup_donor() +{ + wsrep_log_info "Cleanup DONOR." +# wsrep_log_debug "Cleanup MySQL ADMIN_PSWD: $ADMIN_PSWD" + wsrep_log_debug "Cleanup MySQL MYSQL_ACLIENT: $MYSQL_ACLIENT" + export MYSQL_PWD=$ADMIN_PSWD + if [ "$CLEANUP_CLONE_PLUGIN" == "yes" ] + then + CLEANUP_CLONE_PLUGIN="UNINSTALL PLUGIN CLONE;" + fi + if [ ! "$WSREP_SST_OPT_REMOTE_JOINER_USER" == "" ]; then + $MYSQL_ACLIENT -e "SET wsrep_on=OFF; DROP USER IF EXISTS '$WSREP_SST_OPT_REMOTE_JOINER_USER'@'%'; $CLEANUP_CLONE_PLUGIN" || : + fi + + rm -rf "$CLONE_EXECUTE_SQL" ||: + rm -rf "$CLONE_PREPARE_SQL" ||: + wsrep_log_info "Cleanup DONOR DONE." +} + +sig_cleanup_joiner() +{ + if [ -n "$CLONE_INSTANCE_PID" ]; then + kill -SIGKILL $CLONE_INSTANCE_PID /dev/null 2>&1 ||: + fi + + if [ -n "$NC_PID" ]; then + kill -SIGKILL $NC_PID /dev/null 2>&1 ||: + fi + + rm -rf $CLONE_SOCK_DIR || : + rm -rf $CLONE_PID_FILE || : + rm -fr $tmp_datadir || : + rm -f $WSREP_SST_OPT_DATA/XST_FILE.txt > /dev/null 2>&1 || : + + + if [ $CLEANUP_FILES ] + then + rm -rf $CLONE_ERR || : + rm -rf $CLONE_SQL || : + fi +} + +cleanup_joiner() +{ + wsrep_log_info "Joiner cleanup. SST daemon PID: $CLONE_INSTANCE_PID" + sig_cleanup_joiner + wsrep_log_info "Joiner cleanup done." +} + +# Check whether process in PID file is still running. +check_pid_file() +{ + local pid_file=$1 + [ -r "$pid_file" ] && ps -p $(cat $pid_file) >/dev/null 2>&1 +} + +# Check client version +check_client_version() +{ + local readonly min_version="8.0.19" + IFS="." read -ra min_vers <<< $min_version # split into major minor and patch + + local readonly client_version=${1%%[-/]*} # take only a.b.c from a.b.c-d.e + IFS="." read -ra client_vers <<< $client_version + + for i in ${!min_vers[@]} + do + if [ "${client_vers[$i]}" -lt "${min_vers[$i]}" ] + then + wsrep_log_error "this operation requires MySQL client version $min_version," \ + " this client is '$client_version'" + return $EINVAL + fi + done +} + +# This function is common for Donor and Joiner. +# Installs clone plugin if necessary. +# If donor does not have clone plugin installed by default, it sets CLEANUP_CLONE_PLUGIN=yes +# This flag is sent to Joiner, to inform the Joiner that it should uninstall clone plugin after SST. +# Flag is also used on Donor side to decide if clone plugin should be uninstalled after SST. +# Sets the following environment variables: CLEANUP_CLONE_PLUGIN_ON_DONOR +install_clone_plugin() +{ + local -r role=$1 + + # Note that install_clone_plugin() is called on Joiner side after Joiner receives + # info that clone plugin should be uninstalled after SST. Joiner stores this info + # in CLEANUP_CLONE_PLUGIN variable, so we don't want to override it here. + if [ "$role" == "donor" ] + then + CLEANUP_CLONE_PLUGIN="no" + fi + + CLONE_PLUGIN_LOADED=$($MYSQL_ACLIENT -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_TYPE = 'CLONE';") + + if [ "$CLONE_PLUGIN_LOADED" -eq 0 ] + then + wsrep_log_info "Installing CLONE plugin" + + # INSTALL PLUGIN is replicated by default, so we need to switch off + # session replication on donor + if [ "$role" = "donor" ] + then + WSREP_OFF="SET SESSION wsrep_on=OFF; " + CLEANUP_CLONE_PLUGIN="yes" + else + WSREP_OFF="" # joiner does not have replication enabled + fi + $MYSQL_ACLIENT -e "${WSREP_OFF}INSTALL PLUGIN CLONE SONAME 'mysql_clone.so';" + fi +} + +# Setup certificates on Donor instance +# 1. If Donor has server certificates set, it means it accepts only secure connections. +# Before SST, we create a dedicated clone user that will be used by Recipient instance +# for cloning. In such a case this user with 'REQUIRE SSL' to enforce SSL during clone. +# This affects 'REQUIRE_SSL' global variable. +# 2. Donor needs to connect to Recipient instance to trigger cloning. +# if [sst] (with fallback to [client]) certificates are present - use them. +# This affects CLIENT_SSL_OPTIONS global variable. +# Sets the following environment variables: REQUIRE_SSL, CLIENT_SSL_OPTIONS +setup_certificates_for_donor() +{ + local client_ssl_cert + local client_ssl_key + local client_ssl_ca + local client_ssl_mode + local server_ssl_cert + local server_ssl_key + local server_ssl_ca + + # client_ssl_* is used by Donor to connect to Joiner's clone (aka Recipient) instance + client_ssl_cert=$(parse_cnf client ssl_cert "") + client_ssl_key=$(parse_cnf client ssl_key "") + client_ssl_ca=$(parse_cnf client ssl_ca "") + client_ssl_mode=$(parse_cnf client ssl_mode "") + + wsrep_log_debug "SSL Client settings on $role from client section: CERT=$client_ssl_cert, KEY=$client_ssl_key, CA=$client_ssl_ca, ssl-mode=$client_ssl_mode " + + # Server info + # If donor certificates are set, we require all connections to donor + # to be SSL-protected. That means connection from Recipient instance + # to Donor instance will have to be over SSL. + server_ssl_cert=$($MYSQL_ACLIENT -e "SELECT @@ssl_cert") + server_ssl_key=$($MYSQL_ACLIENT -e "SELECT @@ssl_key") + server_ssl_ca=$($MYSQL_ACLIENT -e "SELECT @@ssl_ca") + + # REQUIRE_SSL is used during clone user creation on Donor server + REQUIRE_SSL="" + if [ -n "$server_ssl_ca" ] && [ -n "$server_ssl_cert" ] && [ -n "$server_ssl_key" ]; then + REQUIRE_SSL="REQUIRE SSL" + fi + wsrep_log_debug "Clone SSL Server settings on $role from runtime: CERT=$server_ssl_cert, KEY=$server_ssl_key, CA=$server_ssl_ca" + + # CLIENT_SSL_OPTIONS is used when Donor connects to the Recipient instance to trigger clone + CLIENT_SSL_OPTIONS="" + if [ -n "$client_ssl_ca" ] && [ -n "$client_ssl_cert" ] && [ -n "$client_ssl_key" ] + then + CLIENT_SSL_OPTIONS="--ssl-cert=$client_ssl_cert --ssl-key=$client_ssl_key" + if [ -n "$client_ssl_ca" ] + then + CLIENT_SSL_OPTIONS+=" --ssl-ca=$client_ssl_ca" + if [ -n "$client_ssl_mode" ] + then + CLIENT_SSL_OPTIONS+=" --ssl-mode=$client_ssl_mode" + fi + fi + else + CLIENT_SSL_OPTIONS+=" --ssl-mode=DISABLED" + fi + wsrep_log_debug "SSL Client settings on $role for connection CLIENT_SSL_OPTIONS=${CLIENT_SSL_OPTIONS}" +} + +# Setup certificates for Joiner's clone instance (aka "Recipient") +# Recipient instance potentially needs clone_ssl_* certificates to be set. +# If they are specified in Joiner's my.cnf, they should be set automatically +# after we install clone plugin in Recipient instance. If not, let's try +# to set them manually. If still not present, Recipient instance will use +# not encrypted connection. Note that if Donor requires encrypted connection, clone +# will fail, but it is a configuration problem. +setup_certificates_for_recipient() +{ + local clone_ssl_cert + local clone_ssl_key + local clone_ssl_ca + + clone_ssl_cert=$($MYSQL_ACLIENT -e "SELECT @@clone_ssl_cert") + clone_ssl_key=$($MYSQL_ACLIENT -e "SELECT @@clone_ssl_key") + clone_ssl_ca=$($MYSQL_ACLIENT -e "SELECT @@clone_ssl_ca") + wsrep_log_debug "Clone SSL settings on $role from runtime: CERT=$clone_ssl_cert, KEY=$clone_ssl_key, CA=$clone_ssl_ca" + + # We check if ssl certificates for clone are available. + # If not we try from cnf + if [ -z "$clone_ssl_cert" ] || [ "$clone_ssl_cert" == "NULL" ] + then + clone_ssl_cert=$(parse_cnf mysqld clone_ssl_cert "") + clone_ssl_key=$(parse_cnf mysqld clone_ssl_key "") + clone_ssl_ca=$(parse_cnf mysqld clone_ssl_ca "") + wsrep_log_debug "Clone SSL settings on $role from mysqld section: CERT=$clone_ssl_cert, KEY=$clone_ssl_key, CA=$clone_ssl_ca" + fi + # If not we try from client section + if [ -z "$clone_ssl_cert" ] || [ "$clone_ssl_cert" == "NULL" ] + then + clone_ssl_cert=$(parse_cnf client ssl_cert "") + clone_ssl_key=$(parse_cnf client ssl_key "") + clone_ssl_ca=$(parse_cnf client ssl_ca "") + wsrep_log_debug "Clone SSL settings on $role from client section: CERT=$clone_ssl_cert, KEY=$clone_ssl_key, CA=$clone_ssl_ca" + fi + + # Even if certificates were set at the beginning, setting them again will not hurt + if [ -n "$clone_ssl_cert" ] && [ -n "$clone_ssl_key" ] && [ -n "$clone_ssl_ca" ] + then + wsrep_log_info "Clone SSL settings on $role we will use: CERT=$clone_ssl_cert, KEY=$clone_ssl_key, CA=$clone_ssl_ca" + + $MYSQL_ACLIENT -e "SET GLOBAL clone_ssl_cert='$clone_ssl_cert'" + $MYSQL_ACLIENT -e "SET GLOBAL clone_ssl_key='$clone_ssl_key'" + $MYSQL_ACLIENT -e "SET GLOBAL clone_ssl_ca='$clone_ssl_ca'" + fi +} + +# Install clone plugin and setup needed certificates +setup_clone_plugin() +{ + # Either donor or recipient + local -r role=$1 + + install_clone_plugin "$role" + + wsrep_log_debug "-> ############## SSL SECTION [START] ($role) ############" + if [ "$role" == "donor" ] + then + setup_certificates_for_donor + else + setup_certificates_for_recipient + fi + wsrep_log_debug "-> ############## SSL SECTION [END] ($role) ############" +} + +# Let's use the simple approach to detect if the transfer is running or not. +# Monitor p_s.clone_progress table. If anything changes there, it means something +# is happening. +# One of the columns is DATA: The amount of data transferred in current state, in bytes, +# so we should be good. +# Allow transfer stuck for sst-idle-timeout +monitor_sst_progress() { + local progress_monitor_command=$1 + local timeout=$2 + local clone_instance_pid=$3 + local parent_process_pid=$4 + + local current_timeout=0 + local previous_command_output= + local sleep=5 + local previous_progress= + + wsrep_log_debug "progress_monitor_command: ${progress_monitor_command}" + wsrep_log_debug "timeout: ${timeout}" + wsrep_log_debug "clone_instance_pid: ${clone_instance_pid}" + wsrep_log_debug "parent_process_pid: ${parent_process_pid}" + + if [ ${timeout} -eq 0 ]; then + return 2; + fi + + while true; do + # check if parent process is still alive + # Is it needed? If parent process dies, SST script dies as well + # and will cleanup clone instance in cleanup_joiner() trap. + kill -0 $parent_process_pid 2> /dev/null + if [ $? -ne 0 ]; then + return 1; + fi + + # check if clone instance process is still alive + kill -0 $clone_instance_pid 2> /dev/null + if [ $? -ne 0 ]; then + # Clone instance is done with its job + return 0; + fi + + # check if there is any SST progress + command_output=$(eval "$progress_monitor_command" 2> /dev/null ||:) + if [ "${command_output}" != "${previous_command_output}" ]; then + current_timeout=0 + previous_command_output=${command_output} + # if the transfer is ongoing, keep checking once per 5 seconds + sleep=5 + else + current_timeout=$((current_timeout + 1)) + wsrep_log_debug "sst idle for ${current_timeout} sec" + # if we detected transfer stall, start checking more often (1 sec) + sleep=1 + fi + + if [ ${current_timeout} -eq ${timeout} ]; then + return 2 + fi + + # Report the current progress + current_progress=`$MYSQL_ACLIENT -NB -e "select format(((data/estimate)*100),2) 'completed%' from performance_schema.clone_progress where stage like 'FILE_COPY';" 2> /dev/null` || : + if [ "$previous_progress" != "$current_progress" ]; then + wsrep_log_info "SST progress: ${current_progress}%" + previous_progress=$current_progress + fi + + sleep ${sleep}; + done + + wsrep_log_debug "We should never get here" + return 0 +} + + +# This is the script execution start point + +if test -z "$WSREP_SST_OPT_HOST"; then wsrep_log_error "HOST cannot be empty"; exit $EINVAL; fi + +# MySQL client does not seem to agree to [] around IPv6 addresses +wsrep_check_programs sed +SST_HOST_STRIPPED=$(echo $WSREP_SST_OPT_HOST | sed 's/^\[//' | sed 's/\]$//') + +# Option to CREATE USER +WITH_OPTION="WITH caching_sha2_password" + +if [ "$WSREP_SST_OPT_ROLE" = "donor" ] +then + WSREP_SST_OPT_REMOTE_JOINER_USER=$WSREP_SST_OPT_REMOTE_USER + WSREP_SST_OPT_REMOTE_JOINER_PSWD=$WSREP_SST_OPT_REMOTE_PSWD + + echo "continue" # donor can resume updating data + + WSREP_SST_OPT_REMOTE_HOST_WITH_PORT=$WSREP_SST_OPT_ADDR_LOCAL + WSREP_SST_OPT_REMOTE_HOST=$(echo $WSREP_SST_OPT_REMOTE_HOST_WITH_PORT | cut -d ':' -f 1) + WSREP_SST_OPT_REMOTE_HOSTPORT=$(echo $WSREP_SST_OPT_REMOTE_HOST_WITH_PORT | cut -d ':' -f 2) + + SST_HOST_STRIPPED=$(echo $WSREP_SST_OPT_REMOTE_HOST | sed 's/^\[//' | sed 's/\]$//') + + wsrep_log_debug "-> WSREP_SST_OPT_REMOTE_JOINER_USER = $WSREP_SST_OPT_REMOTE_JOINER_USER " + wsrep_log_debug "-> WSREP_SST_OPT_REMOTE_HOST_WITH_PORT = $WSREP_SST_OPT_REMOTE_HOST_WITH_PORT " + wsrep_log_debug "-> WSREP_SST_OPT_REMOTE_HOST = $WSREP_SST_OPT_REMOTE_HOST " + wsrep_log_debug "-> WSREP_SST_OPT_REMOTE_HOSTPORT = $WSREP_SST_OPT_REMOTE_HOSTPORT " + wsrep_log_debug "-> SST_HOST_STRIPPED = $SST_HOST_STRIPPED " + + # Split auth string at the last ':' + if test -z "$WSREP_SST_OPT_USER"; then wsrep_log_error "USER cannot be empty"; exit $EINVAL; fi + if test -z "$WSREP_SST_OPT_REMOTE_JOINER_USER"; then wsrep_log_error "REMOTE_USER cannot be empty"; exit $EINVAL; fi + if test -z "$WSREP_SST_OPT_PORT"; then wsrep_log_error "PORT cannot be empty"; exit $EINVAL; fi + if test -z "$WSREP_SST_OPT_SOCKET"; then wsrep_log_error "SOCKET cannot be empty"; exit $EINVAL; fi + + CLIENT_VERSION=$($MYSQL_CLIENT --version | grep -vi MariaDB | cut -d ' ' -f 4) + check_client_version $CLIENT_VERSION + + readonly ADMIN_USER=$WSREP_SST_OPT_USER + readonly ADMIN_PSWD=$WSREP_SST_OPT_PSWD + + MYSQL_RCLIENT="$MYSQL_CLIENT -u$WSREP_SST_OPT_REMOTE_JOINER_USER -h$SST_HOST_STRIPPED \ + -P$WSREP_SST_OPT_REMOTE_HOSTPORT --batch --skip_column_names --silent" + MYSQL_ACLIENT="$MYSQL_CLIENT -u$ADMIN_USER -S$WSREP_SST_OPT_SOCKET \ + --batch --skip_column_names --silent" + + export MYSQL_PWD=$ADMIN_PSWD + readonly WSREP_SST_OPT_LPORT=`$MYSQL_ACLIENT -e "select @@port"` + + wsrep_log_debug "-> WSREP_SST_OPT_LPORT: $WSREP_SST_OPT_LPORT " + wsrep_log_debug "-> MYSQL_ACLIENT: $MYSQL_ACLIENT " + #wsrep_log_debug "-> MYSQL_ACLIENT ADMIN_PSWD: $ADMIN_PSWD" + wsrep_log_debug "-> MYSQL_RCLIENT: $MYSQL_RCLIENT " + #wsrep_log_debug "-> MYSQL_RUSER: $WSREP_SST_OPT_REMOTE_JOINER_USER | PWD $WSREP_SST_OPT_REMOTE_JOINER_PSWD" + + if [ $WSREP_SST_OPT_BYPASS -eq 0 ] + then + # + # Prepare DONOR for cloning + # + + # Use script file to avoid sensitive information on the command line + CLONE_EXECUTE_SQL=$(wsrep_mktemp_in_dir "$WSREP_SST_OPT_DATA" --suffix=.sql clone_execute_XXXX) + CLONE_PREPARE_SQL=$(wsrep_mktemp_in_dir "$WSREP_SST_OPT_DATA" --suffix=.sql clone_prepare_XXXX) + + cleanup_donor # Remove potentially existing clone user + + export MYSQL_PWD=$ADMIN_PSWD + wsrep_log_debug "-> PREPARE DONOR" + setup_clone_plugin "donor" + + wsrep_log_info "REQUIRE_SSL=$REQUIRE_SSL, CLIENT_SSL_OPTIONS=$CLIENT_SSL_OPTIONS" + + # Server configuration might have changed, make sure it is restored + # on exit + trap cleanup_donor EXIT + +cat << EOF > "$CLONE_PREPARE_SQL" +SET wsrep_on=OFF; +CREATE USER "$WSREP_SST_OPT_REMOTE_JOINER_USER"@"%" IDENTIFIED BY '$WSREP_SST_OPT_REMOTE_JOINER_PSWD' $REQUIRE_SSL; +GRANT BACKUP_ADMIN ON *.* TO "$WSREP_SST_OPT_REMOTE_JOINER_USER"@"%"; +GRANT SELECT ON performance_schema.* TO "$WSREP_SST_OPT_REMOTE_JOINER_USER"@"%"; +GRANT EXECUTE ON *.* TO "$WSREP_SST_OPT_REMOTE_JOINER_USER"@"%"; +EOF + RC=0 + export MYSQL_PWD=$ADMIN_PSWD +# wsrep_log_debug "-> connecting to donor: $CLONE_PREPARE_SQL $MYSQL_PWD $MYSQL_ACLIENT" + $MYSQL_ACLIENT --connect-timeout=60 < $CLONE_PREPARE_SQL || RC=$? + wsrep_log_debug "-> $RC connect to donor done" + + if [ "$RC" -ne 0 ] + then + wsrep_log_error "Donor prepare returned code $RC" + cat $CLONE_PREPARE_SQL >> /dev/stderr + exit $RC + fi + + # Before waiting for the Joiner Clone mysql we send out the message this is a SST + wsrep_log_debug "-> NETCAT signal to nc -w 1 $SST_HOST_STRIPPED $WSREP_SST_OPT_REMOTE_HOSTPORT" + wsrep_log_debug "-> Signal for CLONE Plugin CLEANUP_CLONE_PLUGIN=$CLEANUP_CLONE_PLUGIN" + echo "$CLEANUP_CLONE_PLUGIN|SST@$WSREP_SST_OPT_GTID" | nc -w 1 $SST_HOST_STRIPPED $WSREP_SST_OPT_REMOTE_HOSTPORT || : + + # We stay on hold now, waiting for the Joiner to expose the service + wsrep_log_info "-> WAIT for Joiner MySQL to be available nc -w 1 -i 1 $SST_HOST_STRIPPED $WSREP_SST_OPT_REMOTE_HOSTPORT" + while [ true ]; do + NCPING=`nc -w 1 -i 1 $SST_HOST_STRIPPED $WSREP_SST_OPT_REMOTE_HOSTPORT 2> /dev/null | tr -d '\0'` || : + if [ "$NCPING" == "" ]; then + wsrep_log_info "[second(s) to timeout: $DONOR_TIMEOUT_WAIT_JOINER_CLONE_INSTANCE]" + else + wsrep_log_info "Joiner instance available at $SST_HOST_STRIPPED:$WSREP_SST_OPT_REMOTE_HOSTPORT" + break + fi + if [ "$DONOR_TIMEOUT_WAIT_JOINER_CLONE_INSTANCE" == "0" ]; then + wsrep_log_error "TIMEOUT Waiting for the Joiner to setup MySQL clone instance $LINENO" + break ; + fi + + sleep 1 + ((DONOR_TIMEOUT_WAIT_JOINER_CLONE_INSTANCE-=1)) + done + # Wait is over + wsrep_log_debug "-> Wait is over" + + export MYSQL_PWD="$WSREP_SST_OPT_REMOTE_JOINER_PSWD" + export MYSQL_USER="$WSREP_SST_OPT_REMOTE_JOINER_USER" + # Find own address (from which we connected) + wsrep_log_debug "-> Connecting to JOINER to get exact IP of donor: $MYSQL_RCLIENT" # $MYSQL_PWD" + USER=`$MYSQL_RCLIENT --skip-column-names $CLIENT_SSL_OPTIONS -e 'SELECT USER()'` + LHOST=${USER##*@} + DONOR=$LHOST:$WSREP_SST_OPT_LPORT + + wsrep_log_debug "-> DONOR string: $DONOR" + + # Use script file to avoid sensitive information on the command line +cat << EOF > "$CLONE_EXECUTE_SQL" +SET GLOBAL clone_valid_donor_list = '$DONOR'; +CLONE INSTANCE FROM '$WSREP_SST_OPT_REMOTE_JOINER_USER'@'$LHOST':$WSREP_SST_OPT_LPORT IDENTIFIED BY '$WSREP_SST_OPT_REMOTE_JOINER_PSWD' $REQUIRE_SSL; +EOF + + wsrep_log_debug "JOINER CLONE ACTION SQL: $CLONE_EXECUTE_SQL $MYSQL_PWD $MYSQL_RCLIENT" + + # Actual cloning process + wsrep_log_info "JOINER CLONE ACTION: cloning" + LOCALOUTPUT=`$MYSQL_RCLIENT --connect-timeout=60 $CLIENT_SSL_OPTIONS < $CLONE_EXECUTE_SQL 2>&1 || RC=$?` + wsrep_log_info "JOINER CLONE ACTION: cloning done $RC" + wsrep_log_debug "-> LOCALOUTPUT: $LOCALOUTPUT" + + # We force the signal to be 0 because we KNOW that with clone when using the mysqld directly (not mysqld_safe) the daemon is shutdown at the end of the clone process. + # However an error is returned because server cannot be restarted + # This error does not indicate a cloning failure. It means that the recipient MySQL server instance must be started again manually after the data is cloned. + if [ "$RC" -ne 0 ]; then + RC=0 + wsrep_log_debug " JOINER CLONE ACTION abruptly terminated, but we can continue" + fi + + # If still there we will manually shutdown + LOCALOUTPUT=`$MYSQL_RCLIENT $CLIENT_SSL_OPTIONS -e "SHUTDOWN" 2>&1 || :` + wsrep_log_debug "-> LOCALOUTPUT: $LOCALOUTPUT" + + if [ "$RC" -ne 0 ] + then + wsrep_log_error "Clone command returned code $RC" + wsrep_log_debug "JOINER RETURN ERROR $MYSQL_RCLIENT" + cat $CLONE_EXECUTE_SQL >> /dev/stderr + # Attempt to shutdown recipient daemon + eval $MYSQL_RCLIENT -e "SHUTDOWN" + wsrep_log_info "Recipient shutdown: $?" + case $RC in + *) RC=255 # unknown error + ;; + esac + exit $RC + fi + else # BYPASS + wsrep_log_info "Bypassing state dump." + + # Instruct recipient to shutdown + # export MYSQL_PWD="$WSREP_SST_OPT_REMOTE_JOINER_PSWD" + wsrep_log_info "BYPASS SENDING IST_FILE TO JOINER NetCat: nc -w 1 $SST_HOST_STRIPPED $WSREP_SST_OPT_REMOTE_HOSTPORT" + echo "$WSREP_SST_OPT_GTID" | nc -w 1 $SST_HOST_STRIPPED $WSREP_SST_OPT_REMOTE_HOSTPORT || : + wsrep_log_debug "-> Exiting with gtid: $WSREP_SST_OPT_GTID" + fi + + wsrep_log_debug "-> SENDING DONE_MESSAGE $WSREP_SST_OPT_GTID" + # DONOR must be clean BEFORE exit given user is removed on DONE + + echo "done $WSREP_SST_OPT_GTID" + +elif [ "$WSREP_SST_OPT_ROLE" = "joiner" ] +then + JOINER_CLONE_HOST="" + JOINER_CLONE_PORT="" + + # These variables are used in sig_clean_joiner. Create here to avoid + # potential unbound variable. + CLEANUP_FILES="" + CLONE_SOCK_DIR="" + CLONE_PID_FILE="" + tmp_datadir="" + CLONE_ERR="" + CLONE_SQL="" + + trap sig_cleanup_joiner HUP PIPE INT TERM + trap cleanup_joiner EXIT + + wsrep_check_programs grep + wsrep_check_programs ps + wsrep_check_programs find + + if test -z "$WSREP_SST_OPT_DATA"; then wsrep_log_error "DATA cannot be empty"; exit $EINVAL; fi + if test -z "$WSREP_SST_OPT_PARENT"; then wsrep_log_error "PARENT cannot be empty"; exit $EINVAL; fi + + # + # Find binary to run + # get_mysqld_path stores the path in MYSQLD_PATH variable. Later in + # the script we use CLONE_BINARY variable. + MYSQLD_PATH="" + get_mysqld_path + CLONE_BINARY="${MYSQLD_PATH}" + + if [ ! -x "$CLONE_BINARY" ] + then + wsrep_log_error "Could not determine binary to run: $CLONE_BINARY" + exit $EINVAL + fi + + # + # Find where libs needed for the binary are + # + CLONE_LIBS="$WSREP_SST_OPT_PLUGINDIR" + + # 1. Try plugins dir + if [ -z "$CLONE_LIBS" ] && $MY_PRINT_DEFAULTS "mysqld" | grep -q plugin[_-]dir + then + CLONE_LIBS=$($MY_PRINT_DEFAULTS mysqld | grep -- "--plugin[_-]dir" | cut -d= -f2) + # scale up to the first "mysql" occurence + while [ ${#CLONE_LIBS} -gt "1" ] + do + [ `basename $CLONE_LIBS` = "mysql" ] && break + CLONE_LIBS=$(dirname $CLONE_LIBS) + done + if ls $CLONE_LIBS/private/lib* > /dev/null 2>&1 + then + CLONE_LIBS="$CLONE_LIBS/private" + else + wsrep_log_info "Could not find private libs in '$CLONE_LIBS' from plugin_dir: $($MY_PRINT_DEFAULTS mysqld | grep -- '--plugin_dir' | cut -d= -f2)" + CLONE_LIBS= + fi + fi + + # 2. Try binary path + if [ -z "$CLONE_LIBS" ] + then + CLONE_LIBS=$(dirname $(dirname $CLONE_BINARY)) + if ls $CLONE_LIBS/lib64/mysql/private/lib* > /dev/null 2>&1 + then + CLONE_LIBS="$CLONE_LIBS/lib64/mysql/private/" + elif ls $CLONE_LIBS/lib/mysql/private/lib* > /dev/null 2>&1 + then + CLONE_LIBS="$CLONE_LIBS/lib/mysql/private/" + else + wsrep_log_info "Could not find private libs by binary name: $(dirname $(dirname $CLONE_BINARY))" + CLONE_LIBS= + fi + fi + + # 3. Try this script path + if [ -z "$CLONE_LIBS" ] + then + CLONE_LIBS=$(dirname $(dirname $0)) + if ls $CLONE_LIBS/lib64/mysql/private/lib* > /dev/null 2>&1 + then + CLONE_LIBS="$CLONE_LIBS/lib64/mysql/private" + elif ls $CLONE_LIBS/lib/mysql/private/lib* > /dev/null 2>&1 + then + CLONE_LIBS="$CLONE_LIBS/lib/mysql/private" + else + wsrep_log_info "Could not find private libs by script path: $(dirname $(dirname $0))" + CLONE_LIBS= + fi + fi + + if [ -d "$CLONE_LIBS" ] + then + CLONE_ENV="LD_LIBRARY_PATH=$CLONE_LIBS:${LD_LIBRARY_PATH:-}" + else + wsrep_log_info "Could not determine private library path for mysqld: $CLONE_LIBS. Leaving LD_LIBRARY_PATH unmodified." + CLONE_ENV="" + fi + + MODULE="clone_sst" + CLONE_SOCK_DIR=`mktemp --tmpdir -d ${MODULE}_XXXXXX` + + CLONE_PID_FILE="$WSREP_SST_OPT_DATA/$MODULE.pid" + + if check_pid_file $CLONE_PID_FILE + then + CLONE_PID=`cat $CLONE_PID_FILE` + wsrep_log_error "cloning daemon already running (PID: $CLONE_PID)" + exit 114 # EALREADY + fi + rm -rf "$CLONE_PID_FILE" + + # If port was not set up explicitly in wsrep_sst_address, + # then we need to set the port to a default that is different to the default port existing in the running instance. + # This because clone port <> mysql port, due to the need to spin a second MySQL instance able to accomodate the clone action + wsrep_log_debug "-> WSREP_SST_OPT_PORT $WSREP_SST_OPT_PORT" + wsrep_log_debug "-> WSREP_SST_OPT_ADDR_LOCAL $WSREP_SST_OPT_ADDR_LOCAL" + + IFS=':' read -ra my_array <<< "$WSREP_SST_OPT_ADDR_LOCAL" + + lenAr=${#my_array[@]} + wsrep_log_debug "-> lenarray $lenAr" + + if [ $lenAr -gt 1 ]; then + JOINER_CLONE_HOST=$(echo $WSREP_SST_OPT_ADDR_LOCAL | cut -d ':' -f 1) + JOINER_CLONE_PORT=$(echo $WSREP_SST_OPT_ADDR_LOCAL | cut -d ':' -f 2) + else + JOINER_CLONE_HOST="$WSREP_SST_OPT_ADDR_LOCAL" + JOINER_CLONE_PORT=$CLONE_INSTANCE_PORT + fi + + wsrep_log_debug "-> JOINER_CLONE_HOST $JOINER_CLONE_HOST" + wsrep_log_debug "-> JOINER_CLONE_PORT $JOINER_CLONE_PORT" + + # Define the tmp directory + tmp_datadir=$(wsrep_mktemp_in_dir "$WSREP_SST_OPT_DATA" -d) + + CLONE_ERR="$WSREP_SST_OPT_DATA/$MODULE.err" + CLONE_SQL="$WSREP_SST_OPT_DATA/$MODULE.sql" + GRASTATE_FILE="$WSREP_SST_OPT_DATA/grastate.dat" + + CLONE_SOCK="$WSREP_SST_OPT_DATA/recover_clone.sock" + CLONE_X_SOCK="$WSREP_SST_OPT_DATA/cmysqlx.sock" + + if [ ${#CLONE_X_SOCK} -ge 104 ] + then + wsrep_log_error "Unix socket path name length for CLONE SST receiver"\ + "'$CLONE_X_SOCK' is greater than commonly acceptable"\ + "limit of 104 bytes." + # Linux: 108, FreeBSD: 104 + exit $EINVAL + fi + + [ -z "$WSREP_SST_OPT_CONF" ] \ + && DEFAULTS_FILE_OPTION="" \ + || DEFAULTS_FILE_OPTION="--defaults-file='$WSREP_SST_OPT_CONF'" + + [ -z "$WSREP_SST_OPT_CONF_SUFFIX" ] \ + && DEFAULTS_GROUP_OPTION="" \ + || DEFAULTS_GROUP_OPTION="--defaults-group-suffix='$WSREP_SST_OPT_CONF_SUFFIX'" + + # Parent process already did the master key rotation + INVARIANT_OPTIONS="--binlog-rotate-encryption-master-key-at-startup=OFF" + + wsrep_log_debug "-> CLONE_SOCK $CLONE_SOCK " + wsrep_log_debug "-> CLONE_LIBS $CLONE_LIBS " + + DEFAULT_OPTIONS=" \ + $DEFAULTS_FILE_OPTION \ + $DEFAULTS_GROUP_OPTION \ + $INVARIANT_OPTIONS \ + --datadir='$WSREP_SST_OPT_DATA' \ + --wsrep_on=0 \ + --secure_file_priv='' \ + --socket='$CLONE_SOCK' \ + --log_error='$CLONE_ERR' \ + --pid_file='$CLONE_PID_FILE' \ + --plugin-dir='$CLONE_LIBS' \ + --port='$JOINER_CLONE_PORT' \ + " + SKIP_NETWORKING_OPTIONS=" \ + --skip-mysqlx \ + --skip-networking \ + " + + # Define USER and PW + CLONE_PSWD=`wsrep_gen_secret`"$PSWD_POLICY" + CLONE_USER="clone_sst" + + ################################################################################################## + # LET US Move the send of the READY here. + # 1) we send the message ready + # 2) we wait for donor to send message + # 3) IF message is SST@DONORIP:PORT (inside the IST file) we exit loop and continue SST + # IF IST we exit SST script sending the UUID:position + # 4) IF SST Process continue + + # Check if there is another netcat process on the IP port we need and try to kill it. + NETCAT_KILL=$(ps -xo pid,command | grep -e "nc -l -k $JOINER_CLONE_HOST $JOINER_CLONE_PORT" | grep -v "grep" | cut -d ' ' -f 1) + if [ ! "$NETCAT_KILL" == "" ];then + wsrep_log_info "-> Existing NetCat PID: $NETCAT_KILL. Will try to kill it" + kill -9 $NETCAT_KILL + if [ "$?" != "0" ]; then + wsrep_log_debug "-> Cannot kill the existing NetCat process PID $NETCAT_KILL" + fi + fi + + # OPEN NETCAT to receive messages from DONOR (like IST) + wsrep_log_info "-> Opening NETCAT: nc -l $JOINER_CLONE_HOST $JOINER_CLONE_PORT" + nc -l $JOINER_CLONE_HOST $JOINER_CLONE_PORT > $WSREP_SST_OPT_DATA/XST_FILE.txt & + NC_PID=$! + wsrep_log_info "-> NETCAT PID $NC_PID" + if [ $NC_PID == "" ];then + wsrep_log_error "-> Cannot open Netcat at given port $JOINER_CLONE_HOST $JOINER_CLONE_PORT check if the port is already taken" + exit 1 + fi + + # Report clone credentials/address to the caller + wsrep_log_debug "-> ready passing string |$CLONE_USER:CLONE_PSWD@$JOINER_CLONE_HOST:$JOINER_CLONE_PORT|" + echo "ready $CLONE_USER:$CLONE_PSWD@$JOINER_CLONE_HOST:$JOINER_CLONE_PORT" + + # WAIT for Donor message + wsrep_log_debug "-> wait $JOINER_TIMEOUT_WAIT_DONOR_MESSAGE" + + until grep -q ".*$" "$WSREP_SST_OPT_DATA/XST_FILE.txt" &> /dev/null + do + if [ "$JOINER_TIMEOUT_WAIT_DONOR_MESSAGE" == "0" ]; then + wsrep_log_error "************ FATAL ERROR ************" + wsrep_log_error "TIMEOUT Waiting for the DONOR MESSAGE" + wsrep_log_error "Donor message was either incomplete or not sent" + donor_message=`cat $WSREP_SST_OPT_DATA/XST_FILE.txt` + wsrep_log_error "donor message received: $donor_message" + wsrep_log_error "*************************************" + exit 1 ; + fi + sleep 1 + + ((JOINER_TIMEOUT_WAIT_DONOR_MESSAGE-=1)) + done + + wsrep_log_debug "-> WAIT DIR DONOR MESSAGE DONE" + + if ! grep -q "SST@" "$WSREP_SST_OPT_DATA/XST_FILE.txt"; then + wsrep_log_info "DONOR SAY IST" + wsrep_log_debug "-> RECOVER POSITION TO SEND OUT DONOR IST" + RP_PURGED=`cat $WSREP_SST_OPT_DATA/XST_FILE.txt` + RP_PURGED=${RP_PURGED%""} + wsrep_log_debug "-> POSITION: $RP_PURGED" + rm -f $WSREP_SST_OPT_DATA/XST_FILE.txt || : + + echo $RP_PURGED + exit 0 + else + RP_PURGED=`cat $WSREP_SST_OPT_DATA/XST_FILE.txt` + wsrep_log_info "DONOR SAY SST ($RP_PURGED)" + readonly CLEANUP_CLONE_PLUGIN=${RP_PURGED%|SST*} + RP_PURGED_EMERGENCY=${RP_PURGED#*SST@} + RP_PURGED_EMERGENCY=${RP_PURGED_EMERGENCY%""} + wsrep_log_debug "-> CLONE Plugin to remove? (yes/no) = $CLEANUP_CLONE_PLUGIN" + wsrep_log_debug "-> recovered position from DONOR: $RP_PURGED_EMERGENCY" + fi + + ################################################################################################## + # innodb_sys_tablespace_encrypt=1 requires keyring to be loaded at bootstrap + # and it will work only with global manifest. So if it is enabled, and we use + # local manifest/keyring config, we need to exit with error + sys_tbl_enc_enabled=$(parse_cnf mysqld innodb_sys_tablespace_encrypt "off") + sys_tbl_enc_enabled=$(normalize_boolean "$sys_tbl_enc_enabled" "off") + if [ "$sys_tbl_enc_enabled" = "on" ]; then + # sys_tablespace_encrypt will work only if we have global manifest and global config + mainfest_config_type=$(get_keyring_manifest_and_config_types $WSREP_SST_OPT_DATA $WSREP_SST_OPT_PLUGINDIR $CLONE_BINARY) + wsrep_log_debug "mainfest_config_type: $mainfest_config_type" + if [ "$mainfest_config_type" != "manifest=global;config=global" ]; then + wsrep_log_error "******************* FATAL ERROR ********************** " + wsrep_log_error "Joiner is configured to use system tablespace encryption." + wsrep_log_error "Joiner is also configured to use local manifest" + wsrep_log_error "or local keyring config file or to not use keyring at all." + wsrep_log_error "These options are not compatible. Either disable system" + wsrep_log_error "tablespace encryption or configure joiner to use global" + wsrep_log_error "manifest and keyring component config." + wsrep_log_error "Current config:" + wsrep_log_error "innodb_sys_tablespace_encrypt=$sys_tbl_enc_enabled" + wsrep_log_error "$mainfest_config_type" + wsrep_log_error "Line $LINENO" + wsrep_log_error "****************************************************** " + exit 1 + fi + fi + + # If we need to SST in any case we must remove the data, so let us do it here and be sure we work on a clean directory + wsrep_log_info "Cleaning Data directory $WSREP_SST_OPT_DATA" + ib_home_dir=$(parse_cnf mysqld innodb-data-home-dir "") + ib_log_dir=$(parse_cnf mysqld innodb-log-group-home-dir "") + ib_undo_dir=$(parse_cnf mysqld innodb-undo-directory "") + + cpat=$(parse_cnf sst cpat '.*\.pem$\|.*init\.ok$\|.*galera\.cache$\|.*gvwstate\.dat$\|.*\.err$\|.*\.log$\|.*RPM_UPGRADE_MARKER$\|.*RPM_UPGRADE_HISTORY$\|.*component_keyring_.*\.cnf$\|.*mysqld.my$') + find $ib_home_dir $ib_log_dir $ib_undo_dir $WSREP_SST_OPT_DATA -mindepth 1 -regex $cpat -prune -o -exec rm -rfv {} 1>/dev/null \+ + + # Before starting let us be sure we remove Netcat given it is using same MySQL port + # check if there is another netcat process on the IP port we need and try to kill it. + NETCAT_KILL=`ps -xo pid,command|grep -e "nc -l -k $JOINER_CLONE_HOST $JOINER_CLONE_PORT"|grep -v "grep"|cut -d ' ' -f 1` + if [ ! "$NETCAT_KILL" == "" ];then + wsrep_log_info "-> NetCat still up PID: $NETCAT_KILL. Will try to kill it" + kill -9 $NETCAT_KILL + if [ "$?" != "0" ]; then + wsrep_log_debug "-> Cannot kill the existing NetCat process PID $NETCAT_KILL" + fi + fi + + NC_PID="" + + # No data dir, need to initialize one first, to make connections to + # this node possible. + # We need to use a temporary empty data directory, because the + # actual datadir may already contain some wsrep-related files + wsrep_log_info "Initializing data directory at $tmp_datadir" + + # Disable any option that would reqiure keyring component. + # All of them can be safely disabled during the initialization and enabled + # later. We disable them, to handle the case when joiner has local manifest + # or keyring component config. In such a case it won't be possible to initialize + # the datadir, because mysqld expects an empty dir (no manifest, no config) + # The case when my.cnf has innodb_sys_tablespace_encrypt=1 is checked above, + # so at this point there is innodb_sys_tablespace_encrypt=0 or manifest and + # config are both global. + CLONE_BOOTSTRAP_EXTRA=" --innodb-undo-log-encrypt=0 --innodb-redo-log-encrypt=0 --innodb-temp-tablespace-encrypt=0 --default-table-encryption=0 --innodb-encrypt-online-alter-logs=0 --binlog-encryption=0" + + echo "" > $CLONE_ERR + eval $CLONE_ENV $CLONE_BINARY $DEFAULT_OPTIONS \ + $SKIP_NETWORKING_OPTIONS $CLONE_BOOTSTRAP_EXTRA \ + --initialize-insecure --wsrep_provider=none --datadir="$tmp_datadir" >> $CLONE_ERR 2>&1 || \ + ( wsrep_log_error "Failed to initialize data directory. Some hints below:" + grep '[ERROR]' $CLONE_ERR | cut -d ']' -f 4- | while read msg + do + wsrep_log_error "> $msg" + done + wsrep_log_error "Full log at $CLONE_ERR" + exit 1 ) + + # Move initialized data directory structure to real datadir and cleanup + mv -n "$tmp_datadir"/* "$WSREP_SST_OPT_DATA/" + wsrep_log_debug "-> REMOVE $tmp_datadir" + rm -rf "$tmp_datadir" + + wsrep_log_info "INITIALIZE DB DONE" + + # Need to create an extra user for 'localhost' because in some installations + # by default exists user ''@'localhost' and shadows every user with '%'. + cat << EOF > "$CLONE_SQL" + SET SESSION sql_log_bin=OFF; + CREATE USER '$CLONE_USER'@'%' IDENTIFIED $WITH_OPTION BY '$CLONE_PSWD'; + GRANT CLONE_ADMIN, SYSTEM_VARIABLES_ADMIN, SUPER, SHUTDOWN, EXECUTE ON *.* to '$CLONE_USER'@'%'; + GRANT INSERT ON mysql.plugin TO '$CLONE_USER'@'%'; + GRANT SELECT,UPDATE,INSERT ON performance_schema.* TO '$CLONE_USER'@'%'; + CREATE USER '$CLONE_USER'@'localhost' IDENTIFIED $WITH_OPTION BY '$CLONE_PSWD'; + GRANT CLONE_ADMIN, SYSTEM_VARIABLES_ADMIN, SUPER, SHUTDOWN, EXECUTE ON *.* TO '$CLONE_USER'@'localhost'; + GRANT EXECUTE ON *.* TO '$CLONE_USER'@'localhost'; + GRANT INSERT ON mysql.plugin TO '$CLONE_USER'@'localhost'; + GRANT SELECT,UPDATE,INSERT ON performance_schema.* TO '$CLONE_USER'@'localhost'; +EOF + + wsrep_log_info "Launching clone recipient daemon" + wsrep_log_debug "-> using: $CLONE_ENV $CLONE_BINARY $DEFAULT_OPTIONS " + wsrep_log_debug "-> Test connection as: -u$CLONE_USER -pxxxxxx -h $JOINER_CLONE_HOST -P $JOINER_CLONE_PORT" + + # Define client to be used on the Joiner side + MYSQL_ACLIENT="$MYSQL_CLIENT -u$CLONE_USER -S$CLONE_SOCK --batch --skip_column_names --silent" + wsrep_log_debug "-> MYSQL_ACLIENT: $MYSQL_ACLIENT" + + # HERE We start the instance that will get the clone and pass the CLONE_SQL file to create user + eval $CLONE_ENV $CLONE_BINARY $DEFAULT_OPTIONS \ + --wsrep_provider=none --init_file="$CLONE_SQL" >> $CLONE_ERR & + + if [ $? -ne 0 ]; then + wsrep_log_error "-> User Creation on Joiner node failed, possible permission denied. Check permissions for " + #we will try to silently shutdown the instance + `$MYSQL_ACLIENT -e "SHUTDOWN" 2> /dev/null` || : + exit 1 + fi + + # Wait for the receiver process to start + until [ -n "$(cat $CLONE_PID_FILE 2>/dev/null)" ] + do + sleep 0.2 + done + CLONE_INSTANCE_PID=$(cat "$CLONE_PID_FILE") + + if [ -n "$WSREP_LOG_DEBUG" ]; then + GRANTS=$($MYSQL_ACLIENT -uroot -NB -e "show grants for '$CLONE_USER'@'localhost'") + wsrep_log_debug "-> Clone user grants: $GRANTS" + fi + + export MYSQL_PWD=$CLONE_PSWD +# wsrep_log_debug "-> Exported MySQL password $MYSQL_PWD" + + wsrep_log_info "Waiting for clone recipient daemon to be ready for connections at port $JOINER_CLONE_PORT" + wsrep_log_debug "-> connecting as: $MYSQL_CLIENT -u$CLONE_USER -h$JOINER_CLONE_HOST -P$JOINER_CLONE_PORT" + to_wait=$JOINER_TIMEOUT_WAIT_CLONE_INSTANCE # 90 sec by defaults but it can be changed in the config + until $MYSQL_CLIENT -u$CLONE_USER -h$JOINER_CLONE_HOST -P$JOINER_CLONE_PORT \ + -e 'SELECT USER()' > /dev/null + do + if [ $to_wait -eq 0 ] + then + wsrep_log_error "Timeout waiting for clone recipient daemon" + exit $ETIMEDOUT + fi + to_wait=$((to_wait - 1)) + sleep 1 + done + + wsrep_log_info "Joiner setup." + setup_clone_plugin "recipient" + + wsrep_log_info "Waiting for clone recipient daemon to finish" + + set +e + monitor_sst_progress "${MYSQL_ACLIENT} -NB -e 'SELECT * FROM performance_schema.clone_progress;'" ${WSREP_SST_IDLE_TIMEOUT} ${CLONE_INSTANCE_PID} ${WSREP_SST_OPT_PARENT} + status=$? + set -e + + if [ $status -eq 1 ]; then + # parent process died, we need to exit as well + wsrep_log_error "Parent mysqld process (PID:$WSREP_SST_OPT_PARENT) terminated unexpectedly." + exit $EPIPE + fi + if [ $status -eq 2 ]; then + # Stale SST, no progress + wsrep_log_error "******************* FATAL ERROR ********************** " + wsrep_log_error "Killing SST ($CLONE_INSTANCE_PID) with SIGKILL after stalling for ${WSREP_SST_IDLE_TIMEOUT} seconds." + wsrep_log_error "Within the last ${WSREP_SST_IDLE_TIMEOUT} seconds (defined by the sst-idle-timeout variable)," + wsrep_log_error "the SST process on the joiner (this node) has not received any data from the donor." + wsrep_log_error "This error could be caused by broken network connectivity between" + wsrep_log_error "the donor and the joiner (this node)." + wsrep_log_error "Check the network connection and restart the joiner node." + wsrep_log_error "Line $LINENO" + wsrep_log_error "****************************************************** " + exit $ETIMEDOUT + fi + + wsrep_log_info "clone recepient daemon finished" + + CLONE_INSTANCE_PID="" + + # Execute first restart for + wsrep_log_info "FIRST RESTART to fix the dictionary" + wsrep_log_info "Performing data recovery" + wsrep_log_debug "-> RECOVERY COMMAND LINE: $CLONE_ENV $CLONE_BINARY $DEFAULT_OPTIONS --wsrep_provider=none" + + # Remove created clone user and SST pxc user + wsrep_log_debug "-> CLEAN OR NOT? $CLEANUP_CLONE_PLUGIN" + CLEANUP_CLONE_PLUGIN_SQL="" + if [ "${CLEANUP_CLONE_PLUGIN}" == "yes" ]; then + CLEANUP_CLONE_PLUGIN_SQL="UNINSTALL PLUGIN CLONE;" + fi +cat << EOF > "$CLONE_SQL" +SET SESSION sql_log_bin=OFF; +DROP USER IF EXISTS $CLONE_USER@'%'; +DROP USER IF EXISTS $CLONE_USER@'localhost'; +DROP USER IF EXISTS 'mysql.pxc.sst.user'@'localhost'; +$CLEANUP_CLONE_PLUGIN_SQL +SHUTDOWN; +EOF + + eval $CLONE_ENV $CLONE_BINARY $DEFAULT_OPTIONS --wsrep_provider=none --init_file="$CLONE_SQL" + cleanup_exit_code=$? + if [ $cleanup_exit_code -ne 0 ]; then + wsrep_log_error "Clone instance cleanup failed with exit code: $cleanup_exit_code" + fi + + wsrep_log_info "CLEANUP and SHUTDOWN done" + wsrep_log_info "Second restart for recovery position" + wsrep_log_debug "-> Second restart: $CLONE_BINARY $DEFAULT_OPTIONS $SKIP_NETWORKING_OPTIONS --wsrep_provider=none --wsrep_recover" + eval $CLONE_ENV $CLONE_BINARY $DEFAULT_OPTIONS $SKIP_NETWORKING_OPTIONS --wsrep_provider=none --wsrep_recover >> $CLONE_ERR 2>&1 + + RP="$(grep -a '\[WSREP\] Recovered position:' $CLONE_ERR || :)" + RP_PURGED=$(echo "$RP" | sed 's/.*WSREP\]\ Recovered\ position://' | sed 's/^[ \t]*//') + wsrep_log_info "Recovered POSITION: [$RP_PURGED]" + + # If an invalid recovery position is returned we report the one we get from Donor + if [ "$RP_PURGED" == "00000000-0000-0000-0000-000000000000:-1" ]; then + RP_PURGED=$RP_PURGED_EMERGENCY + fi + + if [ -n "$RP_PURGED" ]; then + ORIG_IFS=$IFS + IFS=':' + read -ra gtid_arr <<< "$RP_PURGED" + uuid="${gtid_arr[0]}" + position="${gtid_arr[1]}" + IFS=$ORIG_IFS + wsrep_log_debug "-> Creating grastate.dat file in $GRASTATE_FILE UUID: $uuid POSITION: $position" +cat << EOF > "$GRASTATE_FILE" +# GALERA saved state +version: 2.1 +uuid: $uuid +seqno: $position +safe_to_bootstrap: 0 +EOF + + else + # $RP_PURGED empty + wsrep_cleanup_progress_file + wsrep_log_debug "Failed to recover position from $CLONE_ERR"; + wsrep_log_debug "Invalid Recovery position. Exiting with error $ENODATA (No data available)" + # We terminate but save the log for inspections given the failure + CLEANUP_FILES=0 + exit $ENODATA + fi + + echo $RP_PURGED + CLEANUP_FILES=1 + # exit 0 is at the end of the script, after printing the message +else + wsrep_log_error "Unrecognized role: '$WSREP_SST_OPT_ROLE'" + exit $EINVAL +fi + +wsrep_log_debug "-> SST PROCESS FINISHED for $WSREP_SST_OPT_ROLE" +exit 0 diff --git a/scripts/wsrep_sst_common.sh b/scripts/wsrep_sst_common.sh index 38dc3973a6d4..c7fe5e393043 100755 --- a/scripts/wsrep_sst_common.sh +++ b/scripts/wsrep_sst_common.sh @@ -28,7 +28,8 @@ WSREP_SST_OPT_USER="" WSREP_SST_OPT_PSWD="" WSREP_SST_OPT_VERSION="" WSREP_SST_OPT_DEBUG="" - +WSREP_SST_OPT_REMOTE_USER="" +WSREP_SST_OPT_REMOTE_PSWD="" WSREP_LOG_DEBUG="" # These are the 'names' of the commands @@ -1030,6 +1031,12 @@ function read_variables_from_stdin() 'sst_password') WSREP_SST_OPT_PSWD="$value" ;; + 'sst_remote_user') + WSREP_SST_OPT_REMOTE_USER="$value" + ;; + 'sst_remote_password') + WSREP_SST_OPT_REMOTE_PSWD="$value" + ;; *) wsrep_log_warning "Unrecognized input: $line" esac @@ -1089,6 +1096,79 @@ function get_absolute_path() return 0 } +# +# Get the value assosciated with the key in a json file +# +# 1st param: json file path +# 2nd param: key to be searched +# +get_json_value() { + local json_file="$1" + local key="$2" + local value=$(cat $json_file | tr -d "\n" | grep -E -o "$key\" *: *(true|false)" | cut -d: -f2 | tr -d ' ') + echo $value +} + +# +# Get the keyring manifest and config file types (none/local/global) +# Note: this file is similar to get_keyring_manifest_and_config +# in wsrep_sst_xtrabackup-v2.sh. Consider refactoring. +# +# 1st param: Datadir +# 2nd param: Plugin dir +# 3rd param: mysqld binary path +get_keyring_manifest_and_config_types() +{ + local datadir=$1 + local plugin_dir=$2 + local mysqld_path=$3 + + local manifest_file="" + + local manifest_type="manifest=none" + local config_type="config=none" + + local mysqld_dir=$(dirname $mysqld_path) + local binary=$(basename $mysqld_path) + + # Get keyring manifest file path + + if [ -e $mysqld_dir/$binary.my ]; then + local local_manifest=$(get_json_value $mysqld_dir/$binary.my "read_local_manifest") + if [[ $local_manifest == "true" ]]; then + # Handle local manifest file + if [ -e $datadir/$binary.my ]; then + manifest_file=$datadir/$binary.my + manifest_type="manifest=local" + fi + else + # Handle global manifest file + manifest_file=$mysqld_dir/$binary.my + manifest_type="manifest=global" + fi + fi + + if [ -e $manifest_file ]; then + # Get the type of the component + keyring_component_type=$(grep -o "component_keyring_[a-z]*" $manifest_file | head -n 1) + + # Get keyring config path + if [ -e $plugin_dir/$keyring_component_type.cnf ]; then + local local_config=$(get_json_value $plugin_dir/$keyring_component_type.cnf "read_local_config") + if [[ $local_config == "true" ]]; then + config_type="config=local" + else + config_type="config=global" + fi + fi + fi + wsrep_log_debug "Manifest type: $manifest_type" + wsrep_log_debug "Config type: $config_type" + + echo "$manifest_type;$config_type" +} + + # timeout for donor to connect to joiner (seconds) readonly WSREP_SST_DONOR_TIMEOUT=$(parse_cnf sst donor-timeout 10) # For backward compatiblitiy: joiner timeout waiting for donor connection diff --git a/scripts/wsrep_sst_xtrabackup-v2.sh b/scripts/wsrep_sst_xtrabackup-v2.sh index 25098fa20c3f..dd51d2b20dd5 100755 --- a/scripts/wsrep_sst_xtrabackup-v2.sh +++ b/scripts/wsrep_sst_xtrabackup-v2.sh @@ -1021,19 +1021,6 @@ cleanup_donor() exit $estatus } -# -# Get the value assosciated with the key in a json file -# -# 1st param: json file path -# 2nd param: key to be searched -# -get_json_value() { - local json_file="$1" - local key="$2" - local value=$(cat $json_file | tr -d "\n" | grep -E -o "$key\" *: *(true|false)" | cut -d: -f2 | tr -d ' ') - echo $value -} - # # Get the keyring manifest and config file paths # @@ -1054,7 +1041,7 @@ get_keyring_manifest_and_config() # Get keyring manifest file path if [ -e $mysqld_dir/$binary.my ]; then - local local_manifest=$(get_json_value $mysqld_dir/mysqld.my "read_local_manifest") + local local_manifest=$(get_json_value $mysqld_dir/$binary.my "read_local_manifest") if [[ $local_manifest == "true" ]]; then # Handle local manifest file if [ -e $datadir/$binary.my ]; then diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index ea983ac12c90..58a5a89d5b8f 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "debug_sync.h" #include "log_event.h" #include "my_rnd.h" @@ -73,6 +74,7 @@ extern const char *wsrep_defaults_group_suffix; #define WSREP_SST_ONLY_IST "ist_only" #define WSREP_SST_XTRABACKUP "xtrabackup" #define WSREP_SST_XTRABACKUP_V2 "xtrabackup-v2" +#define WSREP_SST_CLONE "clone" #define WSREP_SST_DEFAULT WSREP_SST_XTRABACKUP_V2 #define WSREP_SST_ADDRESS_AUTO "AUTO" @@ -82,7 +84,7 @@ const char *wsrep_sst_method = WSREP_SST_DEFAULT; const char *wsrep_sst_receive_address = WSREP_SST_ADDRESS_AUTO; const char *wsrep_sst_donor = ""; -#define WSREP_SST_ALLOWED_METHODS_DEFAULT WSREP_SST_XTRABACKUP_V2 +#define WSREP_SST_ALLOWED_METHODS_DEFAULT WSREP_SST_XTRABACKUP_V2 "," WSREP_SST_CLONE const char *wsrep_sst_allowed_methods = WSREP_SST_ALLOWED_METHODS_DEFAULT; static std::vector allowed_sst_methods; @@ -203,10 +205,10 @@ bool wsrep_setup_allowed_sst_methods() { static bool sst_awaiting_callback = false; bool wsrep_sst_in_progress() { - if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort(); - bool in_progress = sst_awaiting_callback; - mysql_mutex_unlock (&LOCK_wsrep_sst); - return in_progress; + if (mysql_mutex_lock(&LOCK_wsrep_sst)) abort(); + bool in_progress = sst_awaiting_callback; + mysql_mutex_unlock(&LOCK_wsrep_sst); + return in_progress; } // Signal end of SST @@ -276,17 +278,24 @@ static char *my_fgets(char *buf, size_t buf_len, FILE *stream) { return ret; } +// New strucutre to host user info +struct sst_auth { + std::string remote_name_; + std::string remote_pswd_; +}; + struct sst_thread_arg { const char *cmd; char **env; + const sst_auth &auth_container; char *ret_str; int err; mysql_mutex_t LOCK_wsrep_sst_thread; mysql_cond_t COND_wsrep_sst_thread; - sst_thread_arg(const char *c, char **e) - : cmd(c), env(e), ret_str(0), err(-1) { + sst_thread_arg(const char *c, char **e, sst_auth &auth) + : cmd(c), env(e), auth_container(auth), ret_str(0), err(-1) { mysql_mutex_init(key_LOCK_wsrep_sst_thread, &LOCK_wsrep_sst_thread, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_wsrep_sst_thread, &COND_wsrep_sst_thread); @@ -754,8 +763,11 @@ static ssize_t sst_prepare_other(const char *method, const char *addr_in, reset_ld_preload(env); #endif + // We define the auth here to pass it to the thread args + sst_auth auth; + pthread_t tmp; - sst_thread_arg arg(cmd_str(), env()); + sst_thread_arg arg(cmd_str(), env(), auth); mysql_mutex_lock(&arg.LOCK_wsrep_sst_thread); ret = pthread_create(&tmp, NULL, sst_joiner_thread, &arg); if (ret) { @@ -792,12 +804,14 @@ std::string wsrep_sst_prepare() { /* Inform Galera that we are done with SST and it can proceed with IST. In fact the following call will wait until the server is fully initialized and then inform Galera about two things: - 1. call sst_received() - so from Galera's point of view it looks like we are done with - sst - 2. return empty string from this function - informs Galera that only IST should - be processed.*/ - WSREP_WARN("State Transfer via SST was prohibited by setting wsrep_sst_method=ist_only. " - "The node will try to join the cluster using only IST."); + 1. call sst_received() - so from Galera's point of view it looks like we are + done with sst + 2. return empty string from this function - informs Galera that only IST + should be processed.*/ + WSREP_WARN( + "State Transfer via SST was prohibited by setting " + "wsrep_sst_method=ist_only. " + "The node will try to join the cluster using only IST."); wsrep_sst_complete(current_thd, 0); return WSREP_STATE_TRANSFER_NO_SST; } @@ -874,7 +888,19 @@ std::string wsrep_sst_prepare() { const char *method_ptr(ret.data()); const char *addr_ptr(ret.data() + strlen(method_ptr) + 1); - WSREP_INFO("Prepared SST request: %s|%s", method_ptr, addr_ptr); + /* we check for @ as separator in user:pw@address format and return only the + address part if not present we do not filter it We decide to do not mask + the PWD but to remove compeletely the information user:pwd to keep + consistency with the already deployed solution for XB. This in case a user + had implemented any kind of log traking + */ + std::string addr_str = addr_ptr; + size_t atPosition = addr_str.find("@"); + if (atPosition > 0) { + addr_str = addr_str.substr(atPosition + 1); + } + + WSREP_INFO("Prepared SST request: %s|%s", method_ptr, addr_str.c_str()); if (mysql_mutex_lock(&LOCK_wsrep_sst)) abort(); sst_awaiting_callback = true; @@ -1170,20 +1196,22 @@ static int wsrep_create_sst_user(bool initialize_thread, const char *password) { // The second entry is the string to be displayed if the query fails // (this can be NULL, in which case the actual query will be used) const char *cmds[] = { - "SET SESSION sql_log_bin = OFF;", - nullptr, - "DROP USER IF EXISTS 'mysql.pxc.sst.user'@localhost;", - nullptr, - "CREATE USER 'mysql.pxc.sst.user'@localhost " - " IDENTIFIED BY '%s' ACCOUNT LOCK;", - "CREATE USER mysql.pxc.sst.user IDENTIFIED WITH * BY * ACCOUNT LOCK", - "GRANT 'mysql.pxc.sst.role'@localhost TO 'mysql.pxc.sst.user'@localhost;", nullptr, - "SET DEFAULT ROLE 'mysql.pxc.sst.role'@localhost to 'mysql.pxc.sst.user'@localhost;", nullptr, - "ALTER USER 'mysql.pxc.sst.user'@localhost ACCOUNT UNLOCK;", - nullptr, - nullptr, - nullptr - }; + "SET SESSION sql_log_bin = OFF;", + nullptr, + "DROP USER IF EXISTS 'mysql.pxc.sst.user'@localhost;", + nullptr, + "CREATE USER 'mysql.pxc.sst.user'@localhost " + " IDENTIFIED BY '%s' ACCOUNT LOCK;", + "CREATE USER mysql.pxc.sst.user IDENTIFIED WITH * BY * ACCOUNT LOCK", + "GRANT 'mysql.pxc.sst.role'@localhost TO 'mysql.pxc.sst.user'@localhost;", + nullptr, + "SET DEFAULT ROLE 'mysql.pxc.sst.role'@localhost to " + "'mysql.pxc.sst.user'@localhost;", + nullptr, + "ALTER USER 'mysql.pxc.sst.user'@localhost ACCOUNT UNLOCK;", + nullptr, + nullptr, + nullptr}; wsrep_allow_server_session = true; session = setup_server_session(initialize_thread); @@ -1254,6 +1282,7 @@ int wsrep_remove_sst_user(bool initialize_thread) { static void *sst_donor_thread(void *a) { sst_thread_arg *arg = (sst_thread_arg *)a; + sst_auth const auth(arg->auth_container); #ifdef HAVE_PSI_INTERFACE wsrep_pfs_register_thread(key_THREAD_wsrep_sst_donor); @@ -1318,6 +1347,18 @@ static void *sst_donor_thread(void *a) { err = (ret < 0 ? ret : -EMSGSIZE); } + // if remote user is defined we will pass the user-name/password pair + if (auth.remote_name_.length()) { + ret = fprintf(proc.write_pipe(), + "sst_remote_user=%s\n" + "sst_remote_password=%s\n", + auth.remote_name_.c_str(), auth.remote_pswd_.c_str()); + if (ret < 0) { + WSREP_ERROR("sst_donor_thread(): fprintf(2) failed: %d", ret); + err = (ret < 0 ? ret : -EMSGSIZE); + } + } + // Close the pipe, so that the other side gets an EOF proc.close_write_pipe(); } @@ -1397,8 +1438,6 @@ static void *sst_donor_thread(void *a) { strerror(err)); } - wsrep_remove_sst_user(true); - if (locked) // don't forget to unlock server before return { sst_disallow_writes(thd.ptr, false); @@ -1416,6 +1455,9 @@ static void *sst_donor_thread(void *a) { // also have exited if (logger_thd) pthread_join(logger_thd, NULL); + // The process has exited, so sst user is no longer needed + wsrep_remove_sst_user(true); + #ifdef HAVE_PSI_INTERFACE wsrep_pfs_delete_thread(); #endif /* HAVE_PSI_INTERFACE */ @@ -1425,7 +1467,7 @@ static void *sst_donor_thread(void *a) { static int sst_donate_other(const char *method, const char *addr, const wsrep::gtid >id, bool bypass, - char **env) // carries auth info + sst_auth &auth, char **env) // carries auth info { int const cmd_len = 4096; wsp::string cmd_str(cmd_len); @@ -1483,7 +1525,7 @@ static int sst_donate_other(const char *method, const char *addr, if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(false); pthread_t tmp; - sst_thread_arg arg(cmd_str(), env); + sst_thread_arg arg(cmd_str(), env, auth); mysql_mutex_lock(&arg.LOCK_wsrep_sst_thread); ret = pthread_create(&tmp, NULL, sst_donor_thread, &arg); if (ret) { @@ -1540,7 +1582,7 @@ static bool is_sst_request_valid(const std::string &msg) { Instead of this we will just allow alpha-num + a few special characters (colon, slash, dot, underscore, square brackets, hyphen). */ std::string data = msg.substr(method_len + 1, data_len); - static const std::regex allowed_chars_regex("[\\w:/.[\\]-]+"); + static const std::regex allowed_chars_regex("[\\w:/.[\\]@-]+"); if (!std::regex_match(data, allowed_chars_regex)) { return false; } @@ -1548,6 +1590,11 @@ static bool is_sst_request_valid(const std::string &msg) { return true; } +static std::string mask_password_in_string(const std::string &str) { + static std::regex password_regex(R"(:[^@]+@)"); + return std::regex_replace(str, password_regex, ":********@"); +} + int wsrep_sst_donate(const std::string &msg, const wsrep::gtid ¤t_gtid, const bool bypass) { /* This will be reset when sync callback is called. @@ -1562,7 +1609,8 @@ int wsrep_sst_donate(const std::string &msg, const wsrep::gtid ¤t_gtid, else ss << ""; }); - WSREP_ERROR("Invalid sst_request: %s", ss.str().c_str()); + WSREP_ERROR("Invalid sst_request: %s", + mask_password_in_string(ss.str()).c_str()); return WSREP_CB_FAILURE; } @@ -1578,6 +1626,32 @@ int wsrep_sst_donate(const std::string &msg, const wsrep::gtid ¤t_gtid, return WSREP_CB_FAILURE; } + /* + [start] + section to support clone user/pw + check for auth@addr separator + */ + const char *addr = strrchr(data, '@'); + wsp::string remote_auth; + if (addr) { + remote_auth.set(strndup(data, addr - data)); + addr++; + } else { + // no auth part + addr = data; + } + + /* Set up auth info (from : strings) */ + sst_auth auth; + if (remote_auth()) { + /* wsp::string is just a dynamically allocated char* underneath + * so we can safely do all that arithmetics */ + const char *col = strchrnul(remote_auth(), ':'); + auth.remote_name_ = std::string(remote_auth(), col - remote_auth()); + auth.remote_pswd_ = std::string(':' == *col ? col + 1 : ""); + } + /* [END] */ + #if defined(HAVE_ASAN) reset_ld_preload(env); #endif @@ -1599,7 +1673,7 @@ int wsrep_sst_donate(const std::string &msg, const wsrep::gtid ¤t_gtid, };); int ret; - ret = sst_donate_other(method, data, current_gtid, bypass, env()); + ret = sst_donate_other(method, addr, current_gtid, bypass, auth, env()); /* Above methods should return 0 in case of success and negative value * in case of failure. If we have any positive value here it means that we diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h index ea11764b3122..6900b1942265 100644 --- a/sql/wsrep_sst.h +++ b/sql/wsrep_sst.h @@ -30,6 +30,7 @@ extern const char *wsrep_sst_method; extern const char *wsrep_sst_receive_address; extern const char *wsrep_sst_donor; +extern const char *wsrep_sst_auth; extern bool wsrep_sst_donor_rejects_queries; extern const char *wsrep_sst_allowed_methods;