Add range tests for container and non-container node endpoints #249
3 changed files with 114 additions and 11 deletions
pytest_tests/testsuites
|
@ -12,9 +12,7 @@ from frostfs_testlib.steps.cli.object import put_object_to_random_node
|
||||||
from frostfs_testlib.storage.cluster import Cluster
|
from frostfs_testlib.storage.cluster import Cluster
|
||||||
from frostfs_testlib.storage.dataclasses.acl import EACLRole
|
from frostfs_testlib.storage.dataclasses.acl import EACLRole
|
||||||
from frostfs_testlib.storage.dataclasses.frostfs_services import InnerRing, StorageNode
|
from frostfs_testlib.storage.dataclasses.frostfs_services import InnerRing, StorageNode
|
||||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
|
||||||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||||
from frostfs_testlib.utils.file_utils import generate_file
|
|
||||||
|
|
||||||
OBJECT_COUNT = 5
|
OBJECT_COUNT = 5
|
||||||
|
|
||||||
|
@ -60,11 +58,6 @@ def wallets(default_wallet: WalletInfo, credentials_provider: CredentialsProvide
|
||||||
return wallets_collection
|
return wallets_collection
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def file_path(object_size: ObjectSize) -> str:
|
|
||||||
yield generate_file(object_size.value)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def eacl_container_with_objects(
|
def eacl_container_with_objects(
|
||||||
wallets: Wallets, client_shell: Shell, cluster: Cluster, file_path: str
|
wallets: Wallets, client_shell: Shell, cluster: Cluster, file_path: str
|
||||||
|
|
|
@ -37,6 +37,7 @@ from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||||
from frostfs_testlib.testing.parallel import parallel
|
from frostfs_testlib.testing.parallel import parallel
|
||||||
from frostfs_testlib.testing.test_control import wait_for_success
|
from frostfs_testlib.testing.test_control import wait_for_success
|
||||||
from frostfs_testlib.utils import env_utils, version_utils
|
from frostfs_testlib.utils import env_utils, version_utils
|
||||||
|
from frostfs_testlib.utils.file_utils import generate_file
|
||||||
|
|
||||||
from pytest_tests.resources.common import HOSTING_CONFIG_FILE, TEST_CYCLES_COUNT
|
from pytest_tests.resources.common import HOSTING_CONFIG_FILE, TEST_CYCLES_COUNT
|
||||||
|
|
||||||
|
@ -190,6 +191,11 @@ def simple_object_size(max_object_size: int) -> ObjectSize:
|
||||||
return ObjectSize("simple", size)
|
return ObjectSize("simple", size)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def file_path(object_size: ObjectSize) -> str:
|
||||||
|
yield generate_file(object_size.value)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def complex_object_size(max_object_size: int) -> ObjectSize:
|
def complex_object_size(max_object_size: int) -> ObjectSize:
|
||||||
size = max_object_size * int(COMPLEX_OBJECT_CHUNKS_COUNT) + int(COMPLEX_OBJECT_TAIL_SIZE)
|
size = max_object_size * int(COMPLEX_OBJECT_CHUNKS_COUNT) + int(COMPLEX_OBJECT_TAIL_SIZE)
|
||||||
|
|
|
@ -13,19 +13,20 @@ from frostfs_testlib.resources.error_patterns import (
|
||||||
OUT_OF_RANGE,
|
OUT_OF_RANGE,
|
||||||
)
|
)
|
||||||
from frostfs_testlib.shell import Shell
|
from frostfs_testlib.shell import Shell
|
||||||
from frostfs_testlib.steps.cli.container import create_container
|
from frostfs_testlib.steps.cli.container import create_container, search_nodes_with_container
|
||||||
from frostfs_testlib.steps.cli.object import (
|
from frostfs_testlib.steps.cli.object import (
|
||||||
get_object_from_random_node,
|
get_object_from_random_node,
|
||||||
get_range,
|
get_range,
|
||||||
get_range_hash,
|
get_range_hash,
|
||||||
head_object,
|
head_object,
|
||||||
|
put_object,
|
||||||
put_object_to_random_node,
|
put_object_to_random_node,
|
||||||
search_object,
|
search_object,
|
||||||
)
|
)
|
||||||
from frostfs_testlib.steps.complex_object_actions import get_complex_object_split_ranges
|
from frostfs_testlib.steps.complex_object_actions import get_complex_object_split_ranges
|
||||||
from frostfs_testlib.steps.storage_object import delete_objects
|
from frostfs_testlib.steps.storage_object import delete_objects
|
||||||
from frostfs_testlib.steps.storage_policy import get_complex_object_copies, get_simple_object_copies
|
from frostfs_testlib.steps.storage_policy import get_complex_object_copies, get_simple_object_copies
|
||||||
from frostfs_testlib.storage.cluster import Cluster
|
from frostfs_testlib.storage.cluster import Cluster, ClusterNode
|
||||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
||||||
from frostfs_testlib.storage.dataclasses.policy import PlacementPolicy
|
from frostfs_testlib.storage.dataclasses.policy import PlacementPolicy
|
||||||
from frostfs_testlib.storage.dataclasses.storage_object_info import StorageObjectInfo
|
from frostfs_testlib.storage.dataclasses.storage_object_info import StorageObjectInfo
|
||||||
|
@ -89,16 +90,45 @@ def generate_ranges(
|
||||||
return file_ranges_to_test
|
return file_ranges_to_test
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def common_container(default_wallet: WalletInfo, client_shell: Shell, cluster: Cluster) -> str:
|
||||||
|
rule = "REP 1 IN X CBF 1 SELECT 1 FROM * AS X"
|
||||||
|
with reporter.step(f"Create container with {rule} and put object"):
|
||||||
|
cid = create_container(default_wallet, client_shell, cluster.default_rpc_endpoint, rule)
|
||||||
|
|
||||||
|
return cid
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def container_nodes(
|
||||||
|
default_wallet: WalletInfo, client_shell: Shell, cluster: Cluster, common_container: str
|
||||||
|
) -> list[ClusterNode]:
|
||||||
|
return search_nodes_with_container(
|
||||||
|
default_wallet, common_container, client_shell, cluster.default_rpc_endpoint, cluster
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def non_container_nodes(cluster: Cluster, container_nodes: list[ClusterNode]) -> list[ClusterNode]:
|
||||||
|
return list(set(cluster.cluster_nodes) - set(container_nodes))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
# Scope session to upload/delete each files set only once
|
# Scope session to upload/delete each files set only once
|
||||||
scope="module"
|
scope="module"
|
||||||
)
|
)
|
||||||
def storage_objects(
|
def storage_objects(
|
||||||
default_wallet: WalletInfo, client_shell: Shell, cluster: Cluster, object_size: ObjectSize, placement_policy: PlacementPolicy
|
default_wallet: WalletInfo,
|
||||||
|
client_shell: Shell,
|
||||||
|
cluster: Cluster,
|
||||||
|
object_size: ObjectSize,
|
||||||
|
placement_policy: PlacementPolicy,
|
||||||
) -> list[StorageObjectInfo]:
|
) -> list[StorageObjectInfo]:
|
||||||
wallet = default_wallet
|
wallet = default_wallet
|
||||||
# Separate containers for complex/simple objects to avoid side-effects
|
# Separate containers for complex/simple objects to avoid side-effects
|
||||||
cid = create_container(wallet, shell=client_shell, rule=placement_policy.value, endpoint=cluster.default_rpc_endpoint)
|
cid = create_container(
|
||||||
|
wallet, shell=client_shell, rule=placement_policy.value, endpoint=cluster.default_rpc_endpoint
|
||||||
|
)
|
||||||
|
|
||||||
file_path = generate_file(object_size.value)
|
file_path = generate_file(object_size.value)
|
||||||
file_hash = get_file_hash(file_path)
|
file_hash = get_file_hash(file_path)
|
||||||
|
@ -461,6 +491,80 @@ class TestObjectApi(ClusterTestBase):
|
||||||
range_cut=range_cut,
|
range_cut=range_cut,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@allure.title("Get range from container and non-container nodes (object_size={object_size})")
|
||||||
|
def test_get_range_from_different_node(
|
||||||
|
self,
|
||||||
|
default_wallet: str,
|
||||||
|
common_container: str,
|
||||||
|
container_nodes: list[ClusterNode],
|
||||||
|
non_container_nodes: list[ClusterNode],
|
||||||
|
file_path: str,
|
||||||
|
):
|
||||||
|
|
||||||
|
with reporter.step("Put object to container"):
|
||||||
|
container_node = random.choice(container_nodes)
|
||||||
|
oid = put_object(
|
||||||
|
default_wallet, file_path, common_container, self.shell, container_node.storage_node.get_rpc_endpoint()
|
||||||
|
)
|
||||||
|
|
||||||
|
with reporter.step("Get range from container node endpoint"):
|
||||||
|
get_range(
|
||||||
|
default_wallet,
|
||||||
|
common_container,
|
||||||
|
oid,
|
||||||
|
"0:10",
|
||||||
|
self.shell,
|
||||||
|
container_node.storage_node.get_rpc_endpoint(),
|
||||||
|
)
|
||||||
|
|
||||||
|
with reporter.step("Get range from non-container node endpoint"):
|
||||||
|
non_container_node = random.choice(non_container_nodes)
|
||||||
|
get_range(
|
||||||
|
default_wallet,
|
||||||
|
common_container,
|
||||||
|
oid,
|
||||||
|
"0:10",
|
||||||
|
self.shell,
|
||||||
|
non_container_node.storage_node.get_rpc_endpoint(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@allure.title("Get range hash from container and non-container nodes (object_size={object_size})")
|
||||||
|
def test_get_range_hash_from_different_node(
|
||||||
|
self,
|
||||||
|
default_wallet: str,
|
||||||
|
common_container: str,
|
||||||
|
container_nodes: list[ClusterNode],
|
||||||
|
non_container_nodes: list[ClusterNode],
|
||||||
|
file_path: str,
|
||||||
|
):
|
||||||
|
|
||||||
|
with reporter.step("Put object to container"):
|
||||||
|
container_node = random.choice(container_nodes)
|
||||||
|
oid = put_object(
|
||||||
|
default_wallet, file_path, common_container, self.shell, container_node.storage_node.get_rpc_endpoint()
|
||||||
|
)
|
||||||
|
|
||||||
|
with reporter.step("Get range hash from container node endpoint"):
|
||||||
|
get_range_hash(
|
||||||
|
default_wallet,
|
||||||
|
common_container,
|
||||||
|
oid,
|
||||||
|
"0:10",
|
||||||
|
self.shell,
|
||||||
|
container_node.storage_node.get_rpc_endpoint(),
|
||||||
|
)
|
||||||
|
|
||||||
|
with reporter.step("Get range hash from non-container node endpoint"):
|
||||||
|
non_container_node = random.choice(non_container_nodes)
|
||||||
|
get_range_hash(
|
||||||
|
default_wallet,
|
||||||
|
common_container,
|
||||||
|
oid,
|
||||||
|
"0:10",
|
||||||
|
self.shell,
|
||||||
|
non_container_node.storage_node.get_rpc_endpoint(),
|
||||||
|
)
|
||||||
|
|
||||||
def check_header_is_presented(self, head_info: dict, object_header: dict) -> None:
|
def check_header_is_presented(self, head_info: dict, object_header: dict) -> None:
|
||||||
for key_to_check, val_to_check in object_header.items():
|
for key_to_check, val_to_check in object_header.items():
|
||||||
assert key_to_check in head_info["header"]["attributes"], f"Key {key_to_check} is found in {head_object}"
|
assert key_to_check in head_info["header"]["attributes"], f"Key {key_to_check} is found in {head_object}"
|
||||||
|
|
Loading…
Reference in a new issue