Skip to content

Commit 4b9da76

Browse files
authored
Fix database restore failure when there are two database admins (#16958)
1 parent 6e70889 commit 4b9da76

File tree

3 files changed

+208
-14
lines changed

3 files changed

+208
-14
lines changed

ydb/apps/ydb/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Support coordination nodes in `ydb scheme rmdir --recursive`.
77
* Fixed return code of command `ydb workload * run --check-canonical` for the case when benchmark query results differ from canonical ones.
88
* Fixed scheme error in `ydb admin cluster dump` when specifying a domain database.
9+
* Fixed unauthorized error in `ydb admin database restore` when multiple database admins are in dump.
910

1011
## 2.20.0 ##
1112

ydb/public/lib/ydb_cli/dump/restore_impl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,10 @@ TRestoreResult TRestoreClient::RestoreUsers(TTableClient& client, const TFsPath&
635635
auto alterStatementResult = client.RetryOperationSync([&](TSession session) {
636636
return session.ExecuteSchemeQuery(alterStatement).ExtractValueSync();
637637
});
638-
if (!alterStatementResult.IsSuccess()) {
638+
if (alterStatementResult.GetStatus() == EStatus::UNAUTHORIZED) {
639+
LOG_W("Not enough rights to restore user from statement " << alterStatement.Quote() << ", skipping");
640+
continue;
641+
} else if (!alterStatementResult.IsSuccess()) {
639642
LOG_E("Failed to execute statement for restoring user: "
640643
<< alterStatement.Quote() << ", error: "
641644
<< alterStatementResult.GetIssues().ToOneLineString());

ydb/tests/functional/ydb_cli/test_ydb_backup.py

Lines changed: 203 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,8 @@ def setup_class(cls):
13061306
"enable_database_admin"
13071307
],
13081308
domain_login_only=False,
1309+
enforce_user_token_requirement=True,
1310+
default_clusteradmin="root@builtin",
13091311
))
13101312
cls.cluster.start()
13111313

@@ -1317,15 +1319,17 @@ def setup_class(cls):
13171319
storage_pool_units_count={
13181320
'hdd': 1
13191321
},
1320-
timeout_seconds=100
1322+
timeout_seconds=100,
1323+
token=cls.cluster.config.default_clusteradmin
13211324
)
13221325

13231326
cls.database_nodes = cls.cluster.register_and_start_slots(cls.database, count=3)
1324-
cls.cluster.wait_tenant_up(cls.database)
1327+
cls.cluster.wait_tenant_up(cls.database, cls.cluster.config.default_clusteradmin)
13251328

13261329
driver_config = ydb.DriverConfig(
13271330
database=cls.database,
1328-
endpoint="%s:%s" % (cls.cluster.nodes[1].host, cls.cluster.nodes[1].port))
1331+
endpoint="%s:%s" % (cls.cluster.nodes[1].host, cls.cluster.nodes[1].port),
1332+
credentials=ydb.AuthTokenCredentials(cls.cluster.config.default_clusteradmin))
13291333
cls.driver = ydb.Driver(driver_config)
13301334
cls.driver.wait(timeout=4)
13311335

@@ -1340,8 +1344,8 @@ def set_test_name(cls, request):
13401344
cls.test_name = request.node.name
13411345

13421346
@classmethod
1343-
def create_backup(cls, command, expected_files, additional_args=[]):
1344-
backup_files_dir = output_path(cls.test_name, "backup_files_dir")
1347+
def create_backup(cls, command, expected_files, output, additional_args=[], token=""):
1348+
backup_files_dir = output_path(cls.test_name, output)
13451349
execution = yatest.common.execute(
13461350
[
13471351
backup_bin(),
@@ -1350,7 +1354,8 @@ def create_backup(cls, command, expected_files, additional_args=[]):
13501354
]
13511355
+ command
13521356
+ ["--output", backup_files_dir]
1353-
+ additional_args
1357+
+ additional_args,
1358+
env={"YDB_TOKEN": token}
13541359
)
13551360

13561361
list_result = fs_recursive_list(backup_files_dir, ListMode.FILES)
@@ -1370,32 +1375,59 @@ def create_backup(cls, command, expected_files, additional_args=[]):
13701375
)
13711376

13721377
@classmethod
1373-
def create_cluster_backup(cls, expected_files, additional_args=[]):
1378+
def create_cluster_backup(cls, expected_files, output="backup_files_dir", additional_args=[]):
13741379
cls.create_backup(
13751380
[
13761381
"--database", cls.root_dir,
13771382
"admin", "cluster", "dump",
13781383
],
13791384
expected_files,
1380-
additional_args
1385+
output,
1386+
additional_args,
1387+
token=cls.cluster.config.default_clusteradmin,
13811388
)
13821389

13831390
@classmethod
1384-
def create_database_backup(cls, expected_files, additional_args=[]):
1391+
def create_database_backup(cls, expected_files, output="backup_files_dir", additional_args=[]):
13851392
cls.create_backup(
13861393
[
13871394
"--database", cls.database,
1395+
"--user", "dbadmin1", "--no-password",
13881396
"admin", "database", "dump",
13891397
],
13901398
expected_files,
1399+
output,
13911400
additional_args
13921401
)
13931402

1403+
@classmethod
1404+
def setup_sample_data(cls):
1405+
pool = ydb.SessionPool(cls.driver)
1406+
with pool.checkout() as session:
1407+
session.execute_scheme("CREATE GROUP people")
1408+
session.execute_scheme("CREATE USER alice PASSWORD '1234'")
1409+
session.execute_scheme("CREATE USER bob PASSWORD '1234'")
1410+
session.execute_scheme("ALTER GROUP people ADD USER alice")
1411+
session.execute_scheme("ALTER GROUP people ADD USER bob")
1412+
1413+
session.execute_scheme("CREATE GROUP dbadmins")
1414+
session.execute_scheme("CREATE USER dbadmin1")
1415+
session.execute_scheme("CREATE USER dbadmin2 PASSWORD '1234'")
1416+
session.execute_scheme("ALTER GROUP dbadmins ADD USER dbadmin1")
1417+
session.execute_scheme("ALTER GROUP dbadmins ADD USER dbadmin2")
1418+
session.execute_scheme(f'GRANT "ydb.generic.use" on `{cls.database}` TO dbadmins')
1419+
1420+
cls.driver.scheme_client.modify_permissions(
1421+
cls.database,
1422+
ydb.ModifyPermissionsSettings().change_owner("dbadmins")
1423+
)
1424+
1425+
create_table_with_data(cls.driver.table_client.session().create(), "db1/table")
1426+
13941427

13951428
class TestClusterBackup(BaseTestClusterBackupInFiles):
13961429
def test_cluster_backup(self):
1397-
session = self.driver.table_client.session().create()
1398-
create_table_with_data(session, "db1/table")
1430+
self.setup_sample_data()
13991431

14001432
self.create_cluster_backup(expected_files=[
14011433
# cluster metadata
@@ -1415,8 +1447,7 @@ def test_cluster_backup(self):
14151447

14161448
class TestDatabaseBackup(BaseTestClusterBackupInFiles):
14171449
def test_database_backup(self):
1418-
session = self.driver.table_client.session().create()
1419-
create_table_with_data(session, "db1/table")
1450+
self.setup_sample_data()
14201451

14211452
self.create_database_backup(expected_files=[
14221453
# database metadata
@@ -1431,3 +1462,162 @@ def test_database_backup(self):
14311462
"table/permissions.pb",
14321463
"table/data_00.csv",
14331464
])
1465+
1466+
1467+
class BaseTestMultipleClusterBackupInFiles(BaseTestClusterBackupInFiles):
1468+
@classmethod
1469+
def setup_class(cls):
1470+
super().setup_class()
1471+
1472+
cfg = KikimrConfigGenerator(
1473+
extra_feature_flags=[
1474+
"enable_strict_acl_check",
1475+
"enable_strict_user_management",
1476+
"enable_database_admin"
1477+
],
1478+
domain_name="Root2",
1479+
domain_login_only=False,
1480+
enforce_user_token_requirement=True,
1481+
default_clusteradmin="root@builtin",
1482+
)
1483+
1484+
cls.restore_cluster = KiKiMR(
1485+
cfg,
1486+
cluster_name="restore_cluster"
1487+
)
1488+
1489+
cls.restore_cluster.start()
1490+
1491+
cls.restore_root_dir = "/Root2"
1492+
cls.restore_database = os.path.join(cls.restore_root_dir, "db1")
1493+
cls.restore_database_nodes = cls.restore_cluster.register_and_start_slots(
1494+
cls.restore_database,
1495+
count=1
1496+
)
1497+
1498+
restore_driver_config = ydb.DriverConfig(
1499+
database=cls.restore_database,
1500+
endpoint="%s:%s" % (
1501+
cls.restore_cluster.nodes[1].host,
1502+
cls.restore_cluster.nodes[1].port
1503+
),
1504+
credentials=ydb.AuthTokenCredentials(cls.cluster.config.default_clusteradmin),
1505+
)
1506+
cls.restore_driver = ydb.Driver(restore_driver_config)
1507+
1508+
@classmethod
1509+
def teardown_class(cls):
1510+
cls.restore_cluster.unregister_and_stop_slots(cls.restore_database_nodes)
1511+
cls.restore_cluster.stop()
1512+
super().teardown_class()
1513+
1514+
@classmethod
1515+
def restore(cls, command, input, additional_args=[], token=""):
1516+
backup_files_dir = os.path.join(
1517+
yatest.common.output_path(),
1518+
cls.test_name,
1519+
input
1520+
)
1521+
1522+
yatest.common.execute(
1523+
[
1524+
backup_bin(),
1525+
"--verbose",
1526+
"--assume-yes",
1527+
"--endpoint", f"grpc://localhost:{cls.restore_cluster.nodes[1].grpc_port}",
1528+
]
1529+
+ command
1530+
+ ["--input", backup_files_dir, "-w", "60s"]
1531+
+ additional_args,
1532+
env={"YDB_TOKEN": token}
1533+
)
1534+
1535+
@classmethod
1536+
def restore_cluster_backup(cls, input="backup_files_dir", additional_args=[]):
1537+
cls.restore(
1538+
[
1539+
"--database", cls.restore_root_dir,
1540+
"admin", "cluster", "restore"
1541+
],
1542+
input,
1543+
additional_args,
1544+
token=cls.restore_cluster.config.default_clusteradmin,
1545+
)
1546+
1547+
@classmethod
1548+
def restore_database_backup(cls, input="backup_files_dir", additional_args=[]):
1549+
cls.restore(
1550+
[
1551+
"--database", cls.restore_database,
1552+
"--user", "dbadmin1", "--no-password",
1553+
"admin", "database", "restore"
1554+
],
1555+
input,
1556+
additional_args
1557+
)
1558+
1559+
1560+
class TestClusterBackupRestore(BaseTestMultipleClusterBackupInFiles):
1561+
def test_cluster_backup_restore(self):
1562+
self.setup_sample_data()
1563+
1564+
self.create_cluster_backup(expected_files=[
1565+
# cluster metadata
1566+
"permissions.pb",
1567+
"create_user.sql",
1568+
"create_group.sql",
1569+
"alter_group.sql",
1570+
1571+
# database metadata
1572+
"Root/db1/database.pb",
1573+
"Root/db1/permissions.pb",
1574+
"Root/db1/create_user.sql",
1575+
"Root/db1/create_group.sql",
1576+
"Root/db1/alter_group.sql",
1577+
])
1578+
1579+
self.restore_cluster_backup()
1580+
1581+
self.restore_cluster.wait_tenant_up(self.restore_database, self.cluster.config.default_clusteradmin)
1582+
self.restore_driver.wait(timeout=10)
1583+
1584+
1585+
class TestDatabaseBackupRestore(BaseTestMultipleClusterBackupInFiles):
1586+
def test_database_backup_restore(self):
1587+
self.setup_sample_data()
1588+
1589+
self.create_cluster_backup(output="cluster_backup", expected_files=[
1590+
# cluster metadata
1591+
"permissions.pb",
1592+
"create_user.sql",
1593+
"create_group.sql",
1594+
"alter_group.sql",
1595+
1596+
# database metadata
1597+
"Root/db1/database.pb",
1598+
"Root/db1/permissions.pb",
1599+
"Root/db1/create_user.sql",
1600+
"Root/db1/create_group.sql",
1601+
"Root/db1/alter_group.sql",
1602+
])
1603+
1604+
self.create_database_backup(output="database_backup", expected_files=[
1605+
# database metadata
1606+
"database.pb",
1607+
"permissions.pb",
1608+
"create_user.sql",
1609+
"create_group.sql",
1610+
"alter_group.sql",
1611+
1612+
# database table
1613+
"table/scheme.pb",
1614+
"table/permissions.pb",
1615+
"table/data_00.csv",
1616+
])
1617+
1618+
self.restore_cluster_backup(input="cluster_backup")
1619+
1620+
self.restore_cluster.wait_tenant_up(self.restore_database, self.cluster.config.default_clusteradmin)
1621+
self.restore_driver.wait(timeout=10)
1622+
1623+
self.restore_database_backup(input="database_backup")

0 commit comments

Comments
 (0)