Commit a1e5a284 authored by Alexey Yurchenko's avatar Alexey Yurchenko Committed by Julius Goryavsky

MDEV-31809 Automatic SST user account management

Implement automatic creation of temporary accounts for SST and pass
account credentials to SST script via socket as opposed to environment
variables. Delete the user after the SST script returns,

Respect wsrep_sst_auth set by the adminitrator in case some additional
privilege grants are needed for particular SST method.

mysqldump SST requires significant change to make use of the new
automatic user generation facility. For now just make it compatible
by ignoring automatically generated user and rely only on wsrep_sst_auth
setting on the joiner node to keep backward compatibility.

Adapt mysqldump SST to automatic SST user generation changes:
 - disable special treatment for mysqldump SST on donor
 - make mysqldump SST script compatible with the new SST script
   interface.

Differentiate user privileges for different SST methods:
 - grant minimum required privileges for clone and xtrabackup SST
   accounts
 - grant all privileges to custom SST accounts as it is not known what
   is needed.
 - disable SST account generation for rsync SST since it is not needed.

MTR tests:
 - add MTR tests for clone and xtrabackup SSTs without wsrep_sst_auth,
 - add MTR test for testing masking of wsrep_sst_auth.
 - don't attmept to restore original wsrep_sst_auth in MTR tests as it
   is always masked.
Signed-off-by: default avatarJulius Goryavsky <julius.goryavsky@mariadb.com>
parent 1aa1a7cf
......@@ -49,7 +49,7 @@ WSREP_SLAVE_FK_CHECKS ON
WSREP_SLAVE_THREADS 1
WSREP_SLAVE_UK_CHECKS OFF
WSREP_SR_STORE table
WSREP_SST_AUTH
WSREP_SST_AUTH ********
WSREP_SST_DONOR
WSREP_SST_DONOR_REJECTS_QUERIES OFF
WSREP_SST_METHOD rsync
......
--- r/galera_sst_mariabackup.result
+++ r/galera_sst_mariabackup.reject
@@ -516,5 +516,189 @@
1
DROP TABLE t1;
COMMIT;
+Performing State Transfer on a server that has been killed and restarted
+while a DDL was in progress on it
+connection node_1;
+CREATE TABLE t1 (id int not null primary key,f1 CHAR(255)) ENGINE=InnoDB;
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO t1 VALUES (1,'node1_committed_before');
+INSERT INTO t1 VALUES (2,'node1_committed_before');
+INSERT INTO t1 VALUES (3,'node1_committed_before');
+INSERT INTO t1 VALUES (4,'node1_committed_before');
+INSERT INTO t1 VALUES (5,'node1_committed_before');
+connection node_2;
+START TRANSACTION;
+INSERT INTO t1 VALUES (6,'node2_committed_before');
+INSERT INTO t1 VALUES (7,'node2_committed_before');
+INSERT INTO t1 VALUES (8,'node2_committed_before');
+INSERT INTO t1 VALUES (9,'node2_committed_before');
+INSERT INTO t1 VALUES (10,'node2_committed_before');
+COMMIT;
+SET GLOBAL debug_dbug = 'd,sync.alter_opened_table';
+connection node_1;
+ALTER TABLE t1 ADD COLUMN f2 INTEGER;
+connection node_2;
+SET wsrep_sync_wait = 0;
+Killing server ...
+connection node_1;
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO t1 (id,f1) VALUES (11,'node1_committed_during');
+INSERT INTO t1 (id,f1) VALUES (12,'node1_committed_during');
+INSERT INTO t1 (id,f1) VALUES (13,'node1_committed_during');
+INSERT INTO t1 (id,f1) VALUES (14,'node1_committed_during');
+INSERT INTO t1 (id,f1) VALUES (15,'node1_committed_during');
+COMMIT;
+START TRANSACTION;
+INSERT INTO t1 (id,f1) VALUES (16,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (17,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (18,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (19,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (20,'node1_to_be_committed_after');
+connect node_1a_galera_st_kill_slave_ddl, 127.0.0.1, root, , test, $NODE_MYPORT_1;
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO t1 (id,f1) VALUES (21,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (22,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (23,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (24,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (25,'node1_to_be_rollbacked_after');
+connection node_2;
+Performing --wsrep-recover ...
+connection node_2;
+Starting server ...
+Using --wsrep-start-position when starting mysqld ...
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO t1 (id,f1) VALUES (26,'node2_committed_after');
+INSERT INTO t1 (id,f1) VALUES (27,'node2_committed_after');
+INSERT INTO t1 (id,f1) VALUES (28,'node2_committed_after');
+INSERT INTO t1 (id,f1) VALUES (29,'node2_committed_after');
+INSERT INTO t1 (id,f1) VALUES (30,'node2_committed_after');
+COMMIT;
+connection node_1;
+INSERT INTO t1 (id,f1) VALUES (31,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (32,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (33,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (34,'node1_to_be_committed_after');
+INSERT INTO t1 (id,f1) VALUES (35,'node1_to_be_committed_after');
+COMMIT;
+SET AUTOCOMMIT=OFF;
+START TRANSACTION;
+INSERT INTO t1 (id,f1) VALUES (36,'node1_committed_after');
+INSERT INTO t1 (id,f1) VALUES (37,'node1_committed_after');
+INSERT INTO t1 (id,f1) VALUES (38,'node1_committed_after');
+INSERT INTO t1 (id,f1) VALUES (39,'node1_committed_after');
+INSERT INTO t1 (id,f1) VALUES (40,'node1_committed_after');
+COMMIT;
+connection node_1a_galera_st_kill_slave_ddl;
+INSERT INTO t1 (id,f1) VALUES (41,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (42,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (43,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (44,'node1_to_be_rollbacked_after');
+INSERT INTO t1 (id,f1) VALUES (45,'node1_to_be_rollbacked_after');
+ROLLBACK;
+SET AUTOCOMMIT=ON;
+SET SESSION wsrep_sync_wait=15;
+SELECT COUNT(*) AS EXPECT_3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
+EXPECT_3
+3
+SELECT COUNT(*) AS EXPECT_35 FROM t1;
+EXPECT_35
+35
+SELECT * FROM t1;
+id f1 f2
+1 node1_committed_before NULL
+2 node1_committed_before NULL
+3 node1_committed_before NULL
+4 node1_committed_before NULL
+5 node1_committed_before NULL
+6 node2_committed_before NULL
+7 node2_committed_before NULL
+8 node2_committed_before NULL
+9 node2_committed_before NULL
+10 node2_committed_before NULL
+11 node1_committed_during NULL
+12 node1_committed_during NULL
+13 node1_committed_during NULL
+14 node1_committed_during NULL
+15 node1_committed_during NULL
+16 node1_to_be_committed_after NULL
+17 node1_to_be_committed_after NULL
+18 node1_to_be_committed_after NULL
+19 node1_to_be_committed_after NULL
+20 node1_to_be_committed_after NULL
+26 node2_committed_after NULL
+27 node2_committed_after NULL
+28 node2_committed_after NULL
+29 node2_committed_after NULL
+30 node2_committed_after NULL
+31 node1_to_be_committed_after NULL
+32 node1_to_be_committed_after NULL
+33 node1_to_be_committed_after NULL
+34 node1_to_be_committed_after NULL
+35 node1_to_be_committed_after NULL
+36 node1_committed_after NULL
+37 node1_committed_after NULL
+38 node1_committed_after NULL
+39 node1_committed_after NULL
+40 node1_committed_after NULL
+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;
+connection node_1;
+SET AUTOCOMMIT=ON;
+SET SESSION wsrep_sync_wait=15;
+SELECT COUNT(*) AS EXPECT_3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
+EXPECT_3
+3
+SELECT COUNT(*) AS EXPECT_35 FROM t1;
+EXPECT_35
+35
+SELECT * FROM t1;
+id f1 f2
+1 node1_committed_before NULL
+2 node1_committed_before NULL
+3 node1_committed_before NULL
+4 node1_committed_before NULL
+5 node1_committed_before NULL
+6 node2_committed_before NULL
+7 node2_committed_before NULL
+8 node2_committed_before NULL
+9 node2_committed_before NULL
+10 node2_committed_before NULL
+11 node1_committed_during NULL
+12 node1_committed_during NULL
+13 node1_committed_during NULL
+14 node1_committed_during NULL
+15 node1_committed_during NULL
+16 node1_to_be_committed_after NULL
+17 node1_to_be_committed_after NULL
+18 node1_to_be_committed_after NULL
+19 node1_to_be_committed_after NULL
+20 node1_to_be_committed_after NULL
+26 node2_committed_after NULL
+27 node2_committed_after NULL
+28 node2_committed_after NULL
+29 node2_committed_after NULL
+30 node2_committed_after NULL
+31 node1_to_be_committed_after NULL
+32 node1_to_be_committed_after NULL
+33 node1_to_be_committed_after NULL
+34 node1_to_be_committed_after NULL
+35 node1_to_be_committed_after NULL
+36 node1_committed_after NULL
+37 node1_committed_after NULL
+38 node1_committed_after NULL
+39 node1_committed_after NULL
+40 node1_committed_after NULL
+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 GLOBAL debug_dbug = $debug_orig;
disconnect node_2;
disconnect node_1;
connection node_2;
connection node_1;
connection node_1;
connection node_2;
Performing State Transfer on a server that has been shut down cleanly and restarted
connection node_1;
CREATE TABLE t1 (id int not null primary key,f1 CHAR(255)) ENGINE=InnoDB;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (1,'node1_committed_before');
INSERT INTO t1 VALUES (2,'node1_committed_before');
INSERT INTO t1 VALUES (3,'node1_committed_before');
INSERT INTO t1 VALUES (4,'node1_committed_before');
INSERT INTO t1 VALUES (5,'node1_committed_before');
COMMIT;
connection node_2;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (6,'node2_committed_before');
INSERT INTO t1 VALUES (7,'node2_committed_before');
INSERT INTO t1 VALUES (8,'node2_committed_before');
INSERT INTO t1 VALUES (9,'node2_committed_before');
INSERT INTO t1 VALUES (10,'node2_committed_before');
COMMIT;
Shutting down server ...
connection node_1;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (11,'node1_committed_during');
INSERT INTO t1 VALUES (12,'node1_committed_during');
INSERT INTO t1 VALUES (13,'node1_committed_during');
INSERT INTO t1 VALUES (14,'node1_committed_during');
INSERT INTO t1 VALUES (15,'node1_committed_during');
COMMIT;
START TRANSACTION;
INSERT INTO t1 VALUES (16,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (17,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (18,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (19,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (20,'node1_to_be_committed_after');
connect node_1a_galera_st_shutdown_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (21,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (22,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (23,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (24,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (25,'node1_to_be_rollbacked_after');
connection node_2;
Starting server ...
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (26,'node2_committed_after');
INSERT INTO t1 VALUES (27,'node2_committed_after');
INSERT INTO t1 VALUES (28,'node2_committed_after');
INSERT INTO t1 VALUES (29,'node2_committed_after');
INSERT INTO t1 VALUES (30,'node2_committed_after');
COMMIT;
connection node_1;
INSERT INTO t1 VALUES (31,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (32,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (33,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (34,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (35,'node1_to_be_committed_after');
COMMIT;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (36,'node1_committed_after');
INSERT INTO t1 VALUES (37,'node1_committed_after');
INSERT INTO t1 VALUES (38,'node1_committed_after');
INSERT INTO t1 VALUES (39,'node1_committed_after');
INSERT INTO t1 VALUES (40,'node1_committed_after');
COMMIT;
connection node_1a_galera_st_shutdown_slave;
INSERT INTO t1 VALUES (41,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (42,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (43,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (44,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (45,'node1_to_be_rollbacked_after');
ROLLBACK;
SET AUTOCOMMIT=ON;
SET SESSION wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_15 FROM t1;
EXPECT_15
35
SELECT * from t1;
id f1
1 node1_committed_before
2 node1_committed_before
3 node1_committed_before
4 node1_committed_before
5 node1_committed_before
6 node2_committed_before
7 node2_committed_before
8 node2_committed_before
9 node2_committed_before
10 node2_committed_before
11 node1_committed_during
12 node1_committed_during
13 node1_committed_during
14 node1_committed_during
15 node1_committed_during
16 node1_to_be_committed_after
17 node1_to_be_committed_after
18 node1_to_be_committed_after
19 node1_to_be_committed_after
20 node1_to_be_committed_after
26 node2_committed_after
27 node2_committed_after
28 node2_committed_after
29 node2_committed_after
30 node2_committed_after
31 node1_to_be_committed_after
32 node1_to_be_committed_after
33 node1_to_be_committed_after
34 node1_to_be_committed_after
35 node1_to_be_committed_after
36 node1_committed_after
37 node1_committed_after
38 node1_committed_after
39 node1_committed_after
40 node1_committed_after
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;
connection node_1;
SET AUTOCOMMIT=ON;
SET SESSION wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_15 FROM t1;
EXPECT_15
35
SELECT * from t1;
id f1
1 node1_committed_before
2 node1_committed_before
3 node1_committed_before
4 node1_committed_before
5 node1_committed_before
6 node2_committed_before
7 node2_committed_before
8 node2_committed_before
9 node2_committed_before
10 node2_committed_before
11 node1_committed_during
12 node1_committed_during
13 node1_committed_during
14 node1_committed_during
15 node1_committed_during
16 node1_to_be_committed_after
17 node1_to_be_committed_after
18 node1_to_be_committed_after
19 node1_to_be_committed_after
20 node1_to_be_committed_after
26 node2_committed_after
27 node2_committed_after
28 node2_committed_after
29 node2_committed_after
30 node2_committed_after
31 node1_to_be_committed_after
32 node1_to_be_committed_after
33 node1_to_be_committed_after
34 node1_to_be_committed_after
35 node1_to_be_committed_after
36 node1_committed_after
37 node1_committed_after
38 node1_committed_after
39 node1_committed_after
40 node1_committed_after
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;
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
connection node_1;
CREATE TABLE t1 (id int not null primary key,f1 CHAR(255)) ENGINE=InnoDB;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (1,'node1_committed_before');
INSERT INTO t1 VALUES (2,'node1_committed_before');
INSERT INTO t1 VALUES (3,'node1_committed_before');
INSERT INTO t1 VALUES (4,'node1_committed_before');
INSERT INTO t1 VALUES (5,'node1_committed_before');
COMMIT;
connection node_2;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (6,'node2_committed_before');
INSERT INTO t1 VALUES (7,'node2_committed_before');
INSERT INTO t1 VALUES (8,'node2_committed_before');
INSERT INTO t1 VALUES (9,'node2_committed_before');
INSERT INTO t1 VALUES (10,'node2_committed_before');
COMMIT;
Shutting down server ...
connection node_1;
Cleaning var directory ...
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (11,'node1_committed_during');
INSERT INTO t1 VALUES (12,'node1_committed_during');
INSERT INTO t1 VALUES (13,'node1_committed_during');
INSERT INTO t1 VALUES (14,'node1_committed_during');
INSERT INTO t1 VALUES (15,'node1_committed_during');
COMMIT;
START TRANSACTION;
INSERT INTO t1 VALUES (16,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (17,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (18,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (19,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (20,'node1_to_be_committed_after');
connect node_1a_galera_st_clean_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (21,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (22,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (23,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (24,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (25,'node1_to_be_rollbacked_after');
connection node_2;
Starting server ...
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (26,'node2_committed_after');
INSERT INTO t1 VALUES (27,'node2_committed_after');
INSERT INTO t1 VALUES (28,'node2_committed_after');
INSERT INTO t1 VALUES (29,'node2_committed_after');
INSERT INTO t1 VALUES (30,'node2_committed_after');
COMMIT;
connection node_1;
INSERT INTO t1 VALUES (31,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (32,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (33,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (34,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (35,'node1_to_be_committed_after');
COMMIT;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (36,'node1_committed_after');
INSERT INTO t1 VALUES (37,'node1_committed_after');
INSERT INTO t1 VALUES (38,'node1_committed_after');
INSERT INTO t1 VALUES (39,'node1_committed_after');
INSERT INTO t1 VALUES (40,'node1_committed_after');
COMMIT;
connection node_1a_galera_st_clean_slave;
INSERT INTO t1 VALUES (41,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (42,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (43,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (44,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (45,'node1_to_be_rollbacked_after');
ROLLBACK;
SET AUTOCOMMIT=ON;
SET SESSION wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_35 FROM t1;
EXPECT_35
35
SELECT * from t1;
id f1
1 node1_committed_before
2 node1_committed_before
3 node1_committed_before
4 node1_committed_before
5 node1_committed_before
6 node2_committed_before
7 node2_committed_before
8 node2_committed_before
9 node2_committed_before
10 node2_committed_before
11 node1_committed_during
12 node1_committed_during
13 node1_committed_during
14 node1_committed_during
15 node1_committed_during
16 node1_to_be_committed_after
17 node1_to_be_committed_after
18 node1_to_be_committed_after
19 node1_to_be_committed_after
20 node1_to_be_committed_after
26 node2_committed_after
27 node2_committed_after
28 node2_committed_after
29 node2_committed_after
30 node2_committed_after
31 node1_to_be_committed_after
32 node1_to_be_committed_after
33 node1_to_be_committed_after
34 node1_to_be_committed_after
35 node1_to_be_committed_after
36 node1_committed_after
37 node1_committed_after
38 node1_committed_after
39 node1_committed_after
40 node1_committed_after
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;
connection node_1;
SET AUTOCOMMIT=ON;
SET SESSION wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_35 FROM t1;
EXPECT_35
35
SELECT * from t1;
id f1
1 node1_committed_before
2 node1_committed_before
3 node1_committed_before
4 node1_committed_before
5 node1_committed_before
6 node2_committed_before
7 node2_committed_before
8 node2_committed_before
9 node2_committed_before
10 node2_committed_before
11 node1_committed_during
12 node1_committed_during
13 node1_committed_during
14 node1_committed_during
15 node1_committed_during
16 node1_to_be_committed_after
17 node1_to_be_committed_after
18 node1_to_be_committed_after
19 node1_to_be_committed_after
20 node1_to_be_committed_after
26 node2_committed_after
27 node2_committed_after
28 node2_committed_after
29 node2_committed_after
30 node2_committed_after
31 node1_to_be_committed_after
32 node1_to_be_committed_after
33 node1_to_be_committed_after
34 node1_to_be_committed_after
35 node1_to_be_committed_after
36 node1_committed_after
37 node1_committed_after
38 node1_committed_after
39 node1_committed_after
40 node1_committed_after
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;
Performing State Transfer on a server that has been killed and restarted
connection node_1;
CREATE TABLE t1 (id int not null primary key,f1 CHAR(255)) ENGINE=InnoDB;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (1,'node1_committed_before');
INSERT INTO t1 VALUES (2,'node1_committed_before');
INSERT INTO t1 VALUES (3,'node1_committed_before');
INSERT INTO t1 VALUES (4,'node1_committed_before');
INSERT INTO t1 VALUES (5,'node1_committed_before');
COMMIT;
connection node_2;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (6,'node2_committed_before');
INSERT INTO t1 VALUES (7,'node2_committed_before');
INSERT INTO t1 VALUES (8,'node2_committed_before');
INSERT INTO t1 VALUES (9,'node2_committed_before');
INSERT INTO t1 VALUES (10,'node2_committed_before');
COMMIT;
Killing server ...
connection node_1;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (11,'node1_committed_during');
INSERT INTO t1 VALUES (12,'node1_committed_during');
INSERT INTO t1 VALUES (13,'node1_committed_during');
INSERT INTO t1 VALUES (14,'node1_committed_during');
INSERT INTO t1 VALUES (15,'node1_committed_during');
COMMIT;
START TRANSACTION;
INSERT INTO t1 VALUES (16,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (17,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (18,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (19,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (20,'node1_to_be_committed_after');
connect node_1a_galera_st_kill_slave, 127.0.0.1, root, , test, $NODE_MYPORT_1;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (21,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (22,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (23,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (24,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (25,'node1_to_be_rollbacked_after');
connection node_2;
Performing --wsrep-recover ...
Starting server ...
Using --wsrep-start-position when starting mysqld ...
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (26,'node2_committed_after');
INSERT INTO t1 VALUES (27,'node2_committed_after');
INSERT INTO t1 VALUES (28,'node2_committed_after');
INSERT INTO t1 VALUES (29,'node2_committed_after');
INSERT INTO t1 VALUES (30,'node2_committed_after');
COMMIT;
connection node_1;
INSERT INTO t1 VALUES (31,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (32,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (33,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (34,'node1_to_be_committed_after');
INSERT INTO t1 VALUES (35,'node1_to_be_committed_after');
COMMIT;
SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (36,'node1_committed_after');
INSERT INTO t1 VALUES (37,'node1_committed_after');
INSERT INTO t1 VALUES (38,'node1_committed_after');
INSERT INTO t1 VALUES (39,'node1_committed_after');
INSERT INTO t1 VALUES (40,'node1_committed_after');
COMMIT;
connection node_1a_galera_st_kill_slave;
INSERT INTO t1 VALUES (41,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (42,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (43,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (45,'node1_to_be_rollbacked_after');
INSERT INTO t1 VALUES (46,'node1_to_be_rollbacked_after');
ROLLBACK;
SET AUTOCOMMIT=ON;
SET SESSION wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_35 FROM t1;
EXPECT_35
35
SELECT * FROM t1;
id f1
1 node1_committed_before
2 node1_committed_before
3 node1_committed_before
4 node1_committed_before
5 node1_committed_before
6 node2_committed_before
7 node2_committed_before
8 node2_committed_before
9 node2_committed_before
10 node2_committed_before
11 node1_committed_during
12 node1_committed_during
13 node1_committed_during
14 node1_committed_during
15 node1_committed_during
16 node1_to_be_committed_after
17 node1_to_be_committed_after
18 node1_to_be_committed_after
19 node1_to_be_committed_after
20 node1_to_be_committed_after
26 node2_committed_after
27 node2_committed_after
28 node2_committed_after
29 node2_committed_after
30 node2_committed_after
31 node1_to_be_committed_after
32 node1_to_be_committed_after
33 node1_to_be_committed_after
34 node1_to_be_committed_after
35 node1_to_be_committed_after
36 node1_committed_after
37 node1_committed_after
38 node1_committed_after
39 node1_committed_after
40 node1_committed_after
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;
connection node_1;
SET AUTOCOMMIT=ON;
SET SESSION wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_35 FROM t1;
EXPECT_35
35
SELECT * FROM t1;
id f1
1 node1_committed_before
2 node1_committed_before
3 node1_committed_before
4 node1_committed_before
5 node1_committed_before
6 node2_committed_before
7 node2_committed_before
8 node2_committed_before
9 node2_committed_before
10 node2_committed_before
11 node1_committed_during
12 node1_committed_during
13 node1_committed_during
14 node1_committed_during
15 node1_committed_during
16 node1_to_be_committed_after
17 node1_to_be_committed_after
18 node1_to_be_committed_after
19 node1_to_be_committed_after
20 node1_to_be_committed_after
26 node2_committed_after
27 node2_committed_after
28 node2_committed_after
29 node2_committed_after
30 node2_committed_after
31 node1_to_be_committed_after
32 node1_to_be_committed_after
33 node1_to_be_committed_after
34 node1_to_be_committed_after
35 node1_to_be_committed_after
36 node1_committed_after
37 node1_committed_after
38 node1_committed_after
39 node1_committed_after
40 node1_committed_after
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;
disconnect node_2;
disconnect node_1;
!include ../galera_2nodes.cnf
[mysqld]
wsrep_sst_method=mariabackup
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]
transferfmt=@ENV.MTR_GALERA_TFMT
streamfmt=mbstream
--source include/big_test.inc
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_mariabackup.inc
# Save original auto_increment_offset values.
--let $node_1=node_1
--let $node_2=node_2
--source include/auto_increment_offset_save.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
# Restore original auto_increment_offset values.
--source include/auto_increment_offset_restore.inc
--source include/galera_end.inc
......@@ -20,7 +20,6 @@ call mtr.add_suppression("Failed to load slave replication state from table mysq
CREATE USER 'sst';
GRANT ALL PRIVILEGES ON *.* TO 'sst';
--let $wsrep_sst_auth_orig = `SELECT @@wsrep_sst_auth`
SET GLOBAL wsrep_sst_auth = 'sst:';
--connection node_2
......
#
# wsrep_sst_auth
#
# save the initial value
SET @wsrep_sst_auth_global_saved = @@global.wsrep_sst_auth;
# default
SELECT @@global.wsrep_sst_auth;
@@global.wsrep_sst_auth
......@@ -47,6 +45,4 @@ NULL
SET @@global.wsrep_sst_auth=user:pass;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ':pass' at line 1
# restore the initial value
SET @@global.wsrep_sst_auth = @wsrep_sst_auth_global_saved;
# End of test
......@@ -4,9 +4,6 @@
--echo # wsrep_sst_auth
--echo #
--echo # save the initial value
SET @wsrep_sst_auth_global_saved = @@global.wsrep_sst_auth;
--echo # default
SELECT @@global.wsrep_sst_auth;
......@@ -39,7 +36,4 @@ SELECT @@global.wsrep_sst_auth;
SET @@global.wsrep_sst_auth=user:pass;
--echo
--echo # restore the initial value
SET @@global.wsrep_sst_auth = @wsrep_sst_auth_global_saved;
--echo # End of test
......@@ -1057,19 +1057,55 @@ if ! wsrep_auth_not_set; then
fi
fi
readonly WSREP_SST_OPT_USER
readonly WSREP_SST_OPT_PSWD
readonly WSREP_SST_OPT_AUTH
WSREP_SST_OPT_REMOTE_USER=
WSREP_SST_OPT_REMOTE_PSWD=
if [ -n "$WSREP_SST_OPT_REMOTE_AUTH" ]; then
# Split auth string at the last ':'
readonly WSREP_SST_OPT_REMOTE_USER="${WSREP_SST_OPT_REMOTE_AUTH%%:*}"
readonly WSREP_SST_OPT_REMOTE_PSWD="${WSREP_SST_OPT_REMOTE_AUTH#*:}"
else
readonly WSREP_SST_OPT_REMOTE_USER=
readonly WSREP_SST_OPT_REMOTE_PSWD=
fi
# Reads incoming data from STDIN and sets the variables
#
# Globals:
# WSREP_SST_OPT_USER (sets this variable)
# WSREP_SST_OPT_PSWD (sets this variable)
#
# Parameters:
# None
#
read_variables_from_stdin()
{
while read line; do
key=${line%%=*}
value=${line#*=}
case "$key" in
'sst_user')
WSREP_SST_OPT_USER="$value"
;;
'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
done
return 0
}
[ "$WSREP_SST_OPT_ROLE" = "donor" ] && read_variables_from_stdin || :
readonly WSREP_SST_OPT_USER
readonly WSREP_SST_OPT_PSWD
readonly WSREP_SST_OPT_AUTH
readonly WSREP_SST_OPT_REMOTE_USER
readonly WSREP_SST_OPT_REMOTE_PSWD
readonly WSREP_SST_OPT_REMOTE_AUTH
if [ -n "$WSREP_SST_OPT_DATA" ]; then
......
......@@ -62,13 +62,21 @@ fi
# whereas (at least on Linux) unprivileged user can't see process environment
# that he does not own. So while it may be not secure in the NSA sense of the
# word, it is arguably more secure than passing password on the command line.
if [ -n "$WSREP_SST_OPT_PSWD" ]; then
export MYSQL_PWD="$WSREP_SST_OPT_PSWD"
if [ -n "$WSREP_SST_OPT_REMOTE_PSWD" ]; then
export MYSQL_PWD="$WSREP_SST_OPT_REMOTE_PSWD"
elif [ $usrst -eq 1 ]; then
# Empty password, used for testing, debugging etc.
unset MYSQL_PWD
fi
# The above also means that both donor and joiner must have the same
# wsrep_sst_auth configuration and and different (and thus automatically
# generated) authentication credentials can't be used for this type of SST
# In this case the SST will fail if joiner does not provide correct
# authentication.
[ -n "$WSREP_SST_OPT_REMOTE_USER" ] && REMOTE_AUTH="-u$WSREP_SST_OPT_REMOTE_USER" || REMOTE_AUTH=
[ -n "$REMOTE_AUTH" ] && AUTH="$REMOTE_AUTH" || AUTH=
STOP_WSREP='SET wsrep_on=OFF;'
# mysqldump cannot restore CSV tables, fix this issue
......@@ -91,6 +99,7 @@ PREPARE stmt FROM @stmt;
EXECUTE stmt;
DROP PREPARE stmt;"
STATE="$WSREP_SST_OPT_GTID $WSREP_SST_OPT_GTID_DOMAIN_ID"
SET_START_POSITION="SET GLOBAL wsrep_start_position='$WSREP_SST_OPT_GTID';"
SET_WSREP_GTID_DOMAIN_ID=""
......@@ -104,7 +113,7 @@ if [ -n "$WSREP_SST_OPT_GTID_DOMAIN_ID" ]; then
fi
MYSQL="$MYSQL_CLIENT$WSREP_SST_OPT_CONF_UNQUOTED "\
"$AUTH -h$WSREP_SST_OPT_HOST_UNESCAPED "\
"$REMOTE_AUTH -h$WSREP_SST_OPT_HOST_UNESCAPED "\
"-P$WSREP_SST_OPT_PORT --disable-reconnect --connect_timeout=10"
# Check if binary logging is enabled on the joiner node.
......@@ -169,5 +178,7 @@ else
echo "$SET_START_POSITION" | $MYSQL || exit $?
fi
echo "done $STATE"
wsrep_log_info "$WSREP_METHOD $WSREP_TRANSFER_TYPE completed on $WSREP_SST_OPT_ROLE"
exit 0
......@@ -1819,7 +1819,6 @@ static void close_connections(void)
{
wsrep_deinit(true);
}
wsrep_sst_auth_free();
#endif
/* All threads has now been aborted */
DBUG_PRINT("quit", ("Waiting for threads to die (count=%u)",
......
......@@ -6212,7 +6212,7 @@ static Sys_var_charptr Sys_wsrep_sst_receive_address(
static Sys_var_charptr Sys_wsrep_sst_auth(
"wsrep_sst_auth", "Authentication for SST connection",
PREALLOCATED GLOBAL_VAR(wsrep_sst_auth), CMD_LINE(REQUIRED_ARG),
DEFAULT(NULL), NO_MUTEX_GUARD,
DEFAULT(WSREP_SST_AUTH_DEFAULT), NO_MUTEX_GUARD,
NOT_IN_BINLOG,
ON_CHECK(wsrep_sst_auth_check),
ON_UPDATE(wsrep_sst_auth_update));
......
......@@ -97,6 +97,9 @@ int wsrep_check_opts()
return 1;
}
}
wsrep_sst_auth_set(wsrep_sst_auth);
return 0;
}
......@@ -849,6 +849,12 @@ void wsrep_init_globals()
}
}
wsrep_init_schema();
{
/* apparently this thread has already called my_thread_init(),
* so we skip it, hence 'false' for initialization. */
wsp::thd thd(false, true);
wsrep_sst_cleanup_user(thd.ptr);
}
if (WSREP_ON)
{
Wsrep_server_state::instance().initialized();
......@@ -868,7 +874,6 @@ int wsrep_init()
assert(wsrep_provider);
wsrep_init_position();
wsrep_sst_auth_init();
if (!*wsrep_provider ||
!strcasecmp(wsrep_provider, WSREP_NONE))
......@@ -1060,11 +1065,6 @@ void wsrep_deinit(bool free_options)
wsrep_provider_capabilities= NULL;
free(p);
}
if (free_options)
{
wsrep_sst_auth_free();
}
}
/* Destroy wsrep thread LOCKs and CONDs */
......
......@@ -21,6 +21,7 @@
#include <mysqld.h>
#include <m_ctype.h>
#include <strfunc.h>
#include <sql_base.h>
#include <sql_class.h>
#include <set_var.h>
#include <sql_acl.h>
......@@ -35,6 +36,7 @@
#include <cstdio>
#include <cstdlib>
#include "debug_sync.h"
#include "my_rnd.h"
#include <my_service_manager.h>
......@@ -58,17 +60,27 @@ my_bool wsrep_sst_donor_rejects_queries= FALSE;
bool sst_joiner_completed = false;
bool sst_donor_completed = false;
struct sst_auth
{
std::string name_;
std::string pswd_;
std::string remote_name_;
std::string remote_pswd_;
};
struct sst_thread_arg
{
const char* method_;
const char* cmd;
const sst_auth& auth_;
char** env;
char* ret_str;
int err;
mysql_mutex_t lock;
mysql_cond_t cond;
sst_thread_arg (const char* c, char** e)
: cmd(c), env(e), ret_str(0), err(-1)
sst_thread_arg (const char* m, const char* c, const sst_auth& auth, char** e)
: method_(m), cmd(c), auth_(auth), env(e), ret_str(0), err(-1)
{
mysql_mutex_init(key_LOCK_wsrep_sst_thread, &lock, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_wsrep_sst_thread, &cond, NULL);
......@@ -280,59 +292,43 @@ bool wsrep_sst_auth_check (sys_var *self, THD* thd, set_var* var)
return 0;
}
static bool sst_auth_real_set (const char* value)
void wsrep_sst_auth_free()
{
if (wsrep_sst_auth) { my_free(const_cast<char *>(wsrep_sst_auth)); }
if (sst_auth_real) { my_free(const_cast<char *>(sst_auth_real)); }
wsrep_sst_auth= NULL;
sst_auth_real= NULL;
}
bool wsrep_sst_auth_set (const char* value)
{
static const char* const auth_mask("********");
const char* v= NULL;
if (value)
if (value && *value != '\0') // empty string means unset
{
// save new value
v= my_strdup(PSI_INSTRUMENT_ME, value, MYF(0));
if (!v) return 1;
}
else // its NULL
{
// cleanup old values
wsrep_sst_auth_free();
return 0;
}
if (v)
{
// set sst_auth_real
if (sst_auth_real) { my_free((void *) sst_auth_real); }
sst_auth_real= v;
// mask wsrep_sst_auth
if (strlen(sst_auth_real))
{
if (wsrep_sst_auth) { my_free((void*) wsrep_sst_auth); }
wsrep_sst_auth= my_strdup(PSI_INSTRUMENT_ME, WSREP_SST_AUTH_MASK, MYF(0));
}
else
{
if (wsrep_sst_auth) { my_free((void*) wsrep_sst_auth); }
wsrep_sst_auth= NULL;
wsrep_sst_auth = my_strdup(PSI_INSTRUMENT_ME, auth_mask, MYF(0));
// set sst_auth_real to actual value
sst_auth_real= v;
}
return 0;
}
return 1;
}
void wsrep_sst_auth_free()
{
if (wsrep_sst_auth) { my_free((void *) wsrep_sst_auth); }
if (sst_auth_real) { my_free((void *) sst_auth_real); }
wsrep_sst_auth= NULL;
sst_auth_real= NULL;
}
bool wsrep_sst_auth_update (sys_var *self, THD* thd, enum_var_type type)
{
return sst_auth_real_set (wsrep_sst_auth);
}
void wsrep_sst_auth_init ()
{
sst_auth_real_set(wsrep_sst_auth);
return wsrep_sst_auth_set(wsrep_sst_auth);
}
bool wsrep_sst_donor_check (sys_var *self, THD* thd, set_var* var)
......@@ -406,7 +402,7 @@ static bool wsrep_sst_complete (THD* thd,
return failed;
}
/*
/*
If wsrep provider is loaded, inform that the new state snapshot
has been received. Also update the local checkpoint.
......@@ -624,13 +620,27 @@ static void sst_handle_total(const char* const input,
}
}
struct sst_thread_init
{
sst_thread_init(const char* const errmsg, bool const abrt)
{
if (my_thread_init()) {
WSREP_ERROR("%s%s", errmsg, abrt ? " Aborting." : "");
if (abrt) unireg_abort(1);
}
}
~sst_thread_init()
{
my_thread_end();
}
};
static void* sst_joiner_thread (void* a)
{
sst_thread_arg* arg= (sst_thread_arg*) a;
int err= 1;
{
THD* thd;
static const char magic[]= "ready";
static const size_t magic_len= sizeof(magic) - 1;
const size_t out_len= 512;
......@@ -803,44 +813,8 @@ static void* sst_joiner_thread (void* a)
Tell initializer thread that SST is complete
For that initialize a THD
*/
if (my_thread_init())
{
WSREP_ERROR("my_thread_init() failed, can't signal end of SST. "
"Aborting.");
unireg_abort(1);
}
thd= new THD(next_thread_id());
if (!thd)
{
WSREP_ERROR("Failed to allocate THD to restore view from local state, "
"can't signal end of SST. Aborting.");
unireg_abort(1);
}
thd->thread_stack= (char*) &thd;
thd->security_ctx->skip_grants();
thd->system_thread= SYSTEM_THREAD_GENERIC;
thd->real_id= pthread_self();
wsrep_assign_from_threadvars(thd);
wsrep_store_threadvars(thd);
/* */
thd->variables.wsrep_on = 0;
/* No binlogging */
thd->variables.sql_log_bin = 0;
thd->variables.option_bits &= ~OPTION_BIN_LOG;
/* No general log */
thd->variables.option_bits |= OPTION_LOG_OFF;
/* Read committed isolation to avoid gap locking */
thd->variables.tx_isolation= ISO_READ_COMMITTED;
wsrep_sst_complete (thd, -err, ret_gtid);
delete thd;
my_thread_end();
wsp::thd thd;
wsrep_sst_complete (thd.ptr, -err, ret_gtid);
}
return NULL;
......@@ -1169,7 +1143,6 @@ static void copy_orig_argv (char* cmd_str)
}
static ssize_t sst_prepare_other (const char* method,
const char* sst_auth,
const char* addr_in,
const char** addr_out)
{
......@@ -1233,19 +1206,20 @@ static ssize_t sst_prepare_other (const char* method,
if (extra_args)
copy_orig_argv(cmd_str() + ret);
sst_auth auth;
if (sst_auth_real)
{
const char* col= strchrnul(sst_auth_real, ':');
auth.name_ = std::string(sst_auth_real, col - sst_auth_real);
auth.pswd_ = std::string(':' == *col ? col + 1 : "");
}
wsp::env env(NULL);
if (env.error())
{
WSREP_ERROR("sst_prepare_other(): env. var ctor failed: %d", -env.error());
return -env.error();
}
if ((ret= sst_append_env_var(env, WSREP_SST_AUTH_ENV, sst_auth)))
{
WSREP_ERROR("sst_prepare_other(): appending auth failed: %d", ret);
return ret;
}
if (data_home_dir)
{
if ((ret= sst_append_env_var(env, DATA_HOME_DIR_ENV, data_home_dir)))
......@@ -1257,7 +1231,7 @@ static ssize_t sst_prepare_other (const char* method,
}
pthread_t tmp, monitor;
sst_thread_arg arg(cmd_str(), env());
sst_thread_arg arg(method, cmd_str(), auth, env());
mysql_mutex_lock (&arg.lock);
......@@ -1384,7 +1358,7 @@ std::string wsrep_sst_prepare()
unireg_abort(1);
}
}
//Attempt 2: wsrep_node_address
// Attempt 2: wsrep_node_address
else if (wsrep_node_address && *wsrep_node_address)
{
addr_in_parser = new wsp::Address(wsrep_node_address);
......@@ -1499,8 +1473,7 @@ std::string wsrep_sst_prepare()
return "";
}
addr_len = sst_prepare_other (method, sst_auth_real,
addr_in, &addr_out);
addr_len = sst_prepare_other(method, addr_in, &addr_out);
if (addr_len < 0)
{
WSREP_ERROR("Failed to prepare for '%s' SST. Unrecoverable.",
......@@ -1523,121 +1496,344 @@ std::string wsrep_sst_prepare()
return ret;
}
// helper method for donors
static int sst_run_shell (const char* cmd_str, char** env, int max_tries)
int run_sql_command(wsp::mysql& mysql, const char* query)
{
int err= mysql.execute(query);
if (err)
{
WSREP_WARN("Error executing '%s': %d (%s)",
query, err, mysql.errstr());
return err;
}
return 0;
}
static int run_sql_command_thd(THD *thd, const char *query)
{
int ret= 0;
for (int tries=1; tries <= max_tries; tries++)
{
wsp::process proc (cmd_str, "r", env);
thd->reset_for_next_command();
thd->set_query((char*)query, strlen(query));
if (NULL != proc.from())
Parser_state ps;
if (ps.init(thd, thd->query(), thd->query_length()))
{
proc.wait();
WSREP_ERROR("SST query: %s failed", query);
ret= -1;
}
else
{
PSI_statement_locker *parent_locker= thd->m_statement_psi;
thd->set_query_id(next_query_id());
if ((ret= proc.error()))
mysql_parse(thd, thd->query(), thd->query_length(), &ps);
if (thd->is_error())
{
WSREP_ERROR("Try %d/%d: '%s' failed: %d (%s)",
tries, max_tries, proc.cmd(), ret, strerror(ret));
sleep (1);
}
else
int const err= thd->get_stmt_da()->sql_errno();
if (err)
{
WSREP_DEBUG("SST script successfully completed.");
break;
WSREP_WARN ("Error executing '%s': %d (%s)",
query, err, thd->get_stmt_da()->message());
ret= err;
}
thd->clear_error();
}
thd->m_statement_psi= parent_locker;
thd->end_statement();
close_thread_tables(thd);
delete_explain_query(thd->lex);
}
return -ret;
thd->reset_query();
thd->cleanup_after_query();
return ret;
}
static void sst_reject_queries(my_bool close_conn)
#define SST_USER_PREFIX "wsrep.sst."
static std::string
generate_user_name(const char* const node_name)
{
WSREP_INFO("Rejecting client queries for the duration of SST.");
if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
struct timeval tv;
gettimeofday(&tv, NULL);
time_t time_now;
time_now = tv.tv_sec;
struct tm tm_now;
(void)gmtime_r(&time_now, &tm_now);
char timestamp[32];
strftime(timestamp, sizeof(timestamp), "%y%m%d_%H%M%S", &tm_now);
return (std::string(SST_USER_PREFIX) + timestamp + "_" + node_name).substr
(0, USERNAME_CHAR_LENGTH);
}
static int sst_donate_mysqldump (const char* addr,
const wsrep::gtid& gtid,
bool bypass,
char** env) // carries auth info
void
wsrep_sst_cleanup_user(THD* const thd)
{
char host[256];
wsp::Address address(addr);
if (!address.is_valid())
{
WSREP_ERROR("Could not parse SST address : %s", addr);
return 0;
int err;
wsp::mysql mysql;
if ((err = mysql.errnum()) ||
(err = mysql.disable_replication()) ||
((err = mysql.execute("DELETE FROM mysql.user WHERE user LIKE '" SST_USER_PREFIX "%';")) &&
err != ER_NO_SUCH_TABLE)) {
WSREP_WARN("Failed to clean up SST user(s): %d (%s)", err, mysql.errstr());
}
memcpy(host, address.get_address(), address.get_address_len());
int port= address.get_port();
bool extra_args;
size_t const cmd_len= estimate_cmd_len(&extra_args);
wsp::string cmd_str(cmd_len);
}
if (!cmd_str())
{
WSREP_ERROR("sst_donate_mysqldump(): "
"could not allocate cmd buffer of %zd bytes", cmd_len);
return -ENOMEM;
}
static std::string
generate_password(int size) {
// Password characters are limited, because we are using these passwords
// within a bash script.
static const std::string g_allowed_pwd_chars(
"qwertyuiopasdfghjklzxcvbnm1234567890"
"QWERTYUIOPASDFGHJKLZXCVBNM");
// To guarantee MySQL password requirements: upper/lowercase, numbers,
// special characters
const char* const pwd_prefix("yx9!A-");
std::stringstream ss;
ss << pwd_prefix;
size -= strlen(pwd_prefix);
ulong const seed1= (ulong) (my_rnd(&sql_rand) * 0xffffffff);
ulong const seed2= (ulong) time((time_t*) 0);
struct my_rnd_struct rand;
my_rnd_init(&rand, seed1 + (ulong)((size_t) &rand), seed2);
while (size > 0) {
int ch = ((int)(my_rnd(&rand) * 0xffff)) % g_allowed_pwd_chars.size();
ss << g_allowed_pwd_chars[ch];
--size;
}
return ss.str();
}
/*
we enable new client connections so that mysqldump donation can connect in,
but we reject local connections from modifyingcdata during SST, to keep
data intact
*/
if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(TRUE);
static const char**
sst_user_grants(const std::string& method)
{
static const char* mariabackup[] = {
"GRANT RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR ON *.* TO '%s'@'localhost';",
nullptr,
nullptr,
nullptr
};
static const char* rsync[] = {
nullptr,
nullptr
};
/* We don't know which privileges might be needed for custom SST scripts,
* so GRANT all */
static const char* other[] = {
"GRANT ALL ON *.* TO '%s'@localhost WITH GRANT OPTION;",
nullptr,
nullptr
};
if (method == "mariabackup-test") {
return mariabackup;
}
if (method == "rsync") {
assert(0); // should not attempt to create user for this SSST
return rsync;
}
return other;
}
make_wsrep_defaults_file();
/**
* cmds[] This array is filled with pairs of entries.
The first entry is the printf template for the query to be run:
optional %s will be filled with name and 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)
*/
static int
run_sql_commands(wsp::mysql& mysql,
const char* cmds[],
const std::string& name,
const std::string& password)
{
int ret = 0;
for (int index = 0; !ret && cmds[index]; index += 2)
{
int const cmd_len = 512;
char cmd_buf[cmd_len] = { 0,};
std::ostringstream uuid_oss;
uuid_oss << gtid.id();
int ret= snprintf (cmd_str(), cmd_len,
"wsrep_sst_mysqldump "
WSREP_SST_OPT_ADDR " '%s' "
WSREP_SST_OPT_PORT " '%u' "
WSREP_SST_OPT_LPORT " '%u' "
WSREP_SST_OPT_SOCKET " '%s' "
"%s"
WSREP_SST_OPT_GTID " '%s:%lld,%d-%d-%llu' "
WSREP_SST_OPT_GTID_DOMAIN_ID " '%d'"
"%s",
addr, port, mysqld_port, mysqld_unix_port,
wsrep_defaults_file,
uuid_oss.str().c_str(), gtid.seqno().get(),
wsrep_gtid_server.domain_id, wsrep_gtid_server.server_id,
wsrep_gtid_server.seqno(),
wsrep_gtid_server.domain_id,
bypass ? " " WSREP_SST_OPT_BYPASS : "");
ret = snprintf(cmd_buf, cmd_len, cmds[index],
name.c_str(), password.c_str());
if (ret < 0 || ret >= cmd_len) {
WSREP_ERROR("run_sql_commands(): snprintf() failed: %d", ret);
ret = (ret < 0 ? ret : -EMSGSIZE);
break;
}
else {
WSREP_INFO("run_sql_commands(%p): '%s'",
cmds, cmds[index + 1] ? cmds[index + 1] : cmd_buf);
}
if (ret < 0 || size_t(ret) >= cmd_len)
{
WSREP_ERROR("sst_donate_mysqldump(): snprintf() failed: %d", ret);
return (ret < 0 ? ret : -EMSGSIZE);
ret = run_sql_command(mysql, cmd_buf);
if (ret) break;
}
return ret;
}
if (extra_args)
copy_orig_argv(cmd_str() + ret);
/*
Helper class to manage SST users
*/
class sst_user
{
wsp::mysql* mysql_;
std::string const name_;
std::string const password_;
int err_;
public:
/* ctor in case we need to create SST user */
sst_user(const std::string& method) :
mysql_(new wsp::mysql),
name_(generate_user_name(wsrep_node_name)),
password_(generate_password(32)),
err_(mysql_->errnum())
{
assert(method != "rsync"); // should not attempt to create user for this SST
// This array is filled with pairs of entries
// The first entry is the tmeplate query to be run
// 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)
static const char *create_sst_user[] = {
"DROP USER IF EXISTS '%s'@localhost;",
nullptr,
"CREATE USER '%s'@localhost IDENTIFIED BY '%s';",
"CREATE USER '*'@localhost IDENTIFIED BY ***** ;", // mangled password for logging
nullptr,
nullptr
};
if (err_) {
WSREP_ERROR("sst_user::user() mysql connect failed: %d (%s)",
err_, mysql_->errstr());
return;
}
err_ = mysql_->disable_replication();
if (err_) {
WSREP_ERROR("sst_user::user() disable replication failed: %d (%s)",
err_, mysql_->errstr());
return;
}
err_ = run_sql_commands(*mysql_, create_sst_user, name_, password_);
if (err_) {
WSREP_ERROR("sst_user::user() create user failed: %d (%s)",
err_, err_ < 0 ? strerror(-err_) : mysql_->errstr());
return;
}
err_ = run_sql_commands(*mysql_, sst_user_grants(method), name_, password_);
if (err_) {
WSREP_ERROR("sst_user::user() grant privileges failed: %d (%s)",
err_, err_ < 0 ? strerror(-err_) : mysql_->errstr());
}
}
/* ctor in case SST user was externally supplied */
sst_user(const std::string& name, const std::string& password) :
mysql_(nullptr),
name_(name),
password_(password),
err_(0)
{}
~sst_user()
{
if (mysql_) {
/* Need to cleanup SST user */
static const char *cmds[] = {
"DROP USER IF EXISTS '%s'@localhost;",
nullptr,
nullptr,
nullptr
};
err_ = run_sql_commands(*mysql_, cmds, name_, password_);
if (err_) {
WSREP_ERROR("sst_user::~user() user '%s' cleanup failed: %d (%s)",
name_.c_str(),
err_, err_ < 0 ? strerror(-err_) : mysql_->errstr());
}
delete mysql_;
}
}
const std::string& name() const { return name_; }
const std::string& password() const { return password_; }
int err() const { return err_; }
};
WSREP_DEBUG("Running: '%s'", cmd_str());
static int
write_auth_info(FILE* const file, const char* method,
const sst_auth& auth, sst_user** ret_user)
{
int err;
sst_user* user(nullptr);
ret= sst_run_shell (cmd_str(), env, 3);
try {
if (auth.name_.length() > 0) {
/* User supplied wsrep_sst_auth */
user= new sst_user(auth.name_, auth.pswd_);
}
else {
/* No wsrep_sst_auth, create a temporary user for SST */
user= new sst_user(method);
}
err= user->err();
}
catch (...) {
err= ENOMEM;
}
*ret_user= user;
wsrep::gtid sst_sent_gtid(ret == 0 ?
gtid :
wsrep::gtid(gtid.id(),
wsrep::seqno::undefined()));
Wsrep_server_state::instance().sst_sent(sst_sent_gtid, ret);
if (err)
{
WSREP_ERROR("Failed to create SST user: %d (%s)", err, strerror(err));
}
else
{
int ret= fprintf(file,
"sst_user=%s\n"
"sst_password=%s\n",
user->name().c_str(),
user->password().c_str());
if (ret < 0)
{
WSREP_ERROR("write_auth_info(): fprintf(1) failed: %d", ret);
err= (ret < 0 ? ret : -EMSGSIZE);
}
wsrep_donor_monitor_end();
if (auth.remote_name_.length())
{
ret= fprintf(file,
"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("write_auth_info(): fprintf(2) failed: %d", ret);
err= (ret < 0 ? ret : -EMSGSIZE);
}
}
}
return ret;
return err;
}
wsrep_seqno_t wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
static void sst_reject_queries(my_bool close_conn)
{
WSREP_INFO("Rejecting client queries for the duration of SST.");
if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
}
/*
Create a file under data directory.
......@@ -1687,28 +1883,7 @@ static int sst_create_file(const char *name, const char *content)
return err;
}
static int run_sql_command(THD *thd, const char *query)
{
thd->set_query((char *)query, strlen(query));
Parser_state ps;
if (ps.init(thd, thd->query(), thd->query_length()))
{
WSREP_ERROR("SST query: %s failed", query);
return -1;
}
mysql_parse(thd, thd->query(), thd->query_length(), &ps);
if (thd->is_error())
{
int const err= thd->get_stmt_da()->sql_errno();
WSREP_WARN ("Error executing '%s': %d (%s)",
query, err, thd->get_stmt_da()->message());
thd->clear_error();
return -1;
}
return 0;
}
wsrep_seqno_t wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
static int sst_flush_tables(THD* thd)
{
......@@ -1734,7 +1909,7 @@ static int sst_flush_tables(THD* thd)
my_charset_latin1.cs_name.str);
}
if (run_sql_command(thd, "FLUSH TABLES WITH READ LOCK"))
if (run_sql_command_thd(thd, "FLUSH TABLES WITH READ LOCK"))
{
err= -1;
}
......@@ -1828,8 +2003,10 @@ static int sst_flush_tables(THD* thd)
static void* sst_donor_thread (void* a)
{
sst_thread_arg* arg= (sst_thread_arg*)a;
std::string const method(arg->method_);
sst_auth const auth(arg->auth_);
WSREP_INFO("Running: '%s'", arg->cmd);
WSREP_INFO("Initiating SST/IST transfer on DONOR side (%s)", arg->cmd);
int err= 1;
bool locked= false;
......@@ -1842,31 +2019,57 @@ static void* sst_donor_thread (void* a)
// seqno of complete SST
wsrep_seqno_t ret_seqno= WSREP_SEQNO_UNDEFINED;
// We turn off wsrep_on for this THD so that it can
// operate with wsrep_ready == OFF
// We also set this SST thread THD as system thread
wsp::thd thd(true, true);
wsp::process proc(arg->cmd, "r", arg->env);
wsp::process proc(arg->cmd, "rw", arg->env);
err= -proc.error();
if (proc.to() && !err)
if (err)
{
// Close the pipe, so that the SST process gets an EOF
proc.close_to();
WSREP_ERROR("Failed to start SST process: %d", err);
}
/* Inform server about SST script startup and release TO isolation */
/* Inform server about SST script startup and release TO isolation */
mysql_mutex_lock (&arg->lock);
arg->err= -err;
mysql_cond_signal (&arg->cond);
mysql_mutex_unlock (&arg->lock); //! @note arg is unusable after that.
arg= nullptr;
wsp::thd thd;
err = thd.err();
if (err)
{
WSREP_ERROR("Can't initialize donor THD: %d", err);
}
sst_user* user(nullptr);
if (proc.to() && !err)
{
/* At this point SST process is waiting for input or EOF on STDIN.
* Create SST user and send sensitive information to it. */
err= write_auth_info(proc.to(), method.c_str(), auth, &user);
// Close the pipe, so that the SST process gets an EOF
proc.close_to();
}
/* This cleanups SST user on thread exit, i.e. on SST end, whether it was
* successfully created or not */
class cleanup_sst_user{
sst_user* const user_;
public:
cleanup_sst_user(sst_user* user) : user_(user) {}
~cleanup_sst_user() {
WSREP_INFO("Cleaning up SST user.");
delete user_;
}
} cleanup_sst_user_guard(user);
if (proc.from() && !err)
{
long long total= 0;
long long complete= 0;
// total form previous stages
// total from previous stages
long long total_prev= 0;
wait_signal:
......@@ -1988,10 +2191,13 @@ static void* sst_donor_thread (void* a)
return nullptr;
}
/* There used to be a separate method for mysqldump SST, but now everything
* goes through this method. */
static int sst_donate_other (const char* method,
const char* addr,
const wsrep::gtid& gtid,
bool bypass,
const bool bypass,
const sst_auth& auth,
char** env) // carries auth info
{
bool extra_args;
......@@ -2064,7 +2270,7 @@ static int sst_donate_other (const char* method,
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(method, cmd_str(), auth, env);
mysql_mutex_lock (&arg.lock);
......@@ -2118,30 +2324,43 @@ int wsrep_sst_donate(const std::string& msg,
return WSREP_CB_FAILURE;
}
wsp::env env(NULL);
if (env.error())
/* Set up auth info (from <user>:<password> strings) */
sst_auth auth;
if (sst_auth_real)
{
WSREP_ERROR("wsrep_sst_donate_cb(): env var ctor failed: %d", -env.error());
return WSREP_CB_FAILURE;
/* User supplied non-trivial wsre_sst_auth, use it */
const char* col= strchrnul(sst_auth_real, ':');
auth.name_ = std::string(sst_auth_real, col - sst_auth_real);
auth.pswd_ = std::string(':' == *col ? col + 1 : "");
}
int ret;
if ((ret= sst_append_env_var(env, WSREP_SST_AUTH_ENV, sst_auth_real)))
else if (0 == strcmp(method, "rsync"))
{
WSREP_ERROR("wsrep_sst_donate_cb(): appending auth env failed: %d", ret);
return WSREP_CB_FAILURE;
/* This dummy auth info will prevent SST account creation for rsync SST -
* as it does not need any ATM. */
auth.name_ = "rsync";
auth.pswd_ = "none";
}
else
{
/* Empty auth will trigger automatic SST account creation. */
}
if (remote_auth())
{
if ((ret= sst_append_env_var(env, WSREP_SST_REMOTE_AUTH_ENV,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 : "");
}
wsp::env env(NULL);
if (env.error())
{
WSREP_ERROR("wsrep_sst_donate_cb(): appending remote auth env failed: "
"%d", ret);
WSREP_ERROR("wsrep_sst_donate_cb(): env var ctor failed: %d", -env.error());
return WSREP_CB_FAILURE;
}
}
int ret;
if (data_home_dir)
{
if ((ret= sst_append_env_var(env, DATA_HOME_DIR_ENV, data_home_dir)))
......@@ -2164,14 +2383,7 @@ int wsrep_sst_donate(const std::string& msg,
return WSREP_CB_FAILURE;
}
if (!strcmp (WSREP_SST_MYSQLDUMP, method))
{
ret= sst_donate_mysqldump(addr, current_gtid, bypass, env());
}
else
{
ret= sst_donate_other(method, addr, current_gtid, bypass, env());
}
ret= sst_donate_other(method, addr, current_gtid, bypass, auth, env());
return (ret >= 0 ? 0 : 1);
return (ret >= 0 ? WSREP_CB_SUCCESS : WSREP_CB_FAILURE);
}
......@@ -56,7 +56,7 @@
#define WSREP_SST_XTRABACKUPV2 "xtrabackupv2"
#define WSREP_SST_DEFAULT WSREP_SST_RSYNC
#define WSREP_SST_ADDRESS_AUTO "AUTO"
#define WSREP_SST_AUTH_MASK "********"
#define WSREP_SST_AUTH_DEFAULT NULL
/* system variables */
extern const char* wsrep_sst_method;
......@@ -71,7 +71,7 @@ extern void wsrep_sst_grab();
extern bool wsrep_sst_wait();
/*! Signals wsrep that initialization is complete, writesets can be applied */
extern bool wsrep_sst_continue();
extern void wsrep_sst_auth_init();
extern bool wsrep_sst_auth_set(const char* value);
extern void wsrep_sst_auth_free();
extern void wsrep_SE_init_grab(); /*! grab init critical section */
......@@ -98,6 +98,12 @@ int wsrep_sst_donate(const std::string& request,
const wsrep::gtid& gtid,
bool bypass);
/**
Cleanup stale SST users from the database records
@param thd wsp::thd object (wraps initialized THD* pointer)
*/
void wsrep_sst_cleanup_user(THD* thd);
#else
#define wsrep_SE_initialized() do { } while(0)
#define wsrep_SE_init_grab() do { } while(0)
......
......@@ -404,11 +404,13 @@ process::process (const char* cmd, const char* type, char** env)
if (read_from_child)
{
setup_parent_pipe_end(READ, read_pipe, READ_END, "r");
assert(from());
}
if (write_to_child)
{
setup_parent_pipe_end(WRITE, write_pipe, WRITE_END, "w");
assert(to());
}
cleanup_fact:
......@@ -532,6 +534,40 @@ thd::~thd ()
}
}
mysql::mysql() :
mysql_(mysql_init(NULL))
{
int err = 0;
if (mysql_real_connect_local(mysql_) == NULL) {
err = mysql_errno(mysql_);
WSREP_ERROR("mysql::mysql() mysql_real_connect() failed: %d (%s)",
err, mysql_error(mysql_));
}
}
mysql::~mysql()
{
mysql_close(mysql_);
}
int
mysql::disable_replication()
{
int err = execute("SET SESSION sql_log_bin = OFF;");
if (err) {
WSREP_ERROR("sst_user::user() disabling log_bin failed: %d (%s)",
err, errstr());
}
else {
err = execute("SET SESSION wsrep_on = OFF;");
if (err) {
WSREP_ERROR("sst_user::user() disabling wsrep replication failed: %d (%s)",
err, errstr());
}
}
return err;
}
} // namespace wsp
/* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
......
......@@ -340,6 +340,28 @@ class thd
THD* const ptr;
};
/* local server connection */
class mysql
{
MYSQL* mysql_;
public:
mysql();
~mysql();
int execute(const std::string& query) {
if (mysql_real_query(mysql_, query.c_str(), query.length())) {
return mysql_errno(mysql_);
}
return 0;
}
int errnum() {
return (mysql_errno(mysql_));
}
const char* errstr() {
return mysql_error(mysql_);
}
int disable_replication();
};
class string
{
public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment