import allure import pytest from frostfs_testlib import reporter from frostfs_testlib.resources.common import EXPIRATION_EPOCH_ATTRIBUTE from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND from frostfs_testlib.storage.controllers.cluster_state_controller import ClusterStateController from frostfs_testlib.storage.controllers.state_managers.config_state_manager import ConfigStateManager from frostfs_testlib.storage.dataclasses.frostfs_services import StorageNode from frostfs_testlib.storage.grpc_operations.interfaces import GrpcClientWrapper from frostfs_testlib.testing.cluster_test_base import ClusterTestBase from frostfs_testlib.utils.file_utils import TestFile @pytest.mark.nightly @pytest.mark.grpc_api class TestObjectTombstone(ClusterTestBase): @pytest.fixture() @allure.title("Change tombstone lifetime") def tombstone_lifetime(self, cluster_state_controller: ClusterStateController, request: pytest.FixtureRequest): config_manager = cluster_state_controller.manager(ConfigStateManager) config_manager.set_on_all_nodes(StorageNode, {"object:delete:tombstone_lifetime": request.param}, True) yield f"Tombstone lifetime was changed to {request.param}" config_manager.revert_all(True) @pytest.mark.parametrize("object_size, tombstone_lifetime", [("simple", 2)], indirect=True) @allure.title("Tombstone object should be removed after expiration") def test_tombstone_lifetime( self, new_epoch: int, container: str, grpc_client: GrpcClientWrapper, test_file: TestFile, rpc_endpoint: str, tombstone_lifetime: str, ): allure.dynamic.description(tombstone_lifetime) with reporter.step("Put object"): oid = grpc_client.object.put(test_file.path, container, rpc_endpoint) with reporter.step("Remove object"): tombstone_oid = grpc_client.object.delete(container, oid, rpc_endpoint) with reporter.step("Get tombstone object lifetime"): tombstone_info = grpc_client.object.head(container, tombstone_oid, rpc_endpoint) tombstone_expiration_epoch = tombstone_info["header"]["attributes"][EXPIRATION_EPOCH_ATTRIBUTE] with reporter.step("Tombstone lifetime should be <= 3"): epochs_to_skip = int(tombstone_expiration_epoch) - new_epoch + 1 assert epochs_to_skip <= 3 with reporter.step("Wait for tombstone expiration"): self.tick_epochs(epochs_to_skip) with reporter.step("Tombstone should be removed after expiration"): with pytest.raises(RuntimeError, match=OBJECT_NOT_FOUND): grpc_client.object.head(container, tombstone_oid, rpc_endpoint) with pytest.raises(RuntimeError, match=OBJECT_NOT_FOUND): grpc_client.object.get(container, tombstone_oid, rpc_endpoint)