Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PBM. Add test to check user restoration during PITR restore from selective / full backup #252

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion pbm-functional/pytest/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,22 @@ def enable_pitr(self,**kwargs):
time.sleep(1)

# disables PITR
def disable_pitr(self):
def disable_pitr(self, time_param=None):
n = testinfra.get_host("docker://" + self.pbm_cli)
if time_param:
target_time = int(datetime.fromisoformat(time_param).timestamp())
pitr_end = 0

while pitr_end < target_time:
result = n.check_output("pbm s -s backups -o json")
backups = json.loads(result)
if 'backups' in backups and 'pitrChunks' in backups['backups'] and 'pitrChunks' in backups['backups']['pitrChunks']:
pitr_end_cur = backups['backups']['pitrChunks']['pitrChunks'][0].get('range', {}).get('end', None)
if pitr_end_cur is not None:
pitr_end = pitr_end_cur
if pitr_end < target_time:
time.sleep(1)

result = n.check_output(
"pbm config --set pitr.enabled=false --out json")
Cluster.log("Disabling PITR: " + result)
Expand Down Expand Up @@ -625,6 +639,12 @@ def setup_authorization(host,uri):
'{"db":"admin","role":"clusterMonitor" },' +
'{"db":"admin","role":"restore" },' +
'{"db":"admin","role":"pbmAnyAction" }]});\'')
init_pbm_t_user = ('\'db.getSiblingDB("admin").createUser({user:"pbm_test",pwd:"pbmpass_test1","roles":[' +
'{"db":"admin","role":"readWrite","collection":""},' +
'{"db":"admin","role":"backup" },' +
'{"db":"admin","role":"clusterMonitor" },' +
'{"db":"admin","role":"restore" },' +
'{"db":"admin","role":"pbmAnyAction" }]});\'')
x509_pbm_user = ('\'db.getSiblingDB("$external").runCommand({createUser:"[email protected],CN=pbm,OU=client,O=Percona,L=SanFrancisco,ST=California,C=US","roles":[' +
'{"db":"admin","role":"readWrite","collection":""},' +
'{"db":"admin","role":"backup" },' +
Expand All @@ -645,6 +665,8 @@ def setup_authorization(host,uri):
'{"db":"admin","role":"pbmAnyAction" }]});\'')
logs = primary.check_output(
"mongo -u root -p root --quiet --eval " + init_pbm_user)
logs = primary.check_output(
"mongo -u root -p root --quiet --eval " + init_pbm_t_user)
#Cluster.log(logs)
if "authMechanism=MONGODB-X509" in uri:
logs = primary.check_output(
Expand Down
175 changes: 175 additions & 0 deletions pbm-functional/pytest/test_user_roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import pytest
import pymongo
import bson
import testinfra
import time
import os
import docker
import threading

from datetime import datetime
from cluster import Cluster


@pytest.fixture(scope="package")
def docker_client():
return docker.from_env()


@pytest.fixture(scope="package")
def config():
return {
"mongos": "mongos",
"configserver": {
"_id": "rscfg",
"members": [{"host": "rscfg01"}, {"host": "rscfg02"}, {"host": "rscfg03"}],
},
"shards": [
{
"_id": "rs1",
"members": [{"host": "rs101"}, {"host": "rs102"}, {"host": "rs103"}],
},
{
"_id": "rs2",
"members": [{"host": "rs201"}, {"host": "rs202"}, {"host": "rs203"}],
},
],
}

@pytest.fixture(scope="package")
def pbm_mongodb_uri():
return 'mongodb://pbm_test:[email protected]:27017/?authSource=admin'

@pytest.fixture(scope="package")
def newcluster(config, pbm_mongodb_uri):
return Cluster(config, pbm_mongodb_uri=pbm_mongodb_uri)

@pytest.fixture(scope="package")
def cluster(config):
return Cluster(config)


@pytest.fixture(scope="function")
def start_cluster(cluster, newcluster, request):
try:
cluster.destroy()
newcluster.destroy()
os.chmod("/backups", 0o777)
os.system("rm -rf /backups/*")
cluster.create()
cluster.setup_pbm()
yield True

finally:
if request.config.getoption("--verbose"):
cluster.get_logs()
try:
cluster.destroy(cleanup_backups=True)
except Exception as e:
newcluster.destroy(cleanup_backups=True)


def check_user(client, db_name, username, expected_roles):
try:
db_query = client.db.command({"usersInfo": {"user": username, "db": db_name}})
if db_query.get("ok") == 1 and len(db_query.get("users", [])) > 0:
roles = {role['role'] for role in db_query['users'][0]['roles']}
return roles == expected_roles
else:
return False
except pymongo.errors.OperationFailure as e:
return False

@pytest.mark.parametrize('restore_type',['part_bck','full_bck_part_rst_wo_user','full_bck_part_rst_user','full_bck'])
@pytest.mark.timeout(350, func_only=True)
def test_logical_PBM_T216(start_cluster, cluster, newcluster, restore_type):
cluster.check_pbm_status()
client = pymongo.MongoClient(cluster.connection)
client_shard = pymongo.MongoClient("mongodb://root:root@rs101,rs102,rs103/?replicaSet=rs1")
client.admin.command({"enableSharding": "test_db1", "primaryShard": "rs1"})
client.admin.command({"enableSharding": "test_db2", "primaryShard": "rs2"})
client.admin.command("shardCollection", "test_db1.test_coll11", key={"_id": "hashed"})
client.admin.command('updateUser', 'pbm_test', pwd='pbmpass_test2')
client.admin.command('createUser', 'admin_random_user1', pwd='test123', roles=[{'role':'readWrite','db':'admin'}, 'userAdminAnyDatabase', 'clusterAdmin'])
client_shard.admin.command('createUser', 'admin_random_user2', pwd='test123', roles=[{'role':'readWrite','db':'admin'}, 'userAdminAnyDatabase', 'clusterAdmin'])
client.test_db1.command('createUser', 'test_random_user1', pwd='test123', roles=[{'role':'readWrite','db':'test_db1'}, {'role':'clusterManager','db':'admin'}])
client_shard.test_db1.command('createUser', 'test_random_user2', pwd='test123', roles=[{'role':'readWrite','db':'test_db1'}, {'role':'clusterManager','db':'admin'}])
for i in range(10):
client["test_db1"]["test_coll11"].insert_one({"key": i, "data": i})
client["test_db2"]["test_coll21"].insert_one({"key": i, "data": i})
backup_full = cluster.make_backup("logical")
backup_partial = cluster.make_backup("logical --ns=test_db1.*,test_db2.*")
cluster.enable_pitr(pitr_extra_args="--set pitr.oplogSpanMin=0.5")
client.admin.command('createUser', 'admin_random_user3', pwd='test123', roles=[{'role':'readWrite','db':'admin'}, 'userAdminAnyDatabase', 'clusterAdmin'])
client_shard.admin.command('createUser', 'admin_random_user4', pwd='test123', roles=[{'role':'readWrite','db':'admin'}, 'userAdminAnyDatabase', 'clusterAdmin'])
client.test_db1.command('createUser', 'test_random_user3', pwd='test123', roles=[{'role':'readWrite','db':'test_db1'}, {'role':'clusterManager','db':'admin'}])
client_shard.test_db1.command('createUser', 'test_random_user4', pwd='test123', roles=[{'role':'readWrite','db':'test_db1'}, {'role':'clusterManager','db':'admin'}])
for i in range(10):
client["test_db1"]["test_coll11"].insert_one({"key": i+10, "data": i+10})
client["test_db2"]["test_coll21"].insert_one({"key": i+10, "data": i+10})
time.sleep(5)
pitr = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
cluster.disable_pitr(pitr)
pitr = " --time=" + pitr
Cluster.log("Time for PITR is: " + pitr)
client.drop_database("test_db1")
client.drop_database("test_db2")
client.admin.command("dropUser", "admin_random_user1")
client_shard.admin.command("dropUser", "admin_random_user2")
client.admin.command("dropUser", "admin_random_user3")
client_shard.admin.command("dropUser", "admin_random_user4")
client.test_db1.command("dropUser", "test_random_user1")
client_shard.test_db1.command("dropUser", "test_random_user2")
client.test_db1.command("dropUser", "test_random_user3")
client_shard.test_db1.command("dropUser", "test_random_user4")

# restoring users and roles from selective backup is not supported
restore_commands = {
'part_bck': " --base-snapshot=" + backup_partial + pitr,
'full_bck_part_rst_wo_user': " --base-snapshot=" + backup_full + pitr + " --ns=test_db1.*,test_db2.*",
'full_bck_part_rst_user': " --base-snapshot=" + backup_full + pitr + " --ns=test_db1.*,test_db2.* --with-users-and-roles",
'full_bck': " --base-snapshot=" + backup_full + pitr
}

# re-create cluster with new PBM user for connection to check that restore and connection to DB are OK
# despite the same user with different password is present in backup
if restore_type == 'full_bck':
cluster.destroy()
newcluster.create()
newcluster.setup_pbm()
newcluster.check_pbm_status()
newcluster.make_restore(restore_commands.get(restore_type), check_pbm_status=True)
else:
cluster.make_restore(restore_commands.get(restore_type), check_pbm_status=True)

assert client["test_db1"]["test_coll11"].count_documents({}) == 20
assert client["test_db1"].command("collstats", "test_coll11").get("sharded", False)
assert client["test_db2"]["test_coll21"].count_documents({}) == 20
assert client["test_db2"].command("collstats", "test_coll21").get("sharded", True) is False

assert check_user(client, "admin", "admin_random_user1", {'readWrite', 'userAdminAnyDatabase', 'clusterAdmin'}) == \
(restore_type == 'full_bck'), \
f"Failed for {restore_type}: admin_random_user1 role mismatch"
assert check_user(client_shard, "admin", "admin_random_user2", {'readWrite', 'userAdminAnyDatabase', 'clusterAdmin'}) == \
(restore_type == 'full_bck'), \
f"Failed for {restore_type}: admin_random_user2 role mismatch"
assert check_user(client, "admin", "admin_random_user3", {'readWrite', 'userAdminAnyDatabase', 'clusterAdmin'}) == \
(restore_type == 'full_bck'), \
f"Failed for {restore_type}: admin_random_user3 role mismatch"
assert check_user(client_shard, "admin", "admin_random_user4", {'readWrite', 'userAdminAnyDatabase', 'clusterAdmin'}) == \
(restore_type == 'full_bck'), \
f"Failed for {restore_type}: admin_random_user4 role mismatch"
assert check_user(client, "test_db1", "test_random_user1", {'readWrite', 'clusterManager'}) == (restore_type not in \
['part_bck','full_bck_part_rst_wo_user']), \
f"Failed for {restore_type}: test_random_user1 role mismatch"
assert check_user(client_shard, "test_db1", "test_random_user2", {'readWrite', 'clusterManager'}) == (restore_type not in \
['part_bck','full_bck_part_rst_wo_user']), \
f"Failed for {restore_type}: test_random_user2 role mismatch"
# current limitation: option with-users-and-roles doesn't work with PITR
assert check_user(client, "test_db1", "test_random_user3", {'readWrite', 'clusterManager'}) == (restore_type not in \
['part_bck','full_bck_part_rst_wo_user', 'full_bck_part_rst_user']), \
f"Failed for {restore_type}: test_random_user3 role mismatch"
assert check_user(client_shard, "test_db1", "test_random_user4", {'readWrite', 'clusterManager'}) == (restore_type not in \
['part_bck','full_bck_part_rst_wo_user', 'full_bck_part_rst_user']), \
f"Failed for {restore_type}: test_random_user4 role mismatch"
Cluster.log("Finished successfully")
Loading